Graph.purs 11.7 KB
Newer Older
James Laver's avatar
James Laver committed
1 2 3 4 5 6
module Gargantext.Components.Graph
  -- ( graph, graphCpt
  -- , sigmaSettings, SigmaSettings, SigmaOptionalSettings
  -- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings
  -- )
  where
7

8
import Data.Either (Either(..))
9
import Data.Generic.Rep (class Generic)
10
import Data.Maybe (Maybe(..))
11
import Data.Nullable (Nullable)
12
import DOM.Simple.Types (Element)
James Laver's avatar
James Laver committed
13 14
import Reactix as R
import Reactix.DOM.HTML as RH
15 16 17 18
import Record as Record
import Toestand as T

import Gargantext.Prelude
19

20
import Gargantext.Components.GraphExplorer.Types as GET
21 22 23
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Hooks.Sigmax.Sigma as Sigma
24 25
import Gargantext.Utils.Reactix as R2

26
here :: R2.Here
27
here = R2.here "Gargantext.Components.Graph"
James Laver's avatar
James Laver committed
28 29 30

type OnProps  = ()

31
data Stage = Init | Ready | Cleanup
32 33 34
derive instance genericStage :: Generic Stage _
derive instance eqStage :: Eq Stage

35

36 37
type Props sigma forceatlas2 =
  ( elRef                 :: R.Ref (Nullable Element)
38 39 40
  , forceAtlas2Settings   :: forceatlas2
  , graph                 :: SigmaxTypes.SGraph
  , mCamera               :: Maybe GET.Camera
41
  , multiSelectEnabledRef :: R.Ref Boolean
42 43
  , selectedNodeIds       :: T.Box SigmaxTypes.NodeIds
  , showEdges             :: T.Box SigmaxTypes.ShowEdgesState
44 45
  , sigmaRef              :: R.Ref Sigmax.Sigma
  , sigmaSettings         :: sigma
46
  , stage                 :: T.Box Stage
47 48
  , startForceAtlas       :: Boolean
  , transformedGraph      :: SigmaxTypes.SGraph
49
  )
James Laver's avatar
James Laver committed
50

51 52
graph :: forall s fa2. R2.Component (Props s fa2)
graph = R.createElement graphCpt
James Laver's avatar
James Laver committed
53 54

graphCpt :: forall s fa2. R.Component (Props s fa2)
55
graphCpt = here.component "graph" cpt where
56 57 58 59 60 61 62 63 64 65 66 67 68
    cpt props@{ elRef
              , mCamera
              , multiSelectEnabledRef
              , selectedNodeIds
              , showEdges
              , sigmaRef
              , stage
              , startForceAtlas
              , transformedGraph } _ = do
      showEdges' <- T.useLive T.unequal showEdges
      stage' <- T.useLive T.unequal stage

      stageHooks (Record.merge { showEdges', stage' } props)
69

70 71
      R.useEffectOnce $ do
        pure $ do
72
          here.log "[graphCpt (Cleanup)]"
73
          Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do
74
            Sigma.stopForceAtlas2 sigma
75
            here.log2 "[graphCpt (Cleanup)] forceAtlas stopped for" sigma
76
            Sigma.kill sigma
77
            here.log "[graphCpt (Cleanup)] sigma killed"
78

79 80
      -- NOTE: This div is not empty after sigma initializes.
      -- When we change state, we make it empty though.
81 82
      --pure $ RH.div { ref: elRef, style: {height: "95%"} } []
      pure $ case R.readNullableRef elRef of
83 84
        Nothing -> RH.div {} []
        Just el -> R.createPortal [] el
85

86 87
    stageHooks props@{ elRef, mCamera, multiSelectEnabledRef, selectedNodeIds, forceAtlas2Settings: fa2, graph: graph'
                     , sigmaRef, stage, stage': Init, startForceAtlas } = do
88
      R.useEffectOnce' $ do
89
        let rSigma = R.readRef sigmaRef
90

91 92
        case Sigmax.readSigma rSigma of
          Nothing -> do
93
            eSigma <- Sigma.sigma {settings: sigmaSettings}
94
            case eSigma of
95
              Left err -> here.log2 "[graphCpt] error creating sigma" err
96 97
              Right sig -> do
                Sigmax.writeSigma rSigma $ Just sig
98

99
                Sigmax.dependOnContainer elRef "[graphCpt (Ready)] container not found" $ \c -> do
100 101 102
                  _ <- Sigma.addRenderer sig {
                      "type": "canvas"
                    , container: c
103
                    , additionalContexts: ["mouseSelector"]
104 105 106
                    }
                  pure unit

107
                Sigmax.refreshData sig $ Sigmax.sigmafy graph'
108

109 110 111
                Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
                  -- bind the click event only initially, when ref was empty
                  Sigmax.bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabledRef
112 113
                  _ <- Sigma.bindMouseSelectorPlugin sigma
                  pure unit
114

115
                Sigmax.setEdges sig false
116

117
                -- here.log2 "[graph] startForceAtlas" startForceAtlas
118
                if startForceAtlas then
119
                  Sigma.startForceAtlas2 sig fa2
120 121
                else
                  Sigma.stopForceAtlas2 sig
122

123
                case mCamera of
124 125 126 127
                  Nothing -> pure unit
                  Just (GET.Camera { ratio, x, y }) -> do
                    Sigma.updateCamera sig { ratio, x, y }

128
                pure unit
129 130
          Just sig -> do
            pure unit
131

132
        T.write Ready stage
133

134

135 136 137 138
    stageHooks { showEdges'
               , sigmaRef
               , stage': Ready
               , transformedGraph } = do
139 140
      let tEdgesMap = SigmaxTypes.edgesGraphMap transformedGraph
      let tNodesMap = SigmaxTypes.nodesGraphMap transformedGraph
141 142 143

      -- TODO Probably this can be optimized to re-mark selected nodes only when they changed
      R.useEffect' $ do
144
        Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
145
          Sigmax.performDiff sigma transformedGraph
146 147
          Sigmax.updateEdges sigma tEdgesMap
          Sigmax.updateNodes sigma tNodesMap
148
          Sigmax.setEdges sigma (not $ SigmaxTypes.edgeStateHidden showEdges')
149 150 151

    stageHooks _ = pure unit

James Laver's avatar
James Laver committed
152 153 154 155 156 157 158 159 160 161

type SigmaSettings =
  ( animationsTime :: Number
  , autoRescale :: Boolean
  , autoResize :: Boolean
  , batchEdgesDrawing :: Boolean
  , borderSize :: Number
  -- , canvasEdgesBatchSize :: Number
  -- , clone :: Boolean
  -- , defaultEdgeColor :: String
162
  , defaultEdgeHoverColor :: String
James Laver's avatar
James Laver committed
163 164 165 166 167
  , defaultEdgeType :: String
  , defaultHoverLabelBGColor :: String
  , defaultHoverLabelColor :: String
  , defaultLabelColor :: String
  -- , defaultLabelHoverColor :: String
168
  , defaultLabelSize :: Number
James Laver's avatar
James Laver committed
169 170 171 172
  , defaultNodeBorderColor :: String
  , defaultNodeColor :: String
  -- , defaultNodeHoverColor :: String
  -- , defaultNodeType :: String
173
  , doubleClickEnabled :: Boolean
James Laver's avatar
James Laver committed
174 175 176 177 178 179 180 181 182 183
  -- , doubleClickTimeout :: Number
  -- , doubleClickZoomDuration :: Number
  -- , doubleClickZoomingRatio :: Number
  -- , doubleTapTimeout :: Number
  -- , dragTimeout :: Number
  , drawEdgeLabels :: Boolean
  , drawEdges :: Boolean
  , drawLabels :: Boolean
  , drawNodes :: Boolean
  -- , edgeColor :: String
184 185
  , edgeHoverColor :: String
  , edgeHoverExtremities :: Boolean
186
  , edgeHoverPrecision :: Number
187
  , edgeHoverSizeRatio :: Number
James Laver's avatar
James Laver committed
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
  -- , edgesPowRatio :: Number
  -- , enableCamera :: Boolean
  , enableEdgeHovering :: Boolean
  , enableHovering :: Boolean
  -- , eventsEnabled :: Boolean
  , font :: String
  , fontStyle :: String
  , hideEdgesOnMove :: Boolean
  -- , hoverFont :: String
  -- , hoverFontStyle :: String
  -- , immutable :: Boolean
  -- , labelColor :: String
  -- , labelHoverBGColor :: String
  -- , labelHoverColor :: String
  -- , labelHoverShadow :: String
  -- , labelHoverShadowColor :: String
  , labelSize :: String
  , labelSizeRatio :: Number
  , labelThreshold :: Number
  , maxEdgeSize :: Number
  , maxNodeSize :: Number
  -- , minArrowSize :: Number
  , minEdgeSize :: Number
  , minNodeSize :: Number
  , mouseEnabled :: Boolean
  -- , mouseInertiaDuration :: Number
  -- , mouseInertiaRatio :: Number
215
  , mouseSelectorSize :: Number
James Laver's avatar
James Laver committed
216 217 218 219
  -- , mouseWheelEnabled :: Boolean
  , mouseZoomDuration :: Number
  , nodeBorderColor :: String
  -- , nodeHoverColor :: String
220
  --, nodesPowRatio :: Number
James Laver's avatar
James Laver committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
  , rescaleIgnoreSize :: Boolean
  -- , scalingMode :: String
  -- , sideMargin :: Number
  , singleHover :: Boolean
  -- , skipErrors :: Boolean
  , touchEnabled :: Boolean
  -- , touchInertiaDuration :: Number
  -- , touchInertiaRatio :: Number
  , twBorderGreyColor     :: String
  , twEdgeDefaultOpacity  :: Number
  , twEdgeGreyColor       :: String
  , twNodeRendBorderColor :: String
  , twNodeRendBorderSize :: Number
  , twNodesGreyOpacity    :: Number
  , twSelectedColor       :: String
  , verbose :: Boolean
  -- , webglEdgesBatchSize :: Number
  -- , webglOversamplingRatio :: Number
  , zoomMax :: Number
  , zoomMin :: Number
  , zoomingRatio :: Number
  )

  -- not selected <=> (1-greyness)
  -- selected nodes <=> special label
sigmaSettings :: {|SigmaSettings}
sigmaSettings =
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
  { animationsTime : 30000.0
  , autoRescale : true
  , autoResize : true
  , batchEdgesDrawing : true
  , borderSize : 1.0                   -- for ex, bigger border when hover
  , defaultEdgeHoverColor : "#f00"
  , defaultEdgeType : "curve"          -- 'curve' or 'line' (curve iff ourRendering)
  , defaultHoverLabelBGColor : "#fff"
  , defaultHoverLabelColor : "#000"
  , defaultLabelColor : "#000"         -- labels text color
  , defaultLabelSize : 15.0                -- (old tina: showLabelsIfZoom)
  , defaultNodeBorderColor  : "#000"   -- <- if nodeBorderColor = 'default'
  , defaultNodeColor : "#FFF"
  , doubleClickEnabled : false -- indicates whether or not the graph can be zoomed on double-click
  , drawEdgeLabels : true
  , drawEdges : true
  , drawLabels : true
  , drawNodes : true
  , enableEdgeHovering : false
  , edgeHoverExtremities : true
  , edgeHoverColor : "edge"
  , edgeHoverPrecision : 2.0
  , edgeHoverSizeRatio : 2.0
  , enableHovering : true
  , font : "arial"
  , fontStyle : ""
  , hideEdgesOnMove : true
  , labelSize  : "proportional" -- alt : proportional, fixed
  -- , labelSize : "fixed"
  , labelSizeRatio : 2.0               -- label size in ratio of node size
  , labelThreshold : 9.0 -- 5.0 for more labels              -- min node cam size to start showing label
  , maxEdgeSize : 1.0
  , maxNodeSize : 10.0
  , minEdgeSize : 0.5              -- in fact used in tina as edge size
  , minNodeSize : 1.0
  , mouseEnabled : true
  , mouseSelectorSize : 15.0
  , mouseZoomDuration : 150.0
  , nodeBorderColor : "default"           -- choices: "default" color vs. "node" color
  --, nodesPowRatio  : 10.8
  , rescaleIgnoreSize  : false
  , singleHover  : true
  , touchEnabled  : true
  , twBorderGreyColor  : "rgba(100, 100, 100, 0.9)"
  , twEdgeDefaultOpacity  : 0.4       -- initial opacity added to src/tgt colors
  , twEdgeGreyColor  : "rgba(100, 100, 100, 0.25)"
  , twNodeRendBorderColor  : "#FFF"
  , twNodeRendBorderSize  : 2.5          -- node borders (only iff ourRendering)
  , twNodesGreyOpacity  : 5.5           -- smaller value: more grey
  , twSelectedColor  : "node"     -- "node" for a label bg like the node color, "default" for white background
  , verbose  : true
  , zoomMax : 1.7
  , zoomMin : 0.0
  , zoomingRatio : 1.4
James Laver's avatar
James Laver committed
302 303 304
  }
  
type ForceAtlas2Settings =
305 306 307 308 309 310 311 312 313 314
  ( adjustSizes                    :: Boolean
  , barnesHutOptimize              :: Boolean
  -- , barnesHutTheta              :: Number
  , edgeWeightInfluence            :: Number
  -- , fixedY                      :: Boolean
  , gravity                        :: Number
  , includeHiddenEdges             :: Boolean
  , includeHiddenNodes             :: Boolean
  , iterationsPerRender            :: Number
  , linLogMode                     :: Boolean
James Laver's avatar
James Laver committed
315
  , outboundAttractionDistribution :: Boolean
316 317 318 319 320 321 322
  , scalingRatio                   :: Number
  , skipHidden                     :: Boolean
  , slowDown                       :: Number
  , startingIterations             :: Number
  , strongGravityMode              :: Boolean
  -- , timeout                     :: Number
  -- , worker                      :: Boolean
James Laver's avatar
James Laver committed
323 324 325 326
  )

forceAtlas2Settings :: {|ForceAtlas2Settings}
forceAtlas2Settings =
327
  { adjustSizes : true
James Laver's avatar
James Laver committed
328
  , barnesHutOptimize   : true
329
  , edgeWeightInfluence : 1.0
James Laver's avatar
James Laver committed
330
    -- fixedY : false
331
  , gravity : 0.01
332 333
  , includeHiddenEdges: false
  , includeHiddenNodes: true
334
  , iterationsPerRender : 50.0 -- 10.0
335
  , linLogMode : false  -- false
James Laver's avatar
James Laver committed
336
  , outboundAttractionDistribution: false
337
  , scalingRatio : 1000.0
James Laver's avatar
James Laver committed
338
  , skipHidden: false
339 340
  , slowDown : 1.0
  , startingIterations : 10.0
James Laver's avatar
James Laver committed
341 342
  , strongGravityMode : false
  }