|
import * as util from '../../../util'; |
|
import * as math from '../../../math'; |
|
|
|
let CRp = {}; |
|
|
|
CRp.eleTextBiggerThanMin = function( ele, scale ){ |
|
if( !scale ){ |
|
let zoom = ele.cy().zoom(); |
|
let pxRatio = this.getPixelRatio(); |
|
let lvl = Math.ceil( math.log2( zoom * pxRatio ) ); |
|
|
|
scale = Math.pow( 2, lvl ); |
|
} |
|
|
|
let computedSize = ele.pstyle( 'font-size' ).pfValue * scale; |
|
let minSize = ele.pstyle( 'min-zoomed-font-size' ).pfValue; |
|
|
|
if( computedSize < minSize ){ |
|
return false; |
|
} |
|
|
|
return true; |
|
}; |
|
|
|
CRp.drawElementText = function( context, ele, shiftToOriginWithBb, force, prefix, useEleOpacity = true ){ |
|
let r = this; |
|
|
|
if( force == null ){ |
|
if( useEleOpacity && !r.eleTextBiggerThanMin( ele ) ){ return; } |
|
} else if( force === false ){ |
|
return; |
|
} |
|
|
|
if( ele.isNode() ){ |
|
let label = ele.pstyle( 'label' ); |
|
|
|
if( !label || !label.value ){ return; } |
|
|
|
let justification = r.getLabelJustification(ele); |
|
|
|
context.textAlign = justification; |
|
context.textBaseline = 'bottom'; |
|
} else { |
|
let badLine = ele.element()._private.rscratch.badLine; |
|
let label = ele.pstyle( 'label' ); |
|
let srcLabel = ele.pstyle( 'source-label' ); |
|
let tgtLabel = ele.pstyle( 'target-label' ); |
|
|
|
if( |
|
badLine || ( |
|
( !label || !label.value ) |
|
&& ( !srcLabel || !srcLabel.value ) |
|
&& ( !tgtLabel || !tgtLabel.value ) |
|
) |
|
){ |
|
return; |
|
} |
|
|
|
context.textAlign = 'center'; |
|
context.textBaseline = 'bottom'; |
|
} |
|
|
|
let applyRotation = !shiftToOriginWithBb; |
|
|
|
let bb; |
|
if( shiftToOriginWithBb ){ |
|
bb = shiftToOriginWithBb; |
|
|
|
context.translate( -bb.x1, -bb.y1 ); |
|
} |
|
|
|
if( prefix == null ){ |
|
r.drawText( context, ele, null, applyRotation, useEleOpacity ); |
|
|
|
if( ele.isEdge() ){ |
|
r.drawText( context, ele, 'source', applyRotation, useEleOpacity ); |
|
|
|
r.drawText( context, ele, 'target', applyRotation, useEleOpacity ); |
|
} |
|
} else { |
|
r.drawText( context, ele, prefix, applyRotation, useEleOpacity ); |
|
} |
|
|
|
if( shiftToOriginWithBb ){ |
|
context.translate( bb.x1, bb.y1 ); |
|
} |
|
}; |
|
|
|
CRp.getFontCache = function( context ){ |
|
let cache; |
|
|
|
this.fontCaches = this.fontCaches || []; |
|
|
|
for( let i = 0; i < this.fontCaches.length; i++ ){ |
|
cache = this.fontCaches[ i ]; |
|
|
|
if( cache.context === context ){ |
|
return cache; |
|
} |
|
} |
|
|
|
cache = { |
|
context: context |
|
}; |
|
this.fontCaches.push( cache ); |
|
|
|
return cache; |
|
}; |
|
|
|
|
|
|
|
CRp.setupTextStyle = function( context, ele, useEleOpacity = true ){ |
|
|
|
let labelStyle = ele.pstyle( 'font-style' ).strValue; |
|
let labelSize = ele.pstyle( 'font-size' ).pfValue + 'px'; |
|
let labelFamily = ele.pstyle( 'font-family' ).strValue; |
|
let labelWeight = ele.pstyle( 'font-weight' ).strValue; |
|
let opacity = (useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1); |
|
let outlineOpacity = ele.pstyle( 'text-outline-opacity' ).value * opacity; |
|
let color = ele.pstyle( 'color' ).value; |
|
let outlineColor = ele.pstyle( 'text-outline-color' ).value; |
|
|
|
context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily; |
|
|
|
context.lineJoin = 'round'; |
|
|
|
this.colorFillStyle( context, color[ 0 ], color[ 1 ], color[ 2 ], opacity ); |
|
|
|
this.colorStrokeStyle( context, outlineColor[ 0 ], outlineColor[ 1 ], outlineColor[ 2 ], outlineOpacity ); |
|
}; |
|
|
|
|
|
function roundRect( ctx, x, y, width, height, radius = 5, stroke){ |
|
ctx.beginPath(); |
|
ctx.moveTo( x + radius, y ); |
|
ctx.lineTo( x + width - radius, y ); |
|
ctx.quadraticCurveTo( x + width, y, x + width, y + radius ); |
|
ctx.lineTo( x + width, y + height - radius ); |
|
ctx.quadraticCurveTo( x + width, y + height, x + width - radius, y + height ); |
|
ctx.lineTo( x + radius, y + height ); |
|
ctx.quadraticCurveTo( x, y + height, x, y + height - radius ); |
|
ctx.lineTo( x, y + radius ); |
|
ctx.quadraticCurveTo( x, y, x + radius, y ); |
|
ctx.closePath(); |
|
if(stroke) |
|
ctx.stroke(); |
|
else |
|
ctx.fill(); |
|
} |
|
|
|
CRp.getTextAngle = function( ele, prefix ){ |
|
let theta; |
|
let _p = ele._private; |
|
let rscratch = _p.rscratch; |
|
let pdash = prefix ? prefix + '-' : ''; |
|
let rotation = ele.pstyle( pdash + 'text-rotation' ); |
|
let textAngle = util.getPrefixedProperty( rscratch, 'labelAngle', prefix ); |
|
|
|
if( rotation.strValue === 'autorotate' ){ |
|
theta = ele.isEdge() ? textAngle : 0; |
|
} else if( rotation.strValue === 'none' ){ |
|
theta = 0; |
|
} else { |
|
theta = rotation.pfValue; |
|
} |
|
|
|
return theta; |
|
}; |
|
|
|
CRp.drawText = function( context, ele, prefix, applyRotation = true, useEleOpacity = true ){ |
|
let _p = ele._private; |
|
let rscratch = _p.rscratch; |
|
let parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1; |
|
|
|
if( useEleOpacity && (parentOpacity === 0 || ele.pstyle( 'text-opacity' ).value === 0) ){ |
|
return; |
|
} |
|
|
|
|
|
if( prefix === 'main' ){ prefix = null; } |
|
|
|
let textX = util.getPrefixedProperty( rscratch, 'labelX', prefix ); |
|
let textY = util.getPrefixedProperty( rscratch, 'labelY', prefix ); |
|
let orgTextX, orgTextY; |
|
let text = this.getLabelText( ele, prefix ); |
|
|
|
if( text != null && text !== '' && !isNaN( textX ) && !isNaN( textY ) ){ |
|
this.setupTextStyle( context, ele, useEleOpacity ); |
|
|
|
let pdash = prefix ? prefix + '-' : ''; |
|
let textW = util.getPrefixedProperty( rscratch, 'labelWidth', prefix ); |
|
let textH = util.getPrefixedProperty( rscratch, 'labelHeight', prefix ); |
|
let marginX = ele.pstyle( pdash + 'text-margin-x' ).pfValue; |
|
let marginY = ele.pstyle( pdash + 'text-margin-y' ).pfValue; |
|
|
|
let isEdge = ele.isEdge(); |
|
|
|
let halign = ele.pstyle( 'text-halign' ).value; |
|
let valign = ele.pstyle( 'text-valign' ).value; |
|
|
|
if( isEdge ){ |
|
halign = 'center'; |
|
valign = 'center'; |
|
} |
|
|
|
textX += marginX; |
|
textY += marginY; |
|
|
|
let theta; |
|
|
|
if( !applyRotation ){ |
|
theta = 0; |
|
} else { |
|
theta = this.getTextAngle(ele, prefix); |
|
} |
|
|
|
if( theta !== 0 ){ |
|
orgTextX = textX; |
|
orgTextY = textY; |
|
|
|
context.translate( orgTextX, orgTextY ); |
|
context.rotate( theta ); |
|
|
|
textX = 0; |
|
textY = 0; |
|
} |
|
|
|
switch( valign ){ |
|
case 'top': |
|
break; |
|
case 'center': |
|
textY += textH / 2; |
|
break; |
|
case 'bottom': |
|
textY += textH; |
|
break; |
|
} |
|
|
|
let backgroundOpacity = ele.pstyle( 'text-background-opacity' ).value; |
|
let borderOpacity = ele.pstyle( 'text-border-opacity' ).value; |
|
let textBorderWidth = ele.pstyle( 'text-border-width' ).pfValue; |
|
let backgroundPadding = ele.pstyle( 'text-background-padding' ).pfValue; |
|
let styleShape = ele.pstyle( 'text-background-shape' ).strValue; |
|
let rounded = styleShape.indexOf('round') === 0; |
|
let roundRadius = 2; |
|
|
|
if( backgroundOpacity > 0 || ( textBorderWidth > 0 && borderOpacity > 0 ) ){ |
|
let bgX = textX - backgroundPadding; |
|
|
|
switch( halign ){ |
|
case 'left': |
|
bgX -= textW; |
|
break; |
|
case 'center': |
|
bgX -= textW / 2; |
|
break; |
|
case 'right': |
|
break; |
|
} |
|
|
|
let bgY = textY - textH - backgroundPadding; |
|
let bgW = textW + 2*backgroundPadding; |
|
let bgH = textH + 2*backgroundPadding; |
|
|
|
if( backgroundOpacity > 0 ){ |
|
let textFill = context.fillStyle; |
|
let textBackgroundColor = ele.pstyle( 'text-background-color' ).value; |
|
|
|
context.fillStyle = 'rgba(' + textBackgroundColor[ 0 ] + ',' + textBackgroundColor[ 1 ] + ',' + textBackgroundColor[ 2 ] + ',' + backgroundOpacity * parentOpacity + ')'; |
|
if( rounded ){ |
|
roundRect( context, bgX, bgY, bgW, bgH, roundRadius ); |
|
} else { |
|
context.fillRect( bgX, bgY, bgW, bgH ); |
|
} |
|
context.fillStyle = textFill; |
|
} |
|
|
|
if( textBorderWidth > 0 && borderOpacity > 0 ){ |
|
let textStroke = context.strokeStyle; |
|
let textLineWidth = context.lineWidth; |
|
let textBorderColor = ele.pstyle( 'text-border-color' ).value; |
|
let textBorderStyle = ele.pstyle( 'text-border-style' ).value; |
|
|
|
context.strokeStyle = 'rgba(' + textBorderColor[ 0 ] + ',' + textBorderColor[ 1 ] + ',' + textBorderColor[ 2 ] + ',' + borderOpacity * parentOpacity + ')'; |
|
context.lineWidth = textBorderWidth; |
|
|
|
if( context.setLineDash ){ |
|
switch( textBorderStyle ){ |
|
case 'dotted': |
|
context.setLineDash( [ 1, 1 ] ); |
|
break; |
|
case 'dashed': |
|
context.setLineDash( [ 4, 2 ] ); |
|
break; |
|
case 'double': |
|
context.lineWidth = textBorderWidth / 4; |
|
context.setLineDash( [] ); |
|
break; |
|
case 'solid': |
|
context.setLineDash( [] ); |
|
break; |
|
} |
|
} |
|
|
|
if( rounded ){ |
|
roundRect( context, bgX, bgY, bgW, bgH, roundRadius, 'stroke' ); |
|
} else { |
|
context.strokeRect( bgX, bgY, bgW, bgH ); |
|
} |
|
|
|
if( textBorderStyle === 'double' ){ |
|
let whiteWidth = textBorderWidth / 2; |
|
if( rounded ){ |
|
roundRect( context, bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2, roundRadius, 'stroke' ); |
|
} else { |
|
context.strokeRect( bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2 ); |
|
} |
|
} |
|
|
|
if( context.setLineDash ){ |
|
context.setLineDash( [] ); |
|
} |
|
context.lineWidth = textLineWidth; |
|
context.strokeStyle = textStroke; |
|
} |
|
|
|
} |
|
|
|
let lineWidth = 2 * ele.pstyle( 'text-outline-width' ).pfValue; |
|
|
|
if( lineWidth > 0 ){ |
|
context.lineWidth = lineWidth; |
|
} |
|
|
|
if( ele.pstyle( 'text-wrap' ).value === 'wrap' ){ |
|
let lines = util.getPrefixedProperty( rscratch, 'labelWrapCachedLines', prefix ); |
|
let lineHeight = util.getPrefixedProperty( rscratch, 'labelLineHeight', prefix ); |
|
let halfTextW = textW/2; |
|
let justification = this.getLabelJustification(ele); |
|
|
|
if( justification === 'auto' ){ |
|
|
|
} else if( halign === 'left' ){ |
|
if( justification === 'left' ){ |
|
textX += -textW; |
|
} else if( justification === 'center' ){ |
|
textX += -halfTextW; |
|
} |
|
} else if( halign === 'center' ){ |
|
if( justification === 'left' ){ |
|
textX += -halfTextW; |
|
} else if( justification === 'right' ){ |
|
textX += halfTextW; |
|
} |
|
} else if( halign === 'right' ){ |
|
if( justification === 'center' ){ |
|
textX += halfTextW; |
|
} else if( justification === 'right' ){ |
|
textX += textW; |
|
} |
|
} |
|
|
|
switch( valign ){ |
|
case 'top': |
|
textY -= ( lines.length - 1 ) * lineHeight; |
|
break; |
|
case 'center': |
|
case 'bottom': |
|
textY -= ( lines.length - 1 ) * lineHeight; |
|
break; |
|
} |
|
|
|
for( let l = 0; l < lines.length; l++ ){ |
|
if( lineWidth > 0 ){ |
|
context.strokeText( lines[ l ], textX, textY ); |
|
} |
|
|
|
context.fillText( lines[ l ], textX, textY ); |
|
|
|
textY += lineHeight; |
|
} |
|
|
|
} else { |
|
if( lineWidth > 0 ){ |
|
context.strokeText( text, textX, textY ); |
|
} |
|
|
|
context.fillText( text, textX, textY ); |
|
} |
|
|
|
if( theta !== 0 ){ |
|
context.rotate( -theta ); |
|
context.translate( -orgTextX, -orgTextY ); |
|
} |
|
} |
|
}; |
|
|
|
export default CRp; |
|
|