1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2026-05-06 20:09:18 +08:00

Support circle pack as initial algorithm #136

This commit is contained in:
lana-k
2026-04-06 19:36:33 +02:00
parent d9435a80c3
commit c0d972c2ab
5 changed files with 74 additions and 72 deletions

View File

@@ -9,16 +9,13 @@
/>
</Field>
<Field
v-if="modelValue.initialAlgorithm === 'random'"
label="Seed value"
fieldContainerClassName="test_fa2_seed_value"
>
<NumericInput
:value="modelValue.seedValue"
@update="update('seedValue', $event)"
/>
</Field>
<component
:is="layoutSettingsComponentMap[modelValue.initialAlgorithm]"
v-if="modelValue.initialAlgorithm !== 'circular'"
:model-value="modelValue"
:keyOptions="keyOptions"
@update:model-value="this.$emit('update:modelValue', $event)"
/>
</template>
<script>
@@ -28,12 +25,16 @@ import Field from 'react-chart-editor/lib/components/fields/Field'
import NumericInput from 'react-chart-editor/lib/components/widgets/NumericInput'
import Dropdown from 'react-chart-editor/lib/components/widgets/Dropdown'
import 'react-chart-editor/lib/react-chart-editor.css'
import CirclePackLayoutSettings from '@/components/Graph/CirclePackLayoutSettings.vue'
import RandomLayoutSettings from '@/components/Graph/RandomLayoutSettings.vue'
export default {
components: {
Field: applyPureReactInVue(Field),
Dropdown: applyPureReactInVue(Dropdown),
NumericInput: applyPureReactInVue(NumericInput)
NumericInput: applyPureReactInVue(NumericInput),
RandomLayoutSettings,
CirclePackLayoutSettings
},
props: {
modelValue: Object,
@@ -44,8 +45,13 @@ export default {
return {
layoutOptions: markRaw([
{ label: 'Circular', value: 'circular' },
{ label: 'Random', value: 'random' }
])
{ label: 'Random', value: 'random' },
{ label: 'Circle pack', value: 'circlepack' }
]),
layoutSettingsComponentMap: markRaw({
random: RandomLayoutSettings,
circlepack: CirclePackLayoutSettings
})
}
},
methods: {

View File

@@ -337,6 +337,12 @@ export default {
circlepack: CirclePackLayoutSettings,
forceAtlas2: ForceAtlasLayoutSettings
}),
layoutMethodMap: markRaw({
circular: this.applyCircularLayout,
random: this.applyRandomLayout,
circlepack: this.applyCirclePackLayout,
forceAtlas2: this.applyFA2Layout
}),
selectedNodeId: undefined,
hoveredNodeId: undefined,
selectedEdgeId: undefined,
@@ -668,54 +674,10 @@ export default {
this.fa2Layout.kill()
}
if (layoutType === 'circular') {
this.applyCircularLayout()
return
}
this.layoutMethodMap[layoutType]()
if (layoutType === 'random') {
this.applyRandomLayout()
return
}
if (layoutType === 'circlepack') {
this.graph.forEachNode(nodeId => {
this.graph.updateNode(nodeId, attributes => {
const newAttributes = { ...attributes }
// Delete old hierarchy attributes
Object.keys(newAttributes)
.filter(key => key.startsWith('hierarchyAttribute'))
.forEach(
hierarchyAttributeKey =>
delete newAttributes[hierarchyAttributeKey]
)
// Set new hierarchy attributes
this.settings.layout.options.hierarchyAttributes?.forEach(
(hierarchyAttribute, index) => {
newAttributes['hierarchyAttribute' + index] =
attributes.data[hierarchyAttribute]
}
)
return newAttributes
})
})
circlepack.assign(this.graph, {
hierarchyAttributes:
this.settings.layout.options.hierarchyAttributes?.map(
(_, index) => 'hierarchyAttribute' + index
) || [],
rng: seedrandom(this.settings.layout.options.seedValue)
})
return
}
if (layoutType === 'forceAtlas2') {
this.applyFA2Layout()
if (layoutType !== prevLayout) {
this.autoRunFA2Layout()
}
if (layoutType === 'forceAtlas2' && layoutType !== prevLayout) {
this.autoRunFA2Layout()
}
},
applyCircularLayout() {
@@ -726,6 +688,37 @@ export default {
rng: seedrandom(this.settings.layout.options.seedValue)
})
},
applyCirclePackLayout() {
this.graph.forEachNode(nodeId => {
this.graph.updateNode(nodeId, attributes => {
const newAttributes = { ...attributes }
// Delete old hierarchy attributes
Object.keys(newAttributes)
.filter(key => key.startsWith('hierarchyAttribute'))
.forEach(
hierarchyAttributeKey =>
delete newAttributes[hierarchyAttributeKey]
)
// Set new hierarchy attributes
this.settings.layout.options.hierarchyAttributes?.forEach(
(hierarchyAttribute, index) => {
newAttributes['hierarchyAttribute' + index] =
attributes.data[hierarchyAttribute]
}
)
return newAttributes
})
})
circlepack.assign(this.graph, {
hierarchyAttributes:
this.settings.layout.options.hierarchyAttributes?.map(
(_, index) => 'hierarchyAttribute' + index
) || [],
rng: seedrandom(this.settings.layout.options.seedValue)
})
},
applyFA2Layout() {
if (
!this.graph.someNode(
@@ -733,20 +726,22 @@ export default {
typeof attributes.x === 'number' && typeof attributes.y === 'number'
)
) {
if (this.settings.layout.options.initialAlgorithm === 'circular') {
this.applyCircularLayout()
} else {
this.applyRandomLayout()
}
this.layoutMethodMap[this.settings.layout.options.initialAlgorithm]()
}
const fa2settings = { ...this.settings.layout.options }
// delete all custom settings because they can break the algorithm running
delete fa2settings.initialAlgorithm
delete fa2settings.seedValue
delete fa2settings.initialIterationsAmount
delete fa2settings.hierarchyAttributes
this.fa2Layout = markRaw(
new FA2Layout(this.graph, {
getEdgeWeight: (_, attr) =>
this.settings.layout.options.weightSource
? attr.data[this.settings.layout.options.weightSource]
: 1,
settings: this.settings.layout.options
settings: fa2settings
})
)
},
@@ -773,6 +768,7 @@ export default {
if (this.fa2Layout.isRunning()) {
this.stopFA2Layout()
}
this.fa2Layout.kill()
clearNodeCoordinates(this.graph)
this.applyFA2Layout()
this.autoRunFA2Layout()

View File

@@ -1,5 +1,5 @@
<template>
<Field label="Seed value">
<Field label="Seed value" fieldContainerClassName="test_seed_value">
<NumericInput
:value="modelValue.seedValue"
@update="update('seedValue', $event)"

View File

@@ -38,8 +38,8 @@ export function dataSourceIsValid(dataSources) {
export function clearNodeCoordinates(graph) {
graph.forEachNode((nodeId, attributes) => {
delete attributes.x
delete attributes.y
attributes.x = undefined
attributes.y = undefined
})
}