\definecolor[HexGridBase][h=E0E0E0]
\definecolor[HexGridNode][h=202020]
\definecolor[HexGridEdge][h=808080]

%\definecolor[HexGridBase][h=1f50da]
%\definecolor[HexGridNode][h=00d687]
%\definecolor[HexGridEdge][h=00d687]

\startluacode
  require "Graph"

  -- Try to generate a different tree each run.
  math.randomseed( math.modf( os.clock() * 10000000 ), os.time() )

  userdata = userdata or {}
  ud = userdata

  -- Create a minimally spanning acyclic graph that's mostly connected.
  --
  -- @param r - numeric - The number of rows
  -- @param c - numeric - The number of columns
  -- @param m - numeric The number of edges
  --
  -- @return A handle to the tree instance.
  function tree( r, c, m )
    local graph = Graph:new()
    local edges = graph:connected( r, c, m )
    local source = edges[ math.random( #edges ) ]
    local tree = graph:compute( source )

    return tree
  end

  -- Extract the first pair of a vertex at the given index inside the tree.
  -- No error checking is performed.
  --
  -- @param graph - object - The tree containing numerous edges.
  -- @param index - non-negative integer - The index for the vertex pair.
  --
  -- @return The (x, y) pair for the first vertex comprising an edge.
  function ud.vertex_a( graph, index )
    u = graph[ index ].u
    return { u.x, u.y }
  end
  
  -- Extract the second pair of a vertex at the given index inside the tree.
  -- No error checking is performed.
  --
  -- @param graph - object - The tree containing numerous edges.
  -- @param index - non-negative integer - The index for the vertex pair.
  --
  -- @return The (x, y) pair for the second vertex comprising an edge.
  function ud.vertex_b( graph, index )
    v = graph[ index ].v
    return { v.x, v.y }
  end
  
  -- Return the number of edges in the tree.
  --
  -- @param graph - object - The tree containing numerous nodes.
  --
  -- @return The number of vertex pairs in the tree.
  function ud.count( graph )
    return #graph
  end
\stopluacode

\startuseMPgraphic{HexGridBaseGraphic}{side}
  % Set the random seed to the current second.
  randomseed := lua( "os.time()" );

  vardef atantwo( expr ax, ay, bx, by ) =
    save theta;

    dx := bx - ax;
    dy := by - ay;
    theta := 0;

    if (dx > 0):
      theta := atan( dy / dx );
    elseif (dx < 0) and (dy >= 0):
      theta := atan( dy / dx ) + pi;
    elseif (dx < 0) and (dy < 0):
      theta := atan( dy / dx ) - pi;
    elseif (dx == 0) and (dy > 0):
      theta := pi / 2;
    elseif (dx == 0) and (dy < 0):
      theta := -pi / 2;
    fi;

    theta
  enddef;

  vardef degrees( expr rad ) =
    rad * 180 / pi
  enddef;

  vardef distance( expr ax, ay, bx, by ) =
    sqrt( ((bx - ax)^2) + ((by - ay)^2) )
  enddef;

  % Set the number of hexagons from top to bottom.
  GRID_C = 124;

  % Set the number of hexagons from left to right.
  GRID_R = 120;

  % Set the number of pathways between nodes (higher is more complexity).
  PATHWAYS = 235;

  % Grid zoom level (smaller is further away).
  SCALE = 5;

  % Hexagon dimensions.
  HEIGHT  = sqrt( 3 );
  HHEIGHT = HEIGHT / 2;
  WIDTH   = 2;

  color colour_base;
  color colour_node;
  color colour_edge;

  colour_base = \MPcolor{HexGridBase};
  colour_node = \MPcolor{HexGridNode};
  colour_edge = \MPcolor{HexGridEdge};

  % Vertex diameters proportional to the hexagon height.
  vertex_sm := HEIGHT / 12;
  vertex_lg := HEIGHT / 5;

  path path_ul;
  path path_ur;
  path path_tp;

  path_ur := dir( 60 * 0 ) -- dir( 60 * 1 );
  path_tp := dir( 60 * 1 ) -- dir( 60 * 2 );
  path_ul := dir( 60 * 2 ) -- dir( 60 * 3 );

  picture hexgrid;
  picture vertices;
  picture perceptrons;
  picture connections;

  hexgrid := nullpicture;
  vertices := nullpicture;
  perceptrons := nullpicture;
  connections := nullpicture;

  % Line width.
  pickup pencircle scaled .05bp;

  addto hexgrid also image(
    for c = 1 upto GRID_C:
      for r = 1 upto GRID_R:
        offset_c := c * 3 / WIDTH;
        offset_r := r * HEIGHT - (c mod 2 * HHEIGHT);

        draw path_ul shifted (offset_c, offset_r) withcolor colour_base;
        draw path_tp shifted (offset_c, offset_r) withcolor colour_base;
        draw path_ur shifted (offset_c, offset_r) withcolor colour_base;

        for i = 0 upto 2:
          addto vertices also image(
            draw
              point i of path_tp
              shifted (offset_c, offset_r)
              withcolor colour_base
              withpen pencircle
              scaled vertex_sm;
          );
        endfor;
      endfor;
    endfor;
  );

  % Create the perceptron matrix.
  lua( "ud.graph = tree( " &
    decimal( GRID_C ) &
    ", " &
    decimal( GRID_R ) &
    ", " &
    decimal( PATHWAYS ) &
    ")"
  );

  edges := lua( "ud.count( ud.graph )" );

  for i := 1 upto edges:
    lua( "va = ud.vertex_a( ud.graph, " & decimal( i ) & ")" );
    lua( "vb = ud.vertex_b( ud.graph, " & decimal( i ) & ")" );

    vac := lua( "va[1]" );
    var := lua( "va[2]" );
    vbc := lua( "vb[1]" );
    vbr := lua( "vb[2]" );

    offset_ac := vac * 3 / WIDTH;
    offset_ar := var * HEIGHT - (vac mod 2 * HHEIGHT);
    offset_bc := vbc * 3 / WIDTH;
    offset_br := vbr * HEIGHT - (vbc mod 2 * HHEIGHT);

    % Draw a large nodes at the starting/ending coordinates.
    addto perceptrons also image(
      draw (offset_ac, offset_ar)
        withcolor colour_node
        withpen pencircle
        scaled vertex_lg;

      draw (offset_bc, offset_br)
        withcolor colour_node
        withpen pencircle
        scaled vertex_lg;
    );

    addto perceptrons also image(
      numeric vangle;
      numeric vc;
      numeric vr;
      numeric sc;
      numeric sr;

      vi := round( degrees( atantwo( vac, var, vbc, vbr ) ) / 60 );

      % Compute direction towards the first segment (to vertex of an edge).
      vangle := vi * 60 * pi / 180;

      % Calculate the position of the first vertex, offset from the center.
      vc := offset_ac + cos( vangle );
      vr := offset_ar + sin( vangle );

      % Draw a line from the starting point to the subsequent vertex.
      addto connections also image(
        draw (offset_ac, offset_ar) -- (vc, vr)
          withcolor colour_edge;
      );

      iterations := 1;

      forever:
        % Draw a circle at the subsequent vertex.
        draw (vc, vr)
          withcolor colour_node
          withpen pencircle
          scaled
            if round( uniformdeviate( 3 ) ) = 0: vertex_lg else: vertex_sm; fi;

        %  2  _   1
        %  3 / \  0
        % -2 \_/ -1
        %
        % Determine the next vertex based on smallest angle of the
        % three possible vertices with respect to the line.
        start := if (vi mod 2) == 1: -1 else: -2 fi;

        % Ensure that at least one vertex will be selected.
        nearest := infinity;

        for k = start step 2 until 3:
          kangle := k * 60 * pi / 180;

          nc := vc + cos( kangle );
          nr := vr + sin( kangle );
          d := distance( offset_bc, offset_br, nc, nr );

          if d < nearest:
            nearest := d;
            sc := nc;
            sr := nr;
            vi := k + 1;
          fi;
        endfor;

        d := distance( offset_bc, offset_br, sc, sr );
        iterations := iterations + 1;

        if (d <= 1) and (iterations > 2) and (iterations mod 2 = 0):
          draw (sc, sr)
            withcolor colour_node
            withpen pencircle
            scaled vertex_sm;
        fi;

        exitif (d <= 1);

        addto connections also image(
          draw (vc, vr) -- (sc, sr)
            withcolor colour_edge
            withpen pencircle
            scaled 0.05bp;
        );

        vc := sc;
        vr := sr;
      endfor;

      if (d < 1) and (iterations > 2):
        draw (sc, sr)
          withcolor colour_node
          withpen pencircle
          scaled vertex_sm;
      fi;

      addto connections also image(
        draw (offset_bc, offset_br) -- (vc, vr)
          withcolor colour_edge
          withpen pencircle
          scaled 0.05bp;
      );
    );
  endfor;

  draw hexgrid scaled SCALE;
  draw vertices scaled SCALE;
  draw connections scaled SCALE;
  draw perceptrons scaled SCALE;
\stopuseMPgraphic

\startuseMPgraphic{HexGridBgGraphic}
  % Define the grid background colour
  color gridback;
  gridback := (0.15, 0.18, 0.24);

  fill unitsquare
    xyscaled (OverlayWidth, OverlayHeight)
    withcolor gridback;
\stopuseMPgraphic

\defineoverlay[HexGridBaseGraphic][\uniqueMPgraphic{HexGridBaseGraphic}]
\defineoverlay[HexGridBgGraphic][\uniqueMPgraphic{HexGridBgGraphic}]

\setupbackgrounds[page][background={HexGridBaseGraphic}]

\starttext
  \startalignment[middle]
    \color[white]{GRID}
  \stopalignment
\stoptext

