diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/findNthNeighbour.ts | 42 | ||||
-rw-r--r-- | util/getLinkColor.ts | 82 | ||||
-rw-r--r-- | util/getLinkNodeColor.ts | 23 | ||||
-rw-r--r-- | util/getNodeColor.ts | 75 | ||||
-rw-r--r-- | util/getNodeColorById.ts | 25 | ||||
-rw-r--r-- | util/getThemeColor.ts | 3 | ||||
-rw-r--r-- | util/hexToRGBA.ts | 11 | ||||
-rw-r--r-- | util/isLinkRelatedToNode.ts | 7 | ||||
-rw-r--r-- | util/nodeSize.ts | 34 | ||||
-rw-r--r-- | util/normalizeLinkEnds.ts | 12 | ||||
-rw-r--r-- | util/numberWithinRange.ts | 3 |
11 files changed, 317 insertions, 0 deletions
diff --git a/util/findNthNeighbour.ts b/util/findNthNeighbour.ts new file mode 100644 index 0000000..d48adbc --- /dev/null +++ b/util/findNthNeighbour.ts @@ -0,0 +1,42 @@ +import { LinksByNodeId } from '../pages' +import { normalizeLinkEnds } from './normalizeLinkEnds' + +export const findNthNeighbors = ({ + ids, + excludedIds, + n, + linksByNodeId, +}: { + ids: string[] + excludedIds: string[] + n: number + linksByNodeId: LinksByNodeId +}) => { + let queue = [ids[0]] + let todo: string[] = [] + const completed = [ids[0]] + Array.from({ length: n }, () => { + queue.forEach((node) => { + const links = linksByNodeId[node as string] ?? [] + links.forEach((link) => { + const [sourceId, targetId] = normalizeLinkEnds(link) + if (excludedIds.some((id) => [sourceId, targetId].includes(id))) { + return + } + if (!completed.includes(sourceId)) { + todo.push(sourceId) + return + } + if (!completed.includes(targetId)) { + todo.push(targetId) + return + } + return + }) + }) + queue = todo + todo.forEach((neighbor) => neighbor && completed.push(neighbor)) + todo = [] + }) + return completed +} diff --git a/util/getLinkColor.ts b/util/getLinkColor.ts new file mode 100644 index 0000000..33f7094 --- /dev/null +++ b/util/getLinkColor.ts @@ -0,0 +1,82 @@ +import { initialColoring, initialVisuals } from '../components/config' +import { LinksByNodeId } from '../pages' +import { getLinkNodeColor } from './getLinkNodeColor' +import { getThemeColor } from './getThemeColor' + +export const getLinkColor = ({ + sourceId, + targetId, + needsHighlighting, + theme, + visuals, + highlightColors, + opacity, + linksByNodeId, + coloring, + cluster, +}: { + sourceId: string + targetId: string + needsHighlighting: boolean + theme: any + visuals: typeof initialVisuals + highlightColors: Record<string, any> + opacity: number + linksByNodeId: LinksByNodeId + coloring: typeof initialColoring + cluster: any +}) => { + if (!visuals.linkHighlight && !visuals.linkColorScheme && !needsHighlighting) { + const nodeColor = getLinkNodeColor({ + sourceId, + targetId, + linksByNodeId, + visuals, + coloring, + cluster, + }) + return getThemeColor(nodeColor, theme) + } + + if (!needsHighlighting && !visuals.linkColorScheme) { + const nodeColor = getLinkNodeColor({ + sourceId, + targetId, + linksByNodeId, + visuals, + coloring, + cluster, + }) + return highlightColors[nodeColor][visuals.backgroundColor](visuals.highlightFade * opacity) + } + + if (!needsHighlighting) { + return highlightColors[visuals.linkColorScheme][visuals.backgroundColor]( + visuals.highlightFade * opacity, + ) + } + + if (!visuals.linkHighlight && !visuals.linkColorScheme) { + const nodeColor = getLinkNodeColor({ + sourceId, + targetId, + linksByNodeId, + visuals, + coloring, + cluster, + }) + return getThemeColor(nodeColor, theme) + } + + if (!visuals.linkHighlight) { + return getThemeColor(visuals.linkColorScheme, theme) + } + + if (!visuals.linkColorScheme) { + return highlightColors[ + getLinkNodeColor({ sourceId, targetId, linksByNodeId, visuals, coloring, cluster }) + ][visuals.linkHighlight](opacity) + } + + return highlightColors[visuals.linkColorScheme][visuals.linkHighlight](opacity) +} diff --git a/util/getLinkNodeColor.ts b/util/getLinkNodeColor.ts new file mode 100644 index 0000000..86cfa4b --- /dev/null +++ b/util/getLinkNodeColor.ts @@ -0,0 +1,23 @@ +import { initialColoring, initialVisuals } from '../components/config' +import { LinksByNodeId } from '../pages' +import { getNodeColorById } from './getNodeColorById' + +export const getLinkNodeColor = ({ + sourceId, + targetId, + linksByNodeId, + visuals, + coloring, + cluster, +}: { + sourceId: string + targetId: string + linksByNodeId: LinksByNodeId + visuals: typeof initialVisuals + coloring: typeof initialColoring + cluster: any +}) => { + return linksByNodeId[sourceId]!.length > linksByNodeId[targetId]!.length + ? getNodeColorById({ id: sourceId, linksByNodeId, visuals, cluster, coloring }) + : getNodeColorById({ id: targetId, visuals, linksByNodeId, cluster, coloring }) +} diff --git a/util/getNodeColor.ts b/util/getNodeColor.ts new file mode 100644 index 0000000..f25a15b --- /dev/null +++ b/util/getNodeColor.ts @@ -0,0 +1,75 @@ +import { OrgRoamNode } from '../api' +import { initialColoring, initialVisuals } from '../components/config' +import { LinksByNodeId } from '../pages' +import { getNodeColorById } from './getNodeColorById' +import { getThemeColor } from './getThemeColor' + +export const getNodeColor = ({ + node, + theme, + highlightedNodes, + previouslyHighlightedNodes, + visuals, + tagColors, + highlightColors, + opacity, + emacsNodeId, + linksByNodeId, + cluster, + coloring, +}: { + node: OrgRoamNode + theme: any + visuals: typeof initialVisuals + highlightedNodes: Record<string, any> + previouslyHighlightedNodes: Record<string, any> + tagColors: Record<string, any> + highlightColors: Record<string, any> + opacity: number + emacsNodeId: string | null + linksByNodeId: LinksByNodeId + cluster: any + coloring: typeof initialColoring +}) => { + const needsHighlighting = highlightedNodes[node.id!] || previouslyHighlightedNodes[node.id!] + //const needsHighlighting = hoverNode?.id === node.id! || lastHoverNode?.current?.id === node.id + // if we are matching the node color and don't have a highlight color + // or we don't have our own scheme and we're not being highlighted + if (visuals.emacsNodeColor && node.id === emacsNodeId) { + return getThemeColor(visuals.emacsNodeColor, theme) + } + if (tagColors && node?.tags.some((tag) => tagColors[tag])) { + const tagColor = tagColors[node?.tags.filter((tag) => tagColors[tag])[0]] + return needsHighlighting + ? highlightColors[tagColor][tagColor](visuals.highlightFade * opacity) + : highlightColors[tagColor][visuals.backgroundColor](visuals.highlightFade * opacity) + } + if (visuals.citeNodeColor && node?.properties?.ROAM_REFS && node?.properties?.FILELESS) { + return needsHighlighting + ? getThemeColor(visuals.citeNodeColor, theme) + : highlightColors[visuals.citeNodeColor][visuals.backgroundColor]( + visuals.highlightFade * opacity, + ) + } + if (visuals.refNodeColor && node.properties.ROAM_REFS) { + return needsHighlighting + ? getThemeColor(visuals.refNodeColor, theme) + : highlightColors[visuals.refNodeColor][visuals.backgroundColor]( + visuals.highlightFade * opacity, + ) + } + if (!needsHighlighting) { + return highlightColors[ + getNodeColorById({ id: node.id as string, cluster, coloring, linksByNodeId, visuals }) + ][visuals.backgroundColor](visuals.highlightFade * opacity) + } + if (!visuals.nodeHighlight) { + return getThemeColor( + getNodeColorById({ id: node.id as string, cluster, coloring, linksByNodeId, visuals }), + theme, + ) + } + return highlightColors[ + getNodeColorById({ id: node.id as string, cluster, coloring, linksByNodeId, visuals }) + ][visuals.nodeHighlight](opacity) +} diff --git a/util/getNodeColorById.ts b/util/getNodeColorById.ts new file mode 100644 index 0000000..d2c198b --- /dev/null +++ b/util/getNodeColorById.ts @@ -0,0 +1,25 @@ +import { initialColoring, initialVisuals } from '../components/config' +import { LinksByNodeId } from '../pages' +import { numberWithinRange } from './numberWithinRange' + +export const getNodeColorById = ({ + id, + linksByNodeId, + visuals, + coloring, + cluster, +}: { + id: string + linksByNodeId: LinksByNodeId + visuals: typeof initialVisuals + cluster: any + coloring: typeof initialColoring +}) => { + const linklen = linksByNodeId[id!]?.length ?? 0 + if (coloring.method === 'degree') { + return visuals.nodeColorScheme[ + numberWithinRange(linklen, 0, visuals.nodeColorScheme.length - 1) + ] + } + return visuals.nodeColorScheme[linklen && cluster[id] % visuals.nodeColorScheme.length] +} diff --git a/util/getThemeColor.ts b/util/getThemeColor.ts new file mode 100644 index 0000000..1175a52 --- /dev/null +++ b/util/getThemeColor.ts @@ -0,0 +1,3 @@ +export const getThemeColor = (name: string, theme: any) => { + return name.split('.').reduce((o, i) => o[i], theme.colors) +} diff --git a/util/hexToRGBA.ts b/util/hexToRGBA.ts new file mode 100644 index 0000000..bacb601 --- /dev/null +++ b/util/hexToRGBA.ts @@ -0,0 +1,11 @@ +export function hexToRGBA(hex: string, opacity: number) { + return ( + 'rgba(' + + (hex = hex.replace('#', '')) + .match(new RegExp('(.{' + hex.length / 3 + '})', 'g'))! + .map((l) => parseInt(hex.length % 2 ? l + l : l, 16)) + .concat(isFinite(opacity) ? opacity : 1) + .join(',') + + ')' + ) +} diff --git a/util/isLinkRelatedToNode.ts b/util/isLinkRelatedToNode.ts new file mode 100644 index 0000000..eeacab7 --- /dev/null +++ b/util/isLinkRelatedToNode.ts @@ -0,0 +1,7 @@ +import { NodeObject, LinkObject } from 'force-graph' + +export const isLinkRelatedToNode = (link: LinkObject, node: NodeObject | null) => { + return ( + (link.source as NodeObject)?.id! === node?.id! || (link.target as NodeObject)?.id! === node?.id! + ) +} diff --git a/util/nodeSize.ts b/util/nodeSize.ts new file mode 100644 index 0000000..3601c1e --- /dev/null +++ b/util/nodeSize.ts @@ -0,0 +1,34 @@ +import { filter } from '@chakra-ui/react' +import { initialVisuals } from '../components/config' +import { LinksByNodeId } from '../pages' +import { NodeObject } from 'force-graph' + +export const nodeSize = ({ + linksByNodeId, + visuals, + highlightedNodes, + previouslyHighlightedNodes, + opacity, + node, +}: { + node: NodeObject + visuals: typeof initialVisuals + + highlightedNodes: Record<string, any> + previouslyHighlightedNodes: Record<string, any> + opacity: number + linksByNodeId: LinksByNodeId +}) => { + const links = linksByNodeId[node.id!] ?? [] + const parentNeighbors = links.length ? links.filter((link) => link.type === 'parent').length : 0 + const basicSize = + 3 + links.length * visuals.nodeSizeLinks - (!filter.parent ? parentNeighbors : 0) + if (visuals.highlightNodeSize === 1) { + return basicSize + } + const highlightSize = + highlightedNodes[node.id!] || previouslyHighlightedNodes[node.id!] + ? 1 + opacity * (visuals.highlightNodeSize - 1) + : 1 + return basicSize * highlightSize +} diff --git a/util/normalizeLinkEnds.ts b/util/normalizeLinkEnds.ts new file mode 100644 index 0000000..43eee9c --- /dev/null +++ b/util/normalizeLinkEnds.ts @@ -0,0 +1,12 @@ +import { OrgRoamLink } from '../api' +import { LinkObject } from 'force-graph' + +export function normalizeLinkEnds(link: OrgRoamLink | LinkObject): [string, string] { + // we need to cover both because force-graph modifies the original data + // but if we supply the original data on each render, the graph will re-render sporadically + const sourceId = + typeof link.source === 'object' ? (link.source.id! as string) : (link.source as string) + const targetId = + typeof link.target === 'object' ? (link.target.id! as string) : (link.target as string) + return [sourceId, targetId] +} diff --git a/util/numberWithinRange.ts b/util/numberWithinRange.ts new file mode 100644 index 0000000..ae435ce --- /dev/null +++ b/util/numberWithinRange.ts @@ -0,0 +1,3 @@ +export const numberWithinRange = (num: number, min: number, max: number) => { + return Math.min(Math.max(num, min), max) +} |