basant307/AI_Governance_Project
048
1/*2The canvas renderer was written by Yue Dong.3 4Modifications tracked on Github.5*/6 7/* global OffscreenCanvas */8 9import * as util from '../../../util/index.mjs';10import * as is from '../../../is.mjs';11import { makeBoundingBox } from '../../../math.mjs';12import { labelHalign, labelValign } from '../../../style/align.mjs';13import ElementTextureCache from './ele-texture-cache.mjs';14import LayeredTextureCache from './layered-texture-cache.mjs';15 16import arrowShapes from './arrow-shapes.mjs';17import drawingElements from './drawing-elements.mjs';18import drawingEdges from './drawing-edges.mjs';19import drawingImages from './drawing-images.mjs';20import drawingLabelText from './drawing-label-text.mjs';21import drawingNodes from './drawing-nodes.mjs';22import drawingRedraw from './drawing-redraw.mjs';23import drawingRedrawWebGL from './webgl/drawing-redraw-webgl.mjs';24import drawingShapes from './drawing-shapes.mjs';25import exportImage from './export-image.mjs';26import nodeShapes from './node-shapes.mjs';27 28var CR = CanvasRenderer;29var CRp = CanvasRenderer.prototype;30 31CRp.CANVAS_LAYERS = 3;32//33CRp.SELECT_BOX = 0;34CRp.DRAG = 1;35CRp.NODE = 2;36CRp.WEBGL = 3;37 38CRp.CANVAS_TYPES = [ '2d', '2d', '2d', 'webgl2' ];39 40CRp.BUFFER_COUNT = 3;41//42CRp.TEXTURE_BUFFER = 0;43CRp.MOTIONBLUR_BUFFER_NODE = 1;44CRp.MOTIONBLUR_BUFFER_DRAG = 2;45 46function CanvasRenderer( options ){47 var r = this;48 49 var containerWindow = r.cy.window();50 var document = containerWindow.document;51 52 if( options.webgl ){53 CRp.CANVAS_LAYERS = r.CANVAS_LAYERS = 4;54 console.log('webgl rendering enabled');55 }56 57 r.data = {58 canvases: new Array( CRp.CANVAS_LAYERS ),59 contexts: new Array( CRp.CANVAS_LAYERS ),60 canvasNeedsRedraw: new Array( CRp.CANVAS_LAYERS ),61 62 bufferCanvases: new Array( CRp.BUFFER_COUNT ),63 bufferContexts: new Array( CRp.CANVAS_LAYERS ),64 };65 66 var tapHlOffAttr = '-webkit-tap-highlight-color';67 var tapHlOffStyle = 'rgba(0,0,0,0)';68 r.data.canvasContainer = document.createElement( 'div' ); // eslint-disable-line no-undef69 var containerStyle = r.data.canvasContainer.style;70 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;71 containerStyle.position = 'relative';72 containerStyle.zIndex = '0';73 containerStyle.overflow = 'hidden';74 75 var container = options.cy.container();76 container.appendChild( r.data.canvasContainer );77 container.style[tapHlOffAttr] = tapHlOffStyle;78 79 var styleMap = {80 '-webkit-user-select': 'none',81 '-moz-user-select': '-moz-none',82 'user-select': 'none',83 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',84 'outline-style': 'none',85 };86 87 if(is.ms()) {88 styleMap['-ms-touch-action'] = 'none';89 styleMap['touch-action'] = 'none';90 }91 92 for( var i = 0; i < CRp.CANVAS_LAYERS; i++ ){93 var canvas = r.data.canvases[ i ] = document.createElement( 'canvas' ); // eslint-disable-line no-undef94 var type = CRp.CANVAS_TYPES[ i ];95 r.data.contexts[ i ] = canvas.getContext( type );96 if( !r.data.contexts[ i ] ) {97 util.error( 'Could not create canvas of type ' + type );98 }99 Object.keys(styleMap).forEach((k) => {100 canvas.style[k] = styleMap[k];101 });102 canvas.style.position = 'absolute';103 canvas.setAttribute( 'data-id', 'layer' + i );104 canvas.style.zIndex = String( CRp.CANVAS_LAYERS - i );105 r.data.canvasContainer.appendChild( canvas );106 107 r.data.canvasNeedsRedraw[ i ] = false;108 }109 r.data.topCanvas = r.data.canvases[0];110 111 r.data.canvases[ CRp.NODE ].setAttribute( 'data-id', 'layer' + CRp.NODE + '-node' );112 r.data.canvases[ CRp.SELECT_BOX ].setAttribute( 'data-id', 'layer' + CRp.SELECT_BOX + '-selectbox' );113 r.data.canvases[ CRp.DRAG ].setAttribute( 'data-id', 'layer' + CRp.DRAG + '-drag' );114 if( r.data.canvases[ CRp.WEBGL ] ) {115 r.data.canvases[ CRp.WEBGL ].setAttribute( 'data-id', 'layer' + CRp.WEBGL + '-webgl' );116 }117 118 for( var i = 0; i < CRp.BUFFER_COUNT; i++ ){119 r.data.bufferCanvases[ i ] = document.createElement( 'canvas' ); // eslint-disable-line no-undef120 r.data.bufferContexts[ i ] = r.data.bufferCanvases[ i ].getContext( '2d' );121 r.data.bufferCanvases[ i ].style.position = 'absolute';122 r.data.bufferCanvases[ i ].setAttribute( 'data-id', 'buffer' + i );123 r.data.bufferCanvases[ i ].style.zIndex = String( -i - 1 );124 r.data.bufferCanvases[ i ].style.visibility = 'hidden';125 //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);126 }127 128 r.pathsEnabled = true;129 130 let emptyBb = makeBoundingBox();131 132 let getBoxCenter = bb => ({ x: (bb.x1 + bb.x2)/2, y: (bb.y1 + bb.y2)/2 });133 134 let getCenterOffset = bb => ({ x: -bb.w/2, y: -bb.h/2 });135 136 let backgroundTimestampHasChanged = ele => {137 let _p = ele[0]._private;138 let same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;139 140 return !same;141 };142 143 let getStyleKey = ele => ele[0]._private.nodeKey;144 let getLabelKey = ele => ele[0]._private.labelStyleKey;145 let getSourceLabelKey = ele => ele[0]._private.sourceLabelStyleKey;146 let getTargetLabelKey = ele => ele[0]._private.targetLabelStyleKey;147 148 let drawElement = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElement( context, ele, bb, false, false, useEleOpacity );149 let drawLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'main', useEleOpacity );150 let drawSourceLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'source', useEleOpacity );151 let drawTargetLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'target', useEleOpacity );152 153 let getElementBox = ele => { ele.boundingBox(); return ele[0]._private.bodyBounds; };154 let getLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.main || emptyBb; };155 let getSourceLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.source || emptyBb; };156 let getTargetLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.target || emptyBb; };157 158 let isLabelVisibleAtScale = (ele, scaledLabelShown) => scaledLabelShown;159 160 let getElementRotationPoint = ele => getBoxCenter( getElementBox(ele) );161 162 let addTextMargin = (prefix, pt, ele) => {163 let pre = prefix ? prefix + '-' : '';164 165 return {166 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,167 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue168 };169 };170 171 let getRsPt = (ele, x, y) => {172 let rs = ele[0]._private.rscratch;173 174 return { x: rs[x], y: rs[y] };175 };176 177 let getLabelRotationPoint = ele => addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);178 let getSourceLabelRotationPoint = ele => addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);179 let getTargetLabelRotationPoint = ele => addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);180 181 let getElementRotationOffset = ele => getCenterOffset( getElementBox(ele) );182 let getSourceLabelRotationOffset = ele => getCenterOffset( getSourceLabelBox(ele) );183 let getTargetLabelRotationOffset = ele => getCenterOffset( getTargetLabelBox(ele) );184 185 let getLabelRotationOffset = ele => {186 let bb = getLabelBox(ele);187 let p = getCenterOffset( getLabelBox(ele) );188 189 if( ele.isNode() ){190 switch( labelHalign( ele.pstyle('text-halign').value ) ){191 case 'left':192 p.x = -bb.w - (bb.leftPad || 0);193 break;194 case 'right':195 p.x = -(bb.rightPad || 0);196 break;197 }198 199 switch( labelValign( ele.pstyle('text-valign').value ) ){200 case 'top':201 p.y = -bb.h - (bb.topPad || 0);202 break;203 case 'bottom':204 p.y = -(bb.botPad || 0);205 break;206 }207 }208 209 return p;210 };211 212 let eleTxrCache = r.data.eleTxrCache = new ElementTextureCache( r, {213 getKey: getStyleKey,214 doesEleInvalidateKey: backgroundTimestampHasChanged,215 drawElement: drawElement,216 getBoundingBox: getElementBox,217 getRotationPoint: getElementRotationPoint,218 getRotationOffset: getElementRotationOffset,219 allowEdgeTxrCaching: false,220 allowParentTxrCaching: false221 } );222 223 let lblTxrCache = r.data.lblTxrCache = new ElementTextureCache( r, {224 getKey: getLabelKey,225 drawElement: drawLabel,226 getBoundingBox: getLabelBox,227 getRotationPoint: getLabelRotationPoint,228 getRotationOffset: getLabelRotationOffset,229 isVisible: isLabelVisibleAtScale230 } );231 232 let slbTxrCache = r.data.slbTxrCache = new ElementTextureCache( r, {233 getKey: getSourceLabelKey,234 drawElement: drawSourceLabel,235 getBoundingBox: getSourceLabelBox,236 getRotationPoint: getSourceLabelRotationPoint,237 getRotationOffset: getSourceLabelRotationOffset,238 isVisible: isLabelVisibleAtScale239 } );240 241 let tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache( r, {242 getKey: getTargetLabelKey,243 drawElement: drawTargetLabel,244 getBoundingBox: getTargetLabelBox,245 getRotationPoint: getTargetLabelRotationPoint,246 getRotationOffset: getTargetLabelRotationOffset,247 isVisible: isLabelVisibleAtScale248 } );249 250 let lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache( r );251 252 r.onUpdateEleCalcs(function invalidateTextureCaches( willDraw, eles ){253 // each cache should check for sub-key diff to see that the update affects that cache particularly254 eleTxrCache.invalidateElements( eles );255 lblTxrCache.invalidateElements( eles );256 slbTxrCache.invalidateElements( eles );257 tlbTxrCache.invalidateElements( eles );258 259 // any change invalidates the layers260 lyrTxrCache.invalidateElements( eles );261 262 // update the old bg timestamp so diffs can be done in the ele txr caches263 for( let i = 0; i < eles.length; i++ ){264 let _p = eles[i]._private;265 266 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;267 }268 });269 270 let refineInLayers = reqs => {271 for( var i = 0; i < reqs.length; i++ ){272 lyrTxrCache.enqueueElementRefinement( reqs[i].ele );273 }274 };275 276 eleTxrCache.onDequeue(refineInLayers);277 lblTxrCache.onDequeue(refineInLayers);278 slbTxrCache.onDequeue(refineInLayers);279 tlbTxrCache.onDequeue(refineInLayers);280 281 if( options.webgl ) {282 r.initWebgl( options, {283 getStyleKey,284 getLabelKey,285 getSourceLabelKey,286 getTargetLabelKey,287 drawElement,288 drawLabel,289 drawSourceLabel,290 drawTargetLabel,291 getElementBox,292 getLabelBox,293 getSourceLabelBox,294 getTargetLabelBox,295 getElementRotationPoint,296 getElementRotationOffset,297 getLabelRotationPoint,298 getSourceLabelRotationPoint,299 getTargetLabelRotationPoint,300 getLabelRotationOffset,301 getSourceLabelRotationOffset,302 getTargetLabelRotationOffset303 } );304 }305}306 307CRp.redrawHint = function( group, bool ){308 var r = this;309 310 switch( group ){311 case 'eles':312 r.data.canvasNeedsRedraw[ CRp.NODE ] = bool;313 break;314 case 'drag':315 r.data.canvasNeedsRedraw[ CRp.DRAG ] = bool;316 break;317 case 'select':318 r.data.canvasNeedsRedraw[ CRp.SELECT_BOX ] = bool;319 break;320 case 'gc':321 r.data.gc = true;322 break;323 }324};325 326// whether to use Path2D caching for drawing327var pathsImpld = typeof Path2D !== 'undefined';328 329CRp.path2dEnabled = function( on ){330 if( on === undefined ){331 return this.pathsEnabled;332 }333 334 this.pathsEnabled = on ? true : false;335};336 337CRp.usePaths = function(){338 return pathsImpld && this.pathsEnabled;339};340 341CRp.setImgSmoothing = function( context, bool ){342 if( context.imageSmoothingEnabled != null ){343 context.imageSmoothingEnabled = bool;344 } else {345 context.webkitImageSmoothingEnabled = bool;346 context.mozImageSmoothingEnabled = bool;347 context.msImageSmoothingEnabled = bool;348 }349};350 351CRp.getImgSmoothing = function( context ){352 if( context.imageSmoothingEnabled != null ){353 return context.imageSmoothingEnabled;354 } else {355 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;356 }357};358 359CRp.makeOffscreenCanvas = function(width, height){360 let canvas;361 362 if( typeof OffscreenCanvas !== typeof undefined ){363 canvas = new OffscreenCanvas(width, height);364 } else {365 var containerWindow = this.cy.window();366 var document = containerWindow.document;367 canvas = document.createElement('canvas'); // eslint-disable-line no-undef368 canvas.width = width;369 canvas.height = height;370 }371 372 return canvas;373};374 375[376 arrowShapes,377 drawingElements,378 drawingEdges,379 drawingImages,380 drawingLabelText,381 drawingNodes,382 drawingRedraw,383 drawingRedrawWebGL,384 drawingShapes,385 exportImage,386 nodeShapes387].forEach( function( props ){388 util.extend( CRp, props );389} );390 391export default CR;392 