module Gargantext.Components.Graph -- ( graph, graphCpt -- , sigmaSettings, SigmaSettings, SigmaOptionalSettings -- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings -- ) where import Data.Either (Either(..)) import Data.Generic.Rep (class Generic) import Data.Maybe (Maybe(..)) import Data.Nullable (Nullable) import DOM.Simple.Types (Element) import Reactix as R import Reactix.DOM.HTML as RH import Record as Record import Toestand as T import Gargantext.Prelude import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Hooks.Sigmax as Sigmax import Gargantext.Hooks.Sigmax.Types as SigmaxTypes import Gargantext.Hooks.Sigmax.Sigma as Sigma import Gargantext.Utils.Reactix as R2 here :: R2.Here here = R2.here "Gargantext.Components.Graph" type OnProps = () data Stage = Init | Ready | Cleanup derive instance Generic Stage _ derive instance Eq Stage type Props sigma forceatlas2 = ( elRef :: R.Ref (Nullable Element) , forceAtlas2Settings :: forceatlas2 , graph :: SigmaxTypes.SGraph , mCamera :: Maybe GET.Camera , multiSelectEnabledRef :: R.Ref Boolean , selectedNodeIds :: T.Box SigmaxTypes.NodeIds , showEdges :: T.Box SigmaxTypes.ShowEdgesState , sigmaRef :: R.Ref Sigmax.Sigma , sigmaSettings :: sigma , stage :: T.Box Stage , startForceAtlas :: Boolean , transformedGraph :: SigmaxTypes.SGraph ) graph :: forall s fa2. R2.Component (Props s fa2) graph = R.createElement graphCpt graphCpt :: forall s fa2. R.Component (Props s fa2) graphCpt = here.component "graph" cpt where cpt props@{ elRef , showEdges , sigmaRef , stage } _ = do showEdges' <- T.useLive T.unequal showEdges stage' <- T.useLive T.unequal stage stageHooks (Record.merge { showEdges', stage' } props) R.useEffectOnce $ do pure $ do here.log "[graphCpt (Cleanup)]" Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do Sigma.stopForceAtlas2 sigma here.log2 "[graphCpt (Cleanup)] forceAtlas stopped for" sigma Sigma.kill sigma here.log "[graphCpt (Cleanup)] sigma killed" -- NOTE: This div is not empty after sigma initializes. -- When we change state, we make it empty though. --pure $ RH.div { ref: elRef, style: {height: "95%"} } [] pure $ case R.readNullableRef elRef of Nothing -> RH.div {} [] Just el -> R.createPortal [] el stageHooks { elRef , mCamera , multiSelectEnabledRef , selectedNodeIds , forceAtlas2Settings: fa2 , graph: graph' , sigmaRef , stage , stage': Init , startForceAtlas } = do R.useEffectOnce' $ do let rSigma = R.readRef sigmaRef case Sigmax.readSigma rSigma of Nothing -> do eSigma <- Sigma.sigma {settings: sigmaSettings} case eSigma of Left err -> here.log2 "[graphCpt] error creating sigma" err Right sig -> do Sigmax.writeSigma rSigma $ Just sig Sigmax.dependOnContainer elRef "[graphCpt (Ready)] container not found" $ \c -> do _ <- Sigma.addRenderer sig { "type": "canvas" , container: c , additionalContexts: ["mouseSelector"] } pure unit Sigmax.refreshData sig $ Sigmax.sigmafy graph' 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 _ <- Sigma.bindMouseSelectorPlugin sigma pure unit Sigmax.setEdges sig false -- here.log2 "[graph] startForceAtlas" startForceAtlas if startForceAtlas then Sigma.startForceAtlas2 sig fa2 else Sigma.stopForceAtlas2 sig case mCamera of Nothing -> pure unit Just (GET.Camera { ratio, x, y }) -> do Sigma.updateCamera sig { ratio, x, y } pure unit Just _sig -> do pure unit T.write Ready stage stageHooks { showEdges' , sigmaRef , stage': Ready , transformedGraph } = do let tEdgesMap = SigmaxTypes.edgesGraphMap transformedGraph let tNodesMap = SigmaxTypes.nodesGraphMap transformedGraph -- TODO Probably this can be optimized to re-mark selected nodes only when they changed R.useEffect' $ do Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do Sigmax.performDiff sigma transformedGraph Sigmax.updateEdges sigma tEdgesMap Sigmax.updateNodes sigma tNodesMap let edgesState = not $ SigmaxTypes.edgeStateHidden showEdges' here.log2 "[graphCpt] edgesState" edgesState Sigmax.setEdges sigma edgesState stageHooks _ = pure unit type SigmaSettings = ( animationsTime :: Number , autoRescale :: Boolean , autoResize :: Boolean , batchEdgesDrawing :: Boolean , borderSize :: Number -- , canvasEdgesBatchSize :: Number -- , clone :: Boolean -- , defaultEdgeColor :: String , defaultEdgeHoverColor :: String , defaultEdgeType :: String , defaultHoverLabelBGColor :: String , defaultHoverLabelColor :: String , defaultLabelColor :: String -- , defaultLabelHoverColor :: String , defaultLabelSize :: Number , defaultNodeBorderColor :: String , defaultNodeColor :: String -- , defaultNodeHoverColor :: String -- , defaultNodeType :: String , doubleClickEnabled :: Boolean -- , doubleClickTimeout :: Number -- , doubleClickZoomDuration :: Number -- , doubleClickZoomingRatio :: Number -- , doubleTapTimeout :: Number -- , dragTimeout :: Number , drawEdgeLabels :: Boolean , drawEdges :: Boolean , drawLabels :: Boolean , drawNodes :: Boolean -- , edgeColor :: String , edgeHoverColor :: String , edgeHoverExtremities :: Boolean , edgeHoverPrecision :: Number , edgeHoverSizeRatio :: Number -- , 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 , mouseSelectorSize :: Number -- , mouseWheelEnabled :: Boolean , mouseZoomDuration :: Number , nodeBorderColor :: String -- , nodeHoverColor :: String --, nodesPowRatio :: Number , 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 = { 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 } type ForceAtlas2Settings = ( adjustSizes :: Boolean , barnesHutOptimize :: Boolean -- , barnesHutTheta :: Number , batchEdgesDrawing :: Boolean , edgeWeightInfluence :: Number -- , fixedY :: Boolean , hideEdgesOnMove :: Boolean , gravity :: Number , includeHiddenEdges :: Boolean , includeHiddenNodes :: Boolean , iterationsPerRender :: Number , linLogMode :: Boolean , outboundAttractionDistribution :: Boolean , scalingRatio :: Number , skipHidden :: Boolean , slowDown :: Number , startingIterations :: Number , strongGravityMode :: Boolean -- , timeout :: Number -- , worker :: Boolean ) forceAtlas2Settings :: {|ForceAtlas2Settings} forceAtlas2Settings = { 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 }