{
float yi = ToPixelY (y * dataY.factor);
gridPen.Width = y == 0 ? 2 : 0;
g.DrawLine (gridPen, lt.X, yi, rb.X, yi);
s = MakeLabel (false, y);
fmt.Alignment = StringAlignment.Far;
g.DrawString (s, wnd.Font, Brushes.Black, lt.X-h, yi-h2, fmt);
}
g.DrawRectangle (pen, lt.X, lt.Y, rb.X-lt.X, rb.Y-lt.Y);
//====== Вывод линии графика
for (int i=0; i<points.Length - 1; i++)
{
float
x1 = ToPixelX (points[i].X),
x2 = ToPixelX (points[i+1].X),
y1 = ToPixelY (points[i].Y),
y2 = ToPixelY (points[i+1].Y);
g.DrawLine (new Pen (Brushes.DarkOrchid, 2), x1, y1, x2, y2);
}
}
//===== Подготовка цифровой метки на оси
string MakeLabel (bool bX, float f)
{
string s = "0";
if (f == 0)
return s;
//=== Выясняем порядок шага сетки, переворачивая его знак (трюк)
int dg = (int)Math.Ceiling (-Math.Log10 (bX ? dataX.step : dataY.step));
//=== Если все изменения происходят до запятой, то цифры после запятой не нужны
if (dg <= 0)
dg = -1;
s = String.Format("{0:f5}", f);
s = s.Substring (0, s.IndexOf (".") + dg + 1); // Справа минимум разрядов
//=== Порядок нужен, если не поместились в (10^-3, 10^+4)
int power = (int) (bX ? dataX.power : dataY.power);
if (power != 0)
s += String.Format("•e{0}", power);
return s;
}
}
}
Запустите проект на выполнение (Ctrl+F5) и, если ваши настройки Windows соответствуют тем, что были использованы в моем проекте, то вы увидите клиентскую форму с внедренным в него элементом управления. Последний отображает график осциллирующей функции.
Однако, более вероятен другой исход, при котором вы получите ошибку на этапе выполнения. Найти ее не так просто, как хотелось бы, так как студия в подобных случаях не в состоянии нам помочь. В MFC проекте в сходной ситуации обычно помогает окно Call Stack. С его помощью можно пройти назад по цепочке вызовов функций вплоть до кода, который спровоцировал ошибку. В проекте нашего типа этот вариант не проходит. Искать причину придется, расставляя точки останова.
При разработке этого кода была проведена достаточно большая работа, но она не содержит принципиально новых технологических решений или новых языковых конструкций. Поэтому мы опустим объяснения и отметим лишь несколько главных моментов.
¨ Данные для графика хранятся в массиве структур PointF[] points. По умолчанию он заполняется 300-ми точками кривой переходного процесса (колебательного типа), соответствующего линейной динамической системе 2-го порядка. Смотрите конструктор класса Graph, который вызывает метод SetDefaultPlot, пользующийся услугами метода OscillatingDelta. Последний вычисляет точки кривой переходного процесса, используя аналитическое выражение.
¨ При управлении сервером в рамках клиентского приложения данные для графика следует устанавливать извне. Для этой цели создан метод SetData. Клиент должен вызвать этот метод и подать ему в качестве параметра ссылку на новый массив с точками графика (PointF[] pts), новый заголовок графика (string title) и новую пометку оси абсцисс (string x).
¨ При построении графика мы хотим, чтобы числа, используемые для оцифровки осей (мантиссы), укладывались в некоторый разумный диапазон и принадлежали множеству чисел, кратных (по модулю 10) стандартным значениям шага: 2, 2.5, 5 и 10. Для этой цели служат: структура данных AxisData, а также функции CalcStep и Scale. Они выполняют главную черновую работу.
¨ Размеры графика отслеживают размеры окна элемента управления. Этим занимаются функции ToPixelX, ToPixelY, которые преобразовывают "мировые" координаты точек в пиксельные координаты окна. Они пользуются опорными точками графика (PointF lt, rb, bc; — лево-верх, право-низ, низ-центр).
¨ Мы хотим, чтобы числа, используемые для разметки осей, из любого разумного диапазона, как можно дольше оставались читабельными. Эту роль выполняет функция MakeLabel. Алгоритм нормирования абсцисс и ординат проще создать, чем кратко и понятно описать, поэтому смотрите, что происходит, ищите ошибки и, если обнаружите, то присылайте рекомендации по исправлению.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.