@@ -7,6 +7,7 @@ import { localStorage } from "../../utils/storage";
77import { EVENTS , emitter } from "../context/eventsContext" ;
88import { graphDatasetActions , graphDatasetAtom , sigmaGraphAtom } from "../graph" ;
99import { dataGraphToFullGraph } from "../graph/utils" ;
10+ import { sessionActions , sessionAtom } from "../session" ;
1011import { resetCamera } from "../sigma" ;
1112import { LAYOUTS } from "./collection" ;
1213import { LayoutMapping , LayoutQuality , LayoutState } from "./types" ;
@@ -34,41 +35,46 @@ export const layoutStateAtom = atom<LayoutState>(getLocalStorageLayoutState());
3435 * Actions:
3536 * ********
3637 */
37- export const startLayout = asyncAction ( async ( id : string , params : unknown ) => {
38+ export const startLayout = asyncAction ( async ( id : string , params : unknown , isForRestart = false ) => {
3839 const { setNodePositions } = graphDatasetActions ;
39- const dataset = graphDatasetAtom . get ( ) ;
40+ const { setLastLayoutUsed } = sessionActions ;
4041
4142 // search the layout
4243 const layout = LAYOUTS . find ( ( l ) => l . id === id ) ;
4344
44- // Sync layout
45- if ( layout && layout . type === "sync" ) {
46- layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "running" , layoutId : id } ) ) ;
47-
48- // generate positions
49- const fullGraph = dataGraphToFullGraph ( dataset ) ;
50- const positions = layout . run ( fullGraph , { settings : params } ) ;
51-
52- // Save it
53- setNodePositions ( positions ) ;
54-
55- // To prevent resetting the camera before sigma receives new data, we
56- // need to wait a frame, and also wait for it to trigger a refresh:
57- setTimeout ( ( ) => {
58- layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "idle" } ) ) ;
59- resetCamera ( { forceRefresh : false } ) ;
60- } , 0 ) ;
61- }
62-
63- // Async layout
64- if ( layout && layout . type === "worker" ) {
65- const worker = new layout . supervisor ( sigmaGraphAtom . get ( ) , { settings : params } ) ;
66- worker . start ( ) ;
67- layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "running" , layoutId : id , supervisor : worker } ) ) ;
45+ if ( layout ) {
46+ if ( ! isForRestart ) setLastLayoutUsed ( layout . id ) ;
47+
48+ // Sync layout
49+ if ( layout . type === "sync" ) {
50+ layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "running" , layoutId : id } ) ) ;
51+
52+ // generate positions
53+ const dataset = graphDatasetAtom . get ( ) ;
54+ const fullGraph = dataGraphToFullGraph ( dataset ) ;
55+ const positions = layout . run ( fullGraph , { settings : params } ) ;
56+
57+ // Save it
58+ setNodePositions ( positions ) ;
59+
60+ // To prevent resetting the camera before sigma receives new data, we
61+ // need to wait a frame, and also wait for it to trigger a refresh:
62+ setTimeout ( ( ) => {
63+ layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "idle" } ) ) ;
64+ resetCamera ( { forceRefresh : false } ) ;
65+ } , 0 ) ;
66+ }
67+
68+ // Async layout
69+ if ( layout . type === "worker" ) {
70+ const worker = new layout . supervisor ( sigmaGraphAtom . get ( ) , { settings : params } ) ;
71+ worker . start ( ) ;
72+ layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "running" , layoutId : id , supervisor : worker } ) ) ;
73+ }
6874 }
6975} ) ;
7076
71- export const stopLayout = asyncAction ( async ( ) => {
77+ export const stopLayout = asyncAction ( async ( isForRestart = false ) => {
7278 const { setNodePositions } = graphDatasetActions ;
7379 const layoutState = layoutStateAtom . get ( ) ;
7480
@@ -77,15 +83,33 @@ export const stopLayout = asyncAction(async () => {
7783 layoutState . supervisor . stop ( ) ;
7884 layoutState . supervisor . kill ( ) ;
7985
80- // Save data
81- const positions : LayoutMapping = { } ;
82- sigmaGraphAtom . get ( ) . forEachNode ( ( node , { x, y } ) => {
83- positions [ node ] = { x, y } ;
84- } ) ;
85- setNodePositions ( positions ) ;
86+ // DOn't save position if it's for a restart
87+ if ( ! isForRestart ) {
88+ // Save data
89+ const positions : LayoutMapping = { } ;
90+ sigmaGraphAtom . get ( ) . forEachNode ( ( node , { x, y } ) => {
91+ positions [ node ] = { x, y } ;
92+ } ) ;
93+ setNodePositions ( positions ) ;
94+ }
8695 }
8796
88- layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "idle" } ) ) ;
97+ // Don't set the state if it's for restart
98+ if ( ! isForRestart ) layoutStateAtom . set ( ( prev ) => ( { ...prev , type : "idle" } ) ) ;
99+ } ) ;
100+
101+ export const restartLastLayout = asyncAction ( async ( ) => {
102+ // Get the algo and its parameters
103+ const session = sessionAtom . get ( ) ;
104+ if ( session . lastLayoutUsed ) {
105+ const layoutId = session . lastLayoutUsed ;
106+ const layout = LAYOUTS . find ( ( e ) => e . id === layoutId ) ;
107+ const params = session . layoutsParameters [ layoutId ] || { } ;
108+ if ( layout ) {
109+ await stopLayout ( true ) ;
110+ await startLayout ( layoutId , params , true ) ;
111+ }
112+ }
89113} ) ;
90114
91115export const setQuality : Producer < LayoutState , [ LayoutQuality ] > = ( quality ) => {
@@ -105,8 +129,9 @@ const _computeLayoutQualityMetric: Producer<LayoutState> = () => {
105129} ;
106130
107131export const layoutActions = {
108- startLayout,
109132 stopLayout,
133+ startLayout,
134+ restartLastLayout,
110135 setQuality : producerToAction ( setQuality , layoutStateAtom ) ,
111136 computeLayoutQualityMetric : producerToAction ( _computeLayoutQualityMetric , layoutStateAtom ) ,
112137} ;
@@ -140,3 +165,13 @@ gridEnabledAtom.bindEffect((connectedClosenessSettings) => {
140165 sigmaGraph . off ( "eachNodeAttributesUpdated" , fn ) ;
141166 } ;
142167} ) ;
168+
169+ layoutStateAtom . bindEffect ( ( state ) => {
170+ if ( state . type !== "running" ) return ;
171+
172+ const fnHandleDraggin = debounce ( restartLastLayout , 100 , { leading : true , trailing : true , maxWait : 100 } ) ;
173+ emitter . on ( EVENTS . nodesDragged , fnHandleDraggin ) ;
174+ return ( ) => {
175+ emitter . off ( EVENTS . nodesDragged , fnHandleDraggin ) ;
176+ } ;
177+ } ) ;
0 commit comments