CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
bounds.mjs1047 linesDownload Raw Back to dimensions
1import * as is from '../../is.mjs';2import { assignBoundingBox, expandBoundingBoxSides,  clearBoundingBox, expandBoundingBox, makeBoundingBox, copyBoundingBox, shiftBoundingBox, updateBoundingBox } from '../../math.mjs';3import {defaults, endsWith, getPrefixedProperty, hashIntsArray, memoize} from '../../util/index.mjs';4import { labelHalign, labelValign } from '../../style/align.mjs';5 6let fn, elesfn;7 8fn = elesfn = {};9 10elesfn.renderedBoundingBox = function( options ){11  let bb = this.boundingBox( options );12  let cy = this.cy();13  let zoom = cy.zoom();14  let pan = cy.pan();15 16  let x1 = bb.x1 * zoom + pan.x;17  let x2 = bb.x2 * zoom + pan.x;18  let y1 = bb.y1 * zoom + pan.y;19  let y2 = bb.y2 * zoom + pan.y;20 21  return {22    x1: x1,23    x2: x2,24    y1: y1,25    y2: y2,26    w: x2 - x1,27    h: y2 - y128  };29};30 31elesfn.dirtyCompoundBoundsCache = function(silent = false){32  let cy = this.cy();33 34  if( !cy.styleEnabled() || !cy.hasCompoundNodes() ){ return this; }35 36  this.forEachUp( ele => {37    if( ele.isParent() ){38      let _p = ele._private;39 40      _p.compoundBoundsClean = false;41      _p.bbCache = null;42 43      if(!silent){44        ele.emitAndNotify('bounds');45      }46    }47  } );48 49  return this;50};51 52elesfn.updateCompoundBounds = function(force = false){53  let cy = this.cy();54 55  // not possible to do on non-compound graphs or with the style disabled56  if( !cy.styleEnabled() || !cy.hasCompoundNodes() ){ return this; }57 58  // save cycles when batching -- but bounds will be stale (or not exist yet)59  if( !force && cy.batching() ){ return this; }60 61  function update( parent ){62    if( !parent.isParent() ){ return; }63 64    let _p = parent._private;65    let children = parent.children();66    let includeLabels = parent.pstyle( 'compound-sizing-wrt-labels' ).value === 'include';67 68    let min = {69      width: {70        val: parent.pstyle( 'min-width' ).pfValue,71        left: parent.pstyle( 'min-width-bias-left' ),72        right: parent.pstyle( 'min-width-bias-right' )73      },74      height: {75        val: parent.pstyle( 'min-height' ).pfValue,76        top: parent.pstyle( 'min-height-bias-top' ),77        bottom: parent.pstyle( 'min-height-bias-bottom' )78      }79    };80 81    let bb = children.boundingBox( {82      includeLabels: includeLabels,83      includeOverlays: false,84 85      // updating the compound bounds happens outside of the regular86      // cache cycle (i.e. before fired events)87      useCache: false88    } );89    let pos = _p.position;90 91    // if children take up zero area then keep position and fall back on stylesheet w/h92    if( bb.w === 0 || bb.h === 0 ){93      bb = {94        w: parent.pstyle('width').pfValue,95        h: parent.pstyle('height').pfValue96      };97 98      bb.x1 = pos.x - bb.w/2;99      bb.x2 = pos.x + bb.w/2;100      bb.y1 = pos.y - bb.h/2;101      bb.y2 = pos.y + bb.h/2;102    }103 104    function computeBiasValues( propDiff, propBias, propBiasComplement ){105      let biasDiff = 0;106      let biasComplementDiff = 0;107      let biasTotal = propBias + propBiasComplement;108 109      if( propDiff > 0 && biasTotal > 0 ){110        biasDiff = ( propBias / biasTotal ) * propDiff;111        biasComplementDiff = ( propBiasComplement / biasTotal ) * propDiff;112      }113      return {114        biasDiff: biasDiff,115        biasComplementDiff: biasComplementDiff116      };117    }118 119    function computePaddingValues( width, height, paddingObject, relativeTo ) {120      // Assuming percentage is number from 0 to 1121      if(paddingObject.units === '%') {122        switch(relativeTo) {123          case 'width':124            return width > 0 ? paddingObject.pfValue * width : 0;125          case 'height':126            return height > 0 ? paddingObject.pfValue * height : 0;127          case 'average':128            return ( width > 0 ) && ( height > 0 ) ? paddingObject.pfValue * ( width + height ) / 2 : 0;129          case 'min':130            return ( width > 0 ) && ( height > 0 ) ? ( ( width > height ) ? paddingObject.pfValue * height : paddingObject.pfValue * width ) : 0;131          case 'max':132            return ( width > 0 ) && ( height > 0 ) ? ( ( width > height ) ? paddingObject.pfValue * width : paddingObject.pfValue * height ) : 0;133          default:134            return 0;135        }136      } else if(paddingObject.units === 'px') {137        return paddingObject.pfValue;138      } else {139        return 0;140      }141    }142 143    let leftVal = min.width.left.value;144    if( min.width.left.units === 'px' && min.width.val > 0 ){145      leftVal = ( leftVal * 100 ) / min.width.val;146    }147    let rightVal = min.width.right.value;148    if( min.width.right.units === 'px' && min.width.val > 0 ){149      rightVal = ( rightVal * 100 ) / min.width.val;150    }151 152    let topVal = min.height.top.value;153    if( min.height.top.units === 'px' && min.height.val > 0 ){154      topVal = ( topVal * 100 ) / min.height.val;155    }156 157    let bottomVal = min.height.bottom.value;158    if( min.height.bottom.units === 'px' && min.height.val > 0 ){159      bottomVal = ( bottomVal * 100 ) / min.height.val;160    }161 162    let widthBiasDiffs = computeBiasValues( min.width.val - bb.w, leftVal, rightVal );163    let diffLeft = widthBiasDiffs.biasDiff;164    let diffRight = widthBiasDiffs.biasComplementDiff;165 166    let heightBiasDiffs = computeBiasValues( min.height.val - bb.h, topVal, bottomVal );167    let diffTop = heightBiasDiffs.biasDiff;168    let diffBottom = heightBiasDiffs.biasComplementDiff;169 170    _p.autoPadding = computePaddingValues( bb.w, bb.h, parent.pstyle( 'padding' ), parent.pstyle( 'padding-relative-to' ).value );171 172    _p.autoWidth = Math.max(bb.w, min.width.val);173    pos.x = (- diffLeft + bb.x1 + bb.x2 + diffRight) / 2;174 175    _p.autoHeight = Math.max(bb.h, min.height.val);176    pos.y = (- diffTop + bb.y1 + bb.y2 + diffBottom) / 2;177  }178 179  for( let i = 0; i < this.length; i++ ){180    let ele = this[i];181    let _p = ele._private;182 183    if( !_p.compoundBoundsClean || force ){184      update( ele );185 186      if( !cy.batching() ){187        _p.compoundBoundsClean = true;188      }189    }190  }191 192  return this;193};194 195let noninf = function( x ){196  if( x === Infinity || x === -Infinity ){197    return 0;198  }199 200  return x;201};202 203let updateBounds = function( b, x1, y1, x2, y2 ){204  // don't update with zero area boxes205  if( x2 - x1 === 0 || y2 - y1 === 0 ){ return; }206 207  // don't update with null dim208  if( x1 == null || y1 == null || x2 == null || y2 == null ){ return; }209 210  b.x1 = x1 < b.x1 ? x1 : b.x1;211  b.x2 = x2 > b.x2 ? x2 : b.x2;212  b.y1 = y1 < b.y1 ? y1 : b.y1;213  b.y2 = y2 > b.y2 ? y2 : b.y2;214  b.w = b.x2 - b.x1;215  b.h = b.y2 - b.y1;216};217 218let updateBoundsFromBox = function( b, b2 ){219  if( b2 == null ){ return b; }220 221  return updateBounds( b, b2.x1, b2.y1, b2.x2, b2.y2 );222};223 224let prefixedProperty = function( obj, field, prefix ){225  return getPrefixedProperty( obj, field, prefix );226};227 228let updateBoundsFromArrow = function( bounds, ele, prefix ){229  if( ele.cy().headless() ){ return; }230 231  let _p = ele._private;232  let rstyle = _p.rstyle;233  let halfArW = rstyle.arrowWidth / 2;234  let arrowType = ele.pstyle( prefix + '-arrow-shape' ).value;235  let x;236  let y;237 238  if( arrowType !== 'none' ){239    if( prefix === 'source' ){240      x = rstyle.srcX;241      y = rstyle.srcY;242    } else if( prefix === 'target' ){243      x = rstyle.tgtX;244      y = rstyle.tgtY;245    } else {246      x = rstyle.midX;247      y = rstyle.midY;248    }249 250    // always store the individual arrow bounds251    let bbs = _p.arrowBounds = _p.arrowBounds || {};252    let bb = bbs[prefix] = bbs[prefix] || {};253    bb.x1 = x - halfArW;254    bb.y1 = y - halfArW;255    bb.x2 = x + halfArW;256    bb.y2 = y + halfArW;257    bb.w = bb.x2 - bb.x1;258    bb.h = bb.y2 - bb.y1;259    expandBoundingBox(bb, 1);260 261    updateBounds( bounds, bb.x1, bb.y1, bb.x2, bb.y2 );262  }263};264 265let updateBoundsFromLabel = function( bounds, ele, prefix ){266  if( ele.cy().headless() ){ return; }267 268  let prefixDash;269 270  if( prefix ){271    prefixDash = prefix + '-';272  } else {273    prefixDash = '';274  }275 276  let _p = ele._private;277  let rstyle = _p.rstyle;278  let label = ele.pstyle( prefixDash + 'label' ).strValue;279 280  if( label ){281    let halign = ele.pstyle( 'text-halign' );282    let valign = ele.pstyle( 'text-valign' );283    let labelWidth = prefixedProperty( rstyle, 'labelWidth', prefix );284    let labelHeight = prefixedProperty( rstyle, 'labelHeight', prefix );285    let labelX = prefixedProperty( rstyle, 'labelX', prefix );286    let labelY = prefixedProperty( rstyle, 'labelY', prefix );287    let marginX = ele.pstyle( prefixDash + 'text-margin-x' ).pfValue;288    let marginY = ele.pstyle( prefixDash + 'text-margin-y' ).pfValue;289    let isEdge = ele.isEdge();290    let rotation = ele.pstyle( prefixDash + 'text-rotation' );291    let outlineWidth = ele.pstyle( 'text-outline-width' ).pfValue;292    let borderWidth = ele.pstyle( 'text-border-width' ).pfValue;293    let halfBorderWidth = borderWidth / 2;294    let padding = ele.pstyle( 'text-background-padding' ).pfValue;295    let marginOfError = 2; // expand to work around browser dimension inaccuracies296 297    let lh = labelHeight;298    let lw = labelWidth;299    let lw_2 = lw / 2;300    let lh_2 = lh / 2;301    let lx1, lx2, ly1, ly2;302 303    if( isEdge ){304      lx1 = labelX - lw_2;305      lx2 = labelX + lw_2;306      ly1 = labelY - lh_2;307      ly2 = labelY + lh_2;308    } else {309      switch( labelHalign( halign.value ) ){310        case 'left':311          lx1 = labelX - lw;312          lx2 = labelX;313          break;314 315        case 'center':316          lx1 = labelX - lw_2;317          lx2 = labelX + lw_2;318          break;319 320        case 'right':321          lx1 = labelX;322          lx2 = labelX + lw;323          break;324      }325 326      switch( labelValign( valign.value ) ){327        case 'top':328          ly1 = labelY - lh;329          ly2 = labelY;330          break;331 332        case 'center':333          ly1 = labelY - lh_2;334          ly2 = labelY + lh_2;335          break;336 337        case 'bottom':338          ly1 = labelY;339          ly2 = labelY + lh;340          break;341      }342    }343 344    // shift by margin and expand by outline and border345    let leftPad  = marginX - Math.max( outlineWidth, halfBorderWidth ) - padding - marginOfError;346    let rightPad = marginX + Math.max( outlineWidth, halfBorderWidth ) + padding + marginOfError;347    let topPad   = marginY - Math.max( outlineWidth, halfBorderWidth ) - padding - marginOfError;348    let botPad   = marginY + Math.max( outlineWidth, halfBorderWidth ) + padding + marginOfError;349 350    lx1 += leftPad;351    lx2 += rightPad;352    ly1 += topPad;353    ly2 += botPad;354 355    // always store the unrotated label bounds separately356    let bbPrefix = prefix || 'main';357    let bbs = _p.labelBounds;358    let bb = bbs[bbPrefix] = bbs[bbPrefix] || {};359    bb.x1 = lx1;360    bb.y1 = ly1;361    bb.x2 = lx2;362    bb.y2 = ly2;363    bb.w = lx2 - lx1;364    bb.h = ly2 - ly1;365    bb.leftPad = leftPad;366    bb.rightPad = rightPad;367    bb.topPad = topPad;368    bb.botPad = botPad;369 370    let isAutorotate = ( isEdge && rotation.strValue === 'autorotate' );371    let isPfValue = ( rotation.pfValue != null && rotation.pfValue !== 0 );372 373    if( isAutorotate || isPfValue ){374      let theta = isAutorotate ? prefixedProperty( _p.rstyle, 'labelAngle', prefix ) : rotation.pfValue;375      let cos = Math.cos( theta );376      let sin = Math.sin( theta );377 378      // rotation point (default value for center-center)379      let xo = (lx1 + lx2)/2;380      let yo = (ly1 + ly2)/2;381 382      if( !isEdge ){383        switch( labelHalign( halign.value ) ){384          case 'left':385            xo = lx2;386            break;387 388          case 'right':389            xo = lx1;390            break;391        }392 393        switch( labelValign( valign.value ) ){394          case 'top':395            yo = ly2;396            break;397 398          case 'bottom':399            yo = ly1;400            break;401        }402      }403 404      let rotate = function( x, y ){405        x = x - xo;406        y = y - yo;407 408        return {409          x: x * cos - y * sin + xo,410          y: x * sin + y * cos + yo411        };412      };413 414      let px1y1 = rotate( lx1, ly1 );415      let px1y2 = rotate( lx1, ly2 );416      let px2y1 = rotate( lx2, ly1 );417      let px2y2 = rotate( lx2, ly2 );418 419      lx1 = Math.min( px1y1.x, px1y2.x, px2y1.x, px2y2.x );420      lx2 = Math.max( px1y1.x, px1y2.x, px2y1.x, px2y2.x );421      ly1 = Math.min( px1y1.y, px1y2.y, px2y1.y, px2y2.y );422      ly2 = Math.max( px1y1.y, px1y2.y, px2y1.y, px2y2.y );423    }424 425    let bbPrefixRot = bbPrefix + 'Rot';426    let bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};427    bbRot.x1 = lx1;428    bbRot.y1 = ly1;429    bbRot.x2 = lx2;430    bbRot.y2 = ly2;431    bbRot.w = lx2 - lx1;432    bbRot.h = ly2 - ly1;433 434    updateBounds( bounds, lx1, ly1, lx2, ly2 );435    updateBounds( _p.labelBounds.all, lx1, ly1, lx2, ly2 );436  }437 438  return bounds;439};440 441let updateBoundsFromOutline = function (bounds, ele) {442  if (ele.cy().headless()) { return; }443  444  let outlineOpacity = ele.pstyle('outline-opacity').value;445  let outlineWidth = ele.pstyle('outline-width').value;446  let outlineOffset = ele.pstyle('outline-offset').value;447  let expansion = outlineWidth + outlineOffset;448 449  updateBoundsFromMiter( bounds, ele, outlineOpacity, expansion, 'outside', expansion/2 );450};451 452let updateBoundsFromMiter = function( bounds, ele, opacity, expansionSize, expansionPosition, useFallbackValue){453  if (opacity === 0 || expansionSize <= 0 || expansionPosition === 'inside') {454    return;455  }456  457  let cy = ele.cy();458  let r = cy.renderer();459  let rshape = r.nodeShapes[r.getNodeShape(ele)];460  if (!rshape) { return; }461  let { x, y } = ele.position();462  let w = ele.width();463  let h = ele.height();464 465  if (rshape.hasMiterBounds) {466    if (expansionPosition === 'center') {467      expansionSize /= 2;468    }469 470    let mbb = rshape.miterBounds(x, y, w, h, expansionSize);471 472    updateBoundsFromBox(bounds, mbb);473  } else if (useFallbackValue != null && useFallbackValue > 0) {474    expandBoundingBoxSides(bounds, [useFallbackValue, useFallbackValue, useFallbackValue, useFallbackValue]);475  }476};477 478let updateBoundsFromMiterBorder = function( bounds, ele ){479  if (ele.cy().headless()) { return; }480 481  let borderOpacity = ele.pstyle('border-opacity').value;482  let borderWidth = ele.pstyle('border-width').pfValue;483  let borderPosition = ele.pstyle('border-position').value;484 485  updateBoundsFromMiter(bounds, ele, borderOpacity, borderWidth, borderPosition);486};487 488// get the bounding box of the elements (in raw model position)489let boundingBoxImpl = function( ele, options ){490  let cy = ele._private.cy;491  let styleEnabled = cy.styleEnabled();492  let headless = cy.headless();493 494  let bounds = makeBoundingBox();495 496  let _p = ele._private;497  let isNode = ele.isNode();498  let isEdge = ele.isEdge();499  let ex1, ex2, ey1, ey2; // extrema of body / lines500  let x, y; // node pos501  let rstyle = _p.rstyle;502  let manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0];503 504  // must use `display` prop only, as reading `compound.width()` causes recursion505  // (other factors like width values will be considered later in this function anyway)506  let isDisplayed = ele => ele.pstyle('display').value !== 'none';507 508  let displayed = (509    !styleEnabled510    || (511      isDisplayed(ele)512 513      // must take into account connected nodes b/c of implicit edge hiding on display:none node514      && ( !isEdge || ( isDisplayed(ele.source()) && isDisplayed(ele.target()) ) )515    )516  );517 518  if( displayed ){ // displayed suffices, since we will find zero area eles anyway519    let overlayOpacity = 0;520    let overlayPadding = 0;521 522    if( styleEnabled && options.includeOverlays ){523      overlayOpacity = ele.pstyle( 'overlay-opacity' ).value;524 525      if( overlayOpacity !== 0 ){526        overlayPadding = ele.pstyle( 'overlay-padding' ).value;527      }528    }529 530    let underlayOpacity = 0;531    let underlayPadding = 0;532 533    if( styleEnabled && options.includeUnderlays ){534      underlayOpacity = ele.pstyle( 'underlay-opacity' ).value;535 536      if( underlayOpacity !== 0 ){537        underlayPadding = ele.pstyle( 'underlay-padding' ).value;538      }539    }540 541    let padding = Math.max(overlayPadding, underlayPadding);542 543    let w = 0;544    let wHalf = 0;545 546    if( styleEnabled ){547      w = ele.pstyle( 'width' ).pfValue;548      wHalf = w / 2;549    }550 551    if( isNode && options.includeNodes ){552      let pos = ele.position();553      x = pos.x;554      y = pos.y;555      let w = ele.outerWidth();556      let halfW = w / 2;557      let h = ele.outerHeight();558      let halfH = h / 2;559 560      // handle node dimensions561      /////////////////////////562 563      ex1 = x - halfW;564      ex2 = x + halfW;565      ey1 = y - halfH;566      ey2 = y + halfH;567 568      updateBounds( bounds, ex1, ey1, ex2, ey2 );569 570      if( styleEnabled ){571        updateBoundsFromOutline(bounds, ele)572      }573 574      if( styleEnabled && options.includeOutlines && !headless ){575        updateBoundsFromOutline( bounds, ele );576      }577 578      if (styleEnabled) {579        updateBoundsFromMiterBorder(bounds, ele);580      }581    } else if( isEdge && options.includeEdges ){582 583      if( styleEnabled && !headless ){584        let curveStyle = ele.pstyle( 'curve-style').strValue;585 586 587        // handle edge dimensions (rough box estimate)588        //////////////////////////////////////////////589 590        ex1 = Math.min( rstyle.srcX, rstyle.midX, rstyle.tgtX );591        ex2 = Math.max( rstyle.srcX, rstyle.midX, rstyle.tgtX );592        ey1 = Math.min( rstyle.srcY, rstyle.midY, rstyle.tgtY );593        ey2 = Math.max( rstyle.srcY, rstyle.midY, rstyle.tgtY );594 595        // take into account edge width596        ex1 -= wHalf;597        ex2 += wHalf;598        ey1 -= wHalf;599        ey2 += wHalf;600 601        updateBounds( bounds, ex1, ey1, ex2, ey2 );602 603 604        // precise edges605        ////////////////606 607        if( curveStyle === 'haystack' ){608          let hpts = rstyle.haystackPts;609 610          if( hpts && hpts.length === 2 ){611            ex1 = hpts[0].x;612            ey1 = hpts[0].y;613            ex2 = hpts[1].x;614            ey2 = hpts[1].y;615 616            if( ex1 > ex2 ){617              let temp = ex1;618              ex1 = ex2;619              ex2 = temp;620            }621 622            if( ey1 > ey2 ){623              let temp = ey1;624              ey1 = ey2;625              ey2 = temp;626            }627 628            updateBounds( bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf );629          }630 631        } else if(632          curveStyle === 'bezier' || curveStyle === 'unbundled-bezier'633          || endsWith(curveStyle, 'segments') || endsWith(curveStyle, 'taxi')634        ){635          let pts;636 637          switch( curveStyle ){638            case 'bezier':639            case 'unbundled-bezier':640              pts = rstyle.bezierPts;641              break;642            case 'segments':643            case 'taxi':644            case 'round-segments':645            case 'round-taxi':646              pts = rstyle.linePts;647              break;648          }649 650          if( pts != null ){651            for( let j = 0; j < pts.length; j++ ){652              let pt = pts[ j ];653 654              ex1 = pt.x - wHalf;655              ex2 = pt.x + wHalf;656              ey1 = pt.y - wHalf;657              ey2 = pt.y + wHalf;658 659              updateBounds( bounds, ex1, ey1, ex2, ey2 );660            }661          }662        } // bezier-like or segment-like edge663      } else { // headless or style disabled664 665        // fallback on source and target positions666        //////////////////////////////////////////667 668        let n1 = ele.source();669        let n1pos = n1.position();670 671        let n2 = ele.target();672        let n2pos = n2.position();673 674        ex1 = n1pos.x;675        ex2 = n2pos.x;676        ey1 = n1pos.y;677        ey2 = n2pos.y;678 679        if( ex1 > ex2 ){680          let temp = ex1;681          ex1 = ex2;682          ex2 = temp;683        }684 685        if( ey1 > ey2 ){686          let temp = ey1;687          ey1 = ey2;688          ey2 = temp;689        }690 691        // take into account edge width692        ex1 -= wHalf;693        ex2 += wHalf;694        ey1 -= wHalf;695        ey2 += wHalf;696 697        updateBounds( bounds, ex1, ey1, ex2, ey2 );698      } // headless or style disabled699 700    } // edges701 702    // handle edge arrow size703    /////////////////////////704 705    if( styleEnabled && options.includeEdges && isEdge ){706      updateBoundsFromArrow( bounds, ele, 'mid-source', options );707      updateBoundsFromArrow( bounds, ele, 'mid-target', options );708      updateBoundsFromArrow( bounds, ele, 'source', options );709      updateBoundsFromArrow( bounds, ele, 'target', options );710    }711 712    // ghost713    ////////714 715    if( styleEnabled ){716      let ghost = ele.pstyle('ghost').value === 'yes';717 718      if( ghost ){719        let gx = ele.pstyle('ghost-offset-x').pfValue;720        let gy = ele.pstyle('ghost-offset-y').pfValue;721 722        updateBounds( bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy );723      }724    }725 726    // always store the body bounds separately from the labels727    let bbBody = _p.bodyBounds = _p.bodyBounds || {};728    assignBoundingBox(bbBody, bounds);729    expandBoundingBoxSides(bbBody, manualExpansion);730    expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies731 732    // overlay733    //////////734 735    if( styleEnabled ){736      ex1 = bounds.x1;737      ex2 = bounds.x2;738      ey1 = bounds.y1;739      ey2 = bounds.y2;740 741      updateBounds( bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding );742    }743 744    // always store the body bounds separately from the labels745    let bbOverlay = _p.overlayBounds = _p.overlayBounds || {};746    assignBoundingBox(bbOverlay, bounds);747    expandBoundingBoxSides(bbOverlay, manualExpansion);748    expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies749 750    // handle label dimensions751    //////////////////////////752 753    let bbLabels = _p.labelBounds = _p.labelBounds || {};754 755    if( bbLabels.all != null ){756      clearBoundingBox(bbLabels.all);757    } else {758      bbLabels.all = makeBoundingBox();759    }760 761    if( styleEnabled && options.includeLabels ){762      if( options.includeMainLabels ){763        updateBoundsFromLabel( bounds, ele, null, options );764      }765 766      if( isEdge ){767        if( options.includeSourceLabels ){768          updateBoundsFromLabel( bounds, ele, 'source', options );769        }770 771        if( options.includeTargetLabels ){772          updateBoundsFromLabel( bounds, ele, 'target', options );773        }774      }775    } // style enabled for labels776  } // if displayed777 778 779  bounds.x1 = noninf( bounds.x1 );780  bounds.y1 = noninf( bounds.y1 );781  bounds.x2 = noninf( bounds.x2 );782  bounds.y2 = noninf( bounds.y2 );783  bounds.w = noninf( bounds.x2 - bounds.x1 );784  bounds.h = noninf( bounds.y2 - bounds.y1 );785 786  if( bounds.w > 0 && bounds.h > 0 && displayed ){787    expandBoundingBoxSides( bounds, manualExpansion );788 789    // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides790    expandBoundingBox( bounds, 1 );791  }792 793  return bounds;794};795 796let getKey = function( opts ){797  let i = 0;798  let tf = val => (val ? 1 : 0) << i++;799  let key = 0;800 801  key += tf( opts.incudeNodes );802  key += tf( opts.includeEdges );803  key += tf( opts.includeLabels );804  key += tf( opts.includeMainLabels );805  key += tf( opts.includeSourceLabels );806  key += tf( opts.includeTargetLabels );807  key += tf( opts.includeOverlays );808  key += tf( opts.includeOutlines );809 810  return key;811};812 813let getBoundingBoxPosKey = ele => {814  let r = x => Math.round(x);815 816  if( ele.isEdge() ){817    let p1 = ele.source().position();818    let p2 = ele.target().position();819 820    return hashIntsArray([ r(p1.x), r(p1.y), r(p2.x), r(p2.y) ]);821  } else {822    let p = ele.position();823 824    return hashIntsArray([ r(p.x), r(p.y) ]);825  }826};827 828let cachedBoundingBoxImpl = function( ele, opts ){829  let _p = ele._private;830  let bb;831  let isEdge = ele.isEdge();832  let key = opts == null ? defBbOptsKey : getKey( opts );833  let usingDefOpts = key === defBbOptsKey;834 835  if( _p.bbCache == null ){836    bb = boundingBoxImpl( ele, defBbOpts );837 838    _p.bbCache = bb;839    _p.bbCachePosKey = getBoundingBoxPosKey( ele );840  } else {841    bb = _p.bbCache;842  }843 844  // not using def opts => need to build up bb from combination of sub bbs845  if( !usingDefOpts ){846    let isNode = ele.isNode();847 848    bb = makeBoundingBox();849 850    if( (opts.includeNodes && isNode) || (opts.includeEdges && !isNode) ){851      if( opts.includeOverlays ){852        updateBoundsFromBox(bb, _p.overlayBounds);853      } else {854        updateBoundsFromBox(bb, _p.bodyBounds);855      }856    }857 858    if( opts.includeLabels ){859      if( opts.includeMainLabels && (!isEdge || (opts.includeSourceLabels && opts.includeTargetLabels)) ){860        updateBoundsFromBox(bb, _p.labelBounds.all);861      } else {862        if( opts.includeMainLabels ){863          updateBoundsFromBox(bb, _p.labelBounds.mainRot);864        }865 866        if( opts.includeSourceLabels ){867          updateBoundsFromBox(bb, _p.labelBounds.sourceRot);868        }869 870        if( opts.includeTargetLabels ){871          updateBoundsFromBox(bb, _p.labelBounds.targetRot);872        }873      }874    }875 876    bb.w = bb.x2 - bb.x1;877    bb.h = bb.y2 - bb.y1;878  }879 880  return bb;881};882 883let defBbOpts = {884  includeNodes: true,885  includeEdges: true,886  includeLabels: true,887  includeMainLabels: true,888  includeSourceLabels: true,889  includeTargetLabels: true,890  includeOverlays: true,891  includeUnderlays: true,892  includeOutlines: true,893  useCache: true894};895 896const defBbOptsKey = getKey( defBbOpts );897 898const filledBbOpts = defaults( defBbOpts );899 900elesfn.boundingBox = function( options ){901  let bounds;902 903  let useCache = (options === undefined || options.useCache === undefined || options.useCache === true);904 905  let isDirty = memoize(ele => {906    let _p = ele._private;907 908    return _p.bbCache == null || _p.styleDirty || _p.bbCachePosKey !== getBoundingBoxPosKey(ele);909  }, ele => ele.id());910 911  // the main usecase is ele.boundingBox() for a single element with no/def options912  // specified s.t. the cache is used, so check for this case to make it faster by913  // avoiding the overhead of the rest of the function914  if (useCache && this.length === 1 && !isDirty(this[0])) {915    if (options === undefined) {916      options = defBbOpts;917    } else {918      options = filledBbOpts( options );919    }920 921    bounds = cachedBoundingBoxImpl(this[0], options);922  } else {923    bounds = makeBoundingBox();924 925    options = options || defBbOpts;926 927    let opts = filledBbOpts(options);928 929    let eles = this;930    let cy = eles.cy();931    let styleEnabled = cy.styleEnabled();932 933    // cache the isDirty state for all eles, edges first since they depend on node state934    this.edges().forEach(isDirty);935    this.nodes().forEach(isDirty);936 937    if(styleEnabled) {938      this.recalculateRenderedStyle(useCache);939    }940 941    this.updateCompoundBounds(!useCache);942 943    for (let i = 0; i < eles.length; i++) {944      let ele = eles[i];945 946      if (isDirty(ele)) {947        ele.dirtyBoundingBoxCache();948      }949 950      updateBoundsFromBox(bounds, cachedBoundingBoxImpl(ele, opts));951    }952  }953 954  bounds.x1 = noninf( bounds.x1 );955  bounds.y1 = noninf( bounds.y1 );956  bounds.x2 = noninf( bounds.x2 );957  bounds.y2 = noninf( bounds.y2 );958  bounds.w = noninf( bounds.x2 - bounds.x1 );959  bounds.h = noninf( bounds.y2 - bounds.y1 );960 961  return bounds;962};963 964elesfn.dirtyBoundingBoxCache = function(){965  for( let i = 0; i < this.length; i++ ){966    let _p = this[i]._private;967 968    _p.bbCache = null;969    _p.bbCachePosKey = null;970    _p.bodyBounds = null;971    _p.overlayBounds = null;972    _p.labelBounds.all = null;973    _p.labelBounds.source = null;974    _p.labelBounds.target = null;975    _p.labelBounds.main = null;976    _p.labelBounds.sourceRot = null;977    _p.labelBounds.targetRot = null;978    _p.labelBounds.mainRot = null;979    _p.arrowBounds.source = null;980    _p.arrowBounds.target = null;981    _p.arrowBounds['mid-source'] = null;982    _p.arrowBounds['mid-target'] = null;983  }984 985  this.emitAndNotify('bounds');986 987  return this;988};989 990// private helper to get bounding box for custom node positions991// - good for perf in certain cases but currently requires dirtying the rendered style992// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...993// - try to use for only things like discrete layouts where the node position would change anyway994elesfn.boundingBoxAt = function( fn ){995  let nodes = this.nodes();996  let cy = this.cy();997  let hasCompoundNodes = cy.hasCompoundNodes();998  let parents = cy.collection();999 1000  if( hasCompoundNodes ){1001    parents = nodes.filter(node => node.isParent());1002    nodes = nodes.not(parents);1003  }1004 1005  if( is.plainObject( fn ) ){1006    let obj = fn;1007 1008    fn = function(){ return obj; };1009  }1010 1011  let storeOldPos = (node, i) => node._private.bbAtOldPos = fn(node, i);1012  let getOldPos = (node) => node._private.bbAtOldPos;1013 1014  cy.startBatch();1015 1016  (1017    nodes1018    .forEach(storeOldPos)1019    .silentPositions(fn)1020  );1021 1022  if( hasCompoundNodes ){1023    parents.dirtyCompoundBoundsCache();1024    parents.dirtyBoundingBoxCache();1025    parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle1026  }1027 1028  let bb = copyBoundingBox( this.boundingBox({ useCache: false }) );1029 1030  nodes.silentPositions(getOldPos);1031 1032  if( hasCompoundNodes ){1033    parents.dirtyCompoundBoundsCache();1034    parents.dirtyBoundingBoxCache();1035    parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle1036  }1037 1038  cy.endBatch();1039 1040  return bb;1041};1042 1043fn.boundingbox = fn.bb = fn.boundingBox;1044fn.renderedBoundingbox = fn.renderedBoundingBox;1045 1046export default elesfn;1047 
basant307/AI_Governance_Project · CoolFace