5.4 Визуализация графика функции. (Рисование графиков в C#, OpenGL).

Создание визуализации анимированного графика функции на С# и OpenGL

В данной главе мы рассмотрим процесс создание программы, задачей которой будет визуализация графика заданной функции. Особенностью программы будет то, что в ней будет анимированно демонстрироваться то, как меняются значения функции на графике.

Это будет реализовано следующим образом: по графику двигается красная точка, принимающая значения y для заданного x в нашем графике (по всей видимой области). Помимо этого возле курсора будут визуализироваться его координаты (рис. 1).

Уроки OpenGL + C#: Визуализация анимированного графика функции Рисунок 1. Визуализация анимированного графика функции.
Создайте основу приложения так, как это было описано в главе 4.4. Только не добавляйте кнопки «Визуализировать» и «Закрыть», ограничьтесь элементом SimpleOpenGLControl.

Окно должно иметь форму, как показано на рисунке 1.

Добавьте в проект таймер, назовите его (параметр name в свойствах таймера) PointInGrap и установите в его свойствах интервал 30 миллисекунд. После этого щелкните по нему дважды, чтобы создалась функция PointInGrap_Tick, отвечающая за обработку события ontimer.

Теперь, когда основа приложения создана, мы перейдем к исходному коду. Он будет основан на 7 функциях, которые мы сейчас рассмотрим, но сначала перед кодом функции-конструктора класса добавьте инициализацию следующих переменных:

/*http://esate.ru, Anvi*/


// размеры окна 
double ScreenW, ScreenH; 

// отношения сторон окна визуализации 
// для корректного перевода координат мыши в координаты, 
// принятые в программе 

private float devX; 
private float devY; 

// массив, который будет хранить значения x,y точек графика 
private float[,] GrapValuesArray; 
// количество элементов в массиве 
private int elements_count = 0; 

// флаг, означающий, что массив с значениями координат графика пока еще не заполнен 
private bool not_calculate = true; 

// номер ячейки массива, из которой будут взяты координаты для красной точки 
// для визуализации текущего кадра 
private int pointPosition = 0; 

// вспомогательные переменные для построения линий от курсора мыши к координатным осям 
float lineX, lineY; 

// текущение координаты курсора мыши 
float Mcoord_X = 0, Mcoord_Y = 0; 


Перед каждой переменной закомментировано ее назначение, так что вопросов возникнуть не должно.
Теперь вернемся к нашим 7 функциям.

Первая функция – это обработчик события загрузки формы Form1_Load. Здесь при загрузке приложения будет произведена инициализация OpenGL для последующей визуализации.

Инициализацию OpenGL с двухмерной проекцией мы рассмотрели в предыдущей части главы, так что здесь все должно быть понятно. Единственным отличием является то, что код стал немного более подробным. Код этой функции:

/*http://esate.ru, Anvi*/

private void Form1_Load(object sender, EventArgs e) 
{ 

  // инициализация библиотеки glut 
  Glut.glutInit(); 
  // инициализация режима экрана 
  Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE); 

  // установка цвета очистки экрана (RGBA) 
  Gl.glClearColor(255, 255, 255, 1); 

  // установка порта вывода 
  Gl.glViewport(0, 0, AnT.Width, AnT.Height); 

  // активация проекционной матрицы 
  Gl.glMatrixMode(Gl.GL_PROJECTION); 
  // очистка матрицы 
  Gl.glLoadIdentity(); 

  // определение параметров настройки проекции в зависимости от размеров сторон элемента AnT. 
  if ((float)AnT.Width <= (float)AnT.Height) 
  { 
    ScreenW = 30.0; 
    ScreenH = 30.0 * (float)AnT.Height / (float)AnT.Width; 
    Glu.gluOrtho2D(0.0, ScreenW, 0.0, ScreenH);
  } 
  else 
  {
    ScreenW = 30.0 * (float)AnT.Width / (float)AnT.Height; 
    ScreenH = 30.0; 
    Glu.gluOrtho2D(0.0, 30.0 * (float)AnT.Width / (float)AnT.Height, 0.0, 30.0);
  } 

  // сохранение коэффициентов, которые нам необходимы для перевода координат указателя в оконной системе в координаты, 
  // принятые в нашей OpenGL сцене 
  devX = (float)ScreenW / (float)AnT.Width; 
  devY = (float)ScreenH / (float)AnT.Height; 

  // установка объектно-видовой матрицы 
  Gl.glMatrixMode(Gl.GL_MODELVIEW); 

  // старт счетчика, отвечающего за вызов функции визуализации сцены 
  PointInGrap.Start();

} 


Как видите, почти ничего не изменилось. Теперь обратимся к функции PointInGrap_Tick. Эта функция вызывается с задержкой в 30 миллисекунд.

В ней мы ведем отсчет того, из какого элемента массива с координатами графика мы сейчас возьмем координаты, которые используем для рисования красной точки.
Отсюда так же вызывается функция Draw, отвечающая за визуализацию.

Код этой функции:

/*http://esate.ru, Anvi*/


// функция обработчик события таймера 
private void PointInGrap_Tick(object sender, EventArgs e) 
{ 

  // если мы дошли до последнего элемента массива 
  if (pointPosition == elements_count-1) 
    pointPosition = 0; // переходим к начальному элементу 

  // функция визуализации 
  Draw(); 

  // переход к следующему элементу массива 
  pointPosition++;

} 


Теперь перед тем как перейти к функциям, отвечающим за визуализацию, мы рассмотрим несолько небольших вспомогательных функций.

Начтем с функции AnT_MouseMove. Эта функция добавляется созданием события MouseMove для элемента SimpleOpnGLControl (AnT). Событие создается аналогично тому, как мы его создавали в главе 2.2. Только в данном случае мы переходим к свойствам элемента AnT и уже в них переходим во вкладку Event и добавляем событие MouseMove.

В данной функции мы производим сохранение текущих координат мыши, чтобы в будущем использовать их при визуализации графика, а также производим вычисление размеров линий, которые будут по нормалям соединять координаты указателя мыши с координатными осями (две красные линии на рисунке 1).

Код этой функции выглядит следующим образом:

/*http://esate.ru, Anvi*/


// обработка движения мыши над элементом AnT 
private void AnT_MouseMove(object sender, MouseEventArgs e) 
{ 

// сохраняем координаты мыши 
Mcoord_X = e.X; 
Mcoord_Y = e.Y; 

// вычисляем параметры для будущей дорисовки линий от указателя мыши к координатным осям. 
lineX = devX * e.X; 
lineY = (float)(ScreenH - devY * e.Y);

} 


Теперь рассмотрим функцию, которая будет осуществлять визуализацию текстовых строк. Эта функция устанавливает координаты вывода растровых символов в соответствии с координатами, переданными в параметрах x и y, а затем в цикле перебирает все символы из указанной в параметре строки текста. Каждый из символов визуализируется с помощью функции. В этой функции указывается шрифт для вывода и переменная типа char для визуализации.

Код функции выглядит следующим образом:

/*http://esate.ru, Anvi*/


// функция визуализации текста 
private void PrintText2D(float x, float y, string text) 
{ 

  // устанавливаем позицию вывода растровых символов 
  // в переданных координатах x и y. 
  Gl.glRasterPos2f(x, y); 

  // в цикле foreach перебираем значения из массива text, 
  // который содержит значение строки для визуализации 
  foreach (char char_for_draw in text) 
  { 
    // символ C визуализируем с помощью функции glutBitmapCharacter, используя шрифт GLUT_BITMAP_9_BY_15. 
    Glut.glutBitmapCharacter(Glut.GLUT_BITMAP_9_BY_15, char_for_draw);
  }

} 


Следующая функция, которой мы коснемся - это функция, вычисляющая координаты для построения графика. В ней инициализируется массив координат и производится вычисление всех координат графика в зависимости от указанного диапазона значений x и шага приращения этих значений.

Обратите внимание на то, что при инициализации массива для хранения координат должно быть указано такое количество элементов массива, чтобы в дальнейшем их хватило для размещения всех координат, иначе произойдет исключение, так как программа в процессе работы попытается обратиться к области памяти, которая ей не принадлежит.

Код этой функции с подробными комментариями:

/*http://esate.ru, Anvi*/

// функция, производящая вычисления координат графика 
// и заносящая их в массив GrapValuesArray 
private void functionCalculation() 
{ 

  // определение локальных переменных X и Y 
  float x = 0, y = 0; 

  // инициализация массива, который будет хранить значение 300 точек, 
  // из которых будет состоять график 

  GrapValuesArray = new float[300, 2]; 

  // счетчик элементов массива 
  elements_count = 0; 

  // вычисления всех значений y для x, принадлежащего промежутку от -15 до 15 с шагом в 0.01f 
  for (x = -15; x < 15; x += 0.1f) 
  { 
    // вычисление y для текущего x 
    // по формуле y = (float)Math.Sin(x)*3 + 1; 
    // эта строка задает формулу, описывающую график функции для нашего уравнения y = f(x). 
    y = (float)Math.Sin(x)*3 + 1; 

    // запись координаты x 
    GrapValuesArray[elements_count, 0] = x; 
    // запись координаты y 
    GrapValuesArray[elements_count, 1] = y; 
    // подсчет элементов 
    elements_count++;

  } 

  // изменяем флаг, сигнализировавший о том, что координаты графика не вычислены 
  not_calculate = false;

} 

Функция, выполняющая визуализацию графика

Так как визуализацию графика можно отнести к конкретной подзадаче функции визуализации сцены (и чтобы не загромождать функцию визуализации сцены), мы вынесем визуализацию графика в отдельную функцию.

В этой функции сначала будет проверен флаг, сигнализирующий о том, что координаты графика вычислены и занесены в массив (переменная not_calculate). В том случае, если флаг указывает, что просчета значений еще не было, вызывается функция, которая посчитает значения координат точек графика и заполнит ими массив.

Далее реализуется проход циклом for по массиву значений координат точек графика и их визуализация, причем мы визуализируем не точки, а объединяем эти точки в линию, основываясь на значении координат точек, как на вершинах.

По завершению отрисовки графика производится рисование красной точки в тех координатах, до которых мы дошли, последовательно перебирая значения элементов в массиве координат.

Исходный код данной функции:

/*http://esate.ru, Anvi*/

// визуализация графика 
private void DrawDiagram() 
{ 

  // проверка флага, сигнализирующего о том, что координаты графика вычислены 
  if (not_calculate) 
  { 
    // если нет, то вызываем функцию вычисления координат графика 
    functionCalculation();
  } 

  // стартуем отрисовку в режиме визуализации точек 
  // объединяемых в линии (GL_LINE_STRIP) 
  Gl.glBegin(Gl.GL_LINE_STRIP); 

  // рисуем начальную точку 
  Gl.glVertex2d(GrapValuesArray[0, 0], GrapValuesArray[0, 1]); 

  // проходим по массиву с координатами вычисленных точек 
  for (int ax = 1; ax < elements_count; ax+=2) 
  { 
    // передаем в OpenGL информацию о вершине, участвующей в построении линий 
    Gl.glVertex2d(GrapValuesArray[ax, 0], GrapValuesArray[ax, 1]);
  } 

  // завершаем режим рисования 
  Gl.glEnd(); 

  // устанавливаем размер точек, равный 5 пикселям 
  Gl.glPointSize(5); 
  // устанавливаем текущим цветом - красный цвет 
  Gl.glColor3f(255, 0, 0); 
  // активируем режим вывода точек (GL_POINTS) 
  Gl.glBegin(Gl.GL_POINTS); 
  // выводим красную точку, используя ту ячейку массива, до которой мы дошли (вычисляется в функции обработчике событий таймера) 
  Gl.glVertex2d(GrapValuesArray[pointPosition, 0], GrapValuesArray[pointPosition, 1]); 
  // завершаем режим рисования 
  Gl.glEnd(); 
  // устанавливаем размер точек равный единице 
  Gl.glPointSize(1);

} 


И теперь нам осталось просмотреть последнюю функцию – функцию Draw.

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

Код этой функции выглядит следующим образом:

/*http://esate.ru, Anvi*/

// функция, управляющая визуализацией сцены 
private void Draw() 
{ 

  // очистка буфера цвета и буфера глубины 
  Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); 

  // очищение текущей матрицы 
  Gl.glLoadIdentity(); 

  // установка черного цвета 
  Gl.glColor3f(0, 0, 0); 

  // помещаем состояние матрицы в стек матриц 
  Gl.glPushMatrix(); 

  // выполняем перемещение в пространстве по осям X и Y 
  Gl.glTranslated(15, 15, 0); 

  // активируем режим рисования (Указанные далее точки будут выводиться как точки GL_POINTS) 
  Gl.glBegin(Gl.GL_POINTS); 

  // с помощью прохода вдумя циклами, создаем сетку из точек 
  for (int ax = -15; ax < 15; ax++) 
  {
    for (int bx = -15; bx < 15; bx++) 
    { 
      // вывод точки 
      Gl.glVertex2d(ax, bx);
    } 
  } 

  // завершение режима рисования примитивов 
  Gl.glEnd(); 

  // активируем режим рисования, каждые 2 последовательно вызванные команды glVertex 
  // объединяются в линии 
  Gl.glBegin(Gl.GL_LINES); 

  // далее мы рисуем координатные оси и стрелки на их концах 
  Gl.glVertex2d(0, -15); 
  Gl.glVertex2d(0, 15); 

  Gl.glVertex2d(-15, 0); 
  Gl.glVertex2d(15, 0); 

  // вертикальная стрелка 
  Gl.glVertex2d(0, 15); 
  Gl.glVertex2d(0.1, 14.5); 
  Gl.glVertex2d(0, 15); 
  Gl.glVertex2d(-0.1, 14.5); 

  // горизонтальная трелка 
  Gl.glVertex2d(15, 0); 
  Gl.glVertex2d(14.5, 0.1); 
  Gl.glVertex2d(15, 0); 
  Gl.glVertex2d(14.5, -0.1); 

  // завершаем режим рисования 
  Gl.glEnd(); 

  // выводим подписи осей "x" и "y" 
  PrintText2D(15.5f, 0, "x"); 
  PrintText2D(0.5f, 14.5f, "y"); 

  // вызываем функцию рисования графика 
  DrawDiagram(); 

  // возвращаем матрицу из стека 
  Gl.glPopMatrix(); 

  // выводим текст со значением координат возле курсора 
  PrintText2D(devX * Mcoord_X + 0.2f, (float)ScreenH - devY * Mcoord_Y + 0.4f, "[ x: " + (devX * Mcoord_X - 15).ToString() + " ; y: " + ((float)ScreenH - devY * Mcoord_Y - 15).ToString() + "]"); 

  // устанавливаем красный цвет 
  Gl.glColor3f(255, 0, 0); 

  // включаем режим рисования линий, для того чтобы нарисовать 
  // линии от курсора мыши к координатным осям 
  Gl.glBegin(Gl.GL_LINES); 

  Gl.glVertex2d(lineX, 15); 
  Gl.glVertex2d(lineX, lineY); 
  Gl.glVertex2d(15, lineY); 
  Gl.glVertex2d(lineX, lineY); 

  Gl.glEnd(); 

  // дожидаемся завершения визуализации кадра 
  Gl.glFlush(); 

  // сигнал для обновление элемента реализующего визуализацию. 
  AnT.Invalidate();

} 


Как видите, в этой функции тоже нет ничего сложного, необходимо только разобраться с координатным методом построения примитивов. Если какой-либо кусок кода остается непонятным, достаточно изменить параметры в коде и посмотреть на изменения, чтобы потом сделать вывод о его работе.
P.S.:
Не забудьте инициализировать компонент SimpleOpenGLControl в конструкторе формы, аналогично тому, как это делалось в предыдущих уроках:
/*http://esate.ru, Anvi*/


public Form1() 
{ 
  InitializeComponent(); 
  AnT.InitializeContexts();
} 

Прикрепленные файлы для скачивания:
Добавить комментарий
Расширенный режим добавления комментариев доступен на форуме: загрузка изображений, цитирование, форматирование текста, и т.д.
Ваше имя:
Текст сообщения:
Комментарии (16):
afonya
afonya,  
Уважаемый автор! На данном примере как сместить центр координат в (-14:-14)? Никак не могу разобраться!
Anvi
Anvi,  
Цитата
afonya пишет:
Уважаемый автор! На данном примере как сместить центр координат в (-14:-14)? Никак не могу разобраться!
Привет.

Весь проект сделан так, что центр смещен в середину экрана. Вам нужно скорректировать все координаты: начального сдвига отрисовки сцены, координатных осей, стрелок к ним и прочему.
Т.е. начиная с:

Код
// выполняем перемещение в пространстве по осям X и Y 
Gl.glTranslated(1, 1, 0);
 
вместо glTranslated(15,15,0), как было изначально.
затем стеку точек отрисовать надо с учетом того, что сместились всего на 1 по каждой оси:

Код
// с помощью прохода двумя циклами, создаем сетку из точек 
            for (int ax = -1; ax < 29; ax++)
            {
                for (int bx = -1; bx < 29; bx++)
                {
                    // вывод точки 
                    Gl.glVertex2d(ax, bx);
                }
            }
 
Координатные оси, подписи к ним, элементы стрелок
Код
// далее мы рисуем координатные оси и стрелки на их концах 
            Gl.glVertex2d(0, -11);
            Gl.glVertex2d(0, 29);

            Gl.glVertex2d(-1, 0);
            Gl.glVertex2d(29, 0);

            // вертикальная стрелка 
            Gl.glVertex2d(0, 29);
            Gl.glVertex2d(0.1, 28.5);
            Gl.glVertex2d(0, 29);
            Gl.glVertex2d(-0.1, 28.5);

            // горизонтальная трелка 
            Gl.glVertex2d(29, 0);
            Gl.glVertex2d(28.5, 0.1);
            Gl.glVertex2d(29, 0);
            Gl.glVertex2d(28.5, -0.1);

            // завершаем режим рисования 
            Gl.glEnd();

            // выводим подписи осей "x" и "y" 
            PrintText2D(29.5f, 0, "x");
            PrintText2D(0.5f, 28.5f, "y");
 
ну и прочие мелочи:

Код
// выводим текст со значением координат возле курсора 
PrintText2D(devX * Mcoord_X + 0.2f, (float)ScreenH - devY * Mcoord_Y + 0.4f, "[ x: " + (devX * Mcoord_X - 1).ToString() + " ; y: " + ((float)ScreenH - devY * Mcoord_Y - 1).ToString() + "]");
 
Gl.glVertex2d(lineX, 1);
Gl.glVertex2d(lineX, lineY);
Gl.glVertex2d(1, lineY);
Gl.glVertex2d(lineX, lineY);
в самой функции рисования графика не забудьте поправить координаты. на выходе получаем что-то вроде:
Anvi
Anvi,  
Прикрепил оригинальный код урока к статье 8)
salta_87@list.ru
salta_87@list.ru,  
Здравствуйте, на одной системе координат как можно построить три графика функции точно так же как на вашем примере?
releyshic
releyshic,  
У меня при помещении в дочернее окно MDI какая то непонятная вещь - когда запускаешь вторую вкладку, берешь её за рамку для перетаскивания на 1 окошке рисуются каляки от окошка при перемещении
не пойму как с этим бороться?


Код
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

// для работы с библиотекой OpenGL 
using Tao.OpenGl;
// для работы с библиотекой FreeGLUT 
using Tao.FreeGlut;
// для работы с элементом управления SimpleOpenGLControl 
using Tao.Platform.Windows; 

namespace GraphRed2010
{
    public partial class MDIChildren1 : Form
    {
        private bool MDIChildren1_Active = false; //активно ли окно
      
        // размеры окна 
        double ScreenW, ScreenH;

        // отношения сторон окна визуализации 
        // для корректного перевода координат мыши в координаты, 
        // принятые в программе 

        private float devX;
        private float devY;

        // массив, который будет хранить значения x,y точек графика 
        private float[,] GrapValuesArray;
        // количество элементов в массиве 
        private int elements_count = 0;

        // флаг, означающий, что массив с значениями координат графика пока еще не заполнен 
        private bool not_calculate = true;

        // номер ячейки массива, из которой будут взяты координаты для красной точки 
        // для визуализации текущего кадра 
        private int pointPosition = 0;

        // вспомогательные переменные для построения линий от курсора мыши к координатным осям 
        float lineX, lineY;

        // текущение координаты курсора мыши 
        float Mcoord_X = 0, Mcoord_Y = 0; 

        public MDIChildren1()
        {
            InitializeComponent();
            AnT.InitializeContexts();        
        }

        private void MDIChildren1_Load(object sender, EventArgs e)
        {
            // инициализация библиотеки glut 
           /* Glut.glutInit();
            // инициализация режима экрана 
            Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE);*/

            // установка цвета очистки экрана (RGBA) 
            Gl.glClearColor(255, 255, 255, 1);

            // установка порта вывода 
            Gl.glViewport(0, 0, AnT.Width, AnT.Height);

            // активация проекционной матрицы 
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            // очистка матрицы 
            Gl.glLoadIdentity();

            // определение параметров настройки проекции в зависимости от размеров сторон элемента AnT. 
            if ((float)AnT.Width <= (float)AnT.Height)
            {
                ScreenW = 30.0;
                ScreenH = 30.0 * (float)AnT.Height / (float)AnT.Width;
                Glu.gluOrtho2D(0.0, ScreenW, 0.0, ScreenH);
            }
            else
            {
                ScreenW = 30.0 * (float)AnT.Width / (float)AnT.Height;
                ScreenH = 30.0;
                Glu.gluOrtho2D(0.0, 30.0 * (float)AnT.Width / (float)AnT.Height, 0.0, 30.0);
            }

            // сохранение коэффициентов, которые нам необходимы для перевода координат указателя в оконной системе в координаты, 
            // принятые в нашей OpenGL сцене 
            devX = (float)ScreenW / (float)AnT.Width;
            devY = (float)ScreenH / (float)AnT.Height;

            // установка объектно-видовой матрицы 
            Gl.glMatrixMode(Gl.GL_MODELVIEW);

            // старт счетчика, отвечающего за вызов функции визуализации сцены 
            /*PointInGrap.Start();
            */
            // функция визуализации 
            Draw();
            // сигнал для обновление элемента реализующего визуализацию. 
            AnT.Invalidate();

            MDIChildren1_Active = true; //окно активно
        }

        private void MDIChildren1_Resize(object sender, EventArgs e)
        {
            //this.Text = "4356436"; 
            // сигнал для обновление элемента реализующего визуализацию. 
            AnT.Invalidate();
        }

        private void PointInGrap_Tick(object sender, EventArgs e)
        {
            // если мы дошли до последнего элемента массива 
            if (pointPosition == elements_count - 1)
                pointPosition = 0; // переходим к начальному элементу 

            /*// функция визуализации 
            Draw();*/
            DrawDiagram();
            // переход к следующему элементу массива 
            pointPosition++;
        }
        
        private void AnT_MouseMove(object sender, MouseEventArgs e)
        {
            // сохраняем координаты мыши 
            Mcoord_X = e.X;
            Mcoord_Y = e.Y;

            // вычисляем параметры для будущей дорисовки линий от указателя мыши к координатным осям. 
            lineX = devX * e.X;
            lineY = (float)(ScreenH - devY * e.Y);

            if (MDIChildren1_Active == true)
            {
                // функция визуализации 
                Draw();
                /*// сигнал для обновление элемента реализующего визуализацию. 
                AnT.Invalidate();*/
            }
        }

        // функция визуализации текста 
        private void PrintText2D(float x, float y, string text)
        {

            // устанавливаем позицию вывода растровых символов 
            // в переданных координатах x и y. 
            Gl.glRasterPos2f(x, y);

            // в цикле foreach перебираем значения из массива text, 
            // который содержит значение строки для визуализации 
            foreach (char char_for_draw in text)
            {
                // символ C визуализируем с помощью функции glutBitmapCharacter, используя шрифт GLUT_BITMAP_9_BY_15. 
                Glut.glutBitmapCharacter(Glut.GLUT_BITMAP_9_BY_15, char_for_draw);
            }

        }

        // функция, производящая вычисления координат графика 
        // и заносящая их в массив GrapValuesArray 
        private void functionCalculation()
        {

            // определение локальных переменных X и Y 
            float x = 0, y = 0;

            // инициализация массива, который будет хранить значение 300 точек, 
            // из которых будет состоять график 

            GrapValuesArray = new float[300, 2];

            // счетчик элементов массива 
            elements_count = 0;

            // вычисления всех значений y для x, принадлежащего промежутку от -15 до 15 с шагом в 0.01f 
            for (x = -15; x < 15; x += 0.1f)
            {
                // вычисление y для текущего x 
                // по формуле y = (float)Math.Sin(x)*3 + 1; 
                // эта строка задает формулу, описывающую график функции для нашего уравнения y = f(x). 
                y = (float)Math.Sin(x) * 3 + 1;

                // запись координаты x 
                GrapValuesArray[elements_count, 0] = x;
                // запись координаты y 
                GrapValuesArray[elements_count, 1] = y;
                // подсчет элементов 
                elements_count++;

            }

            // изменяем флаг, сигнализировавший о том, что координаты графика не вычислены 
            not_calculate = false;

        }

        // визуализация графика 
        private void DrawDiagram()
        {

            // проверка флага, сигнализирующего о том, что координаты графика вычислены 
            if (not_calculate)
            {
                // если нет, то вызываем функцию вычисления координат графика 
                functionCalculation();
            }

            // стартуем отрисовку в режиме визуализации точек 
            // объединяемых в линии (GL_LINE_STRIP) 
            Gl.glBegin(Gl.GL_LINE_STRIP);

            // рисуем начальную точку 
            Gl.glVertex2d(GrapValuesArray[0, 0], GrapValuesArray[0, 1]);

            // проходим по массиву с координатами вычисленных точек 
            for (int ax = 1; ax < elements_count; ax += 2)
            {
                // передаем в OpenGL информацию о вершине, участвующей в построении линий 
                Gl.glVertex2d(GrapValuesArray[ax, 0], GrapValuesArray[ax, 1]);
            }

            // завершаем режим рисования 
            Gl.glEnd();

            // устанавливаем размер точек, равный 5 пикселям 
            Gl.glPointSize(5);
            // устанавливаем текущим цветом - красный цвет 
            Gl.glColor3f(255, 0, 0);
            // активируем режим вывода точек (GL_POINTS) 
            Gl.glBegin(Gl.GL_POINTS);
            // выводим красную точку, используя ту ячейку массива, до которой мы дошли (вычисляется в функции обработчике событий таймера) 
            Gl.glVertex2d(GrapValuesArray[pointPosition, 0], GrapValuesArray[pointPosition, 1]);
            // завершаем режим рисования 
            Gl.glEnd();
            // устанавливаем размер точек равный единице 
            Gl.glPointSize(1);

            // возвращаем матрицу из стека 
            Gl.glPopMatrix();

            // дожидаемся завершения визуализации кадра 
            Gl.glFlush();

            // сигнал для обновление элемента реализующего визуализацию. 
            AnT.Invalidate();
        }

        // функция, управляющая визуализацией сцены 
        private void Draw()
        {

            // очистка буфера цвета и буфера глубины 
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

            // очищение текущей матрицы 
            Gl.glLoadIdentity();

            // установка черного цвета 
            Gl.glColor3f(0, 0, 0);

            // помещаем состояние матрицы в стек матриц 
            Gl.glPushMatrix();

            // выполняем перемещение в пространстве по осям X и Y 
            Gl.glTranslated(15, 15, 0);

            // активируем режим рисования (Указанные далее точки будут выводиться как точки GL_POINTS) 
            Gl.glBegin(Gl.GL_POINTS);

            // с помощью прохода вдумя циклами, создаем сетку из точек 
            for (int ax = -15; ax < 15; ax++)
            {
                for (int bx = -15; bx < 15; bx++)
                {
                    // вывод точки 
                    Gl.glVertex2d(ax, bx);
                }
            }

            // завершение режима рисования примитивов 
            Gl.glEnd();

            // активируем режим рисования, каждые 2 последовательно вызванные команды glVertex 
            // объединяются в линии 
            Gl.glBegin(Gl.GL_LINES);

            // далее мы рисуем координатные оси и стрелки на их концах 
            Gl.glVertex2d(0, -15);
            Gl.glVertex2d(0, 15);

            Gl.glVertex2d(-15, 0);
            Gl.glVertex2d(15, 0);

            // вертикальная стрелка 
            Gl.glVertex2d(0, 15);
            Gl.glVertex2d(0.1, 14.5);
            Gl.glVertex2d(0, 15);
            Gl.glVertex2d(-0.1, 14.5);

            // горизонтальная трелка 
            Gl.glVertex2d(15, 0);
            Gl.glVertex2d(14.5, 0.1);
            Gl.glVertex2d(15, 0);
            Gl.glVertex2d(14.5, -0.1);

            // завершаем режим рисования 
            Gl.glEnd();

            // выводим подписи осей "x" и "y" 
            PrintText2D(15.5f, 0, "x");
            PrintText2D(0.5f, 14.5f, "y");

            // вызываем функцию рисования графика 
            DrawDiagram();

            // возвращаем матрицу из стека 
            Gl.glPopMatrix();

            // выводим текст со значением координат возле курсора 
            PrintText2D(devX * Mcoord_X + 0.2f, (float)ScreenH - devY * Mcoord_Y + 0.4f, "[ x: " + (devX * Mcoord_X - 15).ToString() + " ; y: " + ((float)ScreenH - devY * Mcoord_Y - 15).ToString() + "]");
            
            // устанавливаем красный цвет 
            Gl.glColor3f(255, 0, 0);

            // включаем режим рисования линий, для того чтобы нарисовать 
            // линии от курсора мыши к координатным осям 
            Gl.glBegin(Gl.GL_LINES);

            Gl.glVertex2d(lineX, 15);
            Gl.glVertex2d(lineX, lineY);
            Gl.glVertex2d(15, lineY);
            Gl.glVertex2d(lineX, lineY);

            Gl.glEnd();

            // дожидаемся завершения визуализации кадра 
            Gl.glFlush();

            // сигнал для обновление элемента реализующего визуализацию. 
            AnT.Invalidate();
        }

        private void MDIChildren1_Deactivate(object sender, EventArgs e)
        {
            MDIChildren1_Active = false; //окно неактивно при деактивации
        }

        private void MDIChildren1_Activated(object sender, EventArgs e)
        {
            MDIChildren1_Active = true; //окно активно при активации формы
        }
    }
}
 
Anvi
Anvi,  
Цитата
У меня при помещении в дочернее окно MDI какая то непонятная вещь - когда запускаешь вторую вкладку, берешь её за рамку для перетаскивания на 1 окошке рисуются каляки от окошка при перемещении
не пойму как с этим бороться?
Да, с ходу не знаю что предположить.

Артефакты на неактивном окне, видимо от края активного угла. Мне кажется это особенности визуализации окон наложившиеся на компонент визуализации TAO.

А если перерисовывать 1 раз (даже без вычислений) неактивное окно по окончанию перемещения активного? Возможно это единственный вариант.
releyshic
releyshic,  
Цитата
Anvi пишет:
А если перерисовывать 1 раз (даже без вычислений) неактивное окно по окончанию перемещения активного? Возможно это единственный вариант.
не совсем понял чем это поможет ведь рисуется крокозябры в процессе движения окна а не "по окончанию"

хорошим вариантом было бы останавливать любую отрисовку на окне при пропадании активности на нем но как это сделать? да еще и чтоб не пропадало последнее нарисованное?
есть ли в АнТе какаянибудь функция "остановки" ? ))
"// завершаем режим рисования Gl.glEnd();" тут не поможет?
releyshic
releyshic,  
Цитата
releyshic пишет:
хорошим вариантом было бы останавливать любую отрисовку на окне при пропадании активности на нем но как это сделать? да еще и чтоб не пропадало последнее нарисованное?
есть ли в АнТе какаянибудь функция "остановки" ? ))
"// завершаем режим рисованияGl.glEnd();" тут не поможет?
а может получится проверять в условном операторе не активно ли какоето еще окно при текущей отрисовке здесь помимо
Код
MDIChildren1_Active == true

Код
 private void AnT_MouseMove(object sender, MouseEventArgs e)
     {
      // сохраняем координаты мыши 
      Mcoord_X = e.X;
      Mcoord_Y = e.Y;
      // вычисляем параметры для будущей дорисовки линий от указателя мыши к координатным осям. 
      lineX = devX * e.X;
      lineY = (float)(ScreenH - devY * e.Y);

      if (MDIChildren1_Active == true)
      {
          // функция визуализации 
          Draw();
          /*// сигнал для обновление элемента реализующего визуализацию. 
          AnT.Invalidate();*/
      }
     }
но вот как проверить на активность все открытые дочерние окна ?
Anvi
Anvi,  
Нужно смотреть весь проект и экспериментировать.

Мне кажется что артефакты можно убрать только перерисовкой окна, на котором они появились, и что они не связаны с тем, как реализован рендер в приложении.
Но это все надо опытным путем делать.
releyshic
releyshic,  
Цитата
Anvi пишет:
ужно смотреть весь проект и экспериментировать.

Мне кажется что артефакты можно убрать только перерисовкой окна, на котором они появились, и что они не связаны с тем, как реализован рендер в приложении.
Но это все надо опытным путем делать.
ниужели надо всегда всё перерисовывать полностью? и что никак нельзя например сделать постоянный фон (кроме картинки конечно)? или есть слои какието? можно ли как то сохранить текущий вид во чтото (для быстрого использования конечно)?
Anvi
Anvi,  
Цитата
releyshic написал:
ниужели надо всегда всё перерисовывать полностью? и что никак нельзя например сделать постоянный фон (кроме картинки конечно)? или есть слои какието? можно ли как то сохранить текущий вид во чтото (для быстрого использования конечно)?
я уже написал выше: мне кажется, что дело в особенностях наложений окон и их перерисовки. Судя по скриншотам на не активном окне артефакты.
Значит как минимум нужно проверить, будет ли повторяться проблема на компьютерах с другими видеокартами (точно помню, что как-то мне попадались скриншоты с сильными артефактами на окне с визуализацией, и проблема была в видеокарте и драйверах ноутбука).

Ну а дальше да, сначала проверить - поможет ли вообще перерисовка неактивного окна от этих артефактов.

А потом уже думать над тем, как выходить из ситуации - либо перерисовка от доп. события, либо еще какие-нибудь варианты.

Обратите внимание на glReadPixels / glDrawPixels, но это не самый быстрый способ.
releyshic
releyshic,  
Цитата
Anvi пишет:
Значит как минимум нужно проверить, будет ли повторяться проблема на компьютерах с другими видеокартами (точно помню, что как-то мне попадались скриншоты с сильными артефактами на окне с визуализацией, и проблема была в видеокарте и драйверах ноутбука).

Ну а дальше да, сначала проверить - поможет ли вообще перерисовка неактивного окна от этих артефактов.

А потом уже думать над тем, как выходить из ситуации - либо перерисовка от доп. события, либо еще какие-нибудь варианты.

Обратите внимание на glReadPixels / glDrawPixels, но это не самый быстрый способ.
Пока отложил эти Артефакты, есть другая проблема более насущная, не могу никак понять почему так?

Запускаешь Одно Окошко MDI, затем второе и Когда по нему водишь мышкой (по 2му, активному), то Рисуется в 1м окне почему-то, а когда выходишь мышкой за пределы окна но над 1м окном (не активным) то рисуется в нём правильно. Когда переключаешьня на 1е окно то всё тоже самое, только наоборот ))
Если добавлять дальше окошки то Рисуется опять в предыдущем а не в активном, хотя Координаты Считываются правильно По активному окну
Вот проект https://cloud.mail.ru/public/2nDr/xeZoYKRbj
Думал думал - ничего не понимаю ))
releyshic
releyshic,  
Цитата
releyshic пишет:
Цитата
Anvi пишет:
Значит как минимум нужно проверить, будет ли повторяться проблема на компьютерах с другими видеокартами (точно помню, что как-то мне попадались скриншоты с сильными артефактами на окне с визуализацией, и проблема была в видеокарте и драйверах ноутбука).

Ну а дальше да, сначала проверить - поможет ли вообще перерисовка неактивного окна от этих артефактов.

А потом уже думать над тем, как выходить из ситуации - либо перерисовка от доп. события, либо еще какие-нибудь варианты.

Обратите внимание на glReadPixels / glDrawPixels, но это не самый быстрый способ.
Пока отложил эти Артефакты, есть другая проблема более насущная, не могу никак понять почему так?

Запускаешь Одно Окошко MDI, затем второе и Когда по нему водишь мышкой (по 2му, активному), то Рисуется в 1м окне почему-то, а когда выходишь мышкой за пределы окна но над 1м окном (не активным) то рисуется в нём правильно. Когда переключаешьня на 1е окно то всё тоже самое, только наоборот ))
Если добавлять дальше окошки то Рисуется опять в предыдущем а не в активном, хотя Координаты Считываются правильно По активному окну
Вот проект https://cloud.mail.ru/public/2nDr/xeZoYKRbj
Думал думал - ничего не понимаю ))
всё исправил сам: уюрал из Таймера ф-цию Draw(); и засунул в Событие AnT_MouseMove.

1 А где посмотреть Список всех Свойств и Методов AnT-а ?
2 Как остановить Отрисовку AnT-а ? то есть чтобы в неактивном окне ничего не рисовалось, а замирало как есть?
Anvi
Anvi,  
Цитата
releyshic написал:
А где посмотреть Список всех Свойств и Методов AnT-а
C:\Program Files (x86)\TaoFramework\doc\Tao.Platform.Windows.chm -> описание класса SimpleOpenGlControl

Цитата
releyshic написал:
Как остановить Отрисовку AnT-а ? то есть чтобы в неактивном окне ничего не рисовалось, а замирало как есть?
Код
if(!bStop)
Draw(); // функция, отвечающая за отрисовку сцены 

ну и bStop меняется, к примеру, по событию нажатия на кнопку
releyshic
releyshic,  
Цитата
Anvi написал:
Цитата
releyshic написал:
Как остановить Отрисовку AnT-а ? то есть чтобы в неактивном окне ничего не рисовалось, а замирало как есть?
Код
 if(!bStop)
Draw(); // функция, отвечающая за отрисовку сцены  

ну и bStop меняется, к примеру, по событию нажатия на кнопку
bStop это какого Объекта?, Пробовал Glut, Gl, AnT, this он у меня подчеркивается как ошибка
Anvi
Anvi,  
Цитата
bStop это какого Объекта
это просто собственная переменная, нужно ее объявить.
по умолчанию она true и сцена отрисовывается. Если стала false - новое рисование сцены останавливается и визуализирован только последний кадр

p.s. это просто пример того, что визуализацию сцены нужно вызывать по условию, которое отрабатывает по вашим правилам. вместо bStop может быть результат работы функции, которая проверяет все условия - рисуем сцену сейчас или нет.
^