feat (graph settings): Add edge thickness range configuration function

This commit is contained in:
choizhang
2025-04-01 00:36:32 +08:00
parent 22a4e08439
commit cd3817ce30
7 changed files with 146 additions and 3 deletions

View File

@@ -36,6 +36,8 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
const enableEdgeEvents = useSettingsStore.use.enableEdgeEvents()
const renderEdgeLabels = useSettingsStore.use.showEdgeLabel()
const renderLabels = useSettingsStore.use.showNodeLabel()
const minEdgeSize = useSettingsStore.use.minEdgeSize()
const maxEdgeSize = useSettingsStore.use.maxEdgeSize()
const selectedNode = useGraphStore.use.selectedNode()
const focusedNode = useGraphStore.use.focusedNode()
const selectedEdge = useGraphStore.use.selectedEdge()
@@ -136,6 +138,51 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
registerEvents(events)
}, [registerEvents, enableEdgeEvents])
/**
* When edge size settings change, recalculate edge sizes and refresh the sigma instance
* to ensure changes take effect immediately
*/
useEffect(() => {
if (sigma && sigmaGraph) {
// Get the graph from sigma
const graph = sigma.getGraph()
// Find min and max weight values
let minWeight = Number.MAX_SAFE_INTEGER
let maxWeight = 0
graph.forEachEdge(edge => {
// Get original weight (before scaling)
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
if (typeof weight === 'number') {
minWeight = Math.min(minWeight, weight)
maxWeight = Math.max(maxWeight, weight)
}
})
// Scale edge sizes based on weight range and current min/max edge size settings
const weightRange = maxWeight - minWeight
if (weightRange > 0) {
const sizeScale = maxEdgeSize - minEdgeSize
graph.forEachEdge(edge => {
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
if (typeof weight === 'number') {
const scaledSize = minEdgeSize + sizeScale * Math.pow((weight - minWeight) / weightRange, 0.5)
graph.setEdgeAttribute(edge, 'size', scaledSize)
}
})
} else {
// If all weights are the same, use default size
graph.forEachEdge(edge => {
graph.setEdgeAttribute(edge, 'size', minEdgeSize)
})
}
// Refresh the sigma instance to apply changes
sigma.refresh()
}
}, [sigma, sigmaGraph, minEdgeSize, maxEdgeSize])
/**
* When component mount or hovered node change
* => Setting the sigma reducers

View File

@@ -120,6 +120,8 @@ export default function Settings() {
const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
const enableHideUnselectedEdges = useSettingsStore.use.enableHideUnselectedEdges()
const showEdgeLabel = useSettingsStore.use.showEdgeLabel()
const minEdgeSize = useSettingsStore.use.minEdgeSize()
const maxEdgeSize = useSettingsStore.use.maxEdgeSize()
const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth()
const graphMinDegree = useSettingsStore.use.graphMinDegree()
const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations()
@@ -269,6 +271,40 @@ export default function Settings() {
label={t('graphPanel.sideBar.settings.edgeEvents')}
/>
<div className="flex flex-col gap-2">
<label className="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{t('graphPanel.sideBar.settings.edgeSizeRange')}
</label>
<div className="flex items-center gap-2">
<Input
type="number"
value={minEdgeSize}
onChange={(e) => {
const newValue = Number(e.target.value);
if (!isNaN(newValue) && newValue >= 1 && newValue <= maxEdgeSize) {
useSettingsStore.setState({ minEdgeSize: newValue });
}
}}
className="h-6 w-16 min-w-0 pr-1"
min={1}
max={maxEdgeSize}
/>
<span>-</span>
<Input
type="number"
value={maxEdgeSize}
onChange={(e) => {
const newValue = Number(e.target.value);
if (!isNaN(newValue) && newValue >= minEdgeSize && newValue >= 1) {
useSettingsStore.setState({ maxEdgeSize: newValue });
}
}}
className="h-6 w-16 min-w-0 pr-1"
min={minEdgeSize}
/>
</div>
</div>
<Separator />
<LabeledNumberInput
label={t('graphPanel.sideBar.settings.maxQueryDepth')}

View File

@@ -68,7 +68,13 @@ export type NodeType = {
color: string
highlighted?: boolean
}
export type EdgeType = { label: string }
export type EdgeType = {
label: string
originalWeight?: number
size?: number
color?: string
hidden?: boolean
}
const fetchGraph = async (label: string, maxDepth: number, minDegree: number) => {
let rawData: any = null;
@@ -174,6 +180,9 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
// Create a new graph instance with the raw graph data
const createSigmaGraph = (rawGraph: RawGraph | null) => {
// Get edge size settings from store
const minEdgeSize = useSettingsStore.getState().minEdgeSize
const maxEdgeSize = useSettingsStore.getState().maxEdgeSize
// Skip graph creation if no data or empty nodes
if (!rawGraph || !rawGraph.nodes.length) {
console.log('No graph data available, skipping sigma graph creation');
@@ -204,8 +213,40 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
// Add edges from raw graph data
for (const rawEdge of rawGraph?.edges ?? []) {
// Get weight from edge properties or default to 1
const weight = rawEdge.properties?.weight !== undefined ? Number(rawEdge.properties.weight) : 1
rawEdge.dynamicId = graph.addDirectedEdge(rawEdge.source, rawEdge.target, {
label: rawEdge.properties?.keywords || undefined
label: rawEdge.properties?.keywords || undefined,
size: weight, // Set initial size based on weight
originalWeight: weight, // Store original weight for recalculation
})
}
// Calculate edge size based on weight range, similar to node size calculation
let minWeight = Number.MAX_SAFE_INTEGER
let maxWeight = 0
// Find min and max weight values
graph.forEachEdge(edge => {
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
minWeight = Math.min(minWeight, weight)
maxWeight = Math.max(maxWeight, weight)
})
// Scale edge sizes based on weight range
const weightRange = maxWeight - minWeight
if (weightRange > 0) {
const sizeScale = maxEdgeSize - minEdgeSize
graph.forEachEdge(edge => {
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
const scaledSize = minEdgeSize + sizeScale * Math.pow((weight - minWeight) / weightRange, 0.5)
graph.setEdgeAttribute(edge, 'size', scaledSize)
})
} else {
// If all weights are the same, use default size
graph.forEachEdge(edge => {
graph.setEdgeAttribute(edge, 'size', minEdgeSize)
})
}

View File

@@ -1,6 +1,6 @@
import { ButtonVariantType } from '@/components/ui/Button'
export const backendBaseUrl = ''
export const backendBaseUrl = 'http://localhost:9621'
export const webuiPrefix = '/webui/'
export const controlButtonVariant: ButtonVariantType = 'ghost'

View File

@@ -141,6 +141,7 @@
"maxQueryDepth": "Max Query Depth",
"minDegree": "Minimum Degree",
"maxLayoutIterations": "Max Layout Iterations",
"edgeSizeRange": "Edge Size Range",
"depth": "Depth",
"degree": "Degree",
"apiKey": "API Key",

View File

@@ -141,6 +141,7 @@
"maxQueryDepth": "最大查询深度",
"minDegree": "最小邻边数",
"maxLayoutIterations": "最大布局迭代次数",
"edgeSizeRange": "边粗细范围",
"depth": "深度",
"degree": "邻边",
"apiKey": "API密钥",

View File

@@ -24,6 +24,12 @@ interface SettingsState {
enableHideUnselectedEdges: boolean
enableEdgeEvents: boolean
minEdgeSize: number
setMinEdgeSize: (size: number) => void
maxEdgeSize: number
setMaxEdgeSize: (size: number) => void
graphQueryMaxDepth: number
setGraphQueryMaxDepth: (depth: number) => void
@@ -76,6 +82,9 @@ const useSettingsStoreBase = create<SettingsState>()(
enableHideUnselectedEdges: true,
enableEdgeEvents: false,
minEdgeSize: 1,
maxEdgeSize: 1,
graphQueryMaxDepth: 3,
graphMinDegree: 0,
graphLayoutMaxIterations: 15,
@@ -132,6 +141,10 @@ const useSettingsStoreBase = create<SettingsState>()(
setGraphMinDegree: (degree: number) => set({ graphMinDegree: degree }),
setMinEdgeSize: (size: number) => set({ minEdgeSize: size }),
setMaxEdgeSize: (size: number) => set({ maxEdgeSize: size }),
setEnableHealthCheck: (enable: boolean) => set({ enableHealthCheck: enable }),
setApiKey: (apiKey: string | null) => set({ apiKey }),
@@ -196,6 +209,10 @@ const useSettingsStoreBase = create<SettingsState>()(
if (version < 9) {
state.showFileName = false
}
if (version < 10) {
state.minEdgeSize = 1
state.maxEdgeSize = 1
}
return state
}
}