| /** | |
|  * Created by Alex on 26-Feb-15. | |
|  */ | |
| 
 | |
| if (typeof window !== 'undefined') { | |
|   window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || | |
|   window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; | |
| } | |
| 
 | |
| class CanvasRenderer { | |
|   constructor(body) { | |
|     this.body = body; | |
| 
 | |
|     this.redrawRequested = false; | |
|     this.renderTimer = false; | |
|     this.requiresTimeout = true; | |
|     this.continueRendering = true; | |
|     this.renderRequests = 0; | |
| 
 | |
|     this.translation = {x: 0, y: 0}; | |
|     this.scale = 1.0; | |
|     this.canvasTopLeft     = {x: 0, y: 0}; | |
|     this.canvasBottomRight = {x: 0, y: 0}; | |
| 
 | |
|     this.body.emitter.on("_setScale",       (scale) => this.scale = scale); | |
|     this.body.emitter.on("_setTranslation", (translation) => {this.translation.x = translation.x; this.translation.y = translation.y;}); | |
|     this.body.emitter.on("_redraw",         this._redraw.bind(this)); | |
|     this.body.emitter.on("_redrawHidden",   this._redraw.bind(this, true)); | |
|     this.body.emitter.on("_requestRedraw",  this._requestRedraw.bind(this)); | |
|     this.body.emitter.on("_startRendering", () => {this.renderRequests += 1; this.continueRendering = true; this.startRendering();}); | |
|     this.body.emitter.on("_stopRendering",  () => {this.renderRequests -= 1; this.continueRendering = this.renderRequests > 0;}); | |
| 
 | |
|     this._determineBrowserMethod(); | |
|   } | |
| 
 | |
| 
 | |
|   startRendering() { | |
|     if (this.continueRendering === true) { | |
|       if (!this.renderTimer) { | |
|         if (this.requiresTimeout == true) { | |
|           this.renderTimer = window.setTimeout(this.renderStep.bind(this), this.simulationInterval); // wait this.renderTimeStep milliseconds and perform the animation step function | |
|         } | |
|         else { | |
|           this.renderTimer = window.requestAnimationFrame(this.renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function | |
|         } | |
|       } | |
|     } | |
|     else { | |
| 
 | |
|     } | |
|   } | |
| 
 | |
|   renderStep() { | |
|     // reset the renderTimer so a new scheduled animation step can be set | |
|     this.renderTimer = undefined; | |
| 
 | |
|     if (this.requiresTimeout == true) { | |
|       // this schedules a new simulation step | |
|       this.startRendering(); | |
|     } | |
| 
 | |
|     this._redraw(); | |
| 
 | |
|     if (this.requiresTimeout == false) { | |
|       // this schedules a new simulation step | |
|       this.startRendering(); | |
|     } | |
|   } | |
| 
 | |
|   setCanvas(canvas) { | |
|     this.canvas = canvas; | |
|   } | |
|   /** | |
|    * Redraw the network with the current data | |
|    * chart will be resized too. | |
|    */ | |
|   redraw() { | |
|     this.setSize(this.constants.width, this.constants.height); | |
|     this._redraw(); | |
|   } | |
| 
 | |
|   /** | |
|    * Redraw the network with the current data | |
|    * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. | |
|    * @private | |
|    */ | |
|   _requestRedraw(hidden) { | |
|     if (this.redrawRequested !== true) { | |
|       this.redrawRequested = true; | |
|       if (this.requiresTimeout === true) { | |
|         window.setTimeout(this._redraw.bind(this, hidden),0); | |
|       } | |
|       else { | |
|         window.requestAnimationFrame(this._redraw.bind(this, hidden, true)); | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   _redraw(hidden = false) { | |
|     this.body.emitter.emit("_beforeRender"); | |
| 
 | |
|     this.redrawRequested = false; | |
|     var ctx = this.canvas.frame.canvas.getContext('2d'); | |
| 
 | |
|     ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); | |
| 
 | |
|     // clear the canvas | |
|     var w = this.canvas.frame.canvas.clientWidth; | |
|     var h = this.canvas.frame.canvas.clientHeight; | |
|     ctx.clearRect(0, 0, w, h); | |
| 
 | |
|     // set scaling and translation | |
|     ctx.save(); | |
|     ctx.translate(this.translation.x, this.translation.y); | |
|     ctx.scale(this.scale, this.scale); | |
| 
 | |
|     this.canvasTopLeft = this.canvas.DOMtoCanvas({x:0,y:0}); | |
|     this.canvasBottomRight = this.canvas.DOMtoCanvas({x:this.canvas.frame.canvas.clientWidth,y:this.canvas.frame.canvas.clientHeight}); | |
| 
 | |
|     if (hidden === false) { | |
|       // todo: solve this | |
|       //if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { | |
|         this._drawEdges(ctx); | |
|       //} | |
|     } | |
| 
 | |
|     // todo: solve this | |
|     //if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { | |
|       this._drawNodes(ctx, this.body.nodes, hidden); | |
|     //} | |
|  | |
|     if (hidden === false) { | |
|       if (this.controlNodesActive == true) { | |
|         this._drawControlNodes(ctx); | |
|       } | |
|     } | |
| 
 | |
|     //this._drawNodes(ctx,this.body.supportNodes,true); | |
|   //  this.physics.nodesSolver._debug(ctx,"#F00F0F"); | |
|  | |
|     // restore original scaling and translation | |
|     ctx.restore(); | |
| 
 | |
|     if (hidden === true) { | |
|       ctx.clearRect(0, 0, w, h); | |
|     } | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * Redraw all nodes | |
|    * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); | |
|    * @param {CanvasRenderingContext2D}   ctx | |
|    * @param {Boolean} [alwaysShow] | |
|    * @private | |
|    */ | |
|   _drawNodes(ctx,nodes,alwaysShow = false) { | |
|     // first draw the unselected nodes | |
|     var selected = []; | |
| 
 | |
|     for (var id in nodes) { | |
|       if (nodes.hasOwnProperty(id)) { | |
|         nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); | |
|         if (nodes[id].isSelected()) { | |
|           selected.push(id); | |
|         } | |
|         else { | |
|           if (alwaysShow === true) { | |
|             nodes[id].draw(ctx); | |
|           } | |
|           else if (nodes[id].inArea() === true) { | |
|             nodes[id].draw(ctx); | |
|           } | |
|         } | |
|       } | |
|     } | |
| 
 | |
|     // draw the selected nodes on top | |
|     for (var s = 0, sMax = selected.length; s < sMax; s++) { | |
|       if (nodes[selected[s]].inArea() || alwaysShow) { | |
|         nodes[selected[s]].draw(ctx); | |
|       } | |
|     } | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * Redraw all edges | |
|    * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); | |
|    * @param {CanvasRenderingContext2D}   ctx | |
|    * @private | |
|    */ | |
|   _drawEdges(ctx) { | |
|     var edges = this.body.edges; | |
|     for (var id in edges) { | |
|       if (edges.hasOwnProperty(id)) { | |
|         var edge = edges[id]; | |
|         edge.setScale(this.scale); | |
|         if (edge.connected === true) { | |
|           edges[id].draw(ctx); | |
|         } | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * Redraw all edges | |
|    * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); | |
|    * @param {CanvasRenderingContext2D}   ctx | |
|    * @private | |
|    */ | |
|   _drawControlNodes(ctx) { | |
|     var edges = this.body.edges; | |
|     for (var id in edges) { | |
|       if (edges.hasOwnProperty(id)) { | |
|         edges[id]._drawControlNodes(ctx); | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because | |
|    * some implementations (safari and IE9) did not support requestAnimationFrame | |
|    * @private | |
|    */ | |
|   _determineBrowserMethod() { | |
|     if (typeof window !== 'undefined') { | |
|       var browserType = navigator.userAgent.toLowerCase(); | |
|       this.requiresTimeout = false; | |
|       if (browserType.indexOf('msie 9.0') != -1) { // IE 9 | |
|         this.requiresTimeout = true; | |
|       } | |
|       else if (browserType.indexOf('safari') != -1) {  // safari | |
|         if (browserType.indexOf('chrome') <= -1) { | |
|           this.requiresTimeout = true; | |
|         } | |
|       } | |
|     } | |
|     else { | |
|       this.requiresTimeout = true; | |
|     } | |
|   } | |
| 
 | |
| } | |
| 
 | |
| export {CanvasRenderer};
 |