From 81c77cb9f2013b9cd5957eadbd26086c4ddeae56 Mon Sep 17 00:00:00 2001
From: Przemek Kaminski <pk@intrepidus.pl>
Date: Wed, 12 Oct 2022 20:14:42 +0200
Subject: [PATCH] [utils] generic renormalization using lens

---
 .../Components/GraphExplorer/Utils.purs       | 54 +++++++++++--------
 src/Gargantext/Utils/Lens.purs                | 26 +++++++++
 2 files changed, 57 insertions(+), 23 deletions(-)
 create mode 100644 src/Gargantext/Utils/Lens.purs

diff --git a/src/Gargantext/Components/GraphExplorer/Utils.purs b/src/Gargantext/Components/GraphExplorer/Utils.purs
index 5ee91e07..94e3009f 100644
--- a/src/Gargantext/Components/GraphExplorer/Utils.purs
+++ b/src/Gargantext/Components/GraphExplorer/Utils.purs
@@ -8,6 +8,7 @@ import Gargantext.Prelude
 
 import Data.Array as A
 import Data.Foldable (maximum, minimum)
+import Data.Lens (lens)
 import Data.Maybe (Maybe(..))
 import Data.Newtype (wrap)
 import Data.Sequence as Seq
@@ -15,6 +16,7 @@ import Gargantext.Components.GraphExplorer.GraphTypes as GEGT
 import Gargantext.Components.GraphExplorer.Types as GET
 import Gargantext.Hooks.Sigmax.Types as ST
 import Gargantext.Utils (getter)
+import Gargantext.Utils.Lens as GUL
 import Gargantext.Utils.Seq as GUS
 
 stEdgeToGET :: Record ST.Edge -> GEGT.Edge
@@ -34,31 +36,37 @@ stNodeToGET { id, label, x, y, _original: GEGT.Node { attributes, size, type_ }
 
 -----------------------------------------------------------------------
 
+-- | Normalize nodes, i.e. set their {x, y} values so that they are in
+-- | range [0, 1].
 normalizeNodes :: Seq.Seq GEGT.Node -> Seq.Seq GEGT.Node
-normalizeNodes ns = Seq.map normalizeNode ns
+normalizeNodes ns = GUL.normalizeLens xLens $ GUL.normalizeLens yLens ns
   where
-    xs = map (\(GEGT.Node { x }) -> x) ns
-    ys = map (\(GEGT.Node { y }) -> y) ns
-    mMinx = minimum xs
-    mMaxx = maximum xs
-    mMiny = minimum ys
-    mMaxy = maximum ys
-    mXrange = do
-      minx <- mMinx
-      maxx <- mMaxx
-      pure $ maxx - minx
-    mYrange = do
-      miny <- mMiny
-      maxy <- mMaxy
-      pure $ maxy - miny
-    xdivisor = case mXrange of
-      Nothing -> 1.0
-      Just xdiv -> 1.0 / xdiv
-    ydivisor = case mYrange of
-      Nothing -> 1.0
-      Just ydiv -> 1.0 / ydiv
-    normalizeNode (GEGT.Node n@{ x, y }) = GEGT.Node $ n { x = x * xdivisor
-                                                         , y = y * ydivisor }
+    xLens = lens (\(GEGT.Node { x }) -> x) $ (\(GEGT.Node n) val -> GEGT.Node (n { x = val }))
+    yLens = lens (\(GEGT.Node { y }) -> y) $ (\(GEGT.Node n) val -> GEGT.Node (n { y = val }))
+-- normalizeNodes ns = Seq.map normalizeNode ns
+--   where
+--     xs = map (\(GEGT.Node { x }) -> x) ns
+--     ys = map (\(GEGT.Node { y }) -> y) ns
+--     mMinx = minimum xs
+--     mMaxx = maximum xs
+--     mMiny = minimum ys
+--     mMaxy = maximum ys
+--     mXrange = do
+--       minx <- mMinx
+--       maxx <- mMaxx
+--       pure $ maxx - minx
+--     mYrange = do
+--       miny <- mMiny
+--       maxy <- mMaxy
+--       pure $ maxy - miny
+--     xdivisor = case mXrange of
+--       Nothing -> 1.0
+--       Just xdiv -> 1.0 / xdiv
+--     ydivisor = case mYrange of
+--       Nothing -> 1.0
+--       Just ydiv -> 1.0 / ydiv
+--     normalizeNode (GEGT.Node n@{ x, y }) = GEGT.Node $ n { x = x * xdivisor
+--                                                          , y = y * ydivisor }
 
 ------------------------------------------------------------------------
 
diff --git a/src/Gargantext/Utils/Lens.purs b/src/Gargantext/Utils/Lens.purs
new file mode 100644
index 00000000..c8b85c02
--- /dev/null
+++ b/src/Gargantext/Utils/Lens.purs
@@ -0,0 +1,26 @@
+module Gargantext.Utils.Lens where
+
+import Gargantext.Prelude
+
+import Data.Foldable (maximum, minimum)
+import Data.Maybe (Maybe(..))
+import Data.Lens
+import Data.Traversable
+
+-- | Given a Traversable of entities and a lens for them, normalize
+-- | over lens getter so that the value of lens setter is in range [0,
+-- | 1].
+normalizeLens :: forall a t. Traversable t => Lens' a Number -> t a -> t a
+normalizeLens l ns = over traversed normalize' ns
+  where
+    values = over traversed (_ ^. l) ns
+    vMin = minimum values
+    vMax = maximum values
+    vRange = do
+      minv <- vMin
+      maxv <- vMax
+      pure $ maxv - minv
+    divisor = case vRange of
+      Nothing -> 1.0
+      Just d -> 1.0 / d
+    normalize' n = over l (_ * divisor) n
-- 
2.21.0