Commit 94c53ffa authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[Graph] implement transformedGraph computation

As an example, this transforms the fetched graph and: hides nodes (via
the nodeSize state) and changes their colour (via selectedNodeIds).
parent e898585b
......@@ -32,9 +32,10 @@ type Props sigma forceatlas2 =
, graph :: Graph
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, sigmaSettings :: sigma
, sigmaRef :: R.Ref Sigmax.Sigma
, sigmaSettings :: sigma
, stage :: R.State Stage
, transformedGraph :: Graph
)
graph :: forall s fa2. Record (Props s fa2) -> R.Element
......@@ -91,14 +92,14 @@ graphCpt = R.hooksComponent "Graph" cpt
pure $ pure unit
stageHooks props@{stage: (Ready /\ setStage)} = do
let edgesMap = SigmaxTypes.edgesGraphMap props.graph
let nodesMap = SigmaxTypes.nodesGraphMap props.graph
let tEdgesMap = SigmaxTypes.edgesGraphMap props.transformedGraph
let tNodesMap = SigmaxTypes.nodesGraphMap props.transformedGraph
-- TODO Probably this can be optimized to re-mark selected nodes only when they changed
R.useEffect' $ do
Sigmax.dependOnSigma (R.readRef props.sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
Sigmax.markSelectedEdges sigma (fst props.selectedEdgeIds) edgesMap
Sigmax.markSelectedNodes sigma (fst props.selectedNodeIds) nodesMap
Sigmax.updateEdges sigma tEdgesMap
Sigmax.updateNodes sigma tNodesMap
stageHooks _ = pure unit
......@@ -228,9 +229,9 @@ sigmaSettings =
, labelSizeRatio: 2.0 -- label size in ratio of node size
, labelThreshold: 5.0 -- min node cam size to start showing label
, maxEdgeSize: 1.0
, maxNodeSize: 7.0
, maxNodeSize: 8.0
, minEdgeSize: 0.5 -- in fact used in tina as edge size
, minNodeSize: 0.1
, minNodeSize: 1.0
, mouseEnabled: true
, mouseZoomDuration: 150.0
, nodeBorderColor: "default" -- choices: "default" color vs. "node" color
......
......@@ -20,7 +20,6 @@ import Reactix.DOM.HTML as RH
import Math (log)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Hooks.Sigmax (Sigma)
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Components.GraphExplorer.Controls as Controls
......@@ -33,6 +32,7 @@ import Gargantext.Ends (Frontends)
import Gargantext.Routes (SessionRoute(NodeAPI), AppRoute)
import Gargantext.Sessions (Session, Sessions, get)
import Gargantext.Types (NodeType(Graph))
import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2
type GraphId = Int
......@@ -78,8 +78,6 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
dataRef <- R.useRef graph
graphRef <- R.useRef null
controls <- Controls.useGraphControls
selectedNodeIds <- R.useState' $ Set.empty
selectedEdgeIds <- R.useState' $ Set.empty
R.useEffect' $ do
case Tuple (R.readRef dataRef) graph of
......@@ -89,16 +87,10 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
let rSigma = R.readRef controls.sigmaRef
Sigmax.cleanupSigma rSigma "explorerCpt"
R.setRef dataRef graph
snd selectedNodeIds $ const Set.empty
snd selectedEdgeIds $ const Set.empty
snd controls.selectedEdgeIds $ const Set.empty
snd controls.selectedNodeIds $ const Set.empty
snd controls.graphStage $ const Graph.Init
R.useEffect' $ do
if fst controls.showSidePanel == GET.InitialClosed && (not Set.isEmpty $ fst selectedNodeIds) then
snd controls.showSidePanel $ \_ -> GET.Opened
else
pure unit
pure $
RH.div
{ id: "graph-explorer" }
......@@ -113,8 +105,20 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
, row [ Controls.controls controls ]
, row [ tree (fst controls.showTree) {sessions, mCurrentRoute, frontends} (snd showLogin)
, RH.div { ref: graphRef, id: "graph-view", className: graphClassName controls, style: {height: "95%"} } [] -- graph container
, mGraph graphRef controls.sigmaRef {graphId, graph, graphStage: controls.graphStage, selectedNodeIds, selectedEdgeIds}
, mSidebar graph mMetaData {frontends, session, selectedNodeIds, showSidePanel: fst controls.showSidePanel}
, mGraph { controls
, elRef: graphRef
, graphId
, graph
, graphStage: controls.graphStage
, selectedEdgeIds: controls.selectedEdgeIds
, selectedNodeIds: controls.selectedNodeIds
, sigmaRef: controls.sigmaRef
}
, mSidebar graph mMetaData { frontends
, session
, selectedNodeIds: controls.selectedNodeIds
, showSidePanel: fst controls.showSidePanel
}
]
, row [
]
......@@ -146,16 +150,18 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
RH.div {className: "col-md-2", style: {paddingTop: "60px"}}
[forest {sessions, route, frontends, showLogin}]
mGraph :: R.Ref (Nullable Element)
-> R.Ref Sigma
-> { graphId :: GraphId
mGraph :: { controls :: Record Controls.Controls
, elRef :: R.Ref (Nullable Element)
, graphId :: GraphId
, graph :: Maybe Graph.Graph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds}
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, sigmaRef :: R.Ref Sigmax.Sigma
}
-> R.Element
mGraph _ _ {graph: Nothing} = RH.div {} []
mGraph graphRef sigmaRef r@{graph: Just graph} = graphView graphRef sigmaRef $ r { graph = graph }
mGraph {graph: Nothing} = RH.div {} []
mGraph r@{graph: Just graph} = graphView $ r { graph = graph }
mSidebar :: Maybe Graph.Graph
-> Maybe GET.MetaData
......@@ -176,29 +182,36 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
}
type GraphProps = (
graphId :: GraphId
controls :: Record Controls.Controls
, elRef :: R.Ref (Nullable Element)
, graphId :: GraphId
, graph :: Graph.Graph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, sigmaRef :: R.Ref Sigmax.Sigma
)
graphView :: R.Ref (Nullable Element) -> R.Ref Sigma -> Record GraphProps -> R.Element
graphView :: Record GraphProps -> R.Element
--graphView sigmaRef props = R.createElement (R.memo el memoCmp) props []
graphView elRef sigmaRef props = R.createElement el props []
graphView props = R.createElement el props []
where
--memoCmp props1 props2 = props1.graphId == props2.graphId
el = R.hooksComponent "GraphView" cpt
cpt {graphId, graph, selectedEdgeIds, selectedNodeIds} _children = do
cpt {controls, elRef, graphId, graph, selectedEdgeIds, selectedNodeIds, sigmaRef} _children = do
-- TODO Cache this?
let transformedGraph = transformGraph controls graph
pure $ Graph.graph {
elRef
, forceAtlas2Settings: Graph.forceAtlas2Settings
, graph
, selectedEdgeIds
, selectedNodeIds
, sigmaRef
, sigmaSettings: Graph.sigmaSettings
, sigmaRef: sigmaRef
, stage: props.graphStage
, transformedGraph
}
convert :: GET.GraphData -> Tuple (Maybe GET.MetaData) Graph.Graph
......@@ -209,6 +222,7 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxTypes.Graph {nodes, edges}
Seq.singleton
{ id : n.id_
, size : log (toNumber n.size + 1.0)
, hidden : false
, label : n.label
, x : n.x -- cos (toNumber i)
, y : n.y -- sin (toNumber i)
......@@ -357,3 +371,27 @@ defaultPalette = ["#5fa571","#ab9ba2","#da876d","#bdd3ff"
getNodes :: Session -> GraphId -> Aff GET.GraphData
getNodes session graphId = get session $ NodeAPI Graph (Just graphId) ""
transformGraph :: Record Controls.Controls -> Graph.Graph -> Graph.Graph
transformGraph controls graph@(SigmaxTypes.Graph {nodes, edges}) = SigmaxTypes.Graph {nodes: newNodes, edges: newEdges}
where
graphEdgesMap = SigmaxTypes.edgesGraphMap graph
graphNodesMap = SigmaxTypes.nodesGraphMap graph
newNodes = nodeSizes <$> nodeMarked <$> nodes
newEdges = edgeMarked <$> edges
nodeSizes node@{ size } =
if Range.within (fst controls.nodeSize) size then
node
else
node { hidden = true }
edgeMarked edge@{ id } =
if Set.member id (fst controls.selectedEdgeIds) then
edge { color = "#ff0000" }
else
edge
nodeMarked node@{ id } =
if Set.member id (fst controls.selectedNodeIds) then
node { color = "#ff0000" }
else
node
......@@ -11,7 +11,8 @@ module Gargantext.Components.GraphExplorer.Controls
) where
import Data.Maybe (Maybe(..))
import Data.Tuple (fst)
import Data.Set as Set
import Data.Tuple (fst, snd)
import Data.Tuple.Nested ((/\), get1)
import Effect (Effect)
import Effect.Timer (setTimeout)
......@@ -26,6 +27,7 @@ import Gargantext.Components.GraphExplorer.SlideButton (cursorSizeButton, labelS
import Gargantext.Components.GraphExplorer.ToggleButton (edgesToggleButton, pauseForceAtlasButton)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2
......@@ -33,6 +35,9 @@ type Controls =
( cursorSize :: R.State Number
, graphStage :: R.State Graph.Stage
, multiNodeSelect :: R.Ref Boolean
, nodeSize :: R.State Range.NumberRange
, selectedEdgeIds :: R.State (Set.Set String)
, selectedNodeIds :: R.State (Set.Set String)
, showControls :: R.State Boolean
, showSidePanel :: R.State GET.SidePanelState
, showTree :: R.State Boolean
......@@ -45,7 +50,6 @@ controlsToSigmaSettings { cursorSize: (cursorSize /\ _)} = Graph.sigmaSettings
type LocalControls =
( edgeSize :: R.State Range.NumberRange
, labelSize :: R.State Number
, nodeSize :: R.State Range.NumberRange
, pauseForceAtlas :: R.State Boolean
, showEdges :: R.State Boolean
)
......@@ -54,14 +58,14 @@ initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do
edgeSize <- R.useState' $ Range.Closed { min: 0.5, max: 1.0 }
labelSize <- R.useState' 14.0
nodeSize <- R.useState' $ Range.Closed { min: 5.0, max: 10.0 }
--nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 10.0 }
pauseForceAtlas <- R.useState' true
showEdges <- R.useState' true
pure $ {
edgeSize
, labelSize
, nodeSize
--, nodeSize
, pauseForceAtlas
, showEdges
}
......@@ -86,6 +90,12 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
R.useEffect' $ Sigmax.handleForceAtlas2Pause props.sigmaRef localControls.pauseForceAtlas (get1 localControls.showEdges) mFAPauseRef
R.useEffect' $ do
if fst props.showSidePanel == GET.InitialClosed && (not Set.isEmpty $ fst props.selectedNodeIds) then
snd props.showSidePanel $ \_ -> GET.Opened
else
pure unit
R.useEffectOnce' $ do
timeoutId <- setTimeout 2000 $ do
let (toggled /\ setToggled) = localControls.pauseForceAtlas
......@@ -114,7 +124,7 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
-- search topics
, RH.li {} [ cursorSizeButton props.cursorSize ] -- cursor size: 0-100
, RH.li {} [ labelSizeButton props.sigmaRef localControls.labelSize ] -- labels size: 1-4
, RH.li {} [ nodeSizeControl props.sigmaRef localControls.nodeSize ] -- node size : 5-15
, RH.li {} [ nodeSizeControl props.nodeSize ]
-- zoom: 0 -100 - calculate ratio
-- toggle multi node selection
-- save button
......@@ -127,15 +137,21 @@ useGraphControls = do
cursorSize <- R.useState' 10.0
graphStage <- R.useState' Graph.Init
multiNodeSelect <- R.useRef false
nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 10.0 }
showTree <- R.useState' false
selectedNodeIds <- R.useState' $ Set.empty
selectedEdgeIds <- R.useState' $ Set.empty
showControls <- R.useState' false
showSidePanel <- R.useState' GET.InitialClosed
showTree <- R.useState' false
sigma <- Sigmax.initSigma
sigmaRef <- R.useRef sigma
pure { cursorSize
, graphStage
, multiNodeSelect
, nodeSize
, selectedEdgeIds
, selectedNodeIds
, showControls
, showSidePanel
, showTree
......
......@@ -55,24 +55,24 @@ edgeSizeControl sigmaRef (state /\ setState) =
}
}
nodeSizeControl :: R.Ref Sigmax.Sigma -> R.State Range.NumberRange -> R.Element
nodeSizeControl sigmaRef (state /\ setState) =
nodeSizeControl :: R.State Range.NumberRange -> R.Element
nodeSizeControl (state /\ setState) =
rangeControl {
caption: "Node Size"
, sliderProps: {
bounds: Range.Closed { min: 5.0, max: 15.0 }
bounds: Range.Closed { min: 0.0, max: 15.0 }
, initialValue: state
, epsilon: 0.1
, step: 1.0
, width: 10.0
, height: 5.0
, onChange: \range@(Range.Closed {min, max}) -> do
let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[nodeSizeControl] sigma: Nothing" $ \s -> do
Sigma.setSettings s {
minNodeSize: min
, maxNodeSize: max
}
-- let sigma = R.readRef sigmaRef
-- Sigmax.dependOnSigma sigma "[nodeSizeControl] sigma: Nothing" $ \s -> do
-- Sigma.setSettings s {
-- minNodeSize: min
-- , maxNodeSize: max
-- }
setState $ const range
}
}
......@@ -184,6 +184,31 @@ markSelectedNodes sigma selectedNodeIds graphNodes = do
Sigma.refresh sigma
updateEdges :: Sigma.Sigma -> EdgesMap -> Effect Unit
updateEdges sigma edgesMap = do
Sigma.forEachNode sigma \e -> do
let mTEdge = Map.lookup e.id edgesMap
case mTEdge of
Nothing -> error $ "Edge id " <> e.id <> " not found in edgesMap"
(Just tEdge@{color: tColor}) -> do
_ <- pure $ (e .= "color") tColor
pure unit
Sigma.refresh sigma
updateNodes :: Sigma.Sigma -> NodesMap -> Effect Unit
updateNodes sigma nodesMap = do
Sigma.forEachNode sigma \n -> do
let mTNode = Map.lookup n.id nodesMap
case mTNode of
Nothing -> error $ "Node id " <> n.id <> " not found in nodesMap"
(Just tNode@{color: tColor, hidden: tHidden}) -> do
_ <- pure $ (n .= "color") tColor
_ <- pure $ (n .= "hidden") tHidden
pure unit
Sigma.refresh sigma
bindSelectedNodesClick :: R.Ref Sigma -> R.State SelectedNodeIds -> Effect Unit
bindSelectedNodesClick sigmaRef (_ /\ setSelectedNodeIds) =
dependOnSigma (R.readRef sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
......
......@@ -21,6 +21,7 @@ type Renderer = { "type" :: String, container :: Element }
type Node =
( id :: String
, label :: String
, hidden :: Boolean
, x :: Number
, y :: Number
, size :: Number
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment