diff --git a/quartz.layout.ts b/quartz.layout.ts index 4a78256aa..c44da7dbd 100644 --- a/quartz.layout.ts +++ b/quartz.layout.ts @@ -30,7 +30,7 @@ export const defaultContentPageLayout: PageLayout = { Component.DesktopOnly(Component.Explorer()), ], right: [ - Component.Graph(), + Component.Graph({ globalGraph: { enableRadial: true } }), Component.DesktopOnly(Component.TableOfContents()), Component.Backlinks(), ], diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx index ec3475d1e..b49298ca7 100644 --- a/quartz/components/Graph.tsx +++ b/quartz/components/Graph.tsx @@ -18,6 +18,7 @@ export interface D3Config { removeTags: string[] showTags: boolean focusOnHover?: boolean + enableRadial?: boolean } interface GraphOptions { @@ -39,6 +40,7 @@ const defaultOptions: GraphOptions = { showTags: true, removeTags: [], focusOnHover: false, + enableRadial: false, }, globalGraph: { drag: true, @@ -53,10 +55,11 @@ const defaultOptions: GraphOptions = { showTags: true, removeTags: [], focusOnHover: true, + enableRadial: false, }, } -export default ((opts?: GraphOptions) => { +export default ((opts?: Partial) => { const Graph: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => { const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph } const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph } diff --git a/quartz/components/scripts/graph.inline.ts b/quartz/components/scripts/graph.inline.ts index dbddae91f..16ee33f64 100644 --- a/quartz/components/scripts/graph.inline.ts +++ b/quartz/components/scripts/graph.inline.ts @@ -8,6 +8,7 @@ import { forceCenter, forceLink, forceCollide, + forceRadial, zoomIdentity, select, drag, @@ -87,6 +88,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) { removeTags, showTags, focusOnHover, + enableRadial, } = JSON.parse(graph.dataset["cfg"]!) as D3Config const data: Map = new Map( @@ -161,15 +163,20 @@ async function renderGraph(container: string, fullSlug: FullSlug) { })), } + const width = graph.offsetWidth + const height = Math.max(graph.offsetHeight, 250) + // we virtualize the simulation and use pixi to actually render it + // Calculate the radius of the container circle + const radius = Math.min(width, height) / 2 - 40 // 40px padding const simulation: Simulation = forceSimulation(graphData.nodes) .force("charge", forceManyBody().strength(-100 * repelForce)) .force("center", forceCenter().strength(centerForce)) .force("link", forceLink(graphData.links).distance(linkDistance)) .force("collide", forceCollide((n) => nodeRadius(n)).iterations(3)) - const width = graph.offsetWidth - const height = Math.max(graph.offsetHeight, 250) + if (enableRadial) + simulation.force("radial", forceRadial(radius * 0.8, width / 2, height / 2).strength(0.3)) // precompute style prop strings as pixi doesn't support css variables const cssVars = [