DuyTa's picture
Upload folder using huggingface_hub
bc20498 verified
raw
history blame
19.8 kB
/* global Path2D */
import * as is from '../../../is';
import {expandPolygon, joinLines} from '../../../math';
import * as util from '../../../util';
import * as round from "../../../round";
import * as math from "../../../math";
let CRp = {};
CRp.drawNode = function( context, node, shiftToOriginWithBb, drawLabel = true, shouldDrawOverlay = true, shouldDrawOpacity = true ){
let r = this;
let nodeWidth, nodeHeight;
let _p = node._private;
let rs = _p.rscratch;
let pos = node.position();
if( !is.number( pos.x ) || !is.number( pos.y ) ){
return; // can't draw node with undefined position
}
if( shouldDrawOpacity && !node.visible() ){ return; }
let eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
let usePaths = r.usePaths();
let path;
let pathCacheHit = false;
let padding = node.padding();
nodeWidth = node.width() + 2 * padding;
nodeHeight = node.height() + 2 * padding;
//
// setup shift
let bb;
if( shiftToOriginWithBb ){
bb = shiftToOriginWithBb;
context.translate( -bb.x1, -bb.y1 );
}
//
// load bg image
let bgImgProp = node.pstyle( 'background-image' );
let urls = bgImgProp.value;
let urlDefined = new Array( urls.length );
let image = new Array( urls.length );
let numImages = 0;
for( let i = 0; i < urls.length; i++ ){
let url = urls[i];
let defd = urlDefined[i] = url != null && url !== 'none';
if( defd ){
let bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
numImages++;
// get image, and if not loaded then ask to redraw when later loaded
image[i] = r.getCachedImage( url, bgImgCrossOrigin, function(){
_p.backgroundTimestamp = Date.now();
node.emitAndNotify('background');
} );
}
}
//
// setup styles
let darkness = node.pstyle('background-blacken').value;
let borderWidth = node.pstyle('border-width').pfValue;
let bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
let borderColor = node.pstyle('border-color').value;
let borderStyle = node.pstyle('border-style').value;
let borderJoin = node.pstyle('border-join').value;
let borderCap = node.pstyle('border-cap').value;
let borderPosition = node.pstyle('border-position').value;
let borderPattern = node.pstyle('border-dash-pattern').pfValue;
let borderOffset = node.pstyle('border-dash-offset').pfValue;
let borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
let outlineWidth = node.pstyle('outline-width').pfValue;
let outlineColor = node.pstyle('outline-color').value;
let outlineStyle = node.pstyle('outline-style').value;
let outlineOpacity = node.pstyle('outline-opacity').value * eleOpacity;
let outlineOffset = node.pstyle('outline-offset').value;
let cornerRadius = node.pstyle('corner-radius').value;
if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
let setupShapeColor = ( bgOpy = bgOpacity ) => {
r.eleFillStyle( context, node, bgOpy );
};
let setupBorderColor = ( bdrOpy = borderOpacity ) => {
r.colorStrokeStyle( context, borderColor[0], borderColor[1], borderColor[2], bdrOpy );
};
let setupOutlineColor = ( otlnOpy = outlineOpacity ) => {
r.colorStrokeStyle( context, outlineColor[0], outlineColor[1], outlineColor[2], otlnOpy );
};
//
// setup shape
let getPath = (width, height, shape, points) => {
let pathCache = r.nodePathCache = r.nodePathCache || [];
let key = util.hashStrings(
shape === 'polygon' ? shape + ',' + points.join(',') : shape,
'' + height,
'' + width,
'' + cornerRadius
);
let cachedPath = pathCache[ key ];
let path;
let cacheHit = false;
if( cachedPath != null ){
path = cachedPath;
cacheHit = true;
rs.pathCache = path;
} else {
path = new Path2D();
pathCache[ key ] = rs.pathCache = path;
}
return {
path,
cacheHit
};
};
let styleShape = node.pstyle('shape').strValue;
let shapePts = node.pstyle('shape-polygon-points').pfValue;
if( usePaths ){
context.translate( pos.x, pos.y );
const shapePath = getPath(nodeWidth, nodeHeight, styleShape, shapePts);
path = shapePath.path;
pathCacheHit = shapePath.cacheHit;
}
let drawShape = () => {
if( !pathCacheHit ){
let npos = pos;
if( usePaths ){
npos = {
x: 0,
y: 0
};
}
r.nodeShapes[ r.getNodeShape( node ) ].draw(
( path || context ),
npos.x,
npos.y,
nodeWidth,
nodeHeight, cornerRadius, rs );
}
if( usePaths ){
context.fill( path );
} else {
context.fill();
}
};
let drawImages = ( nodeOpacity = eleOpacity, inside = true ) => {
let prevBging = _p.backgrounding;
let totalCompleted = 0;
for( let i = 0; i < image.length; i++ ){
const bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', i);
if( inside && bgContainment === 'over' || !inside && bgContainment === 'inside' ){
totalCompleted++;
continue;
}
if( urlDefined[i] && image[i].complete && !image[i].error ){
totalCompleted++;
r.drawInscribedImage( context, image[i], node, i, nodeOpacity );
}
}
_p.backgrounding = !(totalCompleted === numImages);
if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed
node.updateStyle( false );
}
};
let drawPie = ( redrawShape = false, pieOpacity = eleOpacity ) => {
if( r.hasPie( node ) ){
r.drawPie( context, node, pieOpacity );
// redraw/restore path if steps after pie need it
if( redrawShape ){
if( !usePaths ){
r.nodeShapes[ r.getNodeShape( node ) ].draw(
context,
pos.x,
pos.y,
nodeWidth,
nodeHeight, cornerRadius, rs );
}
}
}
};
let darken = ( darkenOpacity = eleOpacity ) => {
let opacity = ( darkness > 0 ? darkness : -darkness ) * darkenOpacity;
let c = darkness > 0 ? 0 : 255;
if( darkness !== 0 ){
r.colorFillStyle( context, c, c, c, opacity );
if( usePaths ){
context.fill( path );
} else {
context.fill();
}
}
};
let drawBorder = () => {
if( borderWidth > 0 ){
context.lineWidth = borderWidth;
context.lineCap = borderCap;
context.lineJoin = borderJoin;
if( context.setLineDash ){ // for very outofdate browsers
switch( borderStyle ){
case 'dotted':
context.setLineDash( [ 1, 1 ] );
break;
case 'dashed':
context.setLineDash( borderPattern );
context.lineDashOffset = borderOffset;
break;
case 'solid':
case 'double':
context.setLineDash( [ ] );
break;
}
}
if ( borderPosition !== 'center') {
context.save();
context.lineWidth *= 2;
if (borderPosition === 'inside') {
usePaths ? context.clip(path) : context.clip();
} else {
const region = new Path2D();
region.rect(
-nodeWidth / 2 - borderWidth,
-nodeHeight / 2 - borderWidth,
nodeWidth + 2 * borderWidth,
nodeHeight + 2 * borderWidth
);
region.addPath(path);
context.clip(region, 'evenodd');
}
usePaths ? context.stroke(path) : context.stroke();
context.restore();
} else {
usePaths ? context.stroke(path) : context.stroke();
}
if( borderStyle === 'double' ){
context.lineWidth = borderWidth / 3;
let gco = context.globalCompositeOperation;
context.globalCompositeOperation = 'destination-out';
if( usePaths ){
context.stroke( path );
} else {
context.stroke();
}
context.globalCompositeOperation = gco;
}
// reset in case we changed the border style
if( context.setLineDash ){ // for very outofdate browsers
context.setLineDash( [ ] );
}
}
};
let drawOutline = () => {
if( outlineWidth > 0 ){
context.lineWidth = outlineWidth;
context.lineCap = 'butt';
if( context.setLineDash ){ // for very outofdate browsers
switch( outlineStyle ){
case 'dotted':
context.setLineDash( [ 1, 1 ] );
break;
case 'dashed':
context.setLineDash( [ 4, 2 ] );
break;
case 'solid':
case 'double':
context.setLineDash( [ ] );
break;
}
}
let npos = pos;
if( usePaths ){
npos = {
x: 0,
y: 0
};
}
let shape = r.getNodeShape( node );
let bWidth = borderWidth;
if( borderPosition === 'inside' ) bWidth = 0;
if( borderPosition === 'outside' ) bWidth *= 2;
let scaleX = (nodeWidth + bWidth + (outlineWidth + outlineOffset)) / nodeWidth;
let scaleY = (nodeHeight + bWidth + (outlineWidth + outlineOffset)) / nodeHeight;
let sWidth = nodeWidth * scaleX;
let sHeight = nodeHeight * scaleY;
let points = r.nodeShapes[ shape ].points;
let path;
if (usePaths) {
let outlinePath = getPath(sWidth, sHeight, shape, points);
path = outlinePath.path;
}
// draw the outline path, either by using expanded points or by scaling
// the dimensions, depending on shape
if (shape === "ellipse") {
r.drawEllipsePath(path || context, npos.x, npos.y, sWidth, sHeight);
} else if ([
'round-diamond', 'round-heptagon', 'round-hexagon', 'round-octagon',
'round-pentagon', 'round-polygon', 'round-triangle', 'round-tag'
].includes(shape)) {
let sMult = 0;
let offsetX = 0;
let offsetY = 0;
if (shape === 'round-diamond') {
sMult = (bWidth + outlineOffset + outlineWidth) * 1.4;
} else if (shape === 'round-heptagon') {
sMult = (bWidth + outlineOffset + outlineWidth) * 1.075;
offsetY = -(bWidth/2 + outlineOffset + outlineWidth) / 35;
} else if (shape === 'round-hexagon') {
sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
} else if (shape === 'round-pentagon') {
sMult = (bWidth + outlineOffset + outlineWidth) * 1.13;
offsetY = -(bWidth/2 + outlineOffset + outlineWidth) / 15;
} else if (shape === 'round-tag') {
sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
offsetX = (bWidth/2 + outlineWidth + outlineOffset) * .07;
} else if (shape === 'round-triangle') {
sMult = (bWidth + outlineOffset + outlineWidth) * (Math.PI/2);
offsetY = -(bWidth + outlineOffset/2 + outlineWidth) / Math.PI;
}
if (sMult !== 0) {
scaleX = (nodeWidth + sMult)/nodeWidth;
sWidth = nodeWidth * scaleX;
if ( ! ['round-hexagon', 'round-tag'].includes(shape) ) {
scaleY = (nodeHeight + sMult)/nodeHeight;
sHeight = nodeHeight * scaleY;
}
}
cornerRadius = cornerRadius === 'auto' ? math.getRoundPolygonRadius( sWidth, sHeight ) : cornerRadius;
const halfW = sWidth / 2;
const halfH = sHeight / 2;
const radius = cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2;
const p = new Array( points.length / 2 );
const corners = new Array( points.length / 2 );
for ( let i = 0; i < points.length / 2; i++ ){
p[i] = {
x: npos.x + offsetX + halfW * points[ i * 2 ],
y: npos.y + offsetY + halfH * points[ i * 2 + 1 ]
};
}
let i, p1, p2, p3, len = p.length;
p1 = p[ len - 1 ];
// for each point
for( i = 0; i < len; i++ ){
p2 = p[ (i) % len ];
p3 = p[ (i + 1) % len ];
corners[ i ] = round.getRoundCorner( p1, p2, p3, radius );
p1 = p2;
p2 = p3;
}
r.drawRoundPolygonPath(path || context, npos.x + offsetX, npos.y + offsetY, nodeWidth * scaleX, nodeHeight * scaleY, points, corners );
} else if (['roundrectangle', 'round-rectangle'].includes(shape)) {
cornerRadius = cornerRadius === 'auto' ? math.getRoundRectangleRadius( sWidth, sHeight ) : cornerRadius;
r.drawRoundRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2 );
} else if (['cutrectangle', 'cut-rectangle'].includes(shape)) {
cornerRadius = cornerRadius === 'auto' ? math.getCutRectangleCornerLength( sWidth, sHeight ) : cornerRadius;
r.drawCutRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, null ,cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 4 );
} else if (['bottomroundrectangle', 'bottom-round-rectangle'].includes(shape)) {
cornerRadius = cornerRadius === 'auto' ? math.getRoundRectangleRadius( sWidth, sHeight ) : cornerRadius;
r.drawBottomRoundRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2 );
} else if (shape === "barrel") {
r.drawBarrelPath(path || context, npos.x, npos.y, sWidth, sHeight);
} else if (shape.startsWith("polygon") || ['rhomboid', 'right-rhomboid', 'round-tag', 'tag', 'vee'].includes(shape)) {
let pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
points = joinLines(expandPolygon(points, pad));
r.drawPolygonPath(path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
} else {
let pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
points = joinLines(expandPolygon(points, -pad));
r.drawPolygonPath(path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
}
if( usePaths ){
context.stroke( path );
} else {
context.stroke();
}
if( outlineStyle === 'double' ){
context.lineWidth = bWidth / 3;
let gco = context.globalCompositeOperation;
context.globalCompositeOperation = 'destination-out';
if( usePaths ){
context.stroke( path );
} else {
context.stroke();
}
context.globalCompositeOperation = gco;
}
// reset in case we changed the border style
if( context.setLineDash ){ // for very outofdate browsers
context.setLineDash( [ ] );
}
}
};
let drawOverlay = () => {
if( shouldDrawOverlay ){
r.drawNodeOverlay( context, node, pos, nodeWidth, nodeHeight );
}
};
let drawUnderlay = () => {
if( shouldDrawOverlay ){
r.drawNodeUnderlay( context, node, pos, nodeWidth, nodeHeight );
}
};
let drawText = () => {
r.drawElementText( context, node, null, drawLabel );
};
let ghost = node.pstyle('ghost').value === 'yes';
if( ghost ){
let gx = node.pstyle('ghost-offset-x').pfValue;
let gy = node.pstyle('ghost-offset-y').pfValue;
let ghostOpacity = node.pstyle('ghost-opacity').value;
let effGhostOpacity = ghostOpacity * eleOpacity;
context.translate( gx, gy );
setupOutlineColor();
drawOutline();
setupShapeColor( ghostOpacity * bgOpacity );
drawShape();
drawImages( effGhostOpacity, true );
setupBorderColor( ghostOpacity * borderOpacity );
drawBorder();
drawPie( darkness !== 0 || borderWidth !== 0 );
drawImages( effGhostOpacity, false );
darken( effGhostOpacity );
context.translate( -gx, -gy );
}
if( usePaths ){
context.translate( -pos.x, -pos.y );
}
drawUnderlay();
if( usePaths ){
context.translate( pos.x, pos.y );
}
setupOutlineColor();
drawOutline();
setupShapeColor();
drawShape();
drawImages(eleOpacity, true);
setupBorderColor();
drawBorder();
drawPie( darkness !== 0 || borderWidth !== 0 );
drawImages(eleOpacity, false);
darken();
if( usePaths ){
context.translate( -pos.x, -pos.y );
}
drawText();
drawOverlay();
//
// clean up shift
if( shiftToOriginWithBb ){
context.translate( bb.x1, bb.y1 );
}
};
const drawNodeOverlayUnderlay = function( overlayOrUnderlay ) {
if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
throw new Error('Invalid state');
}
return function( context, node, pos, nodeWidth, nodeHeight ){
let r = this;
if( !node.visible() ){ return; }
let padding = node.pstyle( `${overlayOrUnderlay}-padding` ).pfValue;
let opacity = node.pstyle( `${overlayOrUnderlay}-opacity` ).value;
let color = node.pstyle( `${overlayOrUnderlay}-color` ).value;
let shape = node.pstyle( `${overlayOrUnderlay}-shape` ).value;
let radius = node.pstyle( `${overlayOrUnderlay}-corner-radius` ).value;
if( opacity > 0 ){
pos = pos || node.position();
if( nodeWidth == null || nodeHeight == null ){
let padding = node.padding();
nodeWidth = node.width() + 2 * padding;
nodeHeight = node.height() + 2 * padding;
}
r.colorFillStyle( context, color[0], color[1], color[2], opacity );
r.nodeShapes[shape].draw(
context,
pos.x,
pos.y,
nodeWidth + padding * 2,
nodeHeight + padding * 2,
radius
);
context.fill();
}
};
};
CRp.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
CRp.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay');
// does the node have at least one pie piece?
CRp.hasPie = function( node ){
node = node[0]; // ensure ele ref
return node._private.hasPie;
};
CRp.drawPie = function( context, node, nodeOpacity, pos ){
node = node[0]; // ensure ele ref
pos = pos || node.position();
let cyStyle = node.cy().style();
let pieSize = node.pstyle( 'pie-size' );
let x = pos.x;
let y = pos.y;
let nodeW = node.width();
let nodeH = node.height();
let radius = Math.min( nodeW, nodeH ) / 2; // must fit in node
let lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
let usePaths = this.usePaths();
if( usePaths ){
x = 0;
y = 0;
}
if( pieSize.units === '%' ){
radius = radius * pieSize.pfValue;
} else if( pieSize.pfValue !== undefined ){
radius = pieSize.pfValue / 2;
}
for( let i = 1; i <= cyStyle.pieBackgroundN; i++ ){ // 1..N
let size = node.pstyle( 'pie-' + i + '-background-size' ).value;
let color = node.pstyle( 'pie-' + i + '-background-color' ).value;
let opacity = node.pstyle( 'pie-' + i + '-background-opacity' ).value * nodeOpacity;
let percent = size / 100; // map integer range [0, 100] to [0, 1]
// percent can't push beyond 1
if( percent + lastPercent > 1 ){
percent = 1 - lastPercent;
}
let angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
let angleDelta = 2 * Math.PI * percent;
let angleEnd = angleStart + angleDelta;
// ignore if
// - zero size
// - we're already beyond the full circle
// - adding the current slice would go beyond the full circle
if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
continue;
}
context.beginPath();
context.moveTo( x, y );
context.arc( x, y, radius, angleStart, angleEnd );
context.closePath();
this.colorFillStyle( context, color[0], color[1], color[2], opacity );
context.fill();
lastPercent += percent;
}
};
export default CRp;