БлогNot. C++ .NET: делаем 3D график с помощью Chart

C++ .NET: делаем 3D график с помощью Chart

Вот здесь есть все основные примеры для работы со стандартной (начиная с версии .Net Framework 4.0) компонентой Chart, служащей для построения графиков и диаграмм. Но все эти примеры - "плоские". А можно ли отобразить трёхмерные данные, пользуясь только возможностями Chart?

Коль скоро каждая из областей (ChartAreas) графика может содержать сколько угодно рядов данных (Series), а каждый ряд - сколько угодно точек (Points), то можно. Тем более, для любой области предусмотрена опция Area3DStyle->Enable3D, позволяющая включить трёхмерный вид её рядов данных.

Создадим пустой проект Windows Forms, кинем как попало на форму компонент Chart, всё остальное сделаем программно по любому удобному событию, например, по загрузке (Load) формы.

Сначала растянем Chart по форме и включим некоторые полезные свойства:

using namespace System::Windows::Forms::DataVisualization::Charting;
chart1->Dock = DockStyle::Fill; //растянуть на всё окно
chart1->Legends["Legend1"]->Enabled = false; //убрать легенду
chart1->ChartAreas["ChartArea1"]->Area3DStyle->Enable3D = true; //включить вид 3D
chart1->ChartAreas["ChartArea1"]->AxisX->Enabled  = AxisEnabled::False; //отключить отображение
chart1->ChartAreas["ChartArea1"]->AxisY->Enabled  = AxisEnabled::False; //цифр на осях
chart1->ChartAreas["ChartArea1"]->Area3DStyle->Inclination = 30; //угол поворота гор.осей, по умолчанию

Зададим минимально нужные настройки:

const int n=50; //Количество узлов сетки по осям X и Y
const double c = -5, d = 5; //границы по осям X, Y

Наконец, создадим ряды и заполним их точками-значениями:

chart1->Series->RemoveAt(0); //Удалить ряд, созданный по умолчанию
for (int i=0; i<n; i++) { //Создать n рядов
 Series ^Series1 = gcnew Series();
 Series1->ChartType = SeriesChartType::Column;
 //Series1->Color = Color::Blue; 
 /*Раскомментарьте строку выше, чтобы вывести все ряды одним цветом*/
 chart1->Series->Add(Series1);
 for (int j=0; j<n; j++) { //Создать n точек в каждом ряде
  double xPoint = c + i * (d-c) / (n-1);
  double yPoint = c + j * (d-c) / (n-1);
  chart1->Series[i]->Points->AddXY ((double)j,f(xPoint,yPoint));
 }
}

Функция f(x,y) - любая функция двух переменных, график которой мы выводим, например, такая:

double f (double x, double y) { //собственно функция, график которой выводим
 return x*x+y*y;
}

Это всё, приложение можно запускать и вот что вышло:

3D диаграмма только средствами Chart
3D диаграмма только средствами Chart

Так как компонента "тянется" по форме, при изменении размеров окна всё пересчитается и перерисуется. Если "подложка" пол графиком не нужна, выберите тип диаграммы, например, SeriesChartType::StepLine вместо SeriesChartType::Column.

У нас отображаемые в 3D величины берутся как значения функции двух аргументов f(x,y), если же они уже "лежат" в двумерном массиве, это только упростит ситуацию - просто вместо f(xPoint,yPoint) будем передавать в AddXY элемент из i-ой строки и j-го столбца этого массива.

Можно также "поиграть" с chart1->ChartAreas["ChartArea1"]->AxisX, chart1->ChartAreas["ChartArea1"]->AxisY для детальной настройки. Например, там есть свойства Interval, Maximum и Minimum.

Управляем цветом

В предыдущем листинге закомментировано управление цветом, а часто хочется иметь вместо пёстрого "коврика" что-нибудь более логичное, например, плавный цветовой градиент.

Сменим тип диаграммы на сплайн, а цвет будем рассчитывать пропорционально значению функции. Перед основным циклом for придётся добавить цикл поиска минимума и максимума функции 2 переменных, а расчёт цвета выполнить отдельным методом calculateColor.

Вместо цикла for предыдущего листинга будет код:

Series ^Series1;
double x, y, z;
double x1 = -1, y1 = -1, x2 = 1, y2 = 1; //общие границы области построения
double xstep = (x2 - x1) / (n - 1), ystep = (y2 - y1) / (n - 1); //шаги
double max = f(x1,y1), min = max;
Color Color1;
for (x = x1; x <= x2; x += xstep)
for (y = y1; y <= y2; y += ystep) {
 z = f(x, y);
 if (z > max) max = z;
 else if (z < min) min = z;
}
for (int i = 0; i < n; i++) { //добавить n рядов
 Series1 = gcnew Series();
 Series1->ChartType = SeriesChartType::Spline;
 Series1->Color = Color::Blue;
 chart1->Series->Add(Series1);
 for (int j = 0; j < n; j++) { //добавить n точек в каждый ряд
  x = x1 + i*xstep;
  y = y1 + j*ystep;
  z = f(x, y);
  chart1->Series[i]->Points->AddXY((double)j, z);
  Color1 = calculateColor(min, z, max);
  chart1->Series[i]->Points[j]->Color = Color1;
  chart1->Series[i]->Points[j]->BackSecondaryColor = Color1;
 }
}

а метод расчёта цвета в простейшем случае можно сделать таким (интенсивность красного всегда оставляем равной 255, чтобы иметь диаграмму в красных тонах):

private: Color calculateColor(double min, double z, double max) {
 int c = Math::Round(0. + (z - min)*(255. - 0) / (max - min));
 return Color::FromArgb(255, c, c);
}
3D диаграмма только средствами Chart - теперь с цветовым градиентом
3D диаграмма только средствами Chart - теперь с цветовым градиентом
Меняем свойства Chart динамически

Разумеется, нужные свойства Chart можно менять в процессе работы программы. Например, сделаем угол поворота горизонтальных осей зависимым от расстояния между точкой клика мышью по форме и левым верхним углом клиентской части формы - чем дальше от левого верхнего угла щёлкнем, тем на больший угол повернётся диаграмма (от 0 до 90 градусов).

Понадобится обработчик события MouseClick диаграммы:

private: System::Void chart1_MouseClick(System::Object^  sender, 
  System::Windows::Forms::MouseEventArgs^  e) {
 Point p = Point(e->X, e->Y);
 int d1 = distance(p, Point(0, 0));
 int d0 = distance(Point(0, 0), Point(this->ClientSize.Width, this->ClientSize.Height));
 int corner = Math::Round((d1+0.) / (d0+0.) * 90.);
 chart1->ChartAreas["ChartArea1"]->Area3DStyle->Inclination = corner;
 chart1->Refresh();
}

и служебная функция вычисления пиксельного расстояния между двумя точками:

private: int distance(Point p1, Point p2) {
 return Math::Round(Math::Pow(Math::Pow(p2.X-p1.X,2)+Math::Pow(p2.Y-p1.Y,2),0.5));
}

25.05.2016, 16:25 [11603 просмотра]


теги: графика цвет c++/cli

К этой статье пока нет комментариев, Ваш будет первым