cytoscape
Advanced tools
Comparing version 3.4.3 to 3.5.0
{ | ||
"build/cytoscape.umd.js": { | ||
"bundled": 893638, | ||
"minified": 330654, | ||
"gzipped": 102819 | ||
"bundled": 897790, | ||
"minified": 332573, | ||
"gzipped": 103470 | ||
}, | ||
"build/cytoscape.cjs.js": { | ||
"bundled": 823278, | ||
"minified": 347723, | ||
"gzipped": 104838 | ||
"bundled": 827112, | ||
"minified": 349788, | ||
"gzipped": 105510 | ||
}, | ||
"build/cytoscape.esm.js": { | ||
"bundled": 823105, | ||
"minified": 347580, | ||
"gzipped": 104803, | ||
"bundled": 826939, | ||
"minified": 349645, | ||
"gzipped": 105476, | ||
"treeshaked": { | ||
"rollup": { | ||
"code": 325392, | ||
"code": 327269, | ||
"import_statements": 51 | ||
}, | ||
"webpack": { | ||
"code": 327218 | ||
"code": 329136 | ||
} | ||
@@ -24,0 +24,0 @@ } |
@@ -48,3 +48,3 @@ <!-- | ||
Fork/clone this JSBin demo and reproduce your issue so that your issue can be addressed quickly: | ||
http://jsbin.com/teworah | ||
http://jsbin.com/fiqugiq | ||
@@ -51,0 +51,0 @@ If your code to reproduce is only two or three lines, you can write it in the issue instead. Format your code in backtick code blocks like this: |
{ | ||
"name": "cytoscape", | ||
"version": "3.4.3", | ||
"version": "3.5.0", | ||
"license": "MIT", | ||
@@ -81,3 +81,4 @@ "description": "Graph theory (a.k.a. network) library for analysis and visualisation", | ||
"dist": "cross-env NODE_ENV=production run-s build dist:*", | ||
"release": "run-s copyright dist", | ||
"release": "run-s copyright dist docs", | ||
"postpublish": "run-s docs:push", | ||
"watch": "run-s watch:fast", | ||
@@ -108,4 +109,4 @@ "watch:sync": "livereload \"build, debug\"", | ||
"devDependencies": { | ||
"@babel/core": "^7.1.2", | ||
"@babel/preset-env": "^7.1.0", | ||
"@babel/core": "^7.3.4", | ||
"@babel/preset-env": "^7.3.4", | ||
"benchmark": "^2.1.4", | ||
@@ -121,3 +122,3 @@ "bluebird": "^3.5.0", | ||
"handlebars": "^4.0.5", | ||
"highlight.js": "^9.3.0", | ||
"highlight.js": "^9.15.6", | ||
"http-server": "^0.11.1", | ||
@@ -127,3 +128,3 @@ "jsonlint": "^1.6.2", | ||
"marked": "^0.6.0", | ||
"mocha": "^5.2.0", | ||
"mocha": "^6.0.2", | ||
"npm-run-all": "^4.1.5", | ||
@@ -134,3 +135,3 @@ "rimraf": "^2.6.2", | ||
"rollup-plugin-commonjs": "^9.1.0", | ||
"rollup-plugin-license": "^0.8.0", | ||
"rollup-plugin-license": "^0.8.1", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
@@ -137,0 +138,0 @@ "rollup-plugin-replace": "^2.0.0", |
@@ -12,3 +12,3 @@ <div style="text-align: center;" align="center"><img style="width: 200px; height: 200px;" src="https://raw.githubusercontent.com/cytoscape/cytoscape.js/unstable/documentation/img/cytoscape-logo.png" width="200" height="200"></img></div> | ||
[![Download](https://img.shields.io/npm/v/cytoscape.svg?label=Download)](https://github.com/cytoscape/cytoscape.js/tree/master/dist) | ||
[![Extensions](https://img.shields.io/badge/Extensions-40-blue.svg)](http://js.cytoscape.org/#extensions) | ||
[![Extensions](https://img.shields.io/badge/Extensions-42-blue.svg)](http://js.cytoscape.org/#extensions) | ||
[![npm installs](https://img.shields.io/npm/dm/cytoscape.svg?label=npm%20installs)](https://www.npmjs.com/package/cytoscape) | ||
@@ -15,0 +15,0 @@ [![master branch tests](https://img.shields.io/travis/cytoscape/cytoscape.js/master.svg?label=master%20branch)](https://travis-ci.org/cytoscape/cytoscape.js) |
@@ -6,7 +6,15 @@ import Set from '../set'; | ||
classes: function( classes ){ | ||
if( !is.array( classes ) ){ | ||
let self = this; | ||
if( classes === undefined ){ | ||
let ret = []; | ||
self[0]._private.classes.forEach(cls => ret.push(cls)); | ||
return ret; | ||
} else if( !is.array( classes ) ){ | ||
// extract classes from string | ||
classes = ( classes || '' ).match( /\S+/g ) || []; | ||
} | ||
let self = this; | ||
let changed = []; | ||
@@ -133,2 +141,4 @@ let classesSet = new Set( classes ); | ||
elesfn.className = elesfn.classNames = elesfn.classes; | ||
export default elesfn; |
import * as is from '../../is'; | ||
import { assignBoundingBox, assignShiftToBoundingBox, clearBoundingBox, expandBoundingBox, makeBoundingBox } from '../../math'; | ||
import { assignBoundingBox, assignShiftToBoundingBox, clearBoundingBox, expandBoundingBox, makeBoundingBox, copyBoundingBox } from '../../math'; | ||
import { defaults, getPrefixedProperty, hashIntsArray } from '../../util'; | ||
@@ -652,4 +652,2 @@ | ||
if( bounds.w > 0 && bounds.h > 0 && displayed ){ | ||
@@ -702,3 +700,3 @@ expandBoundingBox( bounds, manualExpansion ); | ||
if( !isPosKeySame ){ | ||
ele.recalculateRenderedStyle( false ); | ||
ele.recalculateRenderedStyle(); | ||
} | ||
@@ -901,3 +899,3 @@ | ||
let bb = this.boundingBox({ useCache: false }); | ||
let bb = copyBoundingBox( this.boundingBox({ useCache: false }) ); | ||
@@ -904,0 +902,0 @@ nodes.silentPositions(getOldPos); |
@@ -11,3 +11,5 @@ import * as util from '../util'; | ||
touchTapThreshold: 8, | ||
wheelSensitivity: 1 | ||
wheelSensitivity: 1, | ||
debug: false, | ||
showFps: false | ||
}); | ||
@@ -14,0 +16,0 @@ |
import * as math from '../../../../math'; | ||
import * as is from '../../../../is'; | ||
import * as util from '../../../../util'; | ||
import Map from '../../../../map'; | ||
var BRp = {}; | ||
let BRp = {}; | ||
BRp.findHaystackPoints = function( edges ){ | ||
for( var i = 0; i < edges.length; i++ ){ | ||
var edge = edges[i]; | ||
var _p = edge._private; | ||
var rs = _p.rscratch; | ||
for( let i = 0; i < edges.length; i++ ){ | ||
let edge = edges[i]; | ||
let _p = edge._private; | ||
let rs = _p.rscratch; | ||
if( !rs.haystack ){ | ||
var angle = Math.random() * 2 * Math.PI; | ||
let angle = Math.random() * 2 * Math.PI; | ||
@@ -21,3 +22,3 @@ rs.source = { | ||
var angle = Math.random() * 2 * Math.PI; | ||
angle = Math.random() * 2 * Math.PI; | ||
@@ -31,12 +32,12 @@ rs.target = { | ||
var src = _p.source; | ||
var tgt = _p.target; | ||
var srcPos = src.position(); | ||
var tgtPos = tgt.position(); | ||
var srcW = src.width(); | ||
var tgtW = tgt.width(); | ||
var srcH = src.height(); | ||
var tgtH = tgt.height(); | ||
var radius = edge.pstyle( 'haystack-radius' ).value; | ||
var halfRadius = radius / 2; // b/c have to half width/height | ||
let src = _p.source; | ||
let tgt = _p.target; | ||
let srcPos = src.position(); | ||
let tgtPos = tgt.position(); | ||
let srcW = src.width(); | ||
let tgtW = tgt.width(); | ||
let srcH = src.height(); | ||
let tgtH = tgt.height(); | ||
let radius = edge.pstyle('haystack-radius').value; | ||
let halfRadius = radius / 2; // b/c have to half width/height | ||
@@ -54,3 +55,3 @@ rs.haystackPts = rs.allpts = [ | ||
// always override as haystack in case set to different type previously | ||
rs.edgeType = rs.lastCurveStyle = 'haystack'; | ||
rs.edgeType = 'haystack'; | ||
rs.haystack = true; | ||
@@ -65,4 +66,455 @@ | ||
BRp.findSegmentsPoints = function( edge, pairInfo ){ | ||
// Segments (multiple straight lines) | ||
const rs = edge._private.rscratch; | ||
const { posPts, intersectionPts, vectorNormInverse } = pairInfo; | ||
const edgeDistances = edge.pstyle('edge-distances').value; | ||
const segmentWs = edge.pstyle( 'segment-weights' ); | ||
const segmentDs = edge.pstyle( 'segment-distances' ); | ||
const segmentsN = Math.min( segmentWs.pfValue.length, segmentDs.pfValue.length ); | ||
rs.edgeType = 'segments'; | ||
rs.segpts = []; | ||
for( let s = 0; s < segmentsN; s++ ){ | ||
let w = segmentWs.pfValue[ s ]; | ||
let d = segmentDs.pfValue[ s ]; | ||
let w1 = 1 - w; | ||
let w2 = w; | ||
let midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts; | ||
let adjustedMidpt = { | ||
x: midptPts.x1 * w1 + midptPts.x2 * w2, | ||
y: midptPts.y1 * w1 + midptPts.y2 * w2 | ||
}; | ||
rs.segpts.push( | ||
adjustedMidpt.x + vectorNormInverse.x * d, | ||
adjustedMidpt.y + vectorNormInverse.y * d | ||
); | ||
} | ||
}; | ||
BRp.findLoopPoints = function( edge, pairInfo, i, edgeIsUnbundled ){ | ||
// Self-edge | ||
const rs = edge._private.rscratch; | ||
const { dirCounts, srcPos } = pairInfo; | ||
const ctrlptDists = edge.pstyle( 'control-point-distances' ); | ||
const ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined; | ||
const loopDir = edge.pstyle('loop-direction').pfValue; | ||
const loopSwp = edge.pstyle('loop-sweep').pfValue; | ||
const stepSize = edge.pstyle( 'control-point-step-size' ).pfValue; | ||
rs.edgeType = 'self'; | ||
let j = i; | ||
let loopDist = stepSize; | ||
if( edgeIsUnbundled ){ | ||
j = 0; | ||
loopDist = ctrlptDist; | ||
} | ||
let loopAngle = loopDir - Math.PI / 2; | ||
let outAngle = loopAngle - loopSwp / 2; | ||
let inAngle = loopAngle + loopSwp / 2; | ||
// increase by step size for overlapping loops, keyed on direction and sweep values | ||
let dc = String(loopDir + '_' + loopSwp); | ||
j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc]; | ||
rs.ctrlpts = [ | ||
srcPos.x + Math.cos(outAngle) * 1.4 * loopDist * (j / 3 + 1), | ||
srcPos.y + Math.sin(outAngle) * 1.4 * loopDist * (j / 3 + 1), | ||
srcPos.x + Math.cos(inAngle) * 1.4 * loopDist * (j / 3 + 1), | ||
srcPos.y + Math.sin(inAngle) * 1.4 * loopDist * (j / 3 + 1) | ||
]; | ||
}; | ||
BRp.findCompoundLoopPoints = function( edge, pairInfo, i, edgeIsUnbundled ){ | ||
// Compound edge | ||
const rs = edge._private.rscratch; | ||
rs.edgeType = 'compound'; | ||
const { srcPos, tgtPos, srcW, srcH, tgtW, tgtH } = pairInfo; | ||
const stepSize = edge.pstyle('control-point-step-size').pfValue; | ||
const ctrlptDists = edge.pstyle('control-point-distances'); | ||
const ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined; | ||
let j = i; | ||
let loopDist = stepSize; | ||
if( edgeIsUnbundled ){ | ||
j = 0; | ||
loopDist = ctrlptDist; | ||
} | ||
let loopW = 50; | ||
let loopaPos = { | ||
x: srcPos.x - srcW / 2, | ||
y: srcPos.y - srcH / 2 | ||
}; | ||
let loopbPos = { | ||
x: tgtPos.x - tgtW / 2, | ||
y: tgtPos.y - tgtH / 2 | ||
}; | ||
let loopPos = { | ||
x: Math.min( loopaPos.x, loopbPos.x ), | ||
y: Math.min( loopaPos.y, loopbPos.y ) | ||
}; | ||
// avoids cases with impossible beziers | ||
let minCompoundStretch = 0.5; | ||
let compoundStretchA = Math.max( minCompoundStretch, Math.log( srcW * 0.01 ) ); | ||
let compoundStretchB = Math.max( minCompoundStretch, Math.log( tgtW * 0.01 ) ); | ||
rs.ctrlpts = [ | ||
loopPos.x, | ||
loopPos.y - (1 + Math.pow( loopW, 1.12 ) / 100) * loopDist * (j / 3 + 1) * compoundStretchA, | ||
loopPos.x - (1 + Math.pow( loopW, 1.12 ) / 100) * loopDist * (j / 3 + 1) * compoundStretchB, | ||
loopPos.y | ||
]; | ||
}; | ||
BRp.findStraightEdgePoints = function( edge ){ | ||
// Straight edge within bundle | ||
edge._private.rscratch.edgeType = 'straight'; | ||
}; | ||
BRp.findBezierPoints = function( edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped ){ | ||
const rs = edge._private.rscratch; | ||
const { vectorNormInverse, posPts, intersectionPts } = pairInfo; | ||
const edgeDistances = edge.pstyle('edge-distances').value; | ||
const stepSize = edge.pstyle('control-point-step-size').pfValue; | ||
const ctrlptDists = edge.pstyle('control-point-distances'); | ||
const ctrlptWs = edge.pstyle('control-point-weights'); | ||
const bezierN = ctrlptDists && ctrlptWs ? Math.min( ctrlptDists.value.length, ctrlptWs.value.length ) : 1; | ||
let ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined; | ||
let ctrlptWeight = ctrlptWs.value[0]; | ||
// (Multi)bezier | ||
const multi = edgeIsUnbundled; | ||
rs.edgeType = multi ? 'multibezier' : 'bezier'; | ||
rs.ctrlpts = []; | ||
for( let b = 0; b < bezierN; b++ ){ | ||
let normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1); | ||
let manctrlptDist; | ||
let sign = math.signum( normctrlptDist ); | ||
if( multi ){ | ||
ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[ b ] : stepSize; // fall back on step size | ||
ctrlptWeight = ctrlptWs.value[ b ]; | ||
} | ||
if( edgeIsUnbundled ){ // multi or single unbundled | ||
manctrlptDist = ctrlptDist; | ||
} else { | ||
manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined; | ||
} | ||
let distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist; | ||
let w1 = 1 - ctrlptWeight; | ||
let w2 = ctrlptWeight; | ||
let midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts; | ||
let adjustedMidpt = { | ||
x: midptPts.x1 * w1 + midptPts.x2 * w2, | ||
y: midptPts.y1 * w1 + midptPts.y2 * w2 | ||
}; | ||
rs.ctrlpts.push( | ||
adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, | ||
adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint | ||
); | ||
} | ||
}; | ||
BRp.findTaxiPoints = function( edge, pairInfo ){ | ||
// Taxicab geometry with two turns maximum | ||
const rs = edge._private.rscratch; | ||
rs.edgeType = 'segments'; | ||
const VERTICAL = 'vertical'; | ||
const HORIZONTAL = 'horizontal'; | ||
const LEFTWARD = 'leftward'; | ||
const RIGHTWARD = 'rightward'; | ||
const DOWNWARD = 'downward'; | ||
const UPWARD = 'upward'; | ||
const AUTO = 'auto'; | ||
const { posPts, srcW, srcH, tgtW, tgtH } = pairInfo; | ||
const edgeDistances = edge.pstyle('edge-distances').value; | ||
const dIncludesNodeBody = edgeDistances !== 'node-position'; | ||
let taxiDir = edge.pstyle('taxi-direction').value; | ||
let rawTaxiDir = taxiDir; // unprocessed value | ||
const taxiTurn = edge.pstyle('taxi-turn'); | ||
const taxiTurnPfVal = taxiTurn.pfValue; | ||
let minD = edge.pstyle('taxi-turn-min-distance').pfValue; | ||
const turnIsPercent = taxiTurn.units === '%'; | ||
const dw = (dIncludesNodeBody ? (srcW + tgtW)/2 : 0); | ||
const dh = (dIncludesNodeBody ? (srcH + tgtH)/2 : 0); | ||
const pdx = posPts.x2 - posPts.x1; | ||
const pdy = posPts.y2 - posPts.y1; | ||
// take away the effective w/h from the magnitude of the delta value | ||
const subDWH = (dxy, dwh) => { | ||
if( dxy > 0 ){ | ||
return Math.max(dxy - dwh, 0); | ||
} else { | ||
return Math.min(dxy + dwh, 0); | ||
} | ||
}; | ||
const dx = subDWH(pdx, dw); | ||
const dy = subDWH(pdy, dh); | ||
let isExplicitDir = false; | ||
if( taxiDir === AUTO ){ | ||
taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL; | ||
} else if( taxiDir === UPWARD || taxiDir === DOWNWARD ){ | ||
taxiDir = VERTICAL; | ||
isExplicitDir = true; | ||
} else if( taxiDir === LEFTWARD || taxiDir === RIGHTWARD ){ | ||
taxiDir = HORIZONTAL; | ||
isExplicitDir = true; | ||
} | ||
const isVert = taxiDir === VERTICAL; | ||
let l = isVert ? dy : dx; | ||
let pl = isVert ? pdy : pdx; | ||
let sgnL = math.signum(pl); | ||
let forcedDir = false; | ||
if( | ||
!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction | ||
&& ( | ||
(rawTaxiDir === DOWNWARD && pl < 0) | ||
|| (rawTaxiDir === UPWARD && pl > 0) | ||
|| (rawTaxiDir === LEFTWARD && pl > 0) | ||
|| (rawTaxiDir === RIGHTWARD && pl < 0) | ||
) | ||
){ | ||
sgnL *= -1; | ||
l = sgnL * Math.abs(l); | ||
forcedDir = true; | ||
} | ||
let d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL; | ||
const getIsTooClose = d => Math.abs(d) < minD || Math.abs(d) >= Math.abs(l); | ||
const isTooCloseSrc = getIsTooClose(d); | ||
const isTooCloseTgt = getIsTooClose(l - d); | ||
const isTooClose = isTooCloseSrc || isTooCloseTgt; | ||
if( isTooClose && !forcedDir ){ // non-ideal routing | ||
if( isVert ){ // vertical fallbacks | ||
const lShapeInsideSrc = Math.abs(pl) <= srcH/2; | ||
const lShapeInsideTgt = Math.abs(pdx) <= tgtW/2; | ||
if( lShapeInsideSrc ){ // horizontal Z-shape (direction not respected) | ||
let x = (posPts.x1 + posPts.x2)/2; | ||
let { y1, y2 } = posPts; | ||
rs.segpts = [ | ||
x, y1, | ||
x, y2 | ||
]; | ||
} else if( lShapeInsideTgt ){ // vertical Z-shape (distance not respected) | ||
let y = (posPts.y1 + posPts.y2)/2; | ||
let { x1, x2 } = posPts; | ||
rs.segpts = [ | ||
x1, y, | ||
x2, y | ||
]; | ||
} else { // L-shape fallback (turn distance not respected, but works well with tree siblings) | ||
rs.segpts = [ | ||
posPts.x1, posPts.y2 | ||
]; | ||
} | ||
} else { // horizontal fallbacks | ||
const lShapeInsideSrc = Math.abs(pl) <= srcW/2; | ||
const lShapeInsideTgt = Math.abs(pdy) <= tgtH/2; | ||
if( lShapeInsideSrc ){ // vertical Z-shape (direction not respected) | ||
let y = (posPts.y1 + posPts.y2)/2; | ||
let { x1, x2 } = posPts; | ||
rs.segpts = [ | ||
x1, y, | ||
x2, y | ||
]; | ||
} else if( lShapeInsideTgt ){ // horizontal Z-shape (turn distance not respected) | ||
let x = (posPts.x1 + posPts.x2)/2; | ||
let { y1, y2 } = posPts; | ||
rs.segpts = [ | ||
x, y1, | ||
x, y2 | ||
]; | ||
} else { // L-shape (turn distance not respected, but works well for tree siblings) | ||
rs.segpts = [ | ||
posPts.x2, | ||
posPts.y1 | ||
]; | ||
} | ||
} | ||
} else { // ideal routing | ||
if( isVert ){ | ||
let y = posPts.y1 + d + (dIncludesNodeBody ? srcH/2 * sgnL : 0); | ||
let { x1, x2 } = posPts; | ||
rs.segpts = [ | ||
x1, y, | ||
x2, y | ||
]; | ||
} else { // horizontal | ||
let x = posPts.x1 + d + (dIncludesNodeBody ? srcW/2 * sgnL : 0); | ||
let { y1, y2 } = posPts; | ||
rs.segpts = [ | ||
x, y1, | ||
x, y2 | ||
]; | ||
} | ||
} | ||
}; | ||
BRp.tryToCorrectInvalidPoints = function( edge, pairInfo ){ | ||
const rs = edge._private.rscratch; | ||
// can only correct beziers for now... | ||
if( rs.edgeType === 'bezier' ){ | ||
const { srcPos, tgtPos, srcW, srcH, tgtW, tgtH, srcShape, tgtShape } = pairInfo; | ||
let badStart = !is.number( rs.startX ) || !is.number( rs.startY ); | ||
let badAStart = !is.number( rs.arrowStartX ) || !is.number( rs.arrowStartY ); | ||
let badEnd = !is.number( rs.endX ) || !is.number( rs.endY ); | ||
let badAEnd = !is.number( rs.arrowEndX ) || !is.number( rs.arrowEndY ); | ||
let minCpADistFactor = 3; | ||
let arrowW = this.getArrowWidth( edge.pstyle( 'width' ).pfValue, edge.pstyle( 'arrow-scale' ).value ) | ||
* this.arrowShapeWidth; | ||
let minCpADist = minCpADistFactor * arrowW; | ||
let startACpDist = math.dist( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.startX, y: rs.startY } ); | ||
let closeStartACp = startACpDist < minCpADist; | ||
let endACpDist = math.dist( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.endX, y: rs.endY } ); | ||
let closeEndACp = endACpDist < minCpADist; | ||
let overlapping = false; | ||
if( badStart || badAStart || closeStartACp ){ | ||
overlapping = true; | ||
// project control point along line from src centre to outside the src shape | ||
// (otherwise intersection will yield nothing) | ||
let cpD = { // delta | ||
x: rs.ctrlpts[0] - srcPos.x, | ||
y: rs.ctrlpts[1] - srcPos.y | ||
}; | ||
let cpL = Math.sqrt( cpD.x * cpD.x + cpD.y * cpD.y ); // length of line | ||
let cpM = { // normalised delta | ||
x: cpD.x / cpL, | ||
y: cpD.y / cpL | ||
}; | ||
let radius = Math.max( srcW, srcH ); | ||
let cpProj = { // *2 radius guarantees outside shape | ||
x: rs.ctrlpts[0] + cpM.x * 2 * radius, | ||
y: rs.ctrlpts[1] + cpM.y * 2 * radius | ||
}; | ||
let srcCtrlPtIntn = srcShape.intersectLine( | ||
srcPos.x, | ||
srcPos.y, | ||
srcW, | ||
srcH, | ||
cpProj.x, | ||
cpProj.y, | ||
0 | ||
); | ||
if( closeStartACp ){ | ||
rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist); | ||
rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist); | ||
} else { | ||
rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist; | ||
rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist; | ||
} | ||
} | ||
if( badEnd || badAEnd || closeEndACp ){ | ||
overlapping = true; | ||
// project control point along line from tgt centre to outside the tgt shape | ||
// (otherwise intersection will yield nothing) | ||
let cpD = { // delta | ||
x: rs.ctrlpts[0] - tgtPos.x, | ||
y: rs.ctrlpts[1] - tgtPos.y | ||
}; | ||
let cpL = Math.sqrt( cpD.x * cpD.x + cpD.y * cpD.y ); // length of line | ||
let cpM = { // normalised delta | ||
x: cpD.x / cpL, | ||
y: cpD.y / cpL | ||
}; | ||
let radius = Math.max( srcW, srcH ); | ||
let cpProj = { // *2 radius guarantees outside shape | ||
x: rs.ctrlpts[0] + cpM.x * 2 * radius, | ||
y: rs.ctrlpts[1] + cpM.y * 2 * radius | ||
}; | ||
let tgtCtrlPtIntn = tgtShape.intersectLine( | ||
tgtPos.x, | ||
tgtPos.y, | ||
tgtW, | ||
tgtH, | ||
cpProj.x, | ||
cpProj.y, | ||
0 | ||
); | ||
if( closeEndACp ){ | ||
rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - endACpDist); | ||
rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - endACpDist); | ||
} else { | ||
rs.ctrlpts[0] = tgtCtrlPtIntn[0] + cpM.x * minCpADist; | ||
rs.ctrlpts[1] = tgtCtrlPtIntn[1] + cpM.y * minCpADist; | ||
} | ||
} | ||
if( overlapping ){ | ||
// recalc endpts | ||
this.findEndpoints( edge ); | ||
} | ||
} | ||
}; | ||
BRp.storeAllpts = function( edge ){ | ||
var rs = edge._private.rscratch; | ||
let rs = edge._private.rscratch; | ||
@@ -74,3 +526,3 @@ if( rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' ){ | ||
for( var b = 0; b + 1 < rs.ctrlpts.length; b += 2 ){ | ||
for( let b = 0; b + 1 < rs.ctrlpts.length; b += 2 ){ | ||
// ctrl pt itself | ||
@@ -87,3 +539,3 @@ rs.allpts.push( rs.ctrlpts[ b ], rs.ctrlpts[ b + 1] ); | ||
var m, mt; | ||
let m, mt; | ||
if( rs.ctrlpts.length / 2 % 2 === 0 ){ | ||
@@ -117,4 +569,4 @@ m = rs.allpts.length / 2 - 1; | ||
if( rs.segpts.length % 4 === 0 ){ | ||
var i2 = rs.segpts.length / 2; | ||
var i1 = i2 - 2; | ||
let i2 = rs.segpts.length / 2; | ||
let i1 = i2 - 2; | ||
@@ -124,3 +576,3 @@ rs.midX = ( rs.segpts[ i1 ] + rs.segpts[ i2 ] ) / 2; | ||
} else { | ||
var i1 = rs.segpts.length / 2 - 1; | ||
let i1 = rs.segpts.length / 2 - 1; | ||
@@ -136,5 +588,7 @@ rs.midX = rs.segpts[ i1 ]; | ||
BRp.checkForInvalidEdgeWarning = function( edge ){ | ||
var rs = edge._private.rscratch; | ||
let rs = edge[0]._private.rscratch; | ||
if( !is.number(rs.startX) || !is.number(rs.startY) || !is.number(rs.endX) || !is.number(rs.endY) ){ | ||
if( rs.nodesOverlap || (is.number(rs.startX) && is.number(rs.startY) && is.number(rs.endX) && is.number(rs.endY)) ){ | ||
rs.loggedErr = false; | ||
} else { | ||
if( !rs.loggedErr ){ | ||
@@ -144,4 +598,2 @@ rs.loggedErr = true; | ||
} | ||
} else { | ||
rs.loggedErr = false; | ||
} | ||
@@ -153,18 +605,14 @@ }; | ||
var r = this; | ||
var cy = r.cy; | ||
var hasCompounds = cy.hasCompoundNodes(); | ||
var hashTable = {}; | ||
var pairIds = []; | ||
var haystackEdges = []; | ||
let r = this; | ||
let cy = r.cy; | ||
let hasCompounds = cy.hasCompoundNodes(); | ||
let hashTable = new Map(); | ||
let pairIds = []; | ||
let haystackEdges = []; | ||
// create a table of edge (src, tgt) => list of edges between them | ||
var pairId; | ||
for( var i = 0; i < edges.length; i++ ){ | ||
var edge = edges[ i ]; | ||
var _p = edge._private; | ||
var data = _p.data; | ||
var curveStyle = edge.pstyle( 'curve-style' ).value; | ||
var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight'; | ||
var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier'; | ||
for( let i = 0; i < edges.length; i++ ){ | ||
let edge = edges[i]; | ||
let _p = edge._private; | ||
let curveStyle = edge.pstyle('curve-style').value; | ||
@@ -182,21 +630,20 @@ // ignore edges who are not to be displayed | ||
var srcId = data.source; | ||
var tgtId = data.target; | ||
let edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi'; | ||
let edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier'; | ||
let srcIndex = _p.source.poolIndex(); | ||
let tgtIndex = _p.target.poolIndex(); | ||
pairId = srcId > tgtId ? | ||
tgtId + '$-$' + srcId : | ||
srcId + '$-$' + tgtId ; | ||
let hash = (edgeIsUnbundled ? -1 : 1) * util.hashIntsArray([ srcIndex, tgtIndex ].sort()); | ||
let pairId = hash; | ||
if( edgeIsUnbundled ){ | ||
pairId = 'unbundled' + '$-$' + data.id; | ||
} | ||
let tableEntry = hashTable.get( pairId ); | ||
var tableEntry = hashTable[ pairId ]; | ||
if( tableEntry == null ){ | ||
tableEntry = { eles: [] }; | ||
if( tableEntry == null ){ | ||
tableEntry = hashTable[ pairId ] = []; | ||
hashTable.set( pairId, tableEntry ); | ||
pairIds.push( pairId ); | ||
} | ||
tableEntry.push( edge ); | ||
tableEntry.eles.push( edge ); | ||
@@ -212,29 +659,27 @@ if( edgeIsUnbundled ){ | ||
var src, tgt, srcPos, tgtPos, srcW, srcH, tgtW, tgtH, srcShape, tgtShape; | ||
var vectorNormInverse; | ||
var badBezier; | ||
// for each pair (src, tgt), create the ctrl pts | ||
// Nested for loop is OK; total number of iterations for both loops = edgeCount | ||
for( var p = 0; p < pairIds.length; p++ ){ | ||
pairId = pairIds[ p ]; | ||
var pairEdges = hashTable[ pairId ]; | ||
for( let p = 0; p < pairIds.length; p++ ){ | ||
let pairId = pairIds[ p ]; | ||
let pairInfo = hashTable.get( pairId ); | ||
let swappedpairInfo; | ||
if( !pairEdges.hasUnbundled ){ | ||
let pllEdges = pairEdges[0].parallelEdges().filter(e => e.isBundledBezier()); | ||
if( !pairInfo.hasUnbundled ){ | ||
let pllEdges = pairInfo.eles[0].parallelEdges().filter(e => e.isBundledBezier()); | ||
util.clearArray( pairEdges ); | ||
util.clearArray( pairInfo.eles ); | ||
pllEdges.forEach( edge => pairEdges.push(edge) ); | ||
pllEdges.forEach( edge => pairInfo.eles.push(edge) ); | ||
// for each pair id, the edges should be sorted by index | ||
pairEdges.sort( (edge1, edge2) => edge1.poolIndex() - edge2.poolIndex() ); | ||
pairInfo.eles.sort( (edge1, edge2) => edge1.poolIndex() - edge2.poolIndex() ); | ||
} | ||
src = pairEdges[0]._private.source; | ||
tgt = pairEdges[0]._private.target; | ||
let firstEdge = pairInfo.eles[0]; | ||
let src = firstEdge.source(); | ||
let tgt = firstEdge.target(); | ||
// make sure src/tgt distinction is consistent for bundled edges | ||
if( !pairEdges.hasUnbundled && src.id() > tgt.id() ){ | ||
var temp = src; | ||
// make sure src/tgt distinction is consistent w.r.t. pairId | ||
if( src.poolIndex() > tgt.poolIndex() ){ | ||
let temp = src; | ||
src = tgt; | ||
@@ -244,21 +689,15 @@ tgt = temp; | ||
srcPos = src.position(); | ||
tgtPos = tgt.position(); | ||
let srcPos = pairInfo.srcPos = src.position(); | ||
let tgtPos = pairInfo.tgtPos = tgt.position(); | ||
srcW = src.outerWidth(); | ||
srcH = src.outerHeight(); | ||
let srcW = pairInfo.srcW = src.outerWidth(); | ||
let srcH = pairInfo.srcH = src.outerHeight(); | ||
tgtW = tgt.outerWidth(); | ||
tgtH = tgt.outerHeight(); | ||
let tgtW = pairInfo.tgtW = tgt.outerWidth(); | ||
let tgtH = pairInfo.tgtH = tgt.outerHeight(); | ||
srcShape = r.nodeShapes[ this.getNodeShape( src ) ]; | ||
tgtShape = r.nodeShapes[ this.getNodeShape( tgt ) ]; | ||
let srcShape = pairInfo.srcShape = r.nodeShapes[ this.getNodeShape( src ) ]; | ||
let tgtShape = pairInfo.tgtShape = r.nodeShapes[ this.getNodeShape( tgt ) ]; | ||
badBezier = false; | ||
var edge; | ||
var edge_p; | ||
var rs; | ||
var dirCounts = { | ||
pairInfo.dirCounts = { | ||
'north': 0, | ||
@@ -274,541 +713,158 @@ 'west': 0, | ||
var srcX2 = srcPos.x; | ||
var srcY2 = srcPos.y; | ||
var srcW2 = srcW; | ||
var srcH2 = srcH; | ||
for( let i = 0; i < pairInfo.eles.length; i++ ){ | ||
const edge = pairInfo.eles[i]; | ||
const rs = edge[0]._private.rscratch; | ||
const curveStyle = edge.pstyle( 'curve-style' ).value; | ||
const edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi'; | ||
var tgtX2 = tgtPos.x; | ||
var tgtY2 = tgtPos.y; | ||
var tgtW2 = tgtW; | ||
var tgtH2 = tgtH; | ||
var numEdges2 = pairEdges.length; | ||
for( var i = 0; i < pairEdges.length; i++ ){ | ||
edge = pairEdges[ i ]; | ||
edge_p = edge._private; | ||
rs = edge_p.rscratch; | ||
var edgeIndex1 = rs.lastEdgeIndex; | ||
var edgeIndex2 = i; | ||
var numEdges1 = rs.lastNumEdges; | ||
var curveStyle = edge.pstyle( 'curve-style' ).value; | ||
var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; | ||
// whether the normalised pair order is the reverse of the edge's src-tgt order | ||
var edgeIsSwapped = src.id() !== edge.source().id(); | ||
const edgeIsSwapped = !src.same(edge.source()); | ||
var ctrlptDists = edge.pstyle( 'control-point-distances' ); | ||
var loopDir = edge.pstyle('loop-direction').pfValue; | ||
var loopSwp = edge.pstyle('loop-sweep').pfValue; | ||
var ctrlptWs = edge.pstyle( 'control-point-weights' ); | ||
var bezierN = ctrlptDists && ctrlptWs ? Math.min( ctrlptDists.value.length, ctrlptWs.value.length ) : 1; | ||
var stepSize = edge.pstyle( 'control-point-step-size' ).pfValue; | ||
var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined; | ||
var ctrlptWeight = ctrlptWs.value[0]; | ||
var edgeDistances = edge.pstyle('edge-distances').value; | ||
var srcDistFNode = edge.pstyle('source-distance-from-node').pfValue; | ||
var tgtDistFNode = edge.pstyle('target-distance-from-node').pfValue; | ||
var segmentWs = edge.pstyle( 'segment-weights' ); | ||
var segmentDs = edge.pstyle( 'segment-distances' ); | ||
var segmentsN = Math.min( segmentWs.pfValue.length, segmentDs.pfValue.length ); | ||
var srcEndpt = edge.pstyle('source-endpoint').value; | ||
var tgtEndpt = edge.pstyle('target-endpoint').value; | ||
var srcArrShape = edge.pstyle('source-arrow-shape').value; | ||
var tgtArrShape = edge.pstyle('target-arrow-shape').value; | ||
var arrowScale = edge.pstyle('arrow-scale').value; | ||
var lineWidth = edge.pstyle('width').pfValue; | ||
if( !pairInfo.calculatedIntersection && src !== tgt && ( pairInfo.hasBezier || pairInfo.hasUnbundled ) ){ | ||
pairInfo.calculatedIntersection = true; | ||
var srcX1 = rs.lastSrcCtlPtX; | ||
var srcY1 = rs.lastSrcCtlPtY; | ||
var srcW1 = rs.lastSrcCtlPtW; | ||
var srcH1 = rs.lastSrcCtlPtH; | ||
// pt outside src shape to calc distance/displacement from src to tgt | ||
let srcOutside = srcShape.intersectLine( | ||
srcPos.x, srcPos.y, | ||
srcW, srcH, | ||
tgtPos.x, tgtPos.y, | ||
0 | ||
); | ||
var tgtX1 = rs.lastTgtCtlPtX; | ||
var tgtY1 = rs.lastTgtCtlPtY; | ||
var tgtW1 = rs.lastTgtCtlPtW; | ||
var tgtH1 = rs.lastTgtCtlPtH; | ||
let srcIntn = pairInfo.srcIntn = srcOutside; | ||
var curveStyle1 = rs.lastCurveStyle; | ||
var curveStyle2 = curveStyle; | ||
// pt outside tgt shape to calc distance/displacement from src to tgt | ||
let tgtOutside = tgtShape.intersectLine( | ||
tgtPos.x, tgtPos.y, | ||
tgtW, tgtH, | ||
srcPos.x, srcPos.y, | ||
0 | ||
); | ||
var ctrlptDists1 = rs.lastCtrlptDists; | ||
var ctrlptDists2 = ctrlptDists ? ctrlptDists.strValue : null; | ||
let tgtIntn = pairInfo.tgtIntn = tgtOutside; | ||
var ctrlptWs1 = rs.lastCtrlptWs; | ||
var ctrlptWs2 = ctrlptWs.strValue; | ||
let intersectionPts = pairInfo.intersectionPts = { | ||
x1: srcOutside[0], | ||
x2: tgtOutside[0], | ||
y1: srcOutside[1], | ||
y2: tgtOutside[1] | ||
}; | ||
var segmentWs1 = rs.lastSegmentWs; | ||
var segmentWs2 = segmentWs.strValue; | ||
let posPts = pairInfo.posPts = { | ||
x1: srcPos.x, | ||
x2: tgtPos.x, | ||
y1: srcPos.y, | ||
y2: tgtPos.y | ||
}; | ||
var segmentDs1 = rs.lastSegmentDs; | ||
var segmentDs2 = segmentDs.strValue; | ||
let dy = ( tgtOutside[1] - srcOutside[1] ); | ||
let dx = ( tgtOutside[0] - srcOutside[0] ); | ||
let l = Math.sqrt( dx * dx + dy * dy ); | ||
var stepSize1 = rs.lastStepSize; | ||
var stepSize2 = stepSize; | ||
let vector = pairInfo.vector = { | ||
x: dx, | ||
y: dy | ||
}; | ||
var loopDir1 = rs.lastLoopDir; | ||
var loopDir2 = loopDir; | ||
let vectorNorm = pairInfo.vectorNorm = { | ||
x: vector.x / l, | ||
y: vector.y / l | ||
}; | ||
var loopSwp1 = rs.lastLoopSwp; | ||
var loopSwp2 = loopSwp; | ||
let vectorNormInverse = { | ||
x: -vectorNorm.y, | ||
y: vectorNorm.x | ||
}; | ||
var edgeDistances1 = rs.lastEdgeDistances; | ||
var edgeDistances2 = edgeDistances; | ||
// if node shapes overlap, then no ctrl pts to draw | ||
pairInfo.nodesOverlap = ( | ||
!is.number(l) | ||
|| tgtShape.checkPoint( srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y ) | ||
|| srcShape.checkPoint( tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y ) | ||
); | ||
var srcDistFNode1 = rs.lastSrcDistFNode; | ||
var srcDistFNode2 = srcDistFNode; | ||
pairInfo.vectorNormInverse = vectorNormInverse; | ||
var tgtDistFNode1 = rs.lastTgtDistFNode; | ||
var tgtDistFNode2 = tgtDistFNode; | ||
swappedpairInfo = { | ||
nodesOverlap: pairInfo.nodesOverlap, | ||
dirCounts: pairInfo.dirCounts, | ||
calculatedIntersection: true, | ||
hasBezier: pairInfo.hasBezier, | ||
hasUnbundled: pairInfo.hasUnbundled, | ||
eles: pairInfo.eles, | ||
srcPos: tgtPos, | ||
tgtPos: srcPos, | ||
srcW: tgtW, | ||
srcH: tgtH, | ||
tgtW: srcW, | ||
tgtH: srcH, | ||
srcIntn: tgtIntn, | ||
tgtIntn: srcIntn, | ||
srcShape: tgtShape, | ||
tgtShape: srcShape, | ||
posPts: { | ||
x1: posPts.x2, y1: posPts.y2, | ||
x2: posPts.x1, y2: posPts.y1 | ||
}, | ||
intersectionPts: { | ||
x1: intersectionPts.x2, y1: intersectionPts.y2, | ||
x2: intersectionPts.x1, y2: intersectionPts.y1 | ||
}, | ||
vector: { x: -vector.x, y: -vector.y }, | ||
vectorNorm: { x: -vectorNorm.x, y: -vectorNorm.y }, | ||
vectorNormInverse: { x: -vectorNormInverse.x, y: -vectorNormInverse.y } | ||
}; | ||
} | ||
var srcEndpt1 = rs.lastSrcEndpt; | ||
var srcEndpt2 = srcEndpt; | ||
const passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo; | ||
var tgtEndpt1 = rs.lastTgtEndpt; | ||
var tgtEndpt2 = tgtEndpt; | ||
rs.nodesOverlap = passedPairInfo.nodesOverlap; | ||
rs.srcIntn = passedPairInfo.srcIntn; | ||
rs.tgtIntn = passedPairInfo.tgtIntn; | ||
var srcArr1 = rs.lastSrcArr; | ||
var srcArr2 = srcArrShape; | ||
if( | ||
hasCompounds && | ||
( src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild() ) && | ||
( src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) ) | ||
){ | ||
this.findCompoundLoopPoints(edge, passedPairInfo, i, edgeIsUnbundled); | ||
var tgtArr1 = rs.lastTgtArr; | ||
var tgtArr2 = tgtArrShape; | ||
} else if( src === tgt ){ | ||
this.findLoopPoints(edge, passedPairInfo, i, edgeIsUnbundled); | ||
var lineW1 = rs.lastLineW; | ||
var lineW2 = lineWidth; | ||
} else if( curveStyle === 'segments' ){ | ||
this.findSegmentsPoints(edge, passedPairInfo); | ||
var arrScl1 = rs.lastArrScl; | ||
var arrScl2 = arrowScale; | ||
} else if( curveStyle === 'taxi' ){ | ||
this.findTaxiPoints(edge, passedPairInfo); | ||
if( badBezier ){ | ||
rs.badBezier = true; | ||
} else { | ||
rs.badBezier = false; | ||
} | ||
} else if( | ||
curveStyle === 'straight' | ||
|| ( | ||
!edgeIsUnbundled | ||
&& pairInfo.eles.length % 2 === 1 | ||
&& i === Math.floor( pairInfo.eles.length / 2 ) | ||
) | ||
){ | ||
this.findStraightEdgePoints(edge); | ||
var ptCacheHit; | ||
if( srcX1 === srcX2 && srcY1 === srcY2 && srcW1 === srcW2 && srcH1 === srcH2 | ||
&& tgtX1 === tgtX2 && tgtY1 === tgtY2 && tgtW1 === tgtW2 && tgtH1 === tgtH2 | ||
&& curveStyle1 === curveStyle2 | ||
&& ctrlptDists1 === ctrlptDists2 | ||
&& ctrlptWs1 === ctrlptWs2 | ||
&& segmentWs1 === segmentWs2 | ||
&& segmentDs1 === segmentDs2 | ||
&& stepSize1 === stepSize2 | ||
&& loopDir1 === loopDir2 | ||
&& loopSwp1 === loopSwp2 | ||
&& edgeDistances1 === edgeDistances2 | ||
&& srcDistFNode1 === srcDistFNode2 | ||
&& tgtDistFNode1 === tgtDistFNode2 | ||
&& srcEndpt1 === srcEndpt2 | ||
&& tgtEndpt1 === tgtEndpt2 | ||
&& srcArr1 === srcArr2 | ||
&& tgtArr1 === tgtArr2 | ||
&& lineW1 === lineW2 | ||
&& arrScl1 === arrScl2 | ||
&& ((edgeIndex1 === edgeIndex2 && numEdges1 === numEdges2) || edgeIsUnbundled) ){ | ||
ptCacheHit = true; // then the control points haven't changed and we can skip calculating them | ||
} else { | ||
ptCacheHit = false; | ||
rs.lastSrcCtlPtX = srcX2; | ||
rs.lastSrcCtlPtY = srcY2; | ||
rs.lastSrcCtlPtW = srcW2; | ||
rs.lastSrcCtlPtH = srcH2; | ||
rs.lastTgtCtlPtX = tgtX2; | ||
rs.lastTgtCtlPtY = tgtY2; | ||
rs.lastTgtCtlPtW = tgtW2; | ||
rs.lastTgtCtlPtH = tgtH2; | ||
rs.lastEdgeIndex = edgeIndex2; | ||
rs.lastNumEdges = numEdges2; | ||
rs.lastCurveStyle = curveStyle2; | ||
rs.lastCtrlptDists = ctrlptDists2; | ||
rs.lastCtrlptWs = ctrlptWs2; | ||
rs.lastSegmentDs = segmentDs2; | ||
rs.lastSegmentWs = segmentWs2; | ||
rs.lastStepSize = stepSize2; | ||
rs.lastLoopDir = loopDir2; | ||
rs.lastLoopSwp = loopSwp2; | ||
rs.lastEdgeDistances = edgeDistances2; | ||
rs.lastSrcDistFNode = srcDistFNode2; | ||
rs.lastTgtDistFNode = tgtDistFNode2; | ||
rs.lastSrcEndpt = srcEndpt2; | ||
rs.lastTgtEndpt = tgtEndpt2; | ||
rs.lastSrcArr = srcArr2; | ||
rs.lastTgtArr = tgtArr2; | ||
rs.lastLineW = lineW2; | ||
rs.lastArrScl = arrScl2; | ||
this.findBezierPoints(edge, passedPairInfo, i, edgeIsUnbundled, edgeIsSwapped); | ||
} | ||
if( !ptCacheHit ){ | ||
this.findEndpoints( edge ); | ||
if( !pairEdges.calculatedIntersection && src !== tgt && ( pairEdges.hasBezier || pairEdges.hasUnbundled ) ){ | ||
this.tryToCorrectInvalidPoints( edge, passedPairInfo ); | ||
pairEdges.calculatedIntersection = true; | ||
this.checkForInvalidEdgeWarning( edge ); | ||
// pt outside src shape to calc distance/displacement from src to tgt | ||
var srcOutside = srcShape.intersectLine( | ||
srcPos.x, | ||
srcPos.y, | ||
srcW, | ||
srcH, | ||
tgtPos.x, | ||
tgtPos.y, | ||
0 | ||
); | ||
pairEdges.srcIntn = srcOutside; | ||
// pt outside tgt shape to calc distance/displacement from src to tgt | ||
var tgtOutside = tgtShape.intersectLine( | ||
tgtPos.x, | ||
tgtPos.y, | ||
tgtW, | ||
tgtH, | ||
srcPos.x, | ||
srcPos.y, | ||
0 | ||
); | ||
pairEdges.tgtIntn = tgtOutside; | ||
var midptSrcPts = { | ||
x1: srcOutside[0], | ||
x2: tgtOutside[0], | ||
y1: srcOutside[1], | ||
y2: tgtOutside[1] | ||
}; | ||
var posPts = { | ||
x1: srcPos.x, | ||
x2: tgtPos.x, | ||
y1: srcPos.y, | ||
y2: tgtPos.y | ||
}; | ||
var dy = ( tgtOutside[1] - srcOutside[1] ); | ||
var dx = ( tgtOutside[0] - srcOutside[0] ); | ||
var l = Math.sqrt( dx * dx + dy * dy ); | ||
var vector = { | ||
x: dx, | ||
y: dy | ||
}; | ||
var vectorNorm = { | ||
x: vector.x / l, | ||
y: vector.y / l | ||
}; | ||
vectorNormInverse = { | ||
x: -vectorNorm.y, | ||
y: vectorNorm.x | ||
}; | ||
// if node shapes overlap, then no ctrl pts to draw | ||
if( | ||
tgtShape.checkPoint( srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y ) && | ||
srcShape.checkPoint( tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y ) | ||
){ | ||
vectorNormInverse = {}; | ||
badBezier = true; | ||
} | ||
} | ||
if( !edgeIsSwapped ){ | ||
rs.srcIntn = pairEdges.srcIntn; | ||
rs.tgtIntn = pairEdges.tgtIntn; | ||
} else { // ensure that the per-edge cached value for intersections are correct for swapped bundled edges | ||
rs.srcIntn = pairEdges.tgtIntn; | ||
rs.tgtIntn = pairEdges.srcIntn; | ||
} | ||
if( src === tgt ){ | ||
// Self-edge | ||
rs.edgeType = 'self'; | ||
var j = i; | ||
var loopDist = stepSize; | ||
if( edgeIsUnbundled ){ | ||
j = 0; | ||
loopDist = ctrlptDist; | ||
} | ||
var loopAngle = loopDir - Math.PI / 2; | ||
var outAngle = loopAngle - loopSwp / 2; | ||
var inAngle = loopAngle + loopSwp / 2; | ||
// increase by step size for overlapping loops, keyed on direction and sweep values | ||
var dc = String(loopDir + '_' + loopSwp); | ||
j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc]; | ||
rs.ctrlpts = [ | ||
srcPos.x + Math.cos(outAngle) * 1.4 * loopDist * (j / 3 + 1), | ||
srcPos.y + Math.sin(outAngle) * 1.4 * loopDist * (j / 3 + 1), | ||
srcPos.x + Math.cos(inAngle) * 1.4 * loopDist * (j / 3 + 1), | ||
srcPos.y + Math.sin(inAngle) * 1.4 * loopDist * (j / 3 + 1) | ||
]; | ||
} else if( | ||
hasCompounds && | ||
( src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild() ) && | ||
( src.parents().anySame( tgt ) || tgt.parents().anySame( src ) ) | ||
){ | ||
// Compound edge | ||
rs.edgeType = 'compound'; | ||
// because the line approximation doesn't apply for compound beziers | ||
// (loop/self edges are already elided b/c of cheap src==tgt check) | ||
rs.badBezier = false; | ||
var j = i; | ||
var loopDist = stepSize; | ||
if( edgeIsUnbundled ){ | ||
j = 0; | ||
loopDist = ctrlptDist; | ||
} | ||
var loopW = 50; | ||
var loopaPos = { | ||
x: srcPos.x - srcW / 2, | ||
y: srcPos.y - srcH / 2 | ||
}; | ||
var loopbPos = { | ||
x: tgtPos.x - tgtW / 2, | ||
y: tgtPos.y - tgtH / 2 | ||
}; | ||
var loopPos = { | ||
x: Math.min( loopaPos.x, loopbPos.x ), | ||
y: Math.min( loopaPos.y, loopbPos.y ) | ||
}; | ||
// avoids cases with impossible beziers | ||
var minCompoundStretch = 0.5; | ||
var compoundStretchA = Math.max( minCompoundStretch, Math.log( srcW * 0.01 ) ); | ||
var compoundStretchB = Math.max( minCompoundStretch, Math.log( tgtW * 0.01 ) ); | ||
rs.ctrlpts = [ | ||
loopPos.x, | ||
loopPos.y - (1 + Math.pow( loopW, 1.12 ) / 100) * loopDist * (j / 3 + 1) * compoundStretchA, | ||
loopPos.x - (1 + Math.pow( loopW, 1.12 ) / 100) * loopDist * (j / 3 + 1) * compoundStretchB, | ||
loopPos.y | ||
]; | ||
} else if( curveStyle === 'segments' ){ | ||
// Segments (multiple straight lines) | ||
rs.edgeType = 'segments'; | ||
rs.segpts = []; | ||
for( var s = 0; s < segmentsN; s++ ){ | ||
var w = segmentWs.pfValue[ s ]; | ||
var d = segmentDs.pfValue[ s ]; | ||
var w1 = 1 - w; | ||
var w2 = w; | ||
var midptPts = edgeDistances === 'node-position' ? posPts : midptSrcPts; | ||
var adjustedMidpt = { | ||
x: midptPts.x1 * w1 + midptPts.x2 * w2, | ||
y: midptPts.y1 * w1 + midptPts.y2 * w2 | ||
}; | ||
rs.segpts.push( | ||
adjustedMidpt.x + vectorNormInverse.x * d, | ||
adjustedMidpt.y + vectorNormInverse.y * d | ||
); | ||
} | ||
// Straight edge | ||
} else if( | ||
pairEdges.length % 2 === 1 | ||
&& i === Math.floor( pairEdges.length / 2 ) | ||
&& !edgeIsUnbundled | ||
){ | ||
rs.edgeType = 'straight'; | ||
} else { | ||
// (Multi)bezier | ||
var multi = edgeIsUnbundled; | ||
rs.edgeType = multi ? 'multibezier' : 'bezier'; | ||
rs.ctrlpts = []; | ||
for( var b = 0; b < bezierN; b++ ){ | ||
var normctrlptDist = (0.5 - pairEdges.length / 2 + i) * stepSize; | ||
var manctrlptDist; | ||
var sign = math.signum( normctrlptDist ); | ||
if( multi ){ | ||
ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[ b ] : stepSize; // fall back on step size | ||
ctrlptWeight = ctrlptWs.value[ b ]; | ||
} | ||
if( edgeIsUnbundled ){ // multi or single unbundled | ||
manctrlptDist = ctrlptDist; | ||
} else { | ||
manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined; | ||
} | ||
var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist; | ||
var w1 = 1 - ctrlptWeight; | ||
var w2 = ctrlptWeight; | ||
if( edgeIsSwapped ){ | ||
var temp = w1; | ||
w1 = w2; | ||
w2 = temp; | ||
} | ||
var midptPts = edgeDistances === 'node-position' ? posPts : midptSrcPts; | ||
var adjustedMidpt = { | ||
x: midptPts.x1 * w1 + midptPts.x2 * w2, | ||
y: midptPts.y1 * w1 + midptPts.y2 * w2 | ||
}; | ||
rs.ctrlpts.push( | ||
adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, | ||
adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint | ||
); | ||
} | ||
} | ||
// find endpts for edge | ||
this.findEndpoints( edge ); | ||
var badStart = !is.number( rs.startX ) || !is.number( rs.startY ); | ||
var badAStart = !is.number( rs.arrowStartX ) || !is.number( rs.arrowStartY ); | ||
var badEnd = !is.number( rs.endX ) || !is.number( rs.endY ); | ||
var badAEnd = !is.number( rs.arrowEndX ) || !is.number( rs.arrowEndY ); | ||
var minCpADistFactor = 3; | ||
var arrowW = this.getArrowWidth( edge.pstyle( 'width' ).pfValue, edge.pstyle( 'arrow-scale' ).value ) | ||
* this.arrowShapeWidth; | ||
var minCpADist = minCpADistFactor * arrowW; | ||
if( rs.edgeType === 'bezier' ){ | ||
var startACpDist = math.dist( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.startX, y: rs.startY } ); | ||
var closeStartACp = startACpDist < minCpADist; | ||
var endACpDist = math.dist( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.endX, y: rs.endY } ); | ||
var closeEndACp = endACpDist < minCpADist; | ||
var overlapping = false; | ||
if( badStart || badAStart || closeStartACp ){ | ||
overlapping = true; | ||
// project control point along line from src centre to outside the src shape | ||
// (otherwise intersection will yield nothing) | ||
var cpD = { // delta | ||
x: rs.ctrlpts[0] - srcPos.x, | ||
y: rs.ctrlpts[1] - srcPos.y | ||
}; | ||
var cpL = Math.sqrt( cpD.x * cpD.x + cpD.y * cpD.y ); // length of line | ||
var cpM = { // normalised delta | ||
x: cpD.x / cpL, | ||
y: cpD.y / cpL | ||
}; | ||
var radius = Math.max( srcW, srcH ); | ||
var cpProj = { // *2 radius guarantees outside shape | ||
x: rs.ctrlpts[0] + cpM.x * 2 * radius, | ||
y: rs.ctrlpts[1] + cpM.y * 2 * radius | ||
}; | ||
var srcCtrlPtIntn = srcShape.intersectLine( | ||
srcPos.x, | ||
srcPos.y, | ||
srcW, | ||
srcH, | ||
cpProj.x, | ||
cpProj.y, | ||
0 | ||
); | ||
if( closeStartACp ){ | ||
rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist); | ||
rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist); | ||
} else { | ||
rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist; | ||
rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist; | ||
} | ||
} | ||
if( badEnd || badAEnd || closeEndACp ){ | ||
overlapping = true; | ||
// project control point along line from tgt centre to outside the tgt shape | ||
// (otherwise intersection will yield nothing) | ||
var cpD = { // delta | ||
x: rs.ctrlpts[0] - tgtPos.x, | ||
y: rs.ctrlpts[1] - tgtPos.y | ||
}; | ||
var cpL = Math.sqrt( cpD.x * cpD.x + cpD.y * cpD.y ); // length of line | ||
var cpM = { // normalised delta | ||
x: cpD.x / cpL, | ||
y: cpD.y / cpL | ||
}; | ||
var radius = Math.max( srcW, srcH ); | ||
var cpProj = { // *2 radius guarantees outside shape | ||
x: rs.ctrlpts[0] + cpM.x * 2 * radius, | ||
y: rs.ctrlpts[1] + cpM.y * 2 * radius | ||
}; | ||
var tgtCtrlPtIntn = tgtShape.intersectLine( | ||
tgtPos.x, | ||
tgtPos.y, | ||
tgtW, | ||
tgtH, | ||
cpProj.x, | ||
cpProj.y, | ||
0 | ||
); | ||
if( closeEndACp ){ | ||
rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - endACpDist); | ||
rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - endACpDist); | ||
} else { | ||
rs.ctrlpts[0] = tgtCtrlPtIntn[0] + cpM.x * minCpADist; | ||
rs.ctrlpts[1] = tgtCtrlPtIntn[1] + cpM.y * minCpADist; | ||
} | ||
} | ||
if( overlapping ){ | ||
// recalc endpts | ||
this.findEndpoints( edge ); | ||
} | ||
} | ||
this.checkForInvalidEdgeWarning( edge ); | ||
this.storeAllpts( edge ); | ||
this.storeEdgeProjections( edge ); | ||
this.calculateArrowAngles( edge ); | ||
} // if point cache miss | ||
this.storeAllpts( edge ); | ||
this.storeEdgeProjections( edge ); | ||
this.calculateArrowAngles( edge ); | ||
this.recalculateEdgeLabelProjections( edge ); | ||
this.calculateLabelAngles( edge ); | ||
} // for pair edges | ||
} // for pair ids | ||
// haystacks avoid the expense of pairInfo stuff (intersections etc.) | ||
this.findHaystackPoints( haystackEdges ); | ||
@@ -815,0 +871,0 @@ }; |
@@ -41,3 +41,7 @@ var BRp = {}; | ||
for( var i = 0; i < elesToUpdate.length; i++ ){ | ||
enqueue( elesToUpdate[i].connectedEdges() ); | ||
var ele = elesToUpdate[i]; | ||
if( ele.isNode() && !ele._private.rstyle.clean ){ | ||
enqueue( ele.connectedEdges() ); | ||
} | ||
} | ||
@@ -124,4 +128,2 @@ | ||
this.recalculateEdgeLabelProjections( ele ); | ||
// update rstyle positions | ||
@@ -128,0 +130,0 @@ rstyle.srcX = rs.arrowStartX; |
@@ -110,3 +110,4 @@ import * as util from '../../../util'; | ||
eleTxrDeq: 200, | ||
lyrTxrDeq: 100 | ||
lyrTxrDeq: 150, | ||
lyrTxrSkip: 100, | ||
}; | ||
@@ -113,0 +114,0 @@ |
@@ -26,3 +26,5 @@ import * as util from '../../../util'; | ||
priority = priority || 0; | ||
if( priority == null ){ | ||
util.error('Priority is not optional for beforeRender'); | ||
} | ||
@@ -29,0 +31,0 @@ var cbs = this.beforeRenderCallbacks; |
@@ -180,2 +180,3 @@ import * as math from '../../../math'; | ||
draw( rs.labelX, rs.labelY, 'red' ); | ||
draw( p.x, p.y, 'magenta' ); | ||
@@ -182,0 +183,0 @@ } else { |
@@ -55,3 +55,3 @@ import * as util from '../../../util'; | ||
} | ||
}); | ||
}, r.beforeRenderPriorities.lyrTxrSkip); | ||
@@ -58,0 +58,0 @@ var qSort = function(a, b){ |
@@ -6,2 +6,3 @@ import * as is from './is'; | ||
import version from './version'; | ||
import { warnings } from './util'; | ||
@@ -36,2 +37,6 @@ let cytoscape = function( options ){ | ||
cytoscape.warnings = function(bool){ | ||
return warnings(bool); | ||
}; | ||
// replaced by build system | ||
@@ -38,0 +43,0 @@ cytoscape.version = version; |
@@ -236,2 +236,6 @@ export const arePositionsSame = ( p1, p2 ) => | ||
export const copyBoundingBox = bb => { | ||
return { x1: bb.x1, x2: bb.x2, w: bb.w, y1: bb.y1, y2: bb.y2, h: bb.h }; | ||
}; | ||
export const clearBoundingBox = bb => { | ||
@@ -238,0 +242,0 @@ bb.x1 = Infinity; |
@@ -41,2 +41,3 @@ import * as util from '../util'; | ||
sizeMaybePercent: { number: true, min: 0, allowPercent: true }, | ||
axisDirection: { enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto'] }, | ||
paddingRelativeTo: { enums: [ 'width', 'height', 'average', 'min', 'max' ] }, | ||
@@ -57,3 +58,3 @@ bgWH: { number: true, min: 0, allowPercent: true, enums: [ 'auto' ], multiple: true }, | ||
borderStyle: { enums: [ 'solid', 'dotted', 'dashed', 'double' ] }, | ||
curveStyle: { enums: [ 'bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight' ] }, | ||
curveStyle: { enums: [ 'bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi' ] }, | ||
fontFamily: { regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$' }, | ||
@@ -306,2 +307,5 @@ fontStyle: { enums: [ 'italic', 'normal', 'oblique' ] }, | ||
{ name: 'segment-weights', type: t.numbers, triggersBounds: diff.any }, | ||
{ name: 'taxi-turn', type: t.sizeMaybePercent, triggersBounds: diff.any }, | ||
{ name: 'taxi-turn-min-distance', type: t.size, triggersBounds: diff.any }, | ||
{ name: 'taxi-direction', type: t.axisDirection, triggersBounds: diff.any }, | ||
{ name: 'edge-distances', type: t.edgeDistances, triggersBounds: diff.any }, | ||
@@ -624,2 +628,5 @@ { name: 'arrow-scale', type: t.positiveNumber, triggersBounds: diff.any }, | ||
'segment-distances': 20, | ||
'taxi-turn': '50%', | ||
'taxi-turn-min-distance': 10, | ||
'taxi-direction': 'auto', | ||
'edge-distances': 'intersection', | ||
@@ -626,0 +633,0 @@ 'curve-style': 'haystack', |
@@ -18,2 +18,3 @@ /*global console */ | ||
let warningsEnabled = true; | ||
let warnSupported = console.warn != null; // eslint-disable-line no-console | ||
@@ -36,3 +37,13 @@ let traceSupported = console.trace != null; // eslint-disable-line no-console | ||
export const warnings = enabled => { | ||
if( enabled !== undefined ){ | ||
warningsEnabled = !!enabled; | ||
} else { | ||
return warningsEnabled; | ||
} | ||
}; | ||
export const warn = msg => { /* eslint-disable no-console */ | ||
if( !warnings() ){ return; } | ||
if( warnSupported ){ | ||
@@ -39,0 +50,0 @@ console.warn( msg ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3738870
101694