Graph.purs 12.1 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
derive instance Generic Stage _
derive instance Eq Stage
34

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
    cpt props@{ elRef
              , showEdges
              , sigmaRef
59
              , stage } _ = do
60 61 62 63
      showEdges' <- T.useLive T.unequal showEdges
      stage' <- T.useLive T.unequal stage

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

65 66
      R.useEffectOnce $ do
        pure $ do
67
          here.log "[graphCpt (Cleanup)]"
68
          Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do
69
            Sigma.stopForceAtlas2 sigma
70
            here.log2 "[graphCpt (Cleanup)] forceAtlas stopped for" sigma
71
            Sigma.kill sigma
72
            here.log "[graphCpt (Cleanup)] sigma killed"
73

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

81 82 83 84 85 86 87 88 89 90
    stageHooks { elRef
               , mCamera
               , multiSelectEnabledRef
               , selectedNodeIds
               , forceAtlas2Settings: fa2
               , graph: graph'
               , sigmaRef
               , stage
               , stage': Init
               , startForceAtlas } = do
91
      R.useEffectOnce' $ do
92
        let rSigma = R.readRef sigmaRef
93

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

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

110
                Sigmax.refreshData sig $ Sigmax.sigmafy graph'
111

112 113 114
                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
115 116
                  _ <- Sigma.bindMouseSelectorPlugin sigma
                  pure unit
117

118
                Sigmax.setEdges sig false
119

120
                -- here.log2 "[graph] startForceAtlas" startForceAtlas
121
                if startForceAtlas then
122
                  Sigma.startForceAtlas2 sig fa2
123 124
                else
                  Sigma.stopForceAtlas2 sig
125

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

131
                pure unit
132
          Just _sig -> do
133
            pure unit
134

135
        T.write Ready stage
136

137

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

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

    stageHooks _ = pure unit

James Laver's avatar
James Laver committed
157 158 159 160 161 162 163 164 165 166

type SigmaSettings =
  ( animationsTime :: Number
  , autoRescale :: Boolean
  , autoResize :: Boolean
  , batchEdgesDrawing :: Boolean
  , borderSize :: Number
  -- , canvasEdgesBatchSize :: Number
  -- , clone :: Boolean
  -- , defaultEdgeColor :: String
167
  , defaultEdgeHoverColor :: String
James Laver's avatar
James Laver committed
168 169 170 171 172
  , defaultEdgeType :: String
  , defaultHoverLabelBGColor :: String
  , defaultHoverLabelColor :: String
  , defaultLabelColor :: String
  -- , defaultLabelHoverColor :: String
173
  , defaultLabelSize :: Number
James Laver's avatar
James Laver committed
174 175 176 177
  , defaultNodeBorderColor :: String
  , defaultNodeColor :: String
  -- , defaultNodeHoverColor :: String
  -- , defaultNodeType :: String
178
  , doubleClickEnabled :: Boolean
James Laver's avatar
James Laver committed
179 180 181 182 183 184 185 186 187 188
  -- , doubleClickTimeout :: Number
  -- , doubleClickZoomDuration :: Number
  -- , doubleClickZoomingRatio :: Number
  -- , doubleTapTimeout :: Number
  -- , dragTimeout :: Number
  , drawEdgeLabels :: Boolean
  , drawEdges :: Boolean
  , drawLabels :: Boolean
  , drawNodes :: Boolean
  -- , edgeColor :: String
189 190
  , edgeHoverColor :: String
  , edgeHoverExtremities :: Boolean
191
  , edgeHoverPrecision :: Number
192
  , edgeHoverSizeRatio :: Number
James Laver's avatar
James Laver committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  -- , 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
220
  , mouseSelectorSize :: Number
James Laver's avatar
James Laver committed
221 222 223 224
  -- , mouseWheelEnabled :: Boolean
  , mouseZoomDuration :: Number
  , nodeBorderColor :: String
  -- , nodeHoverColor :: String
225
  --, nodesPowRatio :: Number
James Laver's avatar
James Laver committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
  , 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 =
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 302 303 304 305 306
  { 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
307
  }
308

James Laver's avatar
James Laver committed
309
type ForceAtlas2Settings =
310 311 312
  ( adjustSizes                    :: Boolean
  , barnesHutOptimize              :: Boolean
  -- , barnesHutTheta              :: Number
313
  , batchEdgesDrawing              :: Boolean
314 315
  , edgeWeightInfluence            :: Number
  -- , fixedY                      :: Boolean
316
  , hideEdgesOnMove                :: Boolean
317 318 319 320 321
  , gravity                        :: Number
  , includeHiddenEdges             :: Boolean
  , includeHiddenNodes             :: Boolean
  , iterationsPerRender            :: Number
  , linLogMode                     :: Boolean
James Laver's avatar
James Laver committed
322
  , outboundAttractionDistribution :: Boolean
323 324 325 326 327 328 329
  , scalingRatio                   :: Number
  , skipHidden                     :: Boolean
  , slowDown                       :: Number
  , startingIterations             :: Number
  , strongGravityMode              :: Boolean
  -- , timeout                     :: Number
  -- , worker                      :: Boolean
James Laver's avatar
James Laver committed
330 331 332 333
  )

forceAtlas2Settings :: {|ForceAtlas2Settings}
forceAtlas2Settings =
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
  { adjustSizes                    : true
  , barnesHutOptimize              : true
  , batchEdgesDrawing              : true
  , edgeWeightInfluence            : 1.0
    -- fixedY                      : false
  , gravity                        : 0.01
  , hideEdgesOnMove                : true
  , includeHiddenEdges             : false
  , includeHiddenNodes             : true
  , iterationsPerRender            : 50.0 -- 10.0
  , linLogMode                     : false  -- false
  , outboundAttractionDistribution : false
  , scalingRatio                   : 1000.0
  , skipHidden                     : false
  , slowDown                       : 1.0
  , startingIterations             : 10.0
  , strongGravityMode              : false
James Laver's avatar
James Laver committed
351
  }