На этом шаге мы рассмотрим особенности построения сектора.
Метод DrawPie рисует границу сектора (рисунок 1).
Рис.1. Значения параметров метода DrawPie определяют сектор как часть эллипса
Инструкция вызова метода выглядит так:
DrawPie (aPen, x, y, w, h, startAngle, sweepAngle);
Параметры x, y, w и h определяют эллипс, частью которого является сектор.
Параметр startAngle задает начальную точку дуги сектора — пересечение эллипса и прямой, проведенной из центра эллипса и образующей угол startAngle с горизонтальной осью эллипса (угловая координата возрастает по часовой стрелке). Параметр sweepAngle — длину дуги сектора (в градусах).
Если значение sweepAngle положительное, то дуга сектора рисуется от начальной точки по часовой стрелке, если отрицательное — против. Величины углов задаются в градусах.
В инструкции вызова метода DrawPie вместо параметров x, y, w и h можно указать структуру типа Rectangle:
DrawPie (aPen, aRect, startAngle, sweepAngle);
Метод FillPie рисует сектор. Параметры у метода FillPie, за исключением первого, вместо которого надо указать кисть, такие же, как и у метода DrawPie.
В качестве примера, иллюстрирующего использование указанных методов, приведем следующее приложение. В его окне в виде круговой диаграммы отображаются результаты анализа log-файлов сайта "Информатика и программирование: шаг за шагом" (http://it.kgsu.ru), полученных в первом полугодии 2016 года, на предмет предпочтения использования посетителями сайта того или иного браузера. Исходные данные для построения диаграммы (заголовок диаграммы, количество записей, названия браузеров и проценты использования) загружаются из файла.
Вот текст приложения:
// конструктор Form1(void) { InitializeComponent(); // //TODO: добавьте код конструктора // System::IO::StreamReader^ sr; try { sr = gcnew System::IO::StreamReader( Application::StartupPath + "\\date.dat", System::Text::Encoding::GetEncoding(1251)); // считываем заголовок диаграммы header = sr->ReadLine(); // считываем данные о количестве записей N = Convert::ToInt16(sr->ReadLine()); // и инициализируем массивы dat = gcnew array<double>(N); p = gcnew array<double>(N); title = gcnew array<String^>(N); // читаем данные int i = 0; String^ st; st = sr->ReadLine(); while ((st != String::Empty) && (i < N)) { title[i] = st; st = sr->ReadLine(); dat[i++] = Convert::ToDouble(st); st = sr->ReadLine(); } // закрываем поток sr->Close(); // Данные загружены. // Задаем функцию обработки события Paint this->Paint += gcnew System::Windows::Forms::PaintEventHandler (this, &Form1::drawDiagram); double sum = 0; int j = 0; // вычислить сумму for (j = 0; j < N; j++) sum += dat[j]; // вычислить долю каждой категории for (j = 0; j < N; j++) p[j] = (double)(dat[j] / sum); } catch (System::IO::FileNotFoundException^ ex) { MessageBox::Show(ex->Message, "Диаграмма", MessageBoxButtons::OK, MessageBoxIcon::Error); } } . . . . . . private: String^ header; // заголовок диаграммы // количество элементов данных int N; array <double>^ dat; // ряд данных array <double>^ p; // доля категории (ряда) в общей сумме // подписи данных array<String^>^ title; // рисуем круговую диаграмму void drawDiagram(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) { // графическая поверхность System::Drawing::Graphics^ g = e->Graphics; // шрифт заголовка System::Drawing::Font^ hFont = gcnew System::Drawing::Font("Tahoma", 12); // выводим заголовок int w = (int)g->MeasureString(header, hFont).Width; int x = (ClientSize.Width - w) / 2; g->DrawString(header,hFont, Brushes::Black, x, 10); // шрифт легенды System::Drawing::Font^ lFont = gcnew System::Drawing::Font("Tahoma", 9); // диаметр диаграммы int d = ClientSize.Height - 80; int x0 = 30; int y0 = (ClientSize.Height - d) / 2 + 10; // координаты левого верхнего угла области легенды int lx = 60 + d; int ly = y0 + (d - N * 20 + 10) / 2; int swe; // длина дуги сектора // кисть для заливки сектора диаграммы System::Drawing::Brush^ fBrush = Brushes::White; // начальная точка дуги сектора int sta = -90; // рисуем диаграмму for (int i = 0; i < N; i++) { // длина дуги swe = (int)(360 * p[i]); // задать цвет сектора switch (i) { case 0: fBrush = Brushes::Gold; break; case 1: fBrush = Brushes::Silver; break; case 2: fBrush = Brushes::DarkGoldenrod; break; case 3: fBrush = Brushes::Azure; break; case 4: fBrush = Brushes::OrangeRed; break; case 5: fBrush = Brushes::RoyalBlue; break; case 6: fBrush = Brushes::SteelBlue; break; case 7: fBrush = Brushes::Chocolate; break; case 8: fBrush = Brushes::LightGray; break; } // Из-за округления возможна ситуация, при которой // будет промежуток между последним и первым секторами if (i == N - 1) // последний сектор swe = 270 - sta; // рисуем сектор g->FillPie(fBrush, x0, y0, d, d, sta, swe); // рисуем границу сектора g->DrawPie(Pens::Black, x0, y0, d, d, sta, swe); // прямоугольник легенды g->FillRectangle(fBrush, lx, ly + i * 20, 20, 10); g->DrawRectangle(Pens::Black, lx, ly + i * 20, 20, 10); // подпись g->DrawString(title[i] + " - " + p[i].ToString("P"), lFont, Brushes::Black, lx + 24, ly + i * 20 - 3); // начальная точка дуги для следующего сектора sta = sta + swe; } } private: System::Void Form1_Resize(System::Object^ sender, System::EventArgs^ e) { this->Refresh(); }
Результат работы приложения изображен на рисунке 2.
Рис.2. Результат работы приложения
Со следующего шага мы начнем рассматривать работу с текстом.