1 /** 2 * It manages all the Connectors on a diagram 3 * 4 * @constructor 5 * @this {ConnectorManager} 6 **/ 7 function ConnectorManager(){ 8 /**An {Array} of {Connector}s. Keeps all Connectors from canvas*/ 9 this.connectors = []; 10 11 /**An {Array} of {ConnectionPoint}s. Keeps all ConnectionPoints from canvas*/ 12 this.connectionPoints = []; 13 14 /**Used to generate unique IDs for ConnectionPoint*/ 15 this.connectionPointCurrentId = 0; // 16 17 /**An {Array} of {Glue}s. Keeps all Glues from canvas*/ 18 this.glues = []; 19 20 /** Tells in what mode are we: 21 * 0 = disabled 22 * 1 = choosing first location (creation) 23 * 2 = choosing second location (creation) 24 * 3 = dragging connector 25 */ 26 this.connectionMode = ConnectorManager.MODE_DISABLED; 27 } 28 29 30 /**Creates a {ConnectorManager} out of JSON parsed object 31 *@param {JSONObject} o - the JSON parsed object 32 *@return {ConnectorManager} a newly constructed ConnectorManager 33 *@author Alex Gheorghiu <alex@scriptoid.com> 34 **/ 35 ConnectorManager.load = function(o){ 36 var newConnectionManager = new ConnectorManager(); //empty constructor 37 38 var localLog = ''; 39 40 //1 - load connectors 41 localLog += '\nCONNECTORS'; 42 newConnectionManager.connectors = Connector.loadArray(o.connectors); 43 // newConnectionManager.connectorSelectedIndex = o.connectorSelectedIndex; 44 for(var i=0;i<newConnectionManager.connectors.length;i++){ 45 localLog += '\n' + newConnectionManager.connectors[i].toString(); 46 } 47 48 //2 - load connection points 49 localLog += '\nCONNECTION POINTS'; 50 newConnectionManager.connectionPoints = ConnectionPoint.loadArray(o.connectionPoints); 51 for(var i=0;i<newConnectionManager.connectionPoints.length; i++){ 52 localLog += "\n" + newConnectionManager.connectionPoints[i].toString(); 53 } 54 //alert(str); 55 56 // newConnectionManager.connectionPointSelectedIndex = o.connectionPointSelectedIndex; 57 newConnectionManager.connectionPointCurrentId = o.connectionPointCurrentId; 58 59 60 //3 - load glues 61 localLog += '\nGLUES'; 62 newConnectionManager.glues = Glue.loadArray(o.glues); 63 64 //localLog += 'Connection manager has ' + newConnectionManager.glues.length + " glues"; 65 for(var i=0;i<newConnectionManager.glues.length; i++){ 66 localLog += "\n" + newConnectionManager.glues[i].toString(); 67 } 68 69 //alert(localLog); 70 71 newConnectionManager.connectionMode = o.connectionMode; 72 73 return newConnectionManager ; 74 } 75 76 ConnectorManager.prototype = { 77 78 /** 79 *Performs a deeps equals 80 *@param {ConnectorManager} anotherConnectionManager - the other object to compare against 81 *@author Alex Gheorghiu <alex@scriptoid.com> 82 **/ 83 equals : function(anotherConnectionManager){ 84 if(!anotherConnectionManager instanceof ConnectorManager){ 85 return false; 86 } 87 88 //test Connectors 89 for(var i=0; i<this.connectors.length; i++){ 90 if(!this.connectors[i].equals(anotherConnectionManager.connectors[i])){ 91 return false; 92 } 93 } 94 95 //test ConnectionPoints 96 for(var i=0; i<this.connectionPoints.length; i++){ 97 if(!this.connectionPoints[i].equals(anotherConnectionManager.connectionPoints[i])){ 98 return false; 99 } 100 } 101 102 //test Glues 103 for(var i=0; i<this.glues.length; i++){ 104 if(!this.glues[i].equals(anotherConnectionManager.glues[i])){ 105 return false; 106 } 107 } 108 109 110 111 return this.connectionPointCurrentId == anotherConnectionManager.connectionPointCurrentId 112 && this.connectionMode == anotherConnectionManager.connectionMode; 113 }, 114 115 116 /**Export the entire ConnectionManager to SVG format*/ 117 toSVG : function(){ 118 var r = ''; 119 120 for(var i=0; i<this.connectors.length; i++){ 121 r += this.connectors[i].toSVG(); 122 } 123 124 return r; 125 }, 126 127 /****************************************************************/ 128 /**************************Connector*****************************/ 129 /****************************************************************/ 130 131 /** 132 * Creates a new connector. Also store the connector into the pool of connectors. 133 * 134 * @param {Point} startPoint 135 * @param {Point} endPoint 136 * @param {String} type of Connector. It can be either 'straight' or 'jagged' 137 * @return {Number} the id of the newly created Connector 138 */ 139 connectorCreate:function(startPoint,endPoint,type){ 140 //get a new id for Connector 141 var id = stack.currentId++; 142 143 //create and save connector 144 this.connectors.push(new Connector(startPoint,endPoint,type, id)); 145 146 //create ConnectionPoints for Connector 147 this.connectionPointCreate(id, startPoint, ConnectionPoint.TYPE_CONNECTOR); 148 this.connectionPointCreate(id, endPoint, ConnectionPoint.TYPE_CONNECTOR); 149 150 //log 151 var c = this.connectorGetById(id); 152 //alert('log:connectorCreate: connector has ' + c.turningPoints.length); 153 154 return id; 155 }, 156 157 /** 158 * Remove a connector 159 * 160 * @param {Connector} connector - the connector you want to remove 161 */ 162 connectorRemove:function(connector){ 163 for(var i=0; i<this.connectors.length; i++){ 164 if(this.connectors[i] == connector){ 165 this.connectors.splice(i,1); 166 this.glueRemoveAllBySecondId(this.connectionPointGetAllByParent(connector.id)[0].id); 167 this.glueRemoveAllBySecondId(this.connectionPointGetAllByParent(connector.id)[1].id); 168 this.connectionPointRemoveAllByParent(connector.id); 169 if(this.connectorSelectedIndex == i){ 170 this.connectorSelectedIndex =-1; 171 } 172 break; 173 }//end if 174 }//end for 175 }, 176 177 178 /** 179 *Remove a connector by Id 180 *@param {Number} connectorId - the {Connector}'s id 181 */ 182 connectorRemoveById:function(connectorId){ 183 for(var i=0; i<this.connectors.length; i++){ 184 if(this.connectors[i].id == connectorId){ 185 this.connectorRemove(this.connectors[i]); 186 break; 187 }//end if 188 }//end for 189 }, 190 191 192 /** 193 *Remove a connector by Id and also it will remove the 194 *ConnectionPoints and Glues 195 *@param {Number} connectorId - the {Connector}'s id 196 */ 197 connectorRemoveByIdCascade:function(connectorId){ 198 var cCPs = this.connectionPointGetAllByParent(connectorId); //Connector's ConnectionPoints 199 200 //remove all Glues 201 for(var i=0;i<cCPs.length; i++){ 202 this.glueRemoveAllBySecondId(cCPs[i].id); 203 } 204 205 //remove connection points 206 this.connectionPointRemoveAllByParent(connectorId); 207 208 //remove the connector 209 this.connectorRemoveById(connectorId); 210 }, 211 212 213 214 /**Paints all connectors and highlight the selected one 215 *@param {Context} context - a reference to HTML5's canvas 216 *@param {Number} highlightedConnectorId - the id of the highlighted connector 217 *@author Zack 218 *@author Alex 219 *TODO: maybe all painting should not be made in managers 220 **/ 221 connectorPaint:function(context, highlightedConnectorId){ 222 for(var i=0; i<this.connectors.length; i++){ 223 //PAINT A CONNECTOR 224 //detect if we have a currenly selected Connector and "highlight" its ConnectionPoints 225 if(this.connectors[i].id == highlightedConnectorId){ 226 //paint a lime line underneath the connector 227 context.save(); 228 if(this.connectors[i].style.lineWidth == null){ 229 this.connectors[i].style.lineWidth = 1; 230 } 231 var oldStyle = this.connectors[i].style.clone(); 232 var oldWidth=this.connectors[i].style.lineWidth; 233 this.connectors[i].style = new Style(); 234 this.connectors[i].style.lineWidth = parseInt(oldWidth)+2; 235 this.connectors[i].style.strokeStyle = "lime"; 236 this.connectors[i].paint(context); 237 this.connectors[i].style = oldStyle; 238 context.restore(); 239 this.connectionPointPaint(context, this.connectors[i].id) 240 } 241 242 //paint the connector 243 context.save(); 244 this.connectors[i].paint(context); 245 context.restore(); 246 247 //(if selected) activate the handlers for the connector 248 if(i == this.connectorSelectedIndex){ 249 HandleManager.figureSet(this.connectors[i]); 250 HandleManager.paint(context); 251 } 252 } 253 }, 254 255 /** 256 * Disconnects all Connectors (actually one) that have a ConnectionPoint 257 * @param {Number} conectionPointId - connectionPoint to disconnect 258 * should only ever be called using the conPoint of a Connector, but could work either way 259 */ 260 connectorDisconnect:function(conectionPointId){ 261 if(this.connectionPointHasGlues(conectionPointId)){ 262 this.glueRemove(conectionPointId); 263 } 264 }, 265 266 267 /** Simplifies the connection of 2 ConnectionPoints 268 * @param {ConnectionPoint} connectionPoint1 - connectionPoint on a figure/connector 269 * @param {ConnectionPoint} connectionPoint2 - connectionPoint on a figure/connector 270 * 271 * TODO: Is it mandatory that one ConnectionPoint is from a figure an the another one from a Connector? 272 */ 273 connectorConnect:function(connectionPoint1, connectionPoint2){ 274 //connect the two figures, they were highlighted, so now unhighlight them 275 connectionPoint1.color = ConnectionPoint.CONNECTED_COLOR; 276 connectionPoint2.color = ConnectionPoint.CONNECTED_COLOR; 277 278 //select the figure we jusfigurest connected to 279 var figurePoint; 280 var nonFigurePoint; 281 282 //find out which one is from a connector 283 //set the connectors point to the figures point, has a "snap to" effect 284 if(connectionPoint1.type == ConnectionPoint.TYPE_FIGURE){ 285 figurePoint = connectionPoint1; 286 nonFigurePoint = connectionPoint2; 287 } 288 else{ 289 figurePoint = connectionPoint2; 290 nonFigurePoint = connectionPoint1; 291 } 292 293 //make connector's point be identical with Figure's point 294 nonFigurePoint.point.x = figurePoint.point.x; 295 nonFigurePoint.point.y = figurePoint.point.y; 296 297 //are these connectionPoints already connected, if not connect them now? 298 if(!this.connectionPointIsConnected(nonFigurePoint.id,figurePoint.id)){ 299 this.glueCreate(nonFigurePoint.id,figurePoint.id); 300 } 301 302 //if we are now connected at two places, make the line jagged. 303 var connector = this.connectorGetById(nonFigurePoint.parentId); 304 305 if(connector != null){ 306 if(this.connectionPointHasGlues(this.connectionPointGetAllByParent(connector.id)[0].id) //is Connector's first ConnectionPoint glued 307 && this.connectionPointHasGlues(this.connectionPointGetAllByParent(connector.id)[1].id) //is Connector's second ConnectionPoint glued 308 && connector.type == Connector.TYPE_JAGGED) //is Connector's type jagged 309 { 310 //TODO: it seems that Connector does not have a parameter....!? 311 alert('Problem connector has ' + connector.turningPoints.length + " points"); 312 //connector.jagged(); 313 connector.jaggedReloaded(); 314 connector.redraw(); 315 } 316 } 317 318 }, 319 320 321 /** Selects a connector using x and y coordinates, same as figure and handle 322 * @param {Number} x - the x coordinate 323 * @param {Number} y - the y coordinate 324 */ 325 connectorSelectXY:function(x,y){ 326 //try to pick the new selected connector 327 this.connectorSelectedIndex = -1; 328 for(var i=0; i<this.connectors.length; i++){ 329 if(this.connectors[i].contains(x,y)){ 330 this.connectorSelectedIndex = i; 331 break; 332 } 333 } 334 }, 335 336 337 /** Returns the currently selected connector 338 * or null if none available 339 */ 340 connectorGetSelected:function(){ 341 if(this.connectorSelectedIndex!=-1){ 342 return this.connectors[this.connectorSelectedIndex]; 343 } 344 return null; 345 }, 346 347 348 /**Get a Connector by its id 349 *@param {Number} connectorId 350 *@return {Connetor} if founded or null if none finded 351 **/ 352 connectorGetById:function(connectorId){ 353 for(var i=0; i<this.connectors.length; i++){ 354 if(this.connectors[i].id == connectorId){ 355 return this.connectors[i]; 356 } 357 } 358 return null; 359 }, 360 361 362 /**Returns the id of the connector the mouse is over. 363 *It actually return the first connector we found. 364 *@param {Number} x - the x coord 365 *@param {Number} y - the y coord 366 *@return {Number} - the id of the connector or -1 if no connector found 367 * 368 *TODO: Note: We are picking the Connector the most far from user as if 369 *we iterate from 0 to connectors.lenght we are going from back to front, similar 370 *to painting 371 */ 372 connectorGetByXY:function(x,y){ 373 var id = -1; 374 for(var i=0; i<this.connectors.length; i++){ 375 if(this.connectors[i].contains(x,y)){ 376 id = this.connectors[i].id; 377 break; 378 } 379 } 380 return id; 381 }, 382 383 384 /**Adjust a connector by a ConnectionPoint id 385 * The ConnectionPoint should be already changed/transformed 386 * Usually the connector changed it's start or end ConnectionPoint and now we need to update it's turning 387 * points (intermediate points) 388 *@param {Number} cpId - the id of the {ConnectionPoint} of the {Connector} 389 *@param {Number} x - not used 390 *@param {Number} y - not used 391 **/ 392 connectorAdjustByConnectionPoint: function(cpId, x, y){ 393 var cp = this.connectionPointGetById(cpId); //ConnectionPoint 394 Log.debug("connectorAdjustByConnectionPoint() - Cp is :" + cp); 395 var con = this.connectorGetById(cp.parentId); //Connector 396 var conCps = this.connectionPointGetAllByParent(con.id); //Conector's ConnectionPoints 397 398 if(con.type == Connector.TYPE_STRAIGHT){ 399 /**For STRAIGHT is very simple just update the tunrning points to the start and end connection points*/ 400 var start = conCps[0].point.clone(); 401 var end = conCps[1].point.clone(); 402 con.turningPoints = [start, end]; 403 } 404 else if(con.type == Connector.TYPE_JAGGED){ 405 var ESCAPE_DISTANCE = 20; 406 // //update connection points 407 // if(conCps[0].id == cpId){ //start point about to change 408 // conCps[0].point.x = x; 409 // conCps[0].point.y = y; 410 // } else if(conCps[1].id == cpId){ //end point about to change 411 // conCps[1].point.x = x; 412 // conCps[1].point.y = y; 413 // } else{ 414 // throw Exception("ConnectorManager:connectorAdjustByConnectionPoint() - this should not happen"); 415 // } 416 417 //update turning points 418 var start = conCps[0].point.clone(); 419 var startFigure = null; 420 var startExitPoint = null; 421 var end = conCps[1].point.clone(); 422 var endFigure = null; 423 var endExitPoint = null; 424 425 426 //START FIGURE - Do we have one? 427 var glue = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(conCps[0].id)[0];//the (only) Glue tied to ConnectionPoint 428 if(glue != null){ //only if there is a Figure glued 429 /**To find the exit point we will go N, E, S and W and move away from the Figure's 430 *bound by 10 pixels. 431 *Then we will pick the shortest distance from start to startExitPoint*/ 432 var fCp = this.connectionPointGetById(glue.id1); 433 startFigure = stack.figureGetById(fCp.parentId); 434 var fb = startFigure.getBounds(); 435 var tempExit = null; 436 437 //north 438 tempExit = new Point(start.x, fb[1] - FIGURE_ESCAPE_DISTANCE); 439 startExitPoint = tempExit; 440 441 //east 442 tempExit = new Point(fb[2] + FIGURE_ESCAPE_DISTANCE, start.y); 443 if(Util.distance(start, tempExit) < Util.distance(start, startExitPoint)){ 444 startExitPoint = tempExit; 445 } 446 447 //south 448 tempExit = new Point(start.x, fb[3] + FIGURE_ESCAPE_DISTANCE); 449 if(Util.distance(start, tempExit) < Util.distance(start, startExitPoint)){ 450 startExitPoint = tempExit; 451 } 452 453 //west 454 tempExit = new Point(fb[0] - FIGURE_ESCAPE_DISTANCE, start.y); 455 if(Util.distance(start, tempExit) < Util.distance(start, startExitPoint)){ 456 startExitPoint = tempExit; 457 } 458 } 459 460 //END FIGURE 461 var glues = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(conCps[1].id); 462 if(glues.length > 0){ //only if there is a Figure glued 463 glue = glues[0];//there will only be one for this 464 Log.debug("connectorAdjustByConnectionPoint() - glue is " + glue); 465 466 /*To find the exit point we will go N, E, S and W and move away from the Figure's 467 *bound by 10 pixels. 468 *Then we will pick the shortest distance from start to startExitPoint*/ 469 fCp = this.connectionPointGetById(glue.id1); 470 endFigure = stack.figureGetById(fCp.parentId); 471 Log.debug("connectorAdjustByConnectionPoint() - figure is " + endFigure); 472 fb = endFigure.getBounds(); 473 tempExit = null; 474 475 //north 476 tempExit = new Point(end.x, fb[1] - FIGURE_ESCAPE_DISTANCE); 477 endExitPoint = tempExit; 478 479 //east 480 tempExit = new Point(fb[2] + FIGURE_ESCAPE_DISTANCE, end.y); 481 if(Util.distance(end, tempExit) < Util.distance(end, endExitPoint)){ 482 endExitPoint = tempExit; 483 } 484 485 //south 486 tempExit = new Point(end.x, fb[3] + FIGURE_ESCAPE_DISTANCE); 487 if(Util.distance(end, tempExit) < Util.distance(end, endExitPoint)){ 488 endExitPoint = tempExit; 489 } 490 491 //west 492 tempExit = new Point(fb[0] - FIGURE_ESCAPE_DISTANCE, end.y); 493 if(Util.distance(end, tempExit) < Util.distance(end, endExitPoint)){ 494 endExitPoint = tempExit; 495 } 496 } else{ 497 Log.debug("connectorAdjustByConnectionPoint() - There is NO figure at the end"); 498 } 499 500 501 var points = [start, end]; 502 var index = 0; //initially set to start 503 504 //add start exit point 505 if(startExitPoint != null){ 506 Log.debug("ConnectionManage:connectorAdjustByConnectionPoint() - startExitPoint: " + startExitPoint) 507 points.splice(index + 1,0, startExitPoint); 508 index++; 509 } 510 511 //add end exit point 512 if(endExitPoint != null){ 513 Log.debug("ConnectionManage:connectorAdjustByConnectionPoint() - endExitPoint: " + endExitPoint) 514 points.splice(index + 1, 0, endExitPoint); 515 //endIndex = endIndex + 1; 516 } 517 //Log.info('Index: ' + index); 518 con.turningPoints = points; 519 520 //NOW THINK :D 521 522 //S0 - solution 523 if(points[index].x == points[index+1].x || points[index].y == points[index+1].y){ 524 //we have the S0 solution we do not have to add any new turning point 525 //Log.info("S0 solution - no additional turning points"); 526 var intersection = false; 527 if(startFigure != null){ 528 if(Util.lineIntersectsRectangle(points[index], points[index+1], startFigure.getBounds())){ 529 intersection = true; 530 } 531 } 532 533 if(endFigure != null){ 534 if(Util.lineIntersectsRectangle(points[index], points[index+1], endFigure.getBounds())){ 535 intersection = true; 536 } 537 } 538 539 if(!intersection){ 540 //ok , there is no intersection so we have a solution 541 con.updateMiddleText(); 542 return; 543 } 544 } 545 546 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 547 var impure = []; //vector of possible solutions (valid connector that might intersect a figure) 548 549 //S1 - solution 550 var s1 = null; 551 552 var s1_1 = new Point(points[index+1].x, points[index].y); //first possible solution 553 var p1_1 = Point.cloneArray(points); 554 p1_1.splice(index+1, 0, s1_1); 555 if(this._validPath(p1_1)){ //if valid collect to impure solutions 556 impure.push(p1_1); 557 } 558 559 var s1_2 = new Point(points[index].x, points[index+1].y); //second possible solution 560 var p1_2 = Point.cloneArray(points); 561 p1_2.splice(index+1, 0, s1_2); 562 if(this._validPath(p1_2)){ //if valid collect to impure solutions 563 impure.push(p1_2); 564 } 565 566 var score1 = this._scorePath( p1_1, true ); 567 var score2 = this._scorePath( p1_2, true ); 568 569 //Log.info("Score 1_1: " + score1 + " score 2_1: " + score2); 570 571 if(score1 >= 0 && score1 >= score2){ 572 s1 = s1_1; 573 } 574 else if(score2 >= 0 && score2 >= score1){ 575 s1 = s1_2; 576 } 577 578 579 580 if(s1 != null){ 581 //Log.info("s1 : " + s1); 582 /**Test so that the connector will not intersect any of the figures*/ 583 var intersection = false; 584 var s1_points = Point.cloneArray(points); 585 s1_points.splice(index + 1, 0, s1); 586 //Log.info("s1_points has " + s1_points.length + " points"); 587 // 588 //we will try to avoid useless intersection tests 589 var startIndex = 1; 590 var endIndex = s1_points.length-2; 591 if(startFigure == null){ 592 startIndex = 0; 593 } 594 if(endFigure == null){ 595 endIndex = s1_points.length-1; 596 } 597 for(var p=startIndex; p < endIndex; p++){ 598 if(startFigure != null){ 599 if(Util.lineIntersectsRectangle(s1_points[p], s1_points[p+1], startFigure.getBounds())){ 600 intersection = true; 601 break; 602 } 603 } 604 605 if(endFigure != null){ 606 if(Util.lineIntersectsRectangle(s1_points[p], s1_points[p+1], endFigure.getBounds())){ 607 intersection = true; 608 break; 609 } 610 } 611 } 612 613 if(!intersection){ //we will consider we have a solution ONLY if there is no intersection 614 con.turningPoints = s1_points; 615 //Log.info("S1 : " + s1_points); 616 con.updateMiddleText(); 617 return; 618 } 619 620 //Log.info("con.turningPoints has " + con.turningPoints.length + " points"); 621 } 622 else{ 623 //Log.info("S2 here we came"); 624 } 625 626 627 //S2 - solution 628 var p2Pure = []; //vector of pure solutions (valid connectors that do not intersects any figure) 629 630 631 632 //I - variant 633 var s2_I_1 = new Point( (points[index].x + points[index+1].x) / 2, points[index].y); 634 var s2_I_2 = new Point( (points[index].x + points[index+1].x) / 2, points[index+1].y); 635 636 var p2_I = Point.cloneArray(points); 637 p2_I.splice(index + 1, 0, s2_I_1, s2_I_2); 638 639 //Log.info("p2_I: " + p2_I); 640 if(this._validPath(p2_I)){ 641 impure.push(p2_I); 642 var I_intersect = false; 643 for(var j=1;j<p2_I.length-2; j++){ 644 if( (startFigure != null && Util.lineIntersectsRectangle(p2_I[j], p2_I[j+1], startFigure.getBounds())) 645 || 646 (endFigure != null && Util.lineIntersectsRectangle(p2_I[j], p2_I[j+1], endFigure.getBounds())) 647 ) 648 { 649 I_intersect = true; 650 break; 651 } 652 } 653 if(!I_intersect){ 654 p2Pure.push(p2_I); 655 } 656 657 Log.debug("S2 I valid"); 658 } 659 else{ 660 Log.debug("S2 I not valid"); 661 } 662 663 664 //II - variant 665 var s2_II_1 = new Point( points[index].x, (points[index].y + points[index+1].y)/2 ); 666 var s2_II_2 = new Point( points[index+1].x, (points[index].y + points[index+1].y)/2); 667 668 var p2_II = Point.cloneArray(points); 669 p2_II.splice(index + 1, 0, s2_II_1, s2_II_2); 670 671 //Log.info("p2_II: " + p2_II); 672 if(this._validPath(p2_II)){ 673 impure.push(p2_II); 674 var II_intersect = false; 675 for(var j=1;j<p2_II.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5) 676 if( (startFigure != null && Util.lineIntersectsRectangle(p2_II[j], p2_II[j+1], startFigure.getBounds())) 677 || 678 (endFigure != null && Util.lineIntersectsRectangle(p2_II[j], p2_II[j+1], endFigure.getBounds())) 679 ) 680 { 681 II_intersect = true; 682 break; 683 } 684 } 685 if(!II_intersect){ 686 p2Pure.push(p2_II); 687 } 688 Log.debug("S2 II valid"); 689 } 690 else{ 691 Log.debug("S2 II not valid"); 692 } 693 694 695 //III - variant (east) 696 //find the amount (stored in delta) of pixels we need to move up so no intersection with a figure will be present 697 //!See: /documents/specs/connected_figures_deltas.jpg file 698 var eastExits = []; 699 if(startFigure != null){ 700 eastExits.push(startFigure.getBounds()[2] + 20); 701 } 702 703 if(endFigure != null){ 704 eastExits.push(endFigure.getBounds()[2] + 20); 705 } 706 707 if(eastExits.length != 0){ 708 var eastExit = Util.max(eastExits); //max east 709 710 var s2_III_1 = new Point(eastExit, points[index].y); 711 var s2_III_2 = new Point(eastExit, points[index+1].y); 712 713 var p2_III = Point.cloneArray(points); 714 p2_III.splice(index + 1, 0, s2_III_1, s2_III_2); 715 716 //Log.info("p2_III: " + p2_III); 717 if(this._validPath(p2_III)){ 718 impure.push(p2_III); 719 720 Log.debug("S2 III valid"); 721 722 var III_intersect = false; 723 for(var j=1;j<p2_III.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5) 724 if( (startFigure != null && Util.lineIntersectsRectangle(p2_III[j], p2_III[j+1], startFigure.getBounds())) 725 || 726 (endFigure != null && Util.lineIntersectsRectangle(p2_III[j], p2_III[j+1], endFigure.getBounds())) 727 ) 728 { 729 III_intersect = true; 730 break; 731 } 732 } 733 if(!III_intersect){ 734 p2Pure.push(p2_III); 735 } 736 } 737 else{ 738 Log.debug("S2 III not valid"); 739 } 740 } 741 742 743 //IV (North) 744 Log.debug("Start S2 IV solution"); 745 var northExits = []; 746 if(startFigure != null){ 747 northExits.push(startFigure.getBounds()[1] - 20); 748 } 749 750 if(endFigure != null){ 751 northExits.push(endFigure.getBounds()[1] - 20); 752 } 753 754 Log.debug("S2 IV test North exit points"); 755 if(northExits.length != 0){ 756 Log.debug("S2 IV we have North points"); 757 var northExit = Util.min(northExits); //northest point 758 759 var s2_IV_1 = new Point(points[index].x, northExit); 760 var s2_IV_2 = new Point(points[index+1].x, northExit); 761 762 var p2_IV = Point.cloneArray(points); 763 p2_IV.splice(index + 1, 0, s2_IV_1, s2_IV_2); 764 765 //Log.info("p2_IV: " + p2_IV); 766 if(this._validPath(p2_IV)){ 767 impure.push(p2_IV); 768 Log.debug("S2 IV valid"); 769 770 var IV_intersect = false; 771 for(var j=1;j<p2_IV.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5) 772 if( (startFigure != null && Util.lineIntersectsRectangle(p2_IV[j], p2_IV[j+1], startFigure.getBounds())) 773 || 774 (endFigure != null && Util.lineIntersectsRectangle(p2_IV[j], p2_IV[j+1], endFigure.getBounds())) 775 ) 776 { 777 IV_intersect = true; 778 break; 779 } 780 } 781 if(!IV_intersect){ 782 p2Pure.push(p2_IV); 783 Log.debug("S2 IV pure"); 784 } 785 else{ 786 Log.debug("S2 IV NOT pure"); 787 } 788 } 789 else{ 790 Log.debug("S2 IV not valid"); 791 } 792 } else{ 793 Log.debug("No North (S2 IV) points"); 794 } 795 796 797 798 //V (West) 799 var westExits = []; 800 if(startFigure != null){ 801 westExits.push(startFigure.getBounds()[0] - 20); 802 } 803 804 if(endFigure != null){ 805 eastExits.push(endFigure.getBounds()[0] - 20); 806 } 807 808 if(westExits.length != 0){ 809 var westExit = Util.min(westExits); //westest point 810 811 var s2_V_1 = new Point(westExit, points[index].y); 812 var s2_V_2 = new Point(westExit, points[index+1].y); 813 814 var p2_V = Point.cloneArray(points); 815 p2_V.splice(index + 1, 0, s2_V_1, s2_V_2); 816 817 //Log.info("p2_V: " + p2_V); 818 if(this._validPath(p2_V)){ 819 impure.push(p2_V); 820 Log.debug("S2 V valid"); 821 822 var V_intersect = false; 823 for(var j=1;j<p2_V.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5) 824 if( (startFigure != null && Util.lineIntersectsRectangle(p2_V[j], p2_V[j+1], startFigure.getBounds())) 825 || 826 (endFigure != null && Util.lineIntersectsRectangle(p2_V[j], p2_V[j+1], endFigure.getBounds())) 827 ) 828 { 829 V_intersect = true; 830 break; 831 } 832 } 833 if(!V_intersect){ 834 p2Pure.push(p2_V); 835 } 836 } 837 else{ 838 Log.debug("S2 V not valid"); 839 } 840 } 841 842 843 //VI (South) 844 //find the amount (stored in delta) of pixels we need to move up so no intersection with a figure will be present 845 var southExits = []; 846 if(startFigure != null){ 847 southExits.push(startFigure.getBounds()[3] + 20); 848 } 849 850 if(endFigure != null){ 851 southExits.push(endFigure.getBounds()[3] + 20); 852 } 853 854 if(southExits.length != 0){ 855 var southExit = Util.max(southExits); //southest point 856 857 var s2_VI_1 = new Point(points[index].x, southExit); 858 var s2_VI_2 = new Point(points[index+1].x, southExit); 859 860 var p2_VI = Point.cloneArray(points); 861 p2_VI.splice(index + 1, 0, s2_VI_1, s2_VI_2); 862 863 //Log.info("p2_VI: " + p2_VI); 864 if(this._validPath(p2_VI)){ 865 impure.push(p2_VI); 866 Log.debug("S2 VI valid"); 867 868 var VI_intersect = false; 869 for(var j=1;j<p2_VI.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5) 870 if( (startFigure != null && Util.lineIntersectsRectangle(p2_VI[j], p2_VI[j+1], startFigure.getBounds())) 871 || 872 (endFigure != null && Util.lineIntersectsRectangle(p2_VI[j], p2_VI[j+1], endFigure.getBounds())) 873 ) 874 { 875 VI_intersect = true; 876 break; 877 } 878 } 879 if(!VI_intersect){ 880 p2Pure.push(p2_VI); 881 } 882 } 883 else{ 884 Log.debug("S2 VI not valid"); 885 } 886 } 887 888 //sort solutions by their length 889 if(p2Pure.length > 1){ //sort only when we have at least 1 890 var sorted = false; 891 while(!sorted){ 892 sorted = true; 893 for(k=0;k<p2Pure.length-1; k++){ 894 if(Util.getPolylineLength(p2Pure[k]) > Util.getPolylineLength(p2Pure[k+1])){ 895 var temp = p2Pure[k]; 896 p2Pure[k] = p2Pure[k+1] 897 p2Pure[k + 1] = temp; 898 sorted = false; 899 } 900 } 901 } 902 } 903 904 Log.info("Pure solution : " + p2Pure.length + " Impure solutions : " + impure.length); 905 906 //pick a solution 907 if(p2Pure.length > 0){ 908 con.turningPoints = p2Pure[0]; 909 } 910 else{ 911 //TODO: maybe sort impure solutions based too ?! 912 con.turningPoints = impure[0]; 913 } 914 915 // //this is the solution 916 // con.turningPoints = points; 917 } 918 919 con.updateMiddleText(); 920 }, 921 922 /**Score a ortogonal path made out of Points 923 *Iterates over a set of points (minimum 3) 924 *For each 3 points if the 3rd one is after the 2nd 925 * on the same line we add +1 if the 3rd is up or down rlated to the 2nd we do not do anything 926 * and if the 3rd goes back we imediatelly retun -1 927 *@param {Array} v - an array of {Point}s 928 *@param {Boolean} smooth - if true the smoothest path will have a greater score, if false the most jagged 929 * will have a bigger score. 930 *@return {Number} - -1 if the path is wrong (goes back) or something >= 0 if is fine 931 * The bigger the number the smooth the path is 932 *@author Alex Gheorghiu <alex@scriptoid.com> 933 **/ 934 _scorePath:function(v, smooth){ 935 var n = v.length; 936 if(n < 3){ 937 return -1; 938 } 939 940 var score = 0; 941 for(var i=1;i<n-1;i++){ 942 if(v[i-1].x == v[i].x && v[i].x == v[i+1].x){ //on the same vertical 943 if(signum(v[i+1].y - v[i].y) == signum(v[i].y - v[i-1].y)){ //same direction 944 if(smooth){ 945 score++; 946 } 947 } 948 else{ //going back - no good 949 return -1; 950 } 951 } 952 else if(v[i-1].y == v[i].y && v[i].y == v[i+1].y){ //on the same horizontal 953 if(signum(v[i+1].x - v[i].x) == signum(v[i].x - v[i-1].x)){ //same direction 954 if(smooth){ 955 score++; 956 } 957 } 958 else{ //going back - no good 959 return -1; 960 } 961 } 962 else{ //not on same vertical nor horizontal 963 if(!smooth){ 964 score++; 965 } 966 //do nothing; is like adding 0 967 } 968 } 969 970 return score; 971 }, 972 973 974 /** 975 *Tests if a vector of points is a valid path (not going back) 976 *@param {Array} v - an {Array} of {Point}s 977 *@return {Boolean} - true if path is valid, false otherwise 978 *@author Alex <alex@scriptoid.com> 979 **/ 980 _validPath:function(v){ 981 var n = v.length; 982 if(n < 3){ 983 return false; 984 } 985 986 for(var i=1;i<n-1;i++){ 987 if(v[i-1].x == v[i].x && v[i].x == v[i+1].x){ //on the same vertical 988 if(signum(v[i+1].y - v[i].y) != signum(v[i].y - v[i-1].y)){ //going back 989 return false; 990 } 991 } 992 else if(v[i-1].y == v[i].y && v[i].y == v[i+1].y){ //on the same horizontal 993 if(signum(v[i+1].x - v[i].x) != signum(v[i].x - v[i-1].x)){ //going back 994 return false; 995 } 996 } 997 } 998 999 return true; 1000 }, 1001 1002 1003 /**Reset this ConnectionManager*/ 1004 reset:function(){ 1005 this.connectors = []; 1006 this.connectorSelectedIndex = -1; 1007 this.connectionPoints = []; 1008 this.connectionPointSelectedIndex = -1; 1009 this.connectionPointCurrentId = 0; 1010 }, 1011 1012 /** Fired from onMouseDown in index.html 1013 * @return {Boolean} - should we check for handle after this 1014 * @param {Number} x - the mouse coord 1015 * @param {Number} y - the mouse coord 1016 * @deprecated 1017 */ 1018 mouseDown_deprecated:function(x,y){ 1019 1020 //get the currently selected connection point and connector 1021 var activeConnector = this.connectorGetSelected(); 1022 1023 this.connectionPointSelectXY(x, y, ConnectionPoint.TYPE_CONNECTOR); 1024 var activeConnectionPoint = this.connectionPointGetSelected(); 1025 var overConnectionPoint; 1026 1027 1028 //if we have an active connector, then we want to get any point that is under that connector 1029 if(activeConnector != null){ 1030 overConnectionPoint = this.connectionPointOver(x, y, -activeConnector.id); 1031 if(this.connectionMode != ConnectorManager.MODE_SECOND){ //not choosing second location 1032 if(this.connectionPointOver(x,y,activeConnector.id) != null){ 1033 activeConnector.activeConnectionPointId = this.connectionPointOver(x,y,activeConnector.id).id; 1034 } 1035 else{ 1036 activeConnector.activeConnectionPointId=-1; 1037 } 1038 } 1039 } 1040 else{//we have no active connector, so just get connectionpoint of any figure 1041 overConnectionPoint = this.connectionPointOver(x, y); 1042 } 1043 1044 1045 if(this.connectionMode == ConnectorManager.MODE_FIRST){ //choosing first location upon creating connector 1046 var conId = this.connectorCreate(new Point(x,y),new Point(x,y), connectorType); 1047 1048 //select this last added connector (we know it's the last but somehow it's not quite right) 1049 //TODO: make the selection based on the conId 1050 this.connectorSelectedIndex = this.connectors.length-1; 1051 1052 //set the new connector as our active connector 1053 activeConnector = this.connectorGetSelected(); 1054 1055 //set the activeConnectionPointId to the id of the end connectionpoint 1056 activeConnector.activeConnectionPointId = this.connectionPointGetAllByParent(activeConnector.id)[1].id; 1057 1058 alert('connection manager:mouseDown: connector has ' + activeConnector.turningPoints.length + ' turning points'); 1059 //if we are over something when we create the connector, connect them 1060 if(overConnectionPoint){ 1061 this.connectorConnect(this.connectionPointGetAllByParent(activeConnector.id)[0], overConnectionPoint); 1062 } 1063 alert('connection manager 2:mouseDown: connector has ' + activeConnector.turningPoints.length + ' turning points'); 1064 setUpEditPanel(activeConnector); 1065 activeConnectionPoint = null; 1066 this.connectionMode = ConnectorManager.MODE_SECOND; 1067 } 1068 1069 //we have chosen our first point, now lets choose our second point 1070 1071 else if(activeConnector!=null){ 1072 drag = true; 1073 1074 //select the handle we are near (if any) 1075 HandleManager.handleSelectXY(x,y); 1076 1077 //make sure no figure is selected, it shouldnt be anyway 1078 stack.figureSelectedIndex=-1; 1079 1080 //if we are over a connection point, or we are not connected at all, we can move 1081 var con=this.connectionPointOver(x,y,activeConnector.id); 1082 //we are in move mode 1083 this.connectionMode=ConnectorManager.MODE_DRAG; 1084 } 1085 stack.figureSelectedIndex = -1; 1086 return true; 1087 }, 1088 1089 1090 /** Fired from onMouseUp in index.html when we know we are draggin a Connector 1091 * @param {Number} x - the mouse coord 1092 * @param {Number} y - the mouse coord 1093 * @deprecated 1094 */ 1095 mouseUp_deprecated:function(x,y){ 1096 1097 //get our active ConnectionPoint and Connector 1098 var activeConnector = this.connectorGetSelected(); 1099 var overConnectionPoint = this.connectionPointOverByType(x,y,ConnectionPoint.TYPE_FIGURE); 1100 var activeConnectionPoint = this.connectionPointGetSelected(); 1101 1102 //if we are moving and have moved away from our connection point, disconnect 1103 //we don't want to do this on move, as if the user changes their mind, it will redraw their connector 1104 //overwriting any changes to it. 1105 if(this.connectionMode == ConnectorManager.MODE_DRAG){ 1106 if(activeConnectionPoint!=null && (overConnectionPoint==null || (this.connectionPointHasGlues(activeConnectionPoint.id) && !this.connectionPointIsConnected(activeConnectionPoint.id,overConnectionPoint.id)))) 1107 { 1108 this.connectorDisconnect(activeConnectionPoint.id); 1109 } 1110 } 1111 else if(this.connectionMode == ConnectorManager.MODE_SECOND){ 1112 1113 //if we are over something, connect them, the connectionPoint is already at the mouse position 1114 if(overConnectionPoint && overConnectionPoint.type==ConnectionPoint.TYPE_FIGURE){ 1115 this.connectorConnect(overConnectionPoint, this.connectionPointGetAllByParent(activeConnector.id)[1]); 1116 activeConnectionPoint = null; 1117 } 1118 activeConnectionPoint=null; 1119 1120 //we have finished, so set our mode back to 0 1121 this.connectionMode = ConnectorManager.MODE_DISABLED; 1122 } 1123 1124 //if we have an overPoint, and we are moving, connect 1125 if(overConnectionPoint && this.connectionMode == ConnectorManager.MODE_DRAG){ 1126 //we are over a connection point, and we are not already connected to something, i.e. we have not moved away from our current point 1127 if(overConnectionPoint && activeConnectionPoint!=overConnectionPoint && activeConnector.activeConnectionPointId!=-1){ 1128 var glue = this.glueGetByConnectionPointId(activeConnector.activeConnectionPointId)[0]; 1129 if(!glue || (glue.id1!=overConnectionPoint.id && glue.id2!=overConnectionPoint.id)){ 1130 this.connectorConnect(this.connectionPointGet(activeConnector.activeConnectionPointId),overConnectionPoint); 1131 this.connectionPointSelectedIndex = -1; 1132 } 1133 } 1134 if (this.connectorGetMouseOver(x,y)!=activeConnector){ 1135 this.connectorSelectedIndex=-1; 1136 } 1137 } 1138 1139 //if we are not in "click" mode (i.e. when creating a new connector) then we are no longer in move mode 1140 if(this.connectionMode!=ConnectorManager.MODE_SECOND){ 1141 this.connectionMode=ConnectorManager.MODE_DISABLED; 1142 } 1143 }, 1144 1145 /** Fired from onMouseMove in index.html 1146 * @param {Number} x - the mouse coord 1147 * @param {Number} y - the mouse coord 1148 * @return {Boolean} - true if should we redraw after this action, false otherwise 1149 * @deprecated 1150 */ 1151 mouseMove_deprecated:function(x,y){ 1152 var redraw = false; 1153 1154 //get our active Connector and ConnectionPoints 1155 var activeConnectionPoint = this.connectionPointGetSelected(); 1156 var activeConnector = this.connectorGetSelected(); 1157 var overConnectionPoint; // ConnectionPoint we are over 1158 1159 //get the point under our mouse 1160 if(activeConnector){ 1161 overConnectionPoint = this.connectionPointOver(x,y,-activeConnector.id); 1162 } 1163 else{ 1164 overConnectionPoint = this.connectionPointOver(x,y); 1165 } 1166 1167 //we have at least made our first point of a new connector, or we are moving an existing one 1168 if(this.connectionMode != ConnectorManager.MODE_DISABLED){ 1169 1170 //we are over a connection point that is not connected, so highlight it 1171 if(overConnectionPoint && this.glueGetByConnectionPointId(overConnectionPoint.id).length==0){ 1172 overConnectionPoint.color = ConnectionPoint.OVER_COLOR; 1173 } 1174 redraw = true; 1175 } 1176 1177 //we are an already made/glued connector 1178 if(this.connectionMode == ConnectorManager.MODE_DRAG){ 1179 1180 //make the relevant matrix (should work with snap to also) 1181 var MOVE = generateMoveMatrix(activeConnector, x, y); 1182 1183 //we are dragging from a connectionpoint 1184 if(activeConnector.activeConnectionPointId!=-1){ 1185 activeConnector.adjust(MOVE, this.connectionPointGet(activeConnector.activeConnectionPointId).point); 1186 } 1187 1188 //we are moving from the middle, so only allow it if we have no connections 1189 else if(!this.connectionPointHasGlues(this.connectionPointGetAllByParent(activeConnector.id)[0].id)&& !this.connectionPointHasGlues(this.connectionPointGetAllByParent(activeConnector.id)[1].id)){ 1190 activeConnector.transform(MOVE); 1191 } 1192 redraw=true; 1193 } 1194 1195 //we have created our first point of a new connector, so move the end point with our mouse 1196 if(this.connectionMode==ConnectorManager.MODE_SECOND){ 1197 var endPoint=activeConnector.turningPoints[activeConnector.turningPoints.length-1]; 1198 1199 //doesnt always work properly 1200 //we are creating a jagged connector, so make sure it moves in right angled turns 1201 //this just creates a simple 1 turn connector, just while creating 1202 if(activeConnector.turningPoints.length==2 && activeConnector.type=='jagged'){ 1203 endPoint=activeConnector.turningPoints.pop(); 1204 activeConnector.turningPoints.push(new Point(endPoint.x,activeConnector.turningPoints[0].y)); 1205 activeConnector.turningPoints.push(endPoint); 1206 } 1207 activeConnector.adjust(Matrix.translationMatrix(x-endPoint.x,y-endPoint.y), endPoint) 1208 redraw = true; 1209 } 1210 return redraw; 1211 }, 1212 1213 /****************************************************************/ 1214 /**************************ConnectionPoint*****************************/ 1215 /****************************************************************/ 1216 1217 /**Returns the selected connection point 1218 *@return a {ConnectionPoint} that is stored at position selectedConnectionPointIndex 1219 *or null if selectedConnectionPointIndex is not set (equal to -1) 1220 **/ 1221 connectionPointGetSelected:function(){ 1222 if(this.connectionPointSelectedIndex == -1){ 1223 return null 1224 } 1225 return this.connectionPoints[this.connectionPointSelectedIndex]; 1226 }, 1227 1228 /** Returns a connection point id based on an x and y and the ConnectionPoint.RADIUS 1229 * It will pick the first one that matches the criteria 1230 *@param {Number} x - the x coordinates of the point 1231 *@param {Number} y - the y coordinates of the point 1232 *@param {Boolean} type - the type of connector to select. Can be 'connector'(ConnectionPoint.TYPE_CONNECTOR) 1233 * or 'figure' (ConnectionPoint.TYPE_FIGURE) 1234 *@author Alex Gheorghiu <alex@scriptoid.com> 1235 */ 1236 connectionPointGetByXY:function(x,y, type){ 1237 var id = -1; 1238 1239 for(var i=0; i<this.connectionPoints.length; i++){ 1240 if( this.connectionPoints[i].contains(x,y) && this.connectionPoints[i].type == type ){ 1241 id = this.connectionPoints[i].id; 1242 break; 1243 } 1244 } 1245 1246 return id; 1247 }, 1248 1249 /** Creates a new ConnectionPoint. Beside creating the ConnectionPoint it will also 1250 * inject the id and store the ConnectionPoint 1251 * 1252 * @param {Number} parentId - the id of the parent ( Figure or Connector )this ConnectionPoint will belong to 1253 * @param {Point} point - the location 1254 * @param {String} type - the type of parent. It can be 'figure' (ConnectionPoint.TYPE_FIGURE) 1255 * or 'connector' {ConnectionPoint.TYPE_CONNECTOR} 1256 * @return {ConnectionPoint} with a proper id set 1257 * 1258 * @author Alex Gheorghiu <alex@scriptoid.com> 1259 */ 1260 connectionPointCreate:function(parentId, point, type){ 1261 var conPoint = new ConnectionPoint(parentId, point.clone(), this.connectionPointCurrentId++, type); 1262 this.connectionPoints.push(conPoint); 1263 1264 return conPoint; 1265 }, 1266 1267 1268 /** Removes the connectionPoints associated with a parent (it can be either Figure or Connector) 1269 * @param {Number} parentId - the figure id 1270 * @author Alex Gheorghiu <alex@scriptoid.com> 1271 */ 1272 connectionPointRemoveAllByParent:function(parentId){ 1273 for(var i=0; i<this.connectionPoints.length; i++){ 1274 if(this.connectionPoints[i].parentId == parentId){ 1275 this.connectionPoints.splice(i,1); 1276 i--; 1277 } 1278 } 1279 }, 1280 1281 1282 /** Returns a ConnectionPoint based on its id 1283 * or null if none finded 1284 *@param {Number} connectionPointId - the id 1285 *@return {ConnectionPoint} 1286 */ 1287 connectionPointGetById:function(connectionPointId){ 1288 for(var i=0; i<this.connectionPoints.length; i++){ 1289 if(this.connectionPoints[i].id == connectionPointId){ 1290 return this.connectionPoints[i]; 1291 } 1292 } 1293 return null; 1294 }, 1295 1296 1297 /** Returns a ConnectionPoint based on its id 1298 * or null if none finded 1299 *@param {Number} parentId - the id of the parent 1300 *@param {Number} x - the x of the point 1301 *@param {Number} y - the y of the point 1302 *@return {ConnectionPoint} 1303 */ 1304 connectionPointGetByParentAndCoordinates:function(parentId, x, y){ 1305 for(var i=0; i<this.connectionPoints.length; i++){ 1306 if(this.connectionPoints[i].parentId == parentId 1307 && this.connectionPoints[i].point.x == x 1308 && this.connectionPoints[i].point.y == y 1309 1310 ) 1311 { 1312 return this.connectionPoints[i]; 1313 } 1314 } 1315 return null; 1316 }, 1317 1318 1319 /** Returns a subset of whole ConnectionPoints that belong to a figure or a connector 1320 *@param {Number} parentId - the figure or connector's id whom subset we want 1321 *@return {Array}{ConnectionPoint}s 1322 *@author Alex Gheorghiu <alex@scriptoid.com> 1323 */ 1324 connectionPointGetAllByParent:function(parentId){ 1325 var collectedPoints = []; 1326 1327 for(var connectionPoint in this.connectionPoints){ 1328 if(this.connectionPoints[connectionPoint].parentId == parentId){ 1329 collectedPoints.push(this.connectionPoints[connectionPoint]); 1330 } 1331 } 1332 return collectedPoints; 1333 }, 1334 1335 /**All ConnectionPoints we are over - but filtered by the parent type 1336 *@param {Number} x - the x coordinate 1337 *@param {Number} y - the x coordinate 1338 *@return {Array} of {ConnectionPoint}s 1339 *@author Alex Gheorghiu <alex@scriptoid.com> 1340 * 1341 *TODO: either documentation or the implementation is weird 1342 *@deprecated 1343 **/ 1344 connectionPointOverByType:function(x,y){ 1345 var collectedPoints = []; 1346 for(var i=0; i<this.connectionPoints.length; i++){ 1347 if( this.connectionPoints[i].contains(x,y)){ 1348 collectedPoints.push(this.connectionPoints[i]); 1349 } 1350 } 1351 return collectedPoints; 1352 }, 1353 1354 1355 /** Get the connectionPoint the mouse is over 1356 * @param {Number} x - coordinate 1357 * @param {Number} y - coordinate 1358 * @param {Number} parentFigureId - the subset to obtain (optional) 1359 * If the parentFigureId is null we will get extend our search to all the ConnectionPoints on the canvas 1360 * If the parentFigureId is positive we will limit our search to that shape (Figure or Connector) 1361 * If the parentFigureId is negative we will search on all Canvas but except that shape (Figure or Connector) 1362 * @return {ConnectionPoint} that fit the criteria or null if none present 1363 * @author Alex Gheorghiu <alex@scriptoid.com> 1364 */ 1365 connectionPointOver:function(x, y, parentFigureId){ 1366 var foundedConnectionPoint = null; 1367 1368 if(typeof(parentFigureId) == 'number'){ //we have a Figure id specified 1369 if(parentFigureId < 0 ){ //search whole canvas except a figure 1370 for(var canvasConnectionPoint in this.connectionPoints){ 1371 if(this.connectionPoints[canvasConnectionPoint].parentId != -parentFigureId && this.connectionPoints[canvasConnectionPoint].contains(x,y)){ 1372 this.connectionPoints[canvasConnectionPoint].color = ConnectionPoint.OVER_COLOR; 1373 foundedConnectionPoint = this.connectionPoints[canvasConnectionPoint]; 1374 } 1375 } 1376 } 1377 else{ //search only a figure 1378 var figureConnectionPoints = this.connectionPointGetAllByParent(parentFigureId); 1379 for(var figureConnectionPoint in figureConnectionPoints){ 1380 if(figureConnectionPoints[figureConnectionPoint].contains(x,y)){ 1381 figureConnectionPoints[figureConnectionPoint].color = ConnectionPoint.OVER_COLOR; 1382 foundedConnectionPoint = figureConnectionPoints[figureConnectionPoint]; 1383 } 1384 } 1385 } 1386 } 1387 else{ //search whole canvas 1388 for(var connectionPoint in this.connectionPoints){ 1389 if(this.connectionPoints[connectionPoint].contains(x,y)){ 1390 this.connectionPoints[connectionPoint].color = ConnectionPoint.OVER_COLOR; 1391 foundedConnectionPoint = this.connectionPoints[connectionPoint]; 1392 } 1393 } 1394 } 1395 1396 1397 return foundedConnectionPoint; 1398 }, 1399 1400 1401 /**Paints ALL ConnectionPoints that are attached to a shape (Figure or Connector) 1402 *@param {Context} context - the HTML5 canvas' context 1403 *@param {Number} parentFigureId - the the parent figure's ID 1404 */ 1405 connectionPointPaint:function(context, parentFigureId){ 1406 var figureConnectionPoints = this.connectionPointGetAllByParent(parentFigureId); 1407 1408 for(var conPoint in figureConnectionPoints){ 1409 figureConnectionPoints[conPoint].paint(context); 1410 } 1411 }, 1412 1413 1414 /** 1415 * Transform all ConnectionPoints of a Figure 1416 * @param {Number} fId - the the parent figure's ID 1417 * @param {Matrix} matrix - the transformation matrix 1418 **/ 1419 connectionPointTransform:function(fId, matrix){ 1420 var fCps = this.connectionPointGetAllByParent(fId); 1421 //Log.info("ConnectionManager: connectionPointTransform()....1"); 1422 //get all shape's connection points 1423 for(var i = 0 ; i < fCps.length; i++){ 1424 //transform figure's connection points (no go for the other side) 1425 fCps[i].transform(matrix); 1426 //Log.info("\tConnectionManager: connectionPointTransform()....2"); 1427 1428 //get all glues for current connection point 1429 var glues = this.glueGetByFirstConnectionPointId(fCps[i].id); 1430 //Log.info("\tConnectionManager: connectionPointTransform()" + fCps[i].id + " glues = " + glues.length); 1431 for(var j=0; j<glues.length; j++){ 1432 //get the ConnectionPoint from other side of the glue (from the connector) 1433 var conCp = this.connectionPointGetById( glues[j].id2); 1434 //Log.info("\t\tConnectionManager: connectionPointTransform() - connector's point " + conCp); 1435 conCp.transform(matrix); 1436 1437 //get attached connector 1438 // var con = this.connectorGetById(conCp.parentId); 1439 1440 //adjust attached Connector through the ConnectionPoint 1441 // con.adjust(matrix, conCp.point.clone()); 1442 this.connectorAdjustByConnectionPoint(glues[j].id2 /*, x, y*/) 1443 //Log.info("ConnectionManager: connectionPointTransform()..."); 1444 } 1445 } 1446 //alert("look ma!"); 1447 }, 1448 1449 1450 /** 1451 * See if two {ConnectionPoint}s are connected 1452 *@param id1 - a {ConnectionPoint}'s id 1453 *@param id2 - another {ConnectionPoint}'s id 1454 *@author Alex Gheorghiu <alex@scriptoid.com> 1455 **/ 1456 connectionPointIsConnected:function(id1,id2){ 1457 for (var i=0; i < this.glues.length; i++){ 1458 if( (id1 == this.glues[i].id1 && id2 == this.glues[i].id2) 1459 || (id1 == this.glues[i].id2 && id2 == this.glues[i].id1) ){ 1460 return true; 1461 } 1462 } 1463 return false; 1464 }, 1465 1466 /** 1467 *TODO: This function needs a serious review 1468 *as it seems is no longer used?!? 1469 *@deprecated 1470 *@author Zack*/ 1471 connectionPointUnhighlightAll:function(x,y){ 1472 for(var i=0; i<this.connectionPoints.length; i++){ 1473 var oppositeType = this.connectionPoints[i].type; 1474 if(oppositeType == ConnectionPoint.TYPE_FIGURE){ 1475 oppositeType = ConnectionPoint.TYPE_CONNECTOR 1476 } 1477 else{ 1478 oppositeType = ConnectionPoint.TYPE_FIGURE 1479 } 1480 1481 var found = false;//we use this like an else if, as we need internal if's to keep code clean 1482 // 1483 //if this is connected, and our point is ontop of its connected point 1484 if(this.connectionPointHasGlues(this.connectionPoints[i].id)){ 1485 var point = this.connectionPointOverByType(this.connectionPoints[i].point.x, this.connectionPoints[i].point.y, oppositeType); 1486 if(point && (point.id == this.glueGetByConnectionPointId(this.connectionPoints[i].id)[0].id1 || 1487 point.id == this.glueGetByConnectionPointId(this.connectionPoints[i].id)[0].id2)){ 1488 this.connectionPoints[i].color = ConnectionPoint.CONNECTED_COLOR; 1489 found = true; 1490 } 1491 1492 } 1493 1494 //we are either not connected, or are currently dragging a connected point away from its respective point, and are near another point 1495 if(!found && this.connectionPointOverByType(x, y, ConnectionPoint.TYPE_FIGURE)==this.connectionPoints[i]){ 1496 this.connectionPoints[i].color = ConnectionPoint.OVER_COLOR; 1497 } 1498 1499 //we are not near a point 1500 else if(!found){ 1501 this.connectionPoints[i].color = ConnectionPoint.NORMAL_COLOR; 1502 } 1503 } 1504 1505 //now make our active connetion point of our selected connector do the same 1506 if(this.connectionPointOverByType(x, y, ConnectionPoint.TYPE_FIGURE) && this.connectorGetSelected()){ 1507 if(this.connectorGetSelected().activeConnectionPointId!=-1){ 1508 var point = this.connectionPointOverByType(x, y, ConnectionPoint.TYPE_FIGURE); 1509 var glues = this.glueGetByConnectionPointId(this.connectorGetSelected().activeConnectionPointId) 1510 if(glues.length == 0 || (glues[0].id1 != point.id && glues[0].id2 != point.id)){ 1511 this.connectionPointGet(this.connectorGetSelected().activeConnectionPointId).color = ConnectionPoint.OVER_COLOR; 1512 } 1513 } 1514 } 1515 }, 1516 1517 /** 1518 *See if a {ConnectionPoint} has {Glue}s attached to it 1519 *@param {Number} conectionPointId - the {ConnectionPoint}'s id 1520 *@return true - if we have {Glue}s with it or false if not 1521 *@author Zack Newsham <zack_newsham@yahoo.co.uk> 1522 *@author Alex Gheorghiu <alex@scriptoid.com> 1523 **/ 1524 connectionPointHasGlues:function(conectionPointId){ 1525 for (var i=0; i<this.glues.length; i++){ 1526 if(conectionPointId == this.glues[i].id1 || conectionPointId == this.glues[i].id2){ 1527 return true; 1528 } 1529 } 1530 return false; 1531 }, 1532 1533 /****************************************************************/ 1534 /**************************Glue*****************************/ 1535 /****************************************************************/ 1536 1537 /** Returns all {Glue}s that have the first Id equals with a certain id value 1538 *@param {Number} pointId - {Figure}'s id 1539 *@return {Array}{Glue}s 1540 *@author Alex Gheorghiu <alex@scriptoid.com> 1541 */ 1542 glueGetByFirstConnectionPointId:function(pointId){ 1543 var collectedGlues = []; 1544 for(var i=0; i<this.glues.length; i++){ 1545 if(this.glues[i].id1 == pointId){ 1546 collectedGlues.push(this.glues[i]); 1547 } 1548 } 1549 return collectedGlues; 1550 }, 1551 1552 /** Returns all {Glue}s that have the second Id equals with a certain id value 1553 *@param {Number} pointId - {ConnectionPoint}'s id 1554 *@return {Array}{Glue}s 1555 *@author Alex Gheorghiu <alex@scriptoid.com> 1556 */ 1557 glueGetBySecondConnectionPointId:function(pointId){ 1558 var collectedGlues = []; 1559 for(var i=0; i<this.glues.length; i++){ 1560 if(this.glues[i].id2 == pointId){ 1561 collectedGlues.push(this.glues[i]); 1562 } 1563 } 1564 return collectedGlues; 1565 }, 1566 1567 1568 1569 /**Creates a new {Glue} and store it into the glue database. Use this instead 1570 *of creating the Glues by simply "new" operator 1571 * 1572 *@param {Number} firstId - the id of the first {ConnectionPoint} - usually the {Figure}'s {ConnectionPoint} id 1573 *@param {Number} secondId - the id of the second {ConnectionPoint} - usually the {Connector}'s {ConnectionPoint} id 1574 *@return {Glue} - the newly created Glue 1575 *@author Alex Gheorghiu <alex@scriptoid.com> 1576 **/ 1577 glueCreate:function(firstId, secondId){ 1578 var glue = new Glue(firstId, secondId); 1579 1580 this.glues.push(glue); 1581 1582 return glue; 1583 }, 1584 1585 1586 /**Removes all the {Glue}s based on it's two IDs 1587 *@param {Number} id1 - the id of the first shape (usually the Figure) 1588 *@param {Number} id2 - the id of the second shape (usually the Connector) 1589 * 1590 *@author Alex Gheorghiu <alex@scriptoid.com> 1591 **/ 1592 glueRemoveByIds:function(id1, id2){ 1593 for (var i=0; i<this.glues.length; i++){ 1594 if(id1 == this.glues[i].id1 && id2 == this.glues[i].id2){ 1595 this.glues.splice(i,1); 1596 } 1597 } 1598 }, 1599 1600 1601 /**Removes all the {Glue}s based on first Id (usually the Figure) 1602 *@param {Number} id - the id of the first shape (usually the Figure) 1603 *@author Alex Gheorghiu <alex@scriptoid.com> 1604 **/ 1605 glueRemoveAllByFirstId:function(id){ 1606 for (var i=0; i<this.glues.length; i++){ 1607 if(id == this.glues[i].id1){ 1608 this.glues.splice(i,1); 1609 } 1610 } 1611 }, 1612 1613 1614 /**Removes all the {Glue}s based on first Id (usually the Connector) 1615 *@param {Number} id - the id of the second shape (usually the Connector) 1616 *@author Alex Gheorghiu <alex@scriptoid.com> 1617 **/ 1618 glueRemoveAllBySecondId:function(id){ 1619 for (var i=0; i<this.glues.length; i++){ 1620 if(id == this.glues[i].id2){ 1621 this.glues.splice(i,1); 1622 } 1623 } 1624 }, 1625 1626 /** Returns all {Glue}s that have the first Id equals with a certain id value 1627 *@param {Number} firstId - first id (usually {Figure}'s id) 1628 *@param {Number} secondId - second id (usually {Connector}'s id) 1629 *@return {Array}{Glue}s 1630 *@author Alex Gheorghiu <alex@scriptoid.com> 1631 */ 1632 glueGetAllByIds:function(firstId, secondId){ 1633 var collectedGlues = []; 1634 for(var i=0; i<this.glues.length; i++){ 1635 if(this.glues[i].id1 == firstId && this.glues[i].id2 == secondId){ 1636 collectedGlues.push(this.glues[i]); 1637 } 1638 } 1639 return collectedGlues; 1640 } 1641 } 1642