Commit 6db0c594 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[Graph] work on sigmajs selector

parent 9e296c86
...@@ -4,13 +4,11 @@ module Gargantext.Components.Graph ...@@ -4,13 +4,11 @@ module Gargantext.Components.Graph
-- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings -- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings
-- ) -- )
where where
import Prelude (bind, const, discard, pure, ($), unit, map, not, show) import Prelude (bind, const, discard, not, pure, unit, ($))
import Data.Array as A
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable) import Data.Nullable (Nullable)
import Data.Tuple (fst)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log, log2) import DOM.Simple.Console (log, log2)
import DOM.Simple.Types (Element) import DOM.Simple.Types (Element)
...@@ -72,6 +70,7 @@ graphCpt = R.hooksComponent "Graph" cpt ...@@ -72,6 +70,7 @@ graphCpt = R.hooksComponent "Graph" cpt
_ <- Sigma.addRenderer sig { _ <- Sigma.addRenderer sig {
"type": "canvas" "type": "canvas"
, container: c , container: c
, additionalContexts: ["mouseSelector"]
} }
pure unit pure unit
...@@ -80,6 +79,8 @@ graphCpt = R.hooksComponent "Graph" cpt ...@@ -80,6 +79,8 @@ graphCpt = R.hooksComponent "Graph" cpt
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
-- bind the click event only initially, when ref was empty -- bind the click event only initially, when ref was empty
Sigmax.bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabledRef Sigmax.bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabledRef
_ <- Sigma.bindMouseSelectorPlugin sigma
pure unit
Sigmax.setEdges sig false Sigmax.setEdges sig false
Sigma.startForceAtlas2 sig props.forceAtlas2Settings Sigma.startForceAtlas2 sig props.forceAtlas2Settings
...@@ -172,6 +173,7 @@ type SigmaSettings = ...@@ -172,6 +173,7 @@ type SigmaSettings =
, mouseEnabled :: Boolean , mouseEnabled :: Boolean
-- , mouseInertiaDuration :: Number -- , mouseInertiaDuration :: Number
-- , mouseInertiaRatio :: Number -- , mouseInertiaRatio :: Number
, mouseSelectorSize :: Number
-- , mouseWheelEnabled :: Boolean -- , mouseWheelEnabled :: Boolean
, mouseZoomDuration :: Number , mouseZoomDuration :: Number
, nodeBorderColor :: String , nodeBorderColor :: String
...@@ -239,6 +241,7 @@ sigmaSettings = ...@@ -239,6 +241,7 @@ sigmaSettings =
, minEdgeSize: 0.5 -- in fact used in tina as edge size , minEdgeSize: 0.5 -- in fact used in tina as edge size
, minNodeSize: 1.0 , minNodeSize: 1.0
, mouseEnabled: true , mouseEnabled: true
, mouseSelectorSize: 10.0
, mouseZoomDuration: 150.0 , mouseZoomDuration: 150.0
, nodeBorderColor: "default" -- choices: "default" color vs. "node" color , nodeBorderColor: "default" -- choices: "default" color vs. "node" color
--, nodesPowRatio : 10.8 --, nodesPowRatio : 10.8
......
...@@ -414,6 +414,6 @@ transformGraph controls graph = SigmaxTypes.Graph {nodes: newNodes, edges: newEd ...@@ -414,6 +414,6 @@ transformGraph controls graph = SigmaxTypes.Graph {nodes: newNodes, edges: newEd
_ -> edge _ -> edge
nodeMarked node@{ id } = nodeMarked node@{ id } =
if Set.member id (fst controls.selectedNodeIds) then if Set.member id (fst controls.selectedNodeIds) then
node { borderColor = "#000", type = "hovered" } node { borderColor = "#000", type = "selected" }
else else
node node
...@@ -25,7 +25,7 @@ import Gargantext.Components.Graph as Graph ...@@ -25,7 +25,7 @@ import Gargantext.Components.Graph as Graph
import Gargantext.Components.GraphExplorer.Button (centerButton) import Gargantext.Components.GraphExplorer.Button (centerButton)
import Gargantext.Components.GraphExplorer.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl) import Gargantext.Components.GraphExplorer.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl)
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl) import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
import Gargantext.Components.GraphExplorer.SlideButton (cursorSizeButton, labelSizeButton) import Gargantext.Components.GraphExplorer.SlideButton (cursorSizeButton, labelSizeButton, mouseSelectorSizeButton)
import Gargantext.Components.GraphExplorer.ToggleButton (multiSelectEnabledButton, edgesToggleButton, pauseForceAtlasButton) import Gargantext.Components.GraphExplorer.ToggleButton (multiSelectEnabledButton, edgesToggleButton, pauseForceAtlasButton)
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax as Sigmax import Gargantext.Hooks.Sigmax as Sigmax
...@@ -56,14 +56,17 @@ controlsToSigmaSettings { cursorSize: (cursorSize /\ _)} = Graph.sigmaSettings ...@@ -56,14 +56,17 @@ controlsToSigmaSettings { cursorSize: (cursorSize /\ _)} = Graph.sigmaSettings
type LocalControls = type LocalControls =
( labelSize :: R.State Number ( labelSize :: R.State Number
, mouseSelectorSize :: R.State Number
) )
initialLocalControls :: R.Hooks (Record LocalControls) initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do initialLocalControls = do
labelSize <- R.useState' 14.0 labelSize <- R.useState' 14.0
mouseSelectorSize <- R.useState' 10.0
pure $ { pure $ {
labelSize labelSize
, mouseSelectorSize
} }
controls :: Record Controls -> R.Element controls :: Record Controls -> R.Element
...@@ -153,6 +156,7 @@ controlsCpt = R.hooksComponent "GraphControls" cpt ...@@ -153,6 +156,7 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
-- save button -- save button
, RH.li {} [ nodeSearchControl { graph: props.graph , RH.li {} [ nodeSearchControl { graph: props.graph
, selectedNodeIds: props.selectedNodeIds } ] , selectedNodeIds: props.selectedNodeIds } ]
, RH.li {} [ mouseSelectorSizeButton props.sigmaRef localControls.mouseSelectorSize ]
] ]
] ]
] ]
......
...@@ -12,8 +12,6 @@ import Reactix as R ...@@ -12,8 +12,6 @@ import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Gargantext.Components.RangeSlider as RS import Gargantext.Components.RangeSlider as RS
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Utils.Range as Range import Gargantext.Utils.Range as Range
type Props = ( type Props = (
......
...@@ -3,6 +3,7 @@ module Gargantext.Components.GraphExplorer.SlideButton ...@@ -3,6 +3,7 @@ module Gargantext.Components.GraphExplorer.SlideButton
, sizeButton , sizeButton
, cursorSizeButton , cursorSizeButton
, labelSizeButton , labelSizeButton
, mouseSelectorSizeButton
) where ) where
import Global (readFloat) import Global (readFloat)
...@@ -48,27 +49,45 @@ sizeButtonCpt = R.hooksComponent "SizeButton" cpt ...@@ -48,27 +49,45 @@ sizeButtonCpt = R.hooksComponent "SizeButton" cpt
cursorSizeButton :: R.State Number -> R.Element cursorSizeButton :: R.State Number -> R.Element
cursorSizeButton state = cursorSizeButton state =
sizeButton { sizeButton {
state: state state
, caption: "Cursor Size" , caption: "Cursor Size"
, min: 1.0 , min: 1.0
, max: 4.0 , max: 4.0
, onChange: \e -> snd state $ const $ readFloat $ R2.unsafeEventValue e , onChange: snd state <<< const <<< readFloat <<< R2.unsafeEventValue
} }
labelSizeButton :: R.Ref Sigmax.Sigma -> R.State Number -> R.Element labelSizeButton :: R.Ref Sigmax.Sigma -> R.State Number -> R.Element
labelSizeButton sigmaRef state = labelSizeButton sigmaRef state =
sizeButton { sizeButton {
state: state state
, caption: "Label Size" , caption: "Label Size"
, min: 5.0 , min: 5.0
, max: 30.0 , max: 30.0
, onChange: \e -> do , onChange: \e -> do
let sigma = R.readRef sigmaRef let sigma = R.readRef sigmaRef
let newValue = readFloat $ R2.unsafeEventValue e let newValue = readFloat $ R2.unsafeEventValue e
let (value /\ setValue) = state let (_ /\ setValue) = state
Sigmax.dependOnSigma sigma "[labelSizeButton] sigma: Nothing" $ \s -> do Sigmax.dependOnSigma sigma "[labelSizeButton] sigma: Nothing" $ \s -> do
Sigma.setSettings s { Sigma.setSettings s {
defaultLabelSize: newValue defaultLabelSize: newValue
} }
setValue $ const newValue setValue $ const newValue
} }
mouseSelectorSizeButton :: R.Ref Sigmax.Sigma -> R.State Number -> R.Element
mouseSelectorSizeButton sigmaRef state =
sizeButton {
state
, caption: "Selector Size"
, min: 1.0
, max: 50.0
, onChange: \e -> do
let sigma = R.readRef sigmaRef
let (_ /\ setValue) = state
let newValue = readFloat $ R2.unsafeEventValue e
Sigmax.dependOnSigma sigma "[mouseSelectorSizeButton] sigma: Nothing" $ \s -> do
Sigma.setSettings s {
mouseSelectorSize: newValue
}
setValue $ const newValue
}
...@@ -19,7 +19,6 @@ import Reactix as R ...@@ -19,7 +19,6 @@ import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
type Props = ( type Props = (
......
module Gargantext.Hooks.Sigmax module Gargantext.Hooks.Sigmax
where where
import Prelude (Unit, bind, discard, flip, pure, unit, ($), (*>), (<<<), (<>), (>>=), (||), not, const, map) import Prelude (Unit, bind, discard, flip, pure, unit, ($), (*>), (<<<), (<>), (>>=), not, const, map)
import Data.Array as A import Data.Array as A
import Data.Either (either) import Data.Either (either)
......
...@@ -9,7 +9,7 @@ if (typeof window !== 'undefined') { ...@@ -9,7 +9,7 @@ if (typeof window !== 'undefined') {
const CustomShapes = require('sigma/plugins/garg.js').init(sigma, window).customShapes; const CustomShapes = require('sigma/plugins/garg.js').init(sigma, window).customShapes;
require('sigma/src/utils/sigma.utils.js').init(sigma); require('sigma/src/utils/sigma.utils.js').init(sigma);
sigma.canvas.nodes.hovered = (node, context, settings) => { sigma.canvas.nodes.selected = (node, context, settings) => {
// hack // hack
// We need to temporarily set node.type to 'def'. This is for 2 reasons // We need to temporarily set node.type to 'def'. This is for 2 reasons
// 1. Make it render as a normal node // 1. Make it render as a normal node
...@@ -17,11 +17,96 @@ sigma.canvas.nodes.hovered = (node, context, settings) => { ...@@ -17,11 +17,96 @@ sigma.canvas.nodes.hovered = (node, context, settings) => {
// again with node.type = 'hovered') // again with node.type = 'hovered')
node.type = 'def'; node.type = 'def';
sigma.canvas.hovers.def(node, context, settings); sigma.canvas.hovers.def(node, context, settings);
node.type = 'hovered'; node.type = 'selected';
}; };
CustomShapes.init(); CustomShapes.init();
let sigmaMouseSelector = (sigma, options) => {
sigma.plugins = sigma.plugins || {};
sigma.plugins.mouseSelector = (s, renderer) => {
var _self = this;
var _offset = null;
const _s = s;
const _renderer = renderer;
const _container = _renderer.container;
//renderer.initDOM('canvas', 'mouseSelector');
// A hack to force resize to be called (there is a width/height equality
// check which can't be escaped in any other way).
//renderer.resize(renderer.width - 1, renderer.height - 1);
//renderer.resize(renderer.width + 1, renderer.height + 1);
const _context = _renderer.contexts.mouseSelector;
_container.onmousemove = function(e) { return mouseMove(e); };
_context.canvas.onclick = function(e) { return onClick(e); };
s.bind('click', function(e) { return onClick(e); })
// The mouseSelector canvas will pass its events down to the "mouse" canvas.
_context.canvas.style.pointerEvents = 'none';
s.bind('kill', () => _self.unbindAll());
this.unbindAll = () => {
console.log('[sigmaMouseSelector] unbinding');
_container.onclick = null;
_container.onmousemove = null;
}
const mouseMove = (e) => {
const size = _s.settings('mouseSelectorSize') || 3;
const x = e.clientX - _offset.left - size/2;
const y = e.clientY - _offset.top - size/2;
_context.clearRect(0, 0, _context.canvas.width, _context.canvas.height);
_context.fillStyle = 'rgba(91, 192, 222, 0.7)';
_context.beginPath();
_context.arc(
x,
y,
size,
0,
Math.PI * 2,
true
);
_context.closePath();
_context.fill();
}
const onClick = (e) => {
const size = _s.settings('mouseSelectorSize') || 3;
const x = e.data.clientX - _offset.left - size/2;
const y = e.data.clientY - _offset.top - size/2;
const prefix = _renderer.options.prefix;
console.log('[sigmaMouseSelector] clicked', e, x, y, size);
let nodes = [];
_s.graph.nodes().forEach((node) => {
const nodeX = node[prefix + 'x'];
const nodeY = node[prefix + 'y'];
if(sigma.utils.getDistance(x, y, nodeX, nodeY) <= size) {
nodes.push(node);
}
});
console.log('[sigmaMouseSelector] nodes', nodes);
}
const calculateOffset = (element) => {
var style = window.getComputedStyle(element);
var getCssProperty = function(prop) {
return parseInt(style.getPropertyValue(prop).replace('px', '')) || 0;
};
return {
left: element.getBoundingClientRect().left + getCssProperty('padding-left'),
top: element.getBoundingClientRect().top + getCssProperty('padding-top')
};
};
_offset = calculateOffset(renderer.container);
}
}
sigmaMouseSelector(sigma);
function _sigma(left, right, opts) { function _sigma(left, right, opts) {
try { try {
return right(new sigma(opts)); return right(new sigma(opts));
...@@ -45,6 +130,14 @@ function addRenderer(left, right, sigma, renderer) { ...@@ -45,6 +130,14 @@ function addRenderer(left, right, sigma, renderer) {
return left(e); return left(e);
} }
} }
function bindMouseSelectorPlugin(left, right, sig) {
try {
return right(sigma.plugins.mouseSelector(sig, sig.renderers[0]));
} catch(e) {
console.log('[bindMouseSelectorPlugin] error', e);
return left(e);
}
}
function killRenderer(left, right, sigma, renderer) { function killRenderer(left, right, sigma, renderer) {
try { try {
sigma.killRenderer(renderer); sigma.killRenderer(renderer);
...@@ -91,6 +184,7 @@ exports._sigma = _sigma; ...@@ -91,6 +184,7 @@ exports._sigma = _sigma;
exports._graphRead = graphRead; exports._graphRead = graphRead;
exports._refresh = refresh; exports._refresh = refresh;
exports._addRenderer = addRenderer; exports._addRenderer = addRenderer;
exports._bindMouseSelectorPlugin = bindMouseSelectorPlugin;
exports._killRenderer = killRenderer; exports._killRenderer = killRenderer;
exports._getRendererContainer = getRendererContainer; exports._getRendererContainer = getRendererContainer;
exports._setRendererContainer = setRendererContainer; exports._setRendererContainer = setRendererContainer;
......
...@@ -70,6 +70,16 @@ foreign import _addRenderer ...@@ -70,6 +70,16 @@ foreign import _addRenderer
r r
(Either err Unit) (Either err Unit)
bindMouseSelectorPlugin :: forall err. Sigma -> Effect (Either err Unit)
bindMouseSelectorPlugin = runEffectFn3 _bindMouseSelectorPlugin Left Right
foreign import _bindMouseSelectorPlugin
:: forall a b err.
EffectFn3 (a -> Either a b)
(b -> Either a b)
Sigma
(Either err Unit)
killRenderer :: forall r err. Sigma -> r -> Effect (Either err Unit) killRenderer :: forall r err. Sigma -> r -> Effect (Either err Unit)
killRenderer = runEffectFn4 _killRenderer Left Right killRenderer = runEffectFn4 _killRenderer Left Right
......
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