# Cell painter

It is known that a set of regular hexagons can describe almost any contour and thereby create a shape of arbitrary shape. This principle can be applied when constructing topographic maps similar to how it is implemented in Sid Meier's Civilization V , or used in systems based on the very same hexagons (cellular operators). To make it more informative, you can assign a control value to each cell of the map, whether it is the signal level of the network, height, depth, the presence of a certain type of resource, etc., which characterizes the area of ​​the map - cell. It might look something like this . However, for the eyes, this representation is not quite presentable, and it can be visualized by adding an appropriate color to each cell.

##### Design work

First you need to build a map frame - a mesh structure. There are some solutions to this problem, for example on codeproject , but the final goal there is slightly different. So, let's build a grid in horizontal projection, the essence is to calculate the new coordinate according to mathematical control, while for every odd row there is a shift by a constant value, depending on the length of the side of the hexagon:
``````private void DrawHexangleHorizon(Graphics gr, int i, int j)
{
int shft;
int shft2;
GraphicsPath gp;
if (i % 2 != 0)
{
shft = (int)(Side * Math.Sqrt(3) / 2);
shft2 = (int)((i - 1) * 1.5 * Side);
gp = new GraphicsPath(new PointF[7]
{
new Point(20+shft+j*(int)(Side * Math.Sqrt(3)), 20+shft2),                                                 //init 1-1
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))+(int)(Side * Math.Sqrt(3)/2),20+shft2+Side/2),                                            //1->2
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))+(int)(Side * Math.Sqrt(3)/2), 20+shft2 +Side+ Side/2),    //2->3
new Point(20+shft+j*(int)(Side * Math.Sqrt(3)), 20+shft2 + 2*Side),               //3->4
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))-(int)(Side * Math.Sqrt(3)/2), 20+shft2 +Side+ Side/2),                    //4->5
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))-(int)(Side * Math.Sqrt(3)/2), 20+shft2 + Side/2),         //5->6
new Point(20+shft+j*(int)(Side * Math.Sqrt(3)), 20+shft2)                                                  //finish 6->1
},
new byte[7]
{
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
}
);
}
else
{
shft2 = (int)((i - 1) * 1.5 * Side);
shft = 0;
gp = new GraphicsPath(new PointF[7]
{
new Point(20+shft+j*(int)(Side * Math.Sqrt(3)), 20+shft2),                                                 //init 1-1
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))+(int)(Side * Math.Sqrt(3)/2),20+shft2+Side/2),                                            //1->2
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))+(int)(Side * Math.Sqrt(3)/2), 20+shft2 +Side+ Side/2),    //2->3
new Point(20+shft+j*(int)(Side * Math.Sqrt(3)), 20+shft2 + 2*Side),               //3->4
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))-(int)(Side * Math.Sqrt(3)/2), 20+shft2 +Side+ Side/2),                    //4->5
new Point(20+shft+j*(int)(Side * Math.Sqrt(3))-(int)(Side * Math.Sqrt(3)/2), 20+shft2 + Side/2),         //5->6
new Point(20+shft+j*(int)(Side * Math.Sqrt(3)), 20+shft2)                                                  //finish 6->1
},
new byte[7]
{
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
}
);
}
gr.DrawPath(Pens.Black, gp);
gr.FillPath(Sbr, gp);
}
``````

After execution, you get something like this:

If you put this frame on a real flat object and “subtract”, only those cells will remain that are the boundaries of the object.
##### Painting works

When the carcass construction apparatus is ready, i.e. borders of the map and cells, you can paint them in the corresponding color. The rules for coloring can be thought up arbitrarily different. In my case, a cell with a higher value was painted red, with a smaller value blue. To solve the visualization problem, we apply the linear gradient of the RGB standard. Coloring:
``````private void ColoredGrig(Graphics gr, double[,] mas)
{
double max = double.MinValue;
double min = double.MaxValue;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (mas[i, j] > max)
{
max = mas[i, j];
}
if (mas[i, j] < min)
{
min = mas[i, j];
}
}
}
double average = (max + min) / 2.0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
var c = new Color();
var red = new double();
var green = new double();
var blue = new double();
if (mas[i, j] == max)
{
red = 255;
blue = 0;
green = 0;
}
if (mas[i, j] == min)
{
red = 0;
blue = 255;
green = 0;
}
if (mas[i, j] == average)
{
red = 0;
blue = 0;
green = 255;
}
//warm colors
if ((mas[i, j] < max) && (mas[i, j] >= ((average + max) / 2)))
{
red = 255;
green = 255 - ((mas[i, j] - ((average + max) / 2)) * 255) / (max - ((max + average) / 2));
}
if ((mas[i, j] > average) && (mas[i, j] < ((average + max) / 2)))
{
red = ((mas[i, j] - average) * 255) / (((max + average) / 2) - average);
green = 255;
}
//cold colors
if ((mas[i, j] < average) && (mas[i, j] > ((average + min) / 2)))
{
blue = 255 - ((mas[i, j] - ((average + min) / 2)) * 255) / (average - ((average + min) / 2));
green = 255;
}
if ((mas[i, j] > min) && (mas[i, j] <= ((average + min) / 2)))
{
blue = 255;
green = ((mas[i, j] - min) * 255) / (((average + min) / 2) - min);
}
c = Color.FromArgb((int)red, (int)green, (int)blue);
Sbr = new SolidBrush(c);
DrawHexangleHorizon(gr, i, j);
}
}
}
``````

If you paint over randomly generated values, the map will resemble something similar to a stained-glass window, and does not carry any special semantic load:

But if the data contains certain linear dependencies, then they are observed on the maps very clearly:

##### Other side

And further. The obtained functions can be used when using Kohonen’s self-organization maps to analyze multidimensional data and cluster features in areas for which it is necessary to analyze large flows of related information, for example, to analyze network traffic in order to identify patterns, deviations or anomalies, both in trunk communication channels and in local networks. This type of neural network is based on competitive training, the organization of which uses lateral connections between neurons. When training the network, the method of teaching without a teacher is used, that is, the result of training depends only on the structure of the input data. This direction of analysis is not signature-based, which in theory can create a new branch of network security products.