Sigmax.purs 9.46 KB
Newer Older
James Laver's avatar
James Laver committed
1 2 3
module Gargantext.Hooks.Sigmax
  where

4
import Prelude (Unit, bind, discard, flip, pure, unit, ($), (*>), (<<<), (<>), (>>=), (&&), not, const, map)
5

James Laver's avatar
James Laver committed
6
import Data.Array as A
7
import Data.Either (either)
8
import Data.Foldable (sequence_, foldl)
9
import Data.Map as Map
10 11
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable)
James Laver's avatar
James Laver committed
12
import Data.Sequence (Seq)
13
import Data.Sequence as Seq
14
import Data.Set as Set
15
import Data.Traversable (traverse_)
16
import Data.Tuple (Tuple(..))
17
import Data.Tuple.Nested((/\))
18 19
import DOM.Simple.Console (log, log2)
import DOM.Simple.Types (Element)
James Laver's avatar
James Laver committed
20
import Effect (Effect)
21
import Effect.Class.Console (error)
22
import Effect.Timer (TimeoutId, clearTimeout)
23
import FFI.Simple ((.=))
24 25
import Reactix as R

James Laver's avatar
James Laver committed
26
import Gargantext.Hooks.Sigmax.Sigma as Sigma
27
import Gargantext.Hooks.Sigmax.Types as ST
28
import Gargantext.Utils.Reactix as R2
James Laver's avatar
James Laver committed
29 30 31

type Sigma =
  { sigma :: R.Ref (Maybe Sigma.Sigma)
32
    -- TODO is Seq in cleanup really necessary?
James Laver's avatar
James Laver committed
33 34 35
  , cleanup :: R.Ref (Seq (Effect Unit))
  }

36
type Data n e = { graph :: R.Ref (ST.Graph n e) }
James Laver's avatar
James Laver committed
37

38 39 40 41 42 43
initSigma :: R.Hooks Sigma
initSigma = do
    s <- R2.nothingRef
    c <- R.useRef Seq.empty
    pure { sigma: s, cleanup: c }

James Laver's avatar
James Laver committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
readSigma :: Sigma -> Maybe Sigma.Sigma
readSigma sigma = R.readRef sigma.sigma

writeSigma :: Sigma -> Maybe Sigma.Sigma -> Effect Unit
writeSigma sigma = R.setRef sigma.sigma

-- | Pushes to the back of the cleanup sequence. Cleanup happens
-- | *before* sigma is destroyed
cleanupLast :: Sigma -> Effect Unit -> Effect Unit
cleanupLast sigma = R.setRef sigma.cleanup <<< Seq.snoc existing
  where existing = R.readRef sigma.cleanup

-- | Pushes to the front of the cleanup sequence. Cleanup happens
-- | *before* sigma is destroyed
cleanupFirst :: Sigma -> Effect Unit -> Effect Unit
cleanupFirst sigma =
  R.setRef sigma.cleanup <<< (flip Seq.cons) (R.readRef sigma.cleanup)

cleanupSigma :: Sigma -> String -> Effect Unit
cleanupSigma sigma context = traverse_ kill (readSigma sigma)
  where
    kill sig = runCleanups *> killSigma *> emptyOut
      where -- close over sig
        killSigma = Sigma.killSigma sig >>= report
    runCleanups = sequence_ (R.readRef sigma.cleanup)
    emptyOut = writeSigma sigma Nothing *> R.setRef sigma.cleanup Seq.empty
    report = either (log2 errorMsg) (\_ -> log successMsg)
    prefix = "[" <> context <> "] "
    errorMsg = prefix <> "Error killing sigma:"
    successMsg = prefix <> "Killed sigma"

refreshData :: forall n e. Sigma.Sigma -> Sigma.Graph n e -> Effect Unit
refreshData sigma graph
  =   log clearingMsg
78
  *>  Sigma.clear sigmaGraph
James Laver's avatar
James Laver committed
79
  *>  log readingMsg
80
  *>  Sigma.graphRead sigmaGraph graph
James Laver's avatar
James Laver committed
81 82
  >>= either (log2 errorMsg) refresh
  where
83
    sigmaGraph = Sigma.graph sigma
James Laver's avatar
James Laver committed
84
    refresh _ = log refreshingMsg *> Sigma.refresh sigma
85 86 87 88
    clearingMsg = "[refreshData] Clearing existing graph data"
    readingMsg = "[refreshData] Reading graph data"
    refreshingMsg = "[refreshData] Refreshing graph"
    errorMsg = "[refreshData] Error reading graph data:"
James Laver's avatar
James Laver committed
89

90 91
sigmafy :: forall n e. ST.Graph n e -> Sigma.Graph n e
sigmafy (ST.Graph g) = {nodes,edges}
James Laver's avatar
James Laver committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  where
    nodes = A.fromFoldable g.nodes
    edges = A.fromFoldable g.edges

dependOnSigma :: Sigma -> String -> (Sigma.Sigma -> Effect Unit) -> Effect Unit
dependOnSigma sigma notFoundMsg f = do
  case readSigma sigma of
    Nothing -> log notFoundMsg
    Just sig -> f sig

dependOnContainer :: R.Ref (Nullable Element) -> String -> (Element -> Effect Unit) -> Effect Unit
dependOnContainer container notFoundMsg f = do
  case R.readNullableRef container of
    Nothing -> log notFoundMsg
    Just c -> f c

108 109 110

-- Effectful versions of the above code

111 112 113
-- | Effect for handling pausing FA via state changes.  We need this because
-- | pausing can be done not only via buttons but also from the initial
-- | setTimer.
114
--handleForceAtlasPause sigmaRef (toggled /\ setToggled) mFAPauseRef = do
115 116
handleForceAtlas2Pause :: R.Ref Sigma -> R.State ST.ForceAtlasState -> R.Ref (Maybe TimeoutId) -> Effect Unit
handleForceAtlas2Pause sigmaRef (toggled /\ setToggled) mFAPauseRef = do
117 118
  let sigma = R.readRef sigmaRef
  dependOnSigma sigma "[handleForceAtlas2Pause] sigma: Nothing" $ \s -> do
119 120
    --log2 "[handleForceAtlas2Pause] mSigma: Just " s
    --log2 "[handleForceAtlas2Pause] toggled: " toggled
121
    let isFARunning = Sigma.isForceAtlas2Running s
122
    --log2 "[handleForceAtlas2Pause] isFARunning: " isFARunning
123
    case Tuple toggled isFARunning of
124 125 126 127
      Tuple ST.InitialRunning false -> do
        -- hide edges during forceAtlas rendering, this prevents flickering
        Sigma.restartForceAtlas2 s
      Tuple ST.Running false -> do
128 129
        -- hide edges during forceAtlas rendering, this prevents flickering
        Sigma.restartForceAtlas2 s
130 131 132
        case R.readRef mFAPauseRef of
          Nothing -> pure unit
          Just timeoutId -> clearTimeout timeoutId
133
      Tuple ST.Paused true -> do
134 135
        -- restore edges state
        Sigma.stopForceAtlas2 s
136
      _ -> pure unit
137 138 139 140 141 142 143 144

setEdges :: Sigma.Sigma -> Boolean -> Effect Unit
setEdges sigma val = do
  let settings = {
        drawEdges: val
      , drawEdgeLabels: val
      , hideEdgesOnMove: not val
    }
145 146
  Sigma.setSettings sigma settings

147

148
updateEdges :: Sigma.Sigma -> ST.EdgesMap -> Effect Unit
149
updateEdges sigma edgesMap = do
150
  Sigma.forEachEdge (Sigma.graph sigma) \e -> do
151 152 153
    let mTEdge = Map.lookup e.id edgesMap
    case mTEdge of
      Nothing -> error $ "Edge id " <> e.id <> " not found in edgesMap"
154
      (Just {color: tColor, hidden: tHidden}) -> do
155
        _ <- pure $ (e .= "color") tColor
156
        _ <- pure $ (e .= "hidden") tHidden
157
        pure unit
158
  --Sigma.refresh sigma
159 160


161
updateNodes :: Sigma.Sigma -> ST.NodesMap -> Effect Unit
162
updateNodes sigma nodesMap = do
163
  Sigma.forEachNode (Sigma.graph sigma) \n -> do
164 165 166
    let mTNode = Map.lookup n.id nodesMap
    case mTNode of
      Nothing -> error $ "Node id " <> n.id <> " not found in nodesMap"
167 168
      (Just { borderColor: tBorderColor
             , color: tColor
169
             , equilateral: tEquilateral
170
             , hidden: tHidden
171
             , type: tType }) -> do
172
        _ <- pure $ (n .= "borderColor") tBorderColor
173
        _ <- pure $ (n .= "color") tColor
174
        _ <- pure $ (n .= "equilateral") tEquilateral
175
        _ <- pure $ (n .= "hidden") tHidden
176
        _ <- pure $ (n .= "type") tType
177
        pure unit
178
  --Sigma.refresh sigma
179 180


181
-- | Toggles item visibility in the selected set
182
multiSelectUpdate :: ST.NodeIds -> ST.NodeIds -> ST.NodeIds
183 184 185 186 187 188 189 190 191
multiSelectUpdate new selected = foldl fld selected new
  where
    fld selectedAcc item =
      if Set.member item selectedAcc then
        Set.delete item selectedAcc
      else
        Set.insert item selectedAcc


192 193
bindSelectedNodesClick :: Sigma.Sigma -> R.State ST.NodeIds -> R.Ref Boolean -> Effect Unit
bindSelectedNodesClick sigma (_ /\ setNodeIds) multiSelectEnabledRef =
194 195 196 197
  Sigma.bindClickNodes sigma $ \nodes -> do
    let multiSelectEnabled = R.readRef multiSelectEnabledRef
    let nodeIds = Set.fromFoldable $ map _.id nodes
    if multiSelectEnabled then
198
      setNodeIds $ multiSelectUpdate nodeIds
199
    else
200
      setNodeIds $ const nodeIds
201

202 203
bindSelectedEdgesClick :: R.Ref Sigma -> R.State ST.EdgeIds -> Effect Unit
bindSelectedEdgesClick sigmaRef (_ /\ setEdgeIds) =
204 205
  dependOnSigma (R.readRef sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
    Sigma.bindClickEdge sigma $ \edge -> do
206
      setEdgeIds \eids ->
207 208 209 210
        if Set.member edge.id eids then
          Set.delete edge.id eids
        else
          Set.insert edge.id eids
211 212 213 214 215

selectorWithSize :: Sigma.Sigma -> Int -> Effect Unit
selectorWithSize sigma size = do
  pure unit

216 217 218 219 220
performDiff :: Sigma.Sigma -> ST.SGraph -> Effect Unit
performDiff sigma g = do
  if (Seq.null addEdges) && (Seq.null addNodes) && (Set.isEmpty removeEdges) && (Set.isEmpty removeNodes) then
    pure unit
  else do
221 222 223 224
    traverse_ (Sigma.addNode sigmaGraph) addNodes
    traverse_ (Sigma.addEdge sigmaGraph) addEdges
    traverse_ (Sigma.removeEdge sigmaGraph) removeEdges
    traverse_ (Sigma.removeNode sigmaGraph) removeNodes
225 226
    Sigma.refresh sigma
    Sigma.killForceAtlas2 sigma
227 228 229 230 231
  where
    sigmaGraph = Sigma.graph sigma
    sigmaEdgeIds = Sigma.sigmaEdgeIds sigmaGraph
    sigmaNodeIds = Sigma.sigmaNodeIds sigmaGraph
    {add: Tuple addEdges addNodes, remove: Tuple removeEdges removeNodes} = ST.sigmaDiff sigmaEdgeIds sigmaNodeIds g
232 233
-- DEPRECATED

234
markSelectedEdges :: Sigma.Sigma -> ST.EdgeIds -> ST.EdgesMap -> Effect Unit
235
markSelectedEdges sigma selectedEdgeIds graphEdges = do
236
  Sigma.forEachEdge (Sigma.graph sigma) \e -> do
237 238 239 240 241 242 243 244 245 246 247 248
    case Map.lookup e.id graphEdges of
      Nothing -> error $ "Edge id " <> e.id <> " not found in graphEdges map"
      Just {color} -> do
        let newColor =
              if Set.member e.id selectedEdgeIds then
                "#ff0000"
              else
                color
        _ <- pure $ (e .= "color") newColor
        pure unit
  Sigma.refresh sigma

249
markSelectedNodes :: Sigma.Sigma -> ST.NodeIds -> ST.NodesMap -> Effect Unit
250
markSelectedNodes sigma selectedNodeIds graphNodes = do
251
  Sigma.forEachNode (Sigma.graph sigma) \n -> do
252 253 254 255 256 257 258 259 260 261 262
    case Map.lookup n.id graphNodes of
      Nothing -> error $ "Node id " <> n.id <> " not found in graphNodes map"
      Just {color} -> do
        let newColor =
              if Set.member n.id selectedNodeIds then
                "#ff0000"
              else
                color
        _ <- pure $ (n .= "color") newColor
        pure unit
  Sigma.refresh sigma
263 264 265 266 267 268

getEdges :: Sigma.Sigma -> Effect (Array (Record ST.Edge))
getEdges sigma = Sigma.getEdges sigma

getNodes :: Sigma.Sigma -> Effect (Array (Record ST.Node))
getNodes sigma = Sigma.getNodes sigma