summaryrefslogtreecommitdiff
path: root/components/Graph
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2021-10-16 23:07:19 +0200
committerGitHub <[email protected]>2021-10-16 23:07:19 +0200
commitca2fa3dc8d405d0ec451f16b9fcc72cde6bbf470 (patch)
treea98c1af5379688b57985eebe051ffdb9422dbbb5 /components/Graph
parenteb794cc2183595a4f42be393db6b52478053000c (diff)
parent677702087e7b1e001a489412f4ed26dfaa138df6 (diff)
Merge pull request #117 from org-roam/feat/better-labels
Feat/better labels
Diffstat (limited to 'components/Graph')
-rw-r--r--components/Graph/drawLabels.ts130
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,
+ )
+ })
+}