Полезное:
Как сделать разговор полезным и приятным
Как сделать объемную звезду своими руками
Как сделать то, что делать не хочется?
Как сделать погремушку
Как сделать так чтобы женщины сами знакомились с вами
Как сделать идею коммерческой
Как сделать хорошую растяжку ног?
Как сделать наш разум здоровым?
Как сделать, чтобы люди обманывали меньше
Вопрос 4. Как сделать так, чтобы вас уважали и ценили?
Как сделать лучше себе и другим людям
Как сделать свидание интересным?
Категории:
АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника
|
Параграф 4. Круговые диаграммы и элементы 3D графики ⇐ ПредыдущаяСтр 9 из 9 Построение круговых диаграмм с элементами 3D графики требует несколько больших затрат по сравнению с рассмотренным выше материалом. Прежде всего, необходимо определить дополнительные переменные для величин: оси эллипса (vfDiamX, vfDiamY), центр круговой диаграммы (vfXcirc, vfYcirc). Кроме того, если мы хотим, что бы в легенде (пояснению к графику) цвета надписей соответствовали цветам секторов диаграммы, то потребуется задать массив цветов однозначно соответствующий массиву цветов кистей. Зададим в классе: private float vfDiamX = 100; private float vfDiamY = 100; private float vfXcirc = 100; private float vfYcirc = 100; private Color[] color ={ Color.LightGreen,Color.Chartreuse,Color.LimeGreen,Color.Green,Color.DarkGreen, Color.DarkOliveGreen,Color.LightPink,Color.LightSeaGreen,Color.LightCoral,Color.DarkCyan, Color.Crimson, Color.CornflowerBlue,Color.Chocolate,Color.CadetBlue,Color.BlueViolet, Color.Maroon,Color.Blue,Color.Brown,Color.DarkBlue, Color.Red, Color.Coral,Color.DarkRed, Color.DarkMagenta, Color.DarkOrange,Color.DarkOrchid}; Основная функция для рисования диаграммы имеет ряд особенностей, связанных с формированием объемности и расположением надписей. Рисовать диаграмму будем в несколько этапов:
Рис.12.1. Первый этап создания круговой диаграммы
Рис.12.2. Второй этап создания круговой диаграммы
Рис.12.3. Третий этап создания круговой диаграммы
Рис.12.4. Четвертый этап создания круговой диаграммы
Рис.12.5. Пятый этап создания круговой диаграммы Алгоритм рисования можно упростить, например, один раз и последним этапом наложить сектора эллипса, нарисованный кистью SolidBrush, но, в этом случае пострадает наглядность. Эти этапы рисования выполняет следующая функция: #region vDravCircle3D //Параметры - Отступ от краев по X слева deltaaxisL, от краев по Y справа deltaaxisR, //deltaaxisH - отступа сверху и снизу, толщина диаграммы viH, сдвиг сектора viDx, viDy public void vDravCircle3D(int deltaaxisL, int deltaaxisR, int deltaaxisH, int viH, int viDx, int viDy) { //Запоминаем отступы viDeltaaxisL = deltaaxisL; viDeltaaxisR = deltaaxisR; viDeltaaxisH = deltaaxisH; float a = viX - (deltaaxisL + deltaaxisR); //Нужен ли выброс сектора int viMov = 1; if (viDx == 0 && viDy == 0) { viMov = 0; } //Запоминаем диаметр vfDiamX = a; vfDiamY = viY - 2 * viDeltaaxisH; //Запоминаем центр элипса vfXcirc = deltaaxisL + a / 2; vfYcirc = viY / 2; graph.SmoothingMode = SmoothingMode.AntiAlias; //Определяем сумму всех значений в массиве float fSum = 0; string s = string.Empty; for (int i = 0; i < viMaxRg; i++) { s = rgsValues[i, 0]; fSum += float.Parse(s); } float f = 0; float fBSum = 0; float fDeltaGrad = (fSum / (float)360); SolidBrush objBrush = new SolidBrush(Color.Aqua); Random rand = new Random(DateTime.Now.Millisecond); float[] frgZn = new float[viMaxRg]; float[] frgSumGr = new float[viMaxRg]; for (int i = 0; i < viMaxRg; i++) { s = rgsValues[i, 0]; frgZn[i] = float.Parse(s); if (i == 0) frgSumGr[i] = 0; else frgSumGr[i] = frgZn[i] + frgSumGr[i - 1]; } for (int i = viMaxRg - 1; i >= 0; i--) { if (i!= viMaxRg - 1 && fBSum < 90) break; //f в градусах fBSum в градусах f = frgZn[i] / fDeltaGrad; //fBSum = frgSumGr[i] / fDeltaGrad; if (i == viMaxRg - 1) { fBSum = 360 - f; } else { fBSum -= f; } //Для цвета int j = i % br.Length; float k = f; if (f < 1) k = 1; //objBrush.Color = Color.FromArgb(rand.Next(255), rand.Next(255), rand.Next(255)); if (i!= 0) { if ((fBSum > 90 && fBSum < 180) || i == viMaxRg - 1) { for (int d = 0; d < viH; d++) { //Этап 1 graph.FillPie(new HatchBrush(HatchStyle.Percent25, color[j]/*objBrush.Color*/), vfXcirc - a / 2, vfYcirc - vfDiamY / 2 + d, vfDiamX, vfDiamY, fBSum, k); } } objBrush.Color = color[j]; //Этап 2 graph.FillPie(objBrush, vfXcirc - a / 2, vfYcirc - vfDiamY / 2, vfDiamX, vfDiamY, fBSum, k); } } fBSum = 0; for (int i = viMov; i < viMaxRg; i++) { //f в градусах fBSum в градусах f = frgZn[i] / fDeltaGrad; if (i == 1) { fBSum = frgZn[0] / fDeltaGrad; } //Для цвета int j = i % br.Length; float k = f; if (f < 1) k = 1;
if (fBSum < 90) { for (int d = 0; d < viH; d++) { //Этап 3 graph.FillPie(new HatchBrush(HatchStyle.Percent25, color[j]), vfXcirc - a / 2, vfYcirc - vfDiamY / 2 + d, vfDiamX, vfDiamY, fBSum, k); } objBrush.Color = color[j]; //Этап 4 graph.FillPie(objBrush, vfXcirc - a / 2, vfYcirc - vfDiamY / 2, vfDiamX, vfDiamY, fBSum, k); } else { break; } fBSum += f; } //Рисуем сдвинутым первый сектор //Этап 5 if (viMov == 1) { f = frgZn[0] / fDeltaGrad; fBSum = 0; float k1 = f; if (f < 1) k1 = 1; for (int d = 0; d < viH; d++) { graph.FillPie(new HatchBrush(HatchStyle.Percent25, color[0]), vfXcirc - a / 2 + viDx, vfYcirc - vfDiamY / 2 + d - viDy, vfDiamX, vfDiamY, fBSum, k1); } objBrush.Color = color[0]; graph.FillPie(objBrush, vfXcirc - a / 2 + viDx, vfYcirc - vfDiamY / 2 - viDy, vfDiamX, vfDiamY, fBSum, k1); } } #endregion Добавляем функции надписи и легенду и, в принципе, построение диаграммы закончено. Единственное, что потребуется от нас при рисовании надписей на диаграмме - это немного вспомнить начальную школу при расчете координат нанесения значений: #region vDravTextCircle public void vDravTextCircle1(bool vfGde) { float fSum = 0; string s = string.Empty; for (int i = 0; i < viMaxRg; i++) { s = rgsValues[i, 0]; fSum += float.Parse(s); } float f = 0; float fBSum = 0; float f1Radian = (float)Math.PI / 180; float fDeltaGrad = fSum / 360; for (int i = 0; i < viMaxRg; i++) { s = rgsValues[i, 0]; f = float.Parse(s); //f в градусах f = f / fDeltaGrad; int j = i % br.Length; //Угол в радианах float fRad = (f + fBSum) * f1Radian; float fty = 0; float ftx = 0; float fSin = (float)Math.Sin((360 - (f / 2 + fBSum)) * f1Radian); float fCos = (float)Math.Cos((360 - (f / 2 + fBSum)) * f1Radian); float c = (float)Math.Sqrt((vfDiamX / 2 * vfDiamX / 2 * vfDiamY / 2 * vfDiamY / 2) / (vfDiamY / 2 * vfDiamY / 2 * fCos * fCos + vfDiamX / 2 * vfDiamX / 2 * fSin * fSin)); c -= 3 * objFont.Size; if (c < 0) c = 0; ftx = c * fCos; fty = c * fSin; ftx = vfXcirc + ftx; fty = vfYcirc - fty; if (vfGde) { graph.DrawString(Convert.ToString(i + 1), objFont, objBrush, ftx, fty); } else { graph.DrawString(rgsValues[i, 0], objFont, objBrush, ftx, fty); } fBSum += f; } } #endregion
#region Текст легенды public void vDravTextKeyCircle(bool vfGde) { float fSum = 0; float f = 0; string s = string.Empty; for (int i = 0; i < viMaxRg; i++) { s = rgsValues[i, 0]; fSum += float.Parse(s); } //Сдвиг от круговой диаграммы float vfSdvig = vfXcirc + vfDiamX / 2; vfSdvig += (viX - vfSdvig) / 5; //Высота места для легенды //На одну строку по высоте отводится - +1 на заголовок float vfHg = viY / (viMaxRg + 2); vSetFont("Arial", 12, true); if (viMaxRg > 100) { graph.DrawString("Легенда не может быть размещена", objFont, Brushes.DarkBlue, vfSdvig + (viX - vfSdvig) / 10, objFont.Size); } else { //Шрифт в 2 раза меньше места на строку надписи if (viMaxRg > 15) { vSetFont("Arial", (vfHg / 2), true); } else { if (viMaxRg > 10) { vSetFont("Arial", (vfHg / 3), true); } else { vSetFont("Arial", (vfHg / 6), true); } } if (vfGde) { graph.DrawString("Пояснения к графику", objFont, Brushes.DarkBlue, vfSdvig /*+ (viX - vfSdvig) / 10*/, objFont.Size); } else { graph.DrawString("Пояснения к графику", objFont, objBrush, vfSdvig/* + (viX - vfSdvig) / 10*/, objFont.Size); } if (viMaxRg > 15) { vSetFont("Arial", (vfHg / 2) + 1, true); } else { if (viMaxRg > 10) { vSetFont("Arial", (vfHg / 4) + 1, true); } else { vSetFont("Arial", (vfHg / 7) + 1, true); } } for (int i = 0; i < rgsValues.Length / 2; i++) { Brush brTxt = null; int j = i % br.Length; if (vfGde) brTxt = br[j]; else brTxt = objBrush; graph.DrawString(Convert.ToString(i + 1), objFont, brTxt, vfSdvig, vfHg * (i + 2)); f = float.Parse(rgsValues[i, 0]); f = (f * 100) / fSum; graph.DrawString(rgsValues[i, 0], objFont, brTxt, vfSdvig + 1 * (viX - vfSdvig) / 5, vfHg * (i + 2)); graph.DrawString(f.ToString("0.0") + "%", objFont, brTxt, vfSdvig + 2 * (viX - vfSdvig) / 5, vfHg * (i + 2)); graph.DrawString(rgsValues[i, 1], objFont, brTxt, vfSdvig + 3 * (viX - vfSdvig) / 5, vfHg * (i + 2)); } } } #endregion
#region Смена шрифта по секторам private void vSetFont(string name, float size, bool bold) { if (objFont!= null) objFont = null; if (bold) { objFont = new Font(name, size, FontStyle.Bold); } else { objFont = new Font(name, size); } } #endregion
Оформим вызовы функций: private void button3_Click(object sender, EventArgs e) { viNumButton = 3; vCreateCircleDiagramm(); } private void vCreateCircleDiagramm() { //Создаем массив значений для вывода на графике vCreateRg(); //Создаем класс и передаем ему размер холсты PaintCl clPaint = new PaintCl(pictureBox1.Width, pictureBox1.Height); //Фон холста clPaint.vSetBackground(Color.White); //Передаем значения массива в класс clPaint.RgValue = rgsValues; //Рисуем график. Параметры: отступ осей x слева, x справа, //y от краев холста, толщина диаграммы,вынос сектора clPaint.vDravCircle3D(20, 250, 50, 20, 20, 40); //Круговые надписи true цифры 1-20, false - значения clPaint.vDravTextCircle1(true); //false - Разноцветные надписи в легенде true - Цветом шрифта clPaint.vDravTextKeyCircle(true); //Принимаем нарисованное в pictureBox pictureBox1.Image = clPaint.Bmp; } Отметим, что при задании толщины диаграммы, равной нулю, получим обычную эллиптическую диаграмму, а при равенстве осей Х и Y - круговую. Результат выполнения решения показан на Рис.12.6: Рис.12.6. Круговая диаграмма В заключении, еще раз повторим, что все параметры целесообразно иметь настраиваемыми, что позволяет быстро подобрать приемлемый вид графического отображения для демонстрации. Целесообразно также выполнить автономную настройку диаграмм по тестовым значениям (как это сделано в программе LitFregMeter - см. Параграф 2.). Тогда мы сможем быстро подбирать параметры, например так: //Смена фона диаграммы private void ColorBackGround_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog() == DialogResult.OK) { //Переменная objColorBackGroung задана глобально, сохраняется в реестре //при закрытии приложения и передается в класс при рисовании диаграммы //clPaint.vSetBackground(objColorBackGroung); objColorBackGroung = colorDialog1.Color; vGhangeDiagramm(); } } //Перерисовка конкретной диаграммы при настройке private void vGhangeDiagramm() { switch (viNumButton) { case 1: vCreateLinGr(); break; case 2: vCreateRectangleDiagramm(); break; case 3: vCreateCircleDiagramm(); break; } }
|