Шаг 70.
Microsoft Visual C++ 2010. Начала.
Программирование. Графика. Графические примитивы. Сектор

    На этом шаге мы рассмотрим особенности построения сектора.

    Метод 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. Результат работы приложения

    Со следующего шага мы начнем рассматривать работу с текстом.




Предыдущий шаг Содержание Следующий шаг