diff options
Diffstat (limited to 'components/Graph/drawLabels.ts')
-rw-r--r-- | components/Graph/drawLabels.ts | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/components/Graph/drawLabels.ts b/components/Graph/drawLabels.ts new file mode 100644 index 0000000..fa19270 --- /dev/null +++ b/components/Graph/drawLabels.ts @@ -0,0 +1,130 @@ +import { OrgRoamNode } from '../../api' +import { NodeObject } from 'force-graph' +import { initialVisuals } from '../config' +import { hexToRGBA, LinksByNodeId } from '../../pages' +import wrap from 'word-wrap' + +export interface drawLabelsProps { + labelBackgroundColor: string + labelTextColor: string + node: NodeObject + ctx: any + globalScale: number + highlightedNodes: { [id: string]: {} } + previouslyHighlightedNodes: { [id: string]: {} } + visuals: typeof initialVisuals + opacity: number + nodeSize: (node: NodeObject) => number + filteredLinksByNodeId: LinksByNodeId + nodeRel: number + hoverNode: NodeObject | null + lastHoverNode: OrgRoamNode | null +} + +export const getLabelOpacity = ( + fadeFactor: number, + visuals: typeof initialVisuals, + globalScale: number, + opacity: number, + isHighlighty: boolean, +) => { + return isHighlighty + ? Math.max(fadeFactor, opacity) + : 1 * fadeFactor * (-1 * (visuals.highlightFade * opacity - 1)) +} + +export function drawLabels(props: drawLabelsProps) { + const { + labelBackgroundColor, + labelTextColor, + node, + ctx, + globalScale, + highlightedNodes, + previouslyHighlightedNodes, + visuals, + opacity, + nodeSize, + filteredLinksByNodeId, + nodeRel, + hoverNode, + lastHoverNode, + } = props + + if (!node) { + return + } + + if (!visuals.labels) { + return + } + const hoverId = hoverNode?.id ?? '' + const lastHoverId = lastHoverNode?.id ?? '' + const links = filteredLinksByNodeId[(node as OrgRoamNode).id] ?? [] + + const isHighlighty = !!(highlightedNodes[node.id!] || previouslyHighlightedNodes[node.id!]) + + const fadeFactor = Math.min( + 5 * (globalScale - visuals.labelScale) + + 2 * + Math.pow(Math.min(links.length, visuals.labelDynamicDegree), visuals.labelDynamicStrength), + 1, + ) + if (fadeFactor < 0.01 && !isHighlighty) { + return + } + const nodeTitle = (node as OrgRoamNode).title ?? '' + + const label = nodeTitle.substring(0, visuals.labelLength) + + const nodeS = Math.cbrt( + (visuals.nodeRel * nodeSize(node)) / Math.pow(globalScale, visuals.nodeZoomSize), + ) + const fontSize = visuals.labelFontSize / Math.cbrt(Math.pow(globalScale, visuals.nodeZoomSize)) + // ? Math.max((visuals.labelFontSize * nodeS) / 2, (visuals.labelFontSize * nodeS) / 3) + // : (visuals.labelFontSize * nodeS) / 3 + + // * nodeS) / 3 + const textWidth = ctx.measureText(label).width + const bckgDimensions = [textWidth * 1.1, fontSize].map((n) => n + fontSize * 0.5) as [ + number, + number, + ] // some padding + + // draw label background + const textOpacity = getLabelOpacity(fadeFactor, visuals, globalScale, opacity, isHighlighty) + if (visuals.labelBackgroundColor && visuals.labelBackgroundOpacity) { + const backgroundOpacity = textOpacity * visuals.labelBackgroundOpacity + const labelBackground = hexToRGBA(labelBackgroundColor, backgroundOpacity) + ctx.fillStyle = labelBackground + ctx.fillRect( + node.x! - bckgDimensions[0] / 2, + node.y! - bckgDimensions[1] / 2 + nodeS, + ...bckgDimensions, + ) + } + + // draw label text + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + const labelText = hexToRGBA(labelTextColor, textOpacity) + ctx.fillStyle = labelText + ctx.font = `${fontSize}px Sans-Serif` + const wordsArray = wrap(label, { width: visuals.labelWordWrap }).split('\n') + + const truncatedWords = + nodeTitle.length > visuals.labelLength + ? [...wordsArray.slice(0, -1), `${wordsArray.slice(-1)}...`] + : wordsArray + + const highlightedNodeOffset = [hoverId, lastHoverId].includes((node as OrgRoamNode).id) + ? 1 + 0.3 * opacity + : 1 + truncatedWords.forEach((word, index) => { + ctx.fillText( + word, + node.x!, + node.y! + highlightedNodeOffset * nodeS * 8 + visuals.labelLineSpace * fontSize * index, + ) + }) +} |