Конструкторские работы
Для начала нужно построить каркас карты – ячеистую структуру. Есть некоторые решения данной задачи, например на codeproject, но конечная цель там немного отличается. Итак построим сетку в горизонтальной проекции, суть заключается в вычислении новой координаты согласно математическому управлению, при этом на каждый нечетный ряд происходит сдвиг на постоянную величину, зависящую от длины стороны шестиугольника:
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); }
После выполнения получится что-то такое:
Если данный каркас наложить на реальный плоский объект и «вычесть», останутся только те ячейки, которые и являются границами объекта.
Малярные работы
Когда готов аппарат строительства каркаса, т.е. границ карты и ячеек, можно их и покрасить в соответствующий цвет. Правила для окрашивания можно придумать сколь угодно разные. В моем случае, ячейка с большим значением окрашивалась в красный цвет, с меньшим в синий колер. Для решения задачи визуализации применим линейный градиент стандарта RGB. Разукрашиваем:
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); } } }
Если закрашивать случайно сгенерированные значения, то карта будет напоминать что-то похожее на витраж, и особой смысловой нагрузки не несет:
Но если данные содержат определенные линейные зависимости, то они наблюдаемы на картах весьма отчетливо:
Другая сторона
И еще. Полученные функции можно применять при использовании карт самоорганизации Кохонена для анализа многомерных данных и кластеризации признаков в сферах, для которых необходимо анализировать большие потоки связанной информации, например для анализа сетевого трафика с целью выявления закономерностей, отклонений или аномалий, как в магистральных каналах связи, так и в локальных сетях. Данный вид нейронных сетей основан на конкурентном обучении, при организации которого применяются латеральные связи между нейронами. При обучении сети используется метод обучения без учителя, то есть результат обучения зависит только от структуры входных данных. Такое направление анализа не является сигнатурным, что в теории может создать новую ветвь продуктов по сетевой безопасности.
ссылка на оригинал статьи http://habrahabr.ru/post/167685/
Добавить комментарий