Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: 3D экран  (Прочитано 18356 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Serguntii
Помогающий

ru
Offline Offline

« : 10-11-2013 15:27 » 

Подскажите ,любые советы может где что прочитать, или может готовые примеры есть. Задачка такая нужно в vs C# сделать форму с темным окном 3D, внизу сетку.
Из библиотеки функция постоянно возвращает координаты xyz нужно нарисовать точку и оставлять за ней след как она двигается.
Еще бы конечно хотелось что бы можно было подвигать мышем для того что бы посмотреть с разных сторон. окна хватило бы 800*600.
Насколько это реально, сложно?
« Последнее редактирование: 10-11-2013 15:31 от sergeyan » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 10-11-2013 17:52 » 

Дак дело типовое даже вручную сделать.

1) Выбираешь мировую систему координат (в основном это максимумы и минимумы значений координат).

2) Выбираешь способ проецирования 3D на 2D и заводишь себе направления зрения как вектор.

3) Записываешь довольно элементарную функцию, которая отображает 3D точку в мировой системе координат на 2D точку окна.

4) Описываешь в мировой системе координат сетку.

5) Заводишь список исторических значений главной (движущейся) точки. И по мере добавления в список нового значения прорисовываешь весь хвост.

6) Обрабатываешь таскания мыши по вертикали и горизонтали как повороты вектора направления зрения вокруг, соответственно, вертикальной и горизонтальной осей. Обычно ещё добавляют колёсико для изменения масштаба (приблизить/отдалить).

7) По любому событию (появление новой точки или действия пользователя) перерисовываешь картинку.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #2 : 10-11-2013 17:59 » 

а экран объект это opengl?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 10-11-2013 18:20 » 

sergeyan, зачем? обычное окно.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #4 : 10-11-2013 18:51 » 

Буду думать и искать примеры.   Мало чего понял из того что выше Здесь была моя ладья...
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 10-11-2013 21:50 » 

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

Код: (C#)
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Graph3D
{
        // Основная программа:
        class Program
        {
                static void Main(string[] args)
                {
                        // Делает лишь то, что запускает цикл приёма-обработки сообщений Windows
                        // для единственного окна, которое привязано к модели.
                        Application.Run(new Window());
                }
        }

        // Окно
        class Window : Form
        {
                // Модель.
                private Model model;
                // 2D представление.
                private View view;
                // Контроллер ввода пользователя.
                private Controller controller;

                // Средства рисования
                Brush background; // кисть для фона
                Pen foreground;   // перо для линий

                // Масштабирующий коэффициент, который любой размер окна приводит к нормальному диапазону 0..1.
                // Позволяет абстрагировать изображение от размеров окна
                private float NormaScale
                {
                        get
                        {
                                return 1.0f / Math.Min(this.ClientSize.Width, this.ClientSize.Height);
                        }
                }

                // Инициализация окна.
                public Window()
                {
                        // Заголовок
                        this.Text = "3D graphics";
                        // Создаём части и связываем их между собой
                        this.model = new Model();
                        this.view = new View(this.model);
                        this.controller = new Controller(this.view);
                        // Включаем двойную буферизацию рисования, чтобы изображение не "мерцало".
                        this.DoubleBuffered = true;
                        // Создаём средства рисования.
                        this.background = new SolidBrush(Color.Black); // чёрная сплошная кисть
                        this.foreground = new Pen(Color.White); // белое перо нормальной толщины
                        // Обрабатываем событие изменения 2D представления.
                        this.view.Updated += new EventHandler(this.View_Updated);
                }

                // Перехват нужных событий от Windows

                // Отрисовка
                protected override void OnPaint(PaintEventArgs args)
                {
                        base.OnPaint(args);
                        // Очищаем фон.
                        args.Graphics.FillRectangle(this.background, new Rectangle(new Point(0, 0), this.ClientSize));
                        // Вспомогательные коэффициенты
                        float scale = this.NormaScale; // Масштаб. Избегаем двойного расчёта.
                        // Сдвиги для неквадратного окна, чтобы изображение оказалось в центре.
                        float shift = (this.ClientSize.Width - this.ClientSize.Height) / 2.0f;
                        float shiftX = shift > 0.0f ? shift : 0.0f;
                        float shiftY = shift < 0.0f ? -shift : 0.0f;
                        // Рисуем все линии, которые генерирует 2D представление
                        foreach(List<PointF> line in this.view.Lines)
                        {
                                // Каждую полученную точку приводим из нормальных в координаты окна:
                                // центр из середины перемещаем в левый верхний угол, разворачиваем ось Y,
                                // чтобы она была направлена вниз, центрируем изображение.
                                PointF previousPoint = line[0];
                                previousPoint.X = (previousPoint.X + 0.5f) / scale + shiftX;
                                previousPoint.Y = (0.5f - previousPoint.Y) / scale + shiftY;
                                for(int i = 1; i < line.Count; ++i)
                                {
                                        PointF nextPoint = line[i];
                                        nextPoint.X = (nextPoint.X + 0.5f) / scale + shiftX;
                                        nextPoint.Y = (0.5f - nextPoint.Y) / scale + shiftY;
                                        args.Graphics.DrawLine(this.foreground, previousPoint, nextPoint);
                                        previousPoint = nextPoint;
                                }
                        }
                }

                // Движения мыши
                protected override void OnMouseMove(MouseEventArgs args)
                {
                        base.OnMouseMove(args);
                        float scale = this.NormaScale; // Избегаем двойного расчёта.
                        // Передаём информацию в контроллер.
                        this.controller.Input(
                                // Нажата ли кнопка мыши
                                args.Button == MouseButtons.Left,
                                // Приведённые координаты, сдвинутые таким образом,
                                // чтобы центр был в середине, а ось Y ориентирована вверх
                                new PointF(args.X * scale - 0.5f, 0.5f - args.Y * scale)
                        );
                }

                // Изменение размеров окна
                protected override void OnResize(EventArgs args)
                {
                        // При изменении размеров окна требуем от Windows перерисовки.
                        this.Invalidate();
                }

                // Обработка внутренних событий

                // Изменения в 2D представлении.
                private void View_Updated(object sender, EventArgs args)
                {
                        // При изменении 2D представления требуем от Windows перерисовки.
                        this.Invalidate();
                }
               
        }

        // Контроллер ввода пользователя
        class Controller
        {
                // Предыдущие координаты
                private PointF? previousCoordinates;
                // 2D представление, на которое влияет ввод.
                private View view;

                // Инициализация
                public Controller(View view)
                {
                        this.previousCoordinates = null;
                        this.view = view;
                }

                // Обработка входных данных от пользователя.
                public void Input(bool pressed, PointF coordinates)
                {
                        // Пока есть нажатие, любое изменение координат рассматривается как угол полукруга,
                        // на который нужно изменить углы поворота в ту или иную сторону.
                        // Если нет нажатия, углы не меняются.
                        if(!pressed)
                        {
                                this.previousCoordinates = null;
                        }
                        else
                        {
                                if(this.previousCoordinates.HasValue)
                                {
                                        this.view.Rotate(
                                                Convert.ToSingle((coordinates.X - this.previousCoordinates.Value.X) * Math.PI),
                                                Convert.ToSingle((coordinates.Y - this.previousCoordinates.Value.Y) * Math.PI)
                                        );
                                }
                                this.previousCoordinates = coordinates;
                        }
                }
        }


        // 2D представление
        class View
        {
                // Линии для вывода
                private List<List<PointF>> lines;
                // Углы ракурса обзора.
                private float horizontalAngle;
                private float verticalAngle;
                // 3D модель
                private Model model;

                // Инициализация
                public View(Model model)
                {
                        this.model = model;
                        this.model.Updated += new EventHandler(this.Model_Updated);
                        this.lines = new List<List<PointF>>();
                        this.horizontalAngle = 0.0f;
                        this.verticalAngle = 0.0f;
                        this.Update();
                }

                // Линии для вывода
                public List<List<PointF>> Lines
                {
                        get
                        {
                                return this.lines;
                        }
                }

                // Вращение ракурса обзора.
                public void Rotate(float horizontalRotate, float verticalRotate)
                {
                        // Меняем углы и устраняем многократное закручивание.
                        float pi2 = Convert.ToSingle(Math.PI * 2.0);
                        this.horizontalAngle += horizontalRotate;
                        if(this.horizontalAngle > pi2)
                        {
                                this.horizontalAngle -= pi2;
                        }
                        if(this.horizontalAngle < pi2)
                        {
                                this.horizontalAngle += pi2;
                        }
                        this.verticalAngle += verticalRotate;
                        if(this.verticalAngle > pi2)
                        {
                                this.verticalAngle -= pi2;
                        }
                        if(this.verticalAngle < pi2)
                        {
                                this.verticalAngle += pi2;
                        }
                        this.Update();
                }

                // Событие обновления
                public event EventHandler Updated;
                protected void OnUpdated()
                {
                        if(this.Updated != null)
                        {
                                this.Updated(this, new EventArgs());
                        }
                }

                // Отображение 3D модели в 2D представление.
                private void Update()
                {
                        this.lines.Clear();
                        // Поворачиваем исходное 3D пространство на углы,
                        // под которыми мы будем его рассматривать.
                        // Отступаем по Z назад и смотрим на результат как на плоскость XY в перспективе.
                        // Преобразованиям подвергается каждая точка.
                        // Вспомогательные коэффициенты
                        float sinV = Convert.ToSingle(Math.Sin(this.verticalAngle));
                        float sinH = Convert.ToSingle(Math.Sin(this.horizontalAngle));
                        float cosV = Convert.ToSingle(Math.Cos(this.verticalAngle));
                        float cosH = Convert.ToSingle(Math.Cos(this.horizontalAngle));
                        foreach(List<float[]> line3D in this.model.Lines)
                        {
                                List<PointF> line2D = new List<PointF>();
                                foreach(float[] point3D in line3D)
                                {
                                        // Исходная точка
                                        float x0 = point3D[0];
                                        float y0 = point3D[1];
                                        float z0 = point3D[2];
                                        // Вертикальный поворот вокруг оси X исходной точки - промежуточная точка 1
                                        float x1 = x0;
                                        float y1 = y0 * cosV - z0 * sinV;
                                        float z1 = y0 * sinV + z0 * cosV;
                                        // Горизонтальный поворот вокруг оси Y промежуточной точки 1 - промежуточная точка 2
                                        float x2 = x1 * cosH + z1 * sinH;
                                        float y2 = y1;
                                        float z2 = z1 * cosH - x1 * sinH;
                                        // Отступ по Z назад и добавление перспективы
                                        float p = z2 + 2; // отступаем с запасом, чтобы p не обратилось в 0.
                                        // Итоговая точка
                                        PointF point2D = new PointF(x2 / p, y2 / p); // по-хорошему нужно придумать обработку для p = 0
                                        line2D.Add(point2D);
                                }
                                this.lines.Add(line2D);
                        }
                        this.OnUpdated();
                }

                // Обработка события изменение модели
                private void Model_Updated(object sender, EventArgs args)
                {
                        this.Update();
                        this.OnUpdated();
                }
        }

        // 3D модель
        class Model
        {
                // Линии координатной сетки
                private List<List<float[]>> grid;
                // Линия графика
                private List<float[]> graph;
                // Таймер, по событию которого в график добавляется новая точка.
                private Timer timer;

                // Инициализация
                public Model()
                {
                        // Создаём линии сетки
                        this.grid = new List<List<float[]>>();
                        for(float t = -0.5f; t <= 0.5f; t += 0.1f)
                        {
                                List<float[]> horizontal = new List<float[]>();
                                horizontal.Add(new float[] { -0.5f, t, 0.0f });
                                horizontal.Add(new float[] { 0.5f, t, 0.0f });
                                this.grid.Add(horizontal);
                                List<float[]> vertical = new List<float[]>();
                                vertical.Add(new float[] { t, -0.5f, 0.0f });
                                vertical.Add(new float[] { t, 0.5f, 0.0f });
                                this.grid.Add(vertical);
                        }
                        this.graph = new List<float[]>();
                        // Создаём начальную точку графика
                        this.graph.Add(new float[] { 0.0f, 0.0f, 0.0f });
                        // Настраиваем таймер
                        this.timer = new Timer();
                        this.timer.Interval = 300; // обновление 1 раз в 300 мс
                        this.timer.Tick += new EventHandler(this.Timer_Tick);
                        this.timer.Start();
                }

                // Все линии в пространстве
                public List<List<float[]>> Lines
                {
                        get
                        {
                                List<List<float[]>> lines = new List<List<float[]>>(this.grid);
                                lines.Add(this.graph);
                                return lines;
                        }
                }

                // Событие обновления
                public event EventHandler Updated;
                protected void OnUpdated()
                {
                        if(this.Updated != null)
                        {
                                this.Updated(this, new EventArgs());
                        }
                }

                // Обработка события таймера
                private void Timer_Tick(object sender, EventArgs args)
                {
                        // Чтобы не накапливались события, если обработка их замедленна,
                        // отключаем таймер на время обработки и включаем после неё.
                        this.timer.Stop();
                        // Добавляем в график следующую точку.
                        // График растёт по восходящей расширяющейся спирали.
                        int steps = this.graph.Count; // номер следующего шага графика.
                        float spiralAngle = Convert.ToSingle(Math.PI * 2.0 / 360.0 * steps);
                        float spiralRadius = Convert.ToSingle(0.005 * steps);
                        float x = Convert.ToSingle(spiralRadius * Math.Cos(spiralAngle));
                        float y = Convert.ToSingle(spiralRadius * Math.Sin(spiralAngle));
                        float z = spiralRadius / 3f;
                        this.graph.Add(new float[] { x, y, z });
                        this.OnUpdated();
                        this.timer.Start();
                }
        }
}

Всякие украшательства, как-то разные цвета линий, подписи, развороты в красивых ракурсах, масштабирование - это уже своими силами. Тут лишь основная идея показана: как уровень за уровнем разложить решение этой задачи на достаточно простые части с ограниченной ответственностью. За основу взята архитектура MVC, хотя Windows Forms под неё и не приспособлены.
« Последнее редактирование: 10-11-2013 21:59 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #6 : 11-11-2013 09:03 » 

Сейчас попробую,  если получиться сделать так будет просто великолепно.)

Добавлено через 8 часов, 46 минут и 47 секунд:
То что нужно, думал будет тормозить но работает даже на нетбуке. спасибо вам большое.
Многим этот пример будет полезен. Улыбаюсь

Добавлено через 19 дней, 22 часа, 44 минуты и 18 секунд:
Как можно сотворить такое, сейчас экран от -0,5 до 0,5   приделы xyz меняются, после того как будут вычислены xyz пределы, как подогнать размер что бы картинка которая будет прорисовываться занимала 80% экрана окна?
Спасибо за помощь.
« Последнее редактирование: 01-12-2013 16:34 от sergeyan » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 01-12-2013 16:37 » 

sergeyan, Form - это наследник Control, если ты обращал внимание на иерархию. Это можно делать в любом Control, но не в любом Control есть, допустим, двойная буферизация вывода. Так что самое разумное - это сделать собственный UserControl, который можно класть куда угодно: хоть в окна, хоть во вкладки, хоть в панели.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #8 : 01-12-2013 16:49 » 

Сообразил уже  ) сделал наследование от usercontrol, теперь другой вопрос нужно как-то менять размеры что бы картинка помещалась  в окно.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 01-12-2013 17:15 » 

sergeyan, а кто мешает? В моём примере картинка масштабируется под размер окна.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #10 : 01-12-2013 17:22 » 

Думаю правильнее? будет не переделывать код который вы написали , а подогнать точки что бы были в пределе от -0,5 до 0,5.
Опишу алгоритм примерно: 1000 раз происходит событие в нем приходят аргументы xyz, это точки они могут быть как отрицательные так и положительные , В этом же событии нужно вычислить переменную  scal . Если есть простой способ вычислить scal от xyz подскажите . Конечно придумаю просто может это уже есть в net что бы не придумывать велосипед?

Добавлено через 1 час, 18 минут и 25 секунд:
сделал через список.
« Последнее редактирование: 01-12-2013 18:41 от sergeyan » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 01-12-2013 18:55 » 

sergeyan, ну да. По списку найти min и max, затем разницу max-min нормализовать (вогнать в интервал 0...1) - т.е. подобрать коэффициент масштабирования. И домножать на него абсолютно все точки - всё пространство смасштабируется.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #12 : 01-12-2013 20:59 » 

Что то все испортил я, хотел сделать что бы можно было цвет точки задавать получилась ошибка в строчке
Цитата
grid[0].Add(horizontal[0]);
(
Код:
    public class WindowGraph : UserControl
    {
        // Модель.
        private Model model;
        // 2D представление.
        private View view;
        // Контроллер ввода пользователя.
        private Controller controller;

        // Средства рисования
        Brush background; // кисть для фона
        Pen   foreground;   // перо для линий

        // Масштабирующий коэффициент, который любой размер окна приводит к нормальному диапазону 0..1.
        // Позволяет абстрагировать изображение от размеров окна
        private float NormaScale
        {
            get
            {
                return 1f / Math.Min(this.ClientSize.Width, this.ClientSize.Height);
            }
        }

        // Инициализация окна.
        public WindowGraph()
        {
            // Заголовок
            this.Text = "3D graphics";
            // Создаём части и связываем их между собой
            this.model = new Model();
            this.view = new View(this.model);
            this.controller = new Controller(this.view);
            // Включаем двойную буферизацию рисования, чтобы изображение не "мерцало".
            this.DoubleBuffered = true;
            // Создаём средства рисования.
            this.background = new SolidBrush(Color.Black); // чёрная сплошная кисть
            this.foreground = new Pen(Color.White); // белое перо нормальной толщины
            // Обрабатываем событие изменения 2D представления.
            this.view.Updated += new EventHandler(this.View_Updated);
        }

        // Перехват нужных событий от Windows

        // Отрисовка
        protected override void OnPaint(PaintEventArgs args)
        {
            base.OnPaint(args);
            // Очищаем фон.
            args.Graphics.FillRectangle(this.background, new Rectangle(new Point(0, 0), this.ClientSize));
            // Вспомогательные коэффициенты
            float scale = this.NormaScale; // Масштаб. Избегаем двойного расчёта.
            // Сдвиги для неквадратного окна, чтобы изображение оказалось в центре.
            float shift = (this.ClientSize.Width - this.ClientSize.Height) / 2.0f;
            float shiftX = shift > 0.0f ? shift : 0.0f;
            float shiftY = shift < 0.0f ? -shift : 0.0f;
            // Рисуем все линии, которые генерирует 2D представление
            foreach (List<PointF> line in this.view.Lines)
            {
                // Каждую полученную точку приводим из нормальных в координаты окна:
                // центр из середины перемещаем в левый верхний угол, разворачиваем ось Y,
                // чтобы она была направлена вниз, центрируем изображение.
                PointF previousPoint = line[0];
                previousPoint.X = (previousPoint.X + 0.5f) / scale + shiftX;
                previousPoint.Y = (0.5f - previousPoint.Y) / scale + shiftY;
                for (int i = 1; i < line.Count; ++i)
                {
                    PointF nextPoint = line[i];
                    nextPoint.X = (nextPoint.X + 0.5f) / scale + shiftX;
                    nextPoint.Y = (0.5f - nextPoint.Y) / scale + shiftY;
                    args.Graphics.DrawLine(this.foreground, previousPoint, nextPoint);
                    previousPoint = nextPoint;
                }
            }
        }

        // Движения мыши
        protected override void OnMouseMove(MouseEventArgs args)
        {
            base.OnMouseMove(args);
            float scale = this.NormaScale; // Избегаем двойного расчёта.
            // Передаём информацию в контроллер.
            this.controller.Input(
                // Нажата ли кнопка мыши
                    args.Button == MouseButtons.Left,
                // Приведённые координаты, сдвинутые таким образом,
                // чтобы центр был в середине, а ось Y ориентирована вверх
                    new PointF((float)(args.X * scale - 0.5f), (float)(0.5f - args.Y * scale))
            );
        }

        // Изменение размеров окна
        protected override void OnResize(EventArgs args)
        {
            // При изменении размеров окна требуем от Windows перерисовки.
            this.Invalidate();
        }

        // Обработка внутренних событий

        // Изменения в 2D представлении.
        private void View_Updated(object sender, EventArgs args)
        {
            // При изменении 2D представления требуем от Windows перерисовки.
            this.Invalidate();
        }


        public void addpoint3d_(double x, double y, double z,Color prn)
        {           
            model.addpoint3d(x, y, z,prn);
        }

    }





        // Контроллер ввода пользователя
        class Controller
        {
            // Предыдущие координаты
            private PointF? previousCoordinates;
            // 2D представление, на которое влияет ввод.
            private View view;

            // Инициализация
            public Controller(View view)
            {
                this.previousCoordinates = null;
                this.view = view;
            }

            // Обработка входных данных от пользователя.
            public void Input(bool pressed, PointF coordinates)
            {
                // Пока есть нажатие, любое изменение координат рассматривается как угол полукруга,
                // на который нужно изменить углы поворота в ту или иную сторону.
                // Если нет нажатия, углы не меняются.
                if (!pressed)
                {
                    this.previousCoordinates = null;
                }
                else
                {
                    if (this.previousCoordinates.HasValue)
                    {
                        this.view.Rotate(
                                Convert.ToSingle((coordinates.X - this.previousCoordinates.Value.X) * Math.PI),
                                Convert.ToSingle((coordinates.Y - this.previousCoordinates.Value.Y) * Math.PI)
                        );
                    }
                    this.previousCoordinates = coordinates;
                }
            }
        }


        // 2D представление
        class View
        {
            // Линии для вывода
            private List<List<PointF>> lines;
            // Углы ракурса обзора.
            private float horizontalAngle;
            private float verticalAngle;
            // 3D модель
            private Model model;

            // Инициализация
            public View(Model model)
            {
                this.model = model;
                this.model.Updated += new EventHandler(this.Model_Updated);
                this.lines = new List<List<PointF>>();
                this.horizontalAngle = 0.0f;
                this.verticalAngle = 0.0f;
                this.Update();
            }

            // Линии для вывода
            public List<List<PointF>> Lines
            {
                get
                {
                    return this.lines;
                }
            }

            // Вращение ракурса обзора.
            public void Rotate(float horizontalRotate, float verticalRotate)
            {
                // Меняем углы и устраняем многократное закручивание.
                float pi2 = Convert.ToSingle(Math.PI * 2.0);
                this.horizontalAngle += horizontalRotate;
                if (this.horizontalAngle > pi2)
                {
                    this.horizontalAngle -= pi2;
                }
                if (this.horizontalAngle < pi2)
                {
                    this.horizontalAngle += pi2;
                }
                this.verticalAngle += verticalRotate;
                if (this.verticalAngle > pi2)
                {
                    this.verticalAngle -= pi2;
                }
                if (this.verticalAngle < pi2)
                {
                    this.verticalAngle += pi2;
                }
                this.Update();
            }

            // Событие обновления
            public event EventHandler Updated;
            protected void OnUpdated()
            {
                if (this.Updated != null)
                {
                    this.Updated(this, new EventArgs());
                }
            }

            // Отображение 3D модели в 2D представление.
            private void Update()
            {
                this.lines.Clear();
                // Поворачиваем исходное 3D пространство на углы,
                // под которыми мы будем его рассматривать.
                // Отступаем по Z назад и смотрим на результат как на плоскость XY в перспективе.
                // Преобразованиям подвергается каждая точка.
                // Вспомогательные коэффициенты
                float sinV = Convert.ToSingle(Math.Sin(this.verticalAngle));
                float sinH = Convert.ToSingle(Math.Sin(this.horizontalAngle));
                float cosV = Convert.ToSingle(Math.Cos(this.verticalAngle));
                float cosH = Convert.ToSingle(Math.Cos(this.horizontalAngle));
                foreach (List<GraphLine> line3D in this.model.Lines)
                {
                    List<PointF> line2D = new List<PointF>();
                    foreach (GraphLine point3D in line3D)
                    {
                        // Исходная точка
                        float x0 = point3D.graphcl[0];
                        float y0 = point3D.graphcl[1];
                        float z0 = point3D.graphcl[2];
                        // Вертикальный поворот вокруг оси X исходной точки - промежуточная точка 1
                        float x1 = x0;
                        float y1 = y0 * cosV - z0 * sinV;
                        float z1 = y0 * sinV + z0 * cosV;
                        // Горизонтальный поворот вокруг оси Y промежуточной точки 1 - промежуточная точка 2
                        float x2 = x1 * cosH + z1 * sinH;
                        float y2 = y1;
                        float z2 = z1 * cosH - x1 * sinH;
                        // Отступ по Z назад и добавление перспективы
                        float p = z2 + (float)1.2; // отступаем с запасом, чтобы p не обратилось в 0.
                        // Итоговая точка
                        PointF point2D = new PointF((float)(x2 / p),(float) (y2 / p)); // по-хорошему нужно придумать обработку для p = 0
                        line2D.Add(point2D);
                    }
                    this.lines.Add(line2D);
                }
                this.OnUpdated();
            }

            // Обработка события изменение модели
            private void Model_Updated(object sender, EventArgs args)
            {
                this.Update();
                this.OnUpdated();
            }
        }

        // 3D модель
        class Model
        {
                public  const float girdxmin = -0.5f;
                public  const float girdxmax  = 0.5f;
            // Линии координатной сетки
                private List<List<GraphLine>> grid;
            // Линия графика
            private List<GraphLine> graph;

            // Инициализация
            public Model()
            {
                // Создаём линии сетки
                this.grid = new List<List<GraphLine>>();
                for (float t = girdxmin; t <= girdxmax; t += 0.1f)
                {
                    GraphLine[] horizontal = new GraphLine[2];
                    horizontal[0] = new GraphLine(girdxmax,t,0.0f,Color.White);
                    horizontal[1] = new GraphLine(girdxmin,t,0.0f,Color.White);
                    grid[0].Add(horizontal[0]);
                    grid[1].Add(horizontal[1]);

                    GraphLine[] vertical = new GraphLine[2];
                    vertical[0] = new GraphLine(girdxmax, t, 0.0f, Color.White);
                    vertical[1] = new GraphLine(girdxmin, t, 0.0f, Color.White);
                    grid[0].Add(vertical[0]);
                    grid[1].Add(vertical[1]);


                }
                GraphLine graph0 = new GraphLine((float)0, (float)0, (float)0, Color.White);
                // Создаём начальную точку графика
                this.graph.Add(graph0);               

            }

            // Все линии в пространстве
            public List<List<GraphLine>> Lines
            {
                get
                {
                    List<List<GraphLine>> lines = new List<List<GraphLine>>(this.grid);
                    lines.Add(this.graph);
                    return lines;
                }
            }

            // Событие обновления
            public event EventHandler Updated;
            protected void OnUpdated()
            {
                if (this.Updated != null)
                {
                    this.Updated(this, new EventArgs());
                }
            }
           
            public void addpoint3d(double x, double y, double z,Color clr)
            {

                // Добавляем в график следующую точку.
                GraphLine  graph0 = new GraphLine((float)x, (float)y, (float)z,clr);
                this.graph.Add(graph0);
                this.OnUpdated();
               
            }
        }



        public class GraphLine
        {

        public Color cl;
        public float[] graphcl;

        public GraphLine(float x, float y, float z, Color clo)
        {
            graphcl = new float[3];
            graphcl[0] = x;
            graphcl[1] = y;
            graphcl[2] = z;
            cl = clo;
        }




    }

Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 02-12-2013 00:09 » 

sergeyan, я не понимаю: ты что, не видишь, что ты пишешь? Какой тип у grid? Что такое grid[0]? К чему применим метод Add?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #14 : 02-12-2013 09:38 » 

Все исправил, нужно было поспать Улыбаюсь Может конечно что то улучшить нужно?
Код:
    public class WindowGraph : UserControl
    {
        // Модель.
        private Model model;
        // 2D представление.
        private View view;
        // Контроллер ввода пользователя.
        private Controller controller;

        // Средства рисования
        Brush background; // кисть для фона
        Pen   foreground;   // перо для линий

        // Масштабирующий коэффициент, который любой размер окна приводит к нормальному диапазону 0..1.
        // Позволяет абстрагировать изображение от размеров окна
        private float NormaScale
        {
            get
            {
                return 1f / Math.Min(this.ClientSize.Width, this.ClientSize.Height);
            }
        }

        // Инициализация окна.
        public WindowGraph()
        {
            // Заголовок
            this.Text = "3D graphics";
            // Создаём части и связываем их между собой
            this.model = new Model();
            this.view = new View(this.model);
            this.controller = new Controller(this.view);
            // Включаем двойную буферизацию рисования, чтобы изображение не "мерцало".
            this.DoubleBuffered = true;
            // Создаём средства рисования.
            this.background = new SolidBrush(Color.Black); // чёрная сплошная кисть
            this.foreground = new Pen(Color.White); // белое перо нормальной толщины
            // Обрабатываем событие изменения 2D представления.
            this.view.Updated += new EventHandler(this.View_Updated);
        }

        // Перехват нужных событий от Windows

        // Отрисовка
        protected override void OnPaint(PaintEventArgs args)
        {
            base.OnPaint(args);
            // Очищаем фон.
            args.Graphics.FillRectangle(this.background, new Rectangle(new Point(0, 0), this.ClientSize));
            // Вспомогательные коэффициенты
            float scale = this.NormaScale; // Масштаб. Избегаем двойного расчёта.
            // Сдвиги для неквадратного окна, чтобы изображение оказалось в центре.
            float shift = (this.ClientSize.Width - this.ClientSize.Height) / 2.0f;
            float shiftX = shift > 0.0f ? shift : 0.0f;
            float shiftY = shift < 0.0f ? -shift : 0.0f;
            // Рисуем все линии, которые генерирует 2D представление
            foreach (List<Pointfcl> line in this.view.Lines)
            {
                // Каждую полученную точку приводим из нормальных в координаты окна:
                // центр из середины перемещаем в левый верхний угол, разворачиваем ось Y,
                // чтобы она была направлена вниз, центрируем изображение.
                PointF previousPoint = line[0].pin;
                previousPoint.X = (previousPoint.X + 0.5f) / scale + shiftX;
                previousPoint.Y = (0.5f - previousPoint.Y) / scale + shiftY;
                for (int i = 1; i < line.Count; ++i)
                {
                    PointF nextPoint = line[i].pin;
                    nextPoint.X = (nextPoint.X + 0.5f) / scale + shiftX;
                    nextPoint.Y = (0.5f - nextPoint.Y) / scale + shiftY;
                    this.foreground.Color = line[i].clr;
                    args.Graphics.DrawLine(this.foreground, previousPoint, nextPoint);
                    previousPoint = nextPoint;
                }
            }
        }

        // Движения мыши
        protected override void OnMouseMove(MouseEventArgs args)
        {
            base.OnMouseMove(args);
            float scale = this.NormaScale; // Избегаем двойного расчёта.
            // Передаём информацию в контроллер.
            this.controller.Input(
                // Нажата ли кнопка мыши
                    args.Button == MouseButtons.Left,
                // Приведённые координаты, сдвинутые таким образом,
                // чтобы центр был в середине, а ось Y ориентирована вверх
                    new PointF((float)(args.X * scale - 0.5f), (float)(0.5f - args.Y * scale))
            );
        }

        // Изменение размеров окна
        protected override void OnResize(EventArgs args)
        {
            // При изменении размеров окна требуем от Windows перерисовки.
            this.Invalidate();
        }

        // Обработка внутренних событий

        // Изменения в 2D представлении.
        private void View_Updated(object sender, EventArgs args)
        {
            // При изменении 2D представления требуем от Windows перерисовки.
            this.Invalidate();
        }


        public void addpoint3d_(double x, double y, double z,Color prn)
        {           
            model.addpoint3d(x, y, z,prn);
        }

    }





        // Контроллер ввода пользователя
        class Controller
        {
            // Предыдущие координаты
            private PointF? previousCoordinates;
            // 2D представление, на которое влияет ввод.
            private View view;

            // Инициализация
            public Controller(View view)
            {
                this.previousCoordinates = null;
                this.view = view;
            }

            // Обработка входных данных от пользователя.
            public void Input(bool pressed, PointF coordinates)
            {
                // Пока есть нажатие, любое изменение координат рассматривается как угол полукруга,
                // на который нужно изменить углы поворота в ту или иную сторону.
                // Если нет нажатия, углы не меняются.
                if (!pressed)
                {
                    this.previousCoordinates = null;
                }
                else
                {
                    if (this.previousCoordinates.HasValue)
                    {
                        this.view.Rotate(
                                Convert.ToSingle((coordinates.X - this.previousCoordinates.Value.X) * Math.PI),
                                Convert.ToSingle((coordinates.Y - this.previousCoordinates.Value.Y) * Math.PI)
                        );
                    }
                    this.previousCoordinates = coordinates;
                }
            }
        }


        // 2D представление
        class View
        {
            // Линии для вывода
            private List<List<Pointfcl>> lines;
            // Углы ракурса обзора.
            private float horizontalAngle;
            private float verticalAngle;
            // 3D модель
            private Model model;

            // Инициализация
            public View(Model model)
            {
                this.model = model;
                this.model.Updated += new EventHandler(this.Model_Updated);
                this.lines = new List<List<Pointfcl>>();
                this.horizontalAngle = 0.0f;
                this.verticalAngle = 0.0f;
                this.Update();
            }

            // Линии для вывода
            public List<List<Pointfcl>> Lines
            {
                get
                {
                    return this.lines;
                }
            }

            // Вращение ракурса обзора.
            public void Rotate(float horizontalRotate, float verticalRotate)
            {
                // Меняем углы и устраняем многократное закручивание.
                float pi2 = Convert.ToSingle(Math.PI * 2.0);
                this.horizontalAngle += horizontalRotate;
                if (this.horizontalAngle > pi2)
                {
                    this.horizontalAngle -= pi2;
                }
                if (this.horizontalAngle < pi2)
                {
                    this.horizontalAngle += pi2;
                }
                this.verticalAngle += verticalRotate;
                if (this.verticalAngle > pi2)
                {
                    this.verticalAngle -= pi2;
                }
                if (this.verticalAngle < pi2)
                {
                    this.verticalAngle += pi2;
                }
                this.Update();
            }

            // Событие обновления
            public event EventHandler Updated;
            protected void OnUpdated()
            {
                if (this.Updated != null)
                {
                    this.Updated(this, new EventArgs());
                }
            }

            // Отображение 3D модели в 2D представление.
            private void Update()
            {
                this.lines.Clear();
                // Поворачиваем исходное 3D пространство на углы,
                // под которыми мы будем его рассматривать.
                // Отступаем по Z назад и смотрим на результат как на плоскость XY в перспективе.
                // Преобразованиям подвергается каждая точка.
                // Вспомогательные коэффициенты
                float sinV = Convert.ToSingle(Math.Sin(this.verticalAngle));
                float sinH = Convert.ToSingle(Math.Sin(this.horizontalAngle));
                float cosV = Convert.ToSingle(Math.Cos(this.verticalAngle));
                float cosH = Convert.ToSingle(Math.Cos(this.horizontalAngle));
                foreach (List<GraphLine> line3D in this.model.Lines)
                {
                    List<Pointfcl> line2D = new List<Pointfcl>();
                    foreach (GraphLine point3D in line3D)
                    {
                        // Исходная точка
                        float x0 = point3D.graphcl[0];
                        float y0 = point3D.graphcl[1];
                        float z0 = point3D.graphcl[2];
                        // Вертикальный поворот вокруг оси X исходной точки - промежуточная точка 1
                        float x1 = x0;
                        float y1 = y0 * cosV - z0 * sinV;
                        float z1 = y0 * sinV + z0 * cosV;
                        // Горизонтальный поворот вокруг оси Y промежуточной точки 1 - промежуточная точка 2
                        float x2 = x1 * cosH + z1 * sinH;
                        float y2 = y1;
                        float z2 = z1 * cosH - x1 * sinH;
                        // Отступ по Z назад и добавление перспективы
                        float p = z2 + (float)1.2; // отступаем с запасом, чтобы p не обратилось в 0.
                        // Итоговая точка
                        PointF point2D = new PointF((float)(x2 / p),(float) (y2 / p)); // по-хорошему нужно придумать обработку для p = 0
                        Pointfcl ppl = new Pointfcl();
                        ppl.pin = point2D;
                        ppl.clr = point3D.cl;
                        line2D.Add(ppl);
                    }
                    this.lines.Add(line2D);
                }
                this.OnUpdated();
            }

            // Обработка события изменение модели
            private void Model_Updated(object sender, EventArgs args)
            {
                this.Update();
                this.OnUpdated();
            }
        }

        // 3D модель
        class Model
        {
             public  const float girdxmin = -0.5f;
             public  const float girdxmax  = 0.5f;
            // Линии координатной сетки
                private List<List<GraphLine>> grid;
            // Линия графика
            private List<GraphLine> graph;

            // Инициализация
            public Model()
            {
                // Создаём линии сетки
                this.grid = new List<List<GraphLine>>();
                for (float t = girdxmin; t <= girdxmax; t += 0.1f)
                {
                    Color clr = new Color();
                    if ((t >= -0.1f) && (t < 0.0f))
                    {
                        clr = Color.Blue;
                    }
                    else
                    {
                        clr = Color.White;
                    }

                    GraphLine[] horizontal = new GraphLine[2];
                    horizontal[0] = new GraphLine(girdxmax, t, 0.0f, clr);
                    horizontal[1] = new GraphLine(girdxmin, t, 0.0f, clr);
                    grid.Add(horizontal.ToList());
                   

                    GraphLine[] vertical = new GraphLine[2];
                    vertical[0] = new GraphLine(t, girdxmax, 0.0f, clr);
                    vertical[1] = new GraphLine(t, girdxmin, 0.0f, clr);
                    grid.Add(vertical.ToList());                 


                }
                this.graph = new List<GraphLine>();
                GraphLine graph0 = new GraphLine((float)0, (float)0, (float)0, Color.White);
                // Создаём начальную точку графика
                this.graph.Add(graph0);               

            }

            // Все линии в пространстве
            public List<List<GraphLine>> Lines
            {
                get
                {
                    List<List<GraphLine>> lines = new List<List<GraphLine>>(this.grid);
                    lines.Add(this.graph);
                    return lines;
                }
            }

            // Событие обновления
            public event EventHandler Updated;
            protected void OnUpdated()
            {
                if (this.Updated != null)
                {
                    this.Updated(this, new EventArgs());
                }
            }
           
            public void addpoint3d(double x, double y, double z,Color clr)
            {

                // Добавляем в график следующую точку.
                GraphLine  graph0 = new GraphLine((float)x, (float)y, (float)z,clr);
                this.graph.Add(graph0);
                this.OnUpdated();
               
            }
        }



        public class GraphLine
        {

        public Color cl;
        public float[] graphcl;
        public GraphLine()
        {

        }
        public GraphLine(float x, float y, float z, Color clo)
        {
            graphcl = new float[3];
            graphcl[0] = x;
            graphcl[1] = y;
            graphcl[2] = z;
            cl = clo;
        }

           
        }


        public class Pointfcl
        {
           public PointF pin;
            public Color clr;
           public  Pointfcl()
            {

            }

        }



Добавлено через 2 часа, 54 минуты и 4 секунды:
С меня пиво Улыбаюсь с получки. Можно еще вопрос,  незнаю насколько реально это сделать нужно когда колесико мыша крутим увеличивать картинку которая под курсором. сейчас получается  иногда такое картинка как бы в угол забивается.  код который ниже работает отлично.
Как сделаю то что хотел, исходники выложу может кому пригодится помогите пожалуйста.
« Последнее редактирование: 02-12-2013 12:33 от sergeyan » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 02-12-2013 12:39 » 

Если у тебя событие происходит 1000 раз в секунду, вся схема выглядит неудачно. Refresh графика с такой частотой 1000 fps бессмысленна со всех точек зрения - ни человек не может разглядеть, ни монитор отобразить. Достаточно частоты старого кинофильма - 24 кадра в секунду, для круглого счёта 25 - 40 мс.

В итоге нужно аккуратно модифицировать связь между поступлением точек в модель и отображением. Для этого либо в модель добавить таймер, который генерирует событие не чаще 25 раз в секунду по условию: в каждый тик таймера смотрим, добавились ли новые точки за период таймера, и если да, генерируем событие, если нет - не генерируем. Либо сделать модель не активной, а пассивной (без события), а таймер поместить, например, в usercontrol. Тогда вместо invalidate во всех местах нужно устанавливать флаг refreshRequired, а в обработчике таймера, если флаг установлен, вызывать invalidate и сбрасывать флаг. Логика работы view и model должна быть изменена с "выталкивающей", на "вытягивающую". Т.е. в onpaint делать запрос к view, и только в этот момент view начинает проецирование точек 3D в 2D, запросив у модели текущее состояние. Т.е. расчёты выполняются строго по запросу в момент рисования, а не заранее в момент добавления новой точки.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #16 : 02-12-2013 12:47 » 

Все отлично работает, осталась одна проблема нужно колесиком делать увеличении место под курсором.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #17 : 02-12-2013 12:52 » 

sergeyan, размер картинки - это строчка, где добавляется точечная перспектива.

Код: (C#)
                       // Отступ по Z назад и добавление перспективы
                        float p = z2 + (float)1.2; // отступаем с запасом, чтобы p не обратилось в 0.
(Писать "(float)1.2" не нужно, числа типа float имеют суффикс "f" - т.е. "1.2f" - это сразу число нужного типа как константа в коде. И даже в C++ так же.)

Чтобы картинка отодвинулась, нужно увеличить отступ по Z - т.е. отойти подальше, тогда в силу точечной перспективы (чем дальше, тем размеры меньше и всё сжимается к точке - центру перспективы) изображение уменьшится. Чтобы приблизилась - уменьшить отступ по Z. Одновременно может быть полезным играться с коэффициентом усиления перспективы (т.е. домножать всё выражение на какое-то значение). Чем сильнее перспектива, тем быстрее сходимость дальних объектов к точке, и сами объекты визуально выглядят как "гигантские" (что удобно при разглядывании вблизи - при малом отступе), и наоборот, чем слабее перспектива, тем объекты визуально выглядят как "миниатюрные" (что удобно при разглядывании издали).

Главный вопрос, что делать с частями картинки, выходящими за спину наблюдателя - когда p обращается в 0 или становится отрицательным. Чтобы при этом не возникло отрицательных x и y, и линии не соскакивали куда-то за экран, правильно такие точки выбрасывать и не рисовать. А вместе с такими точками и те отрезки, в которых они встречаются. Т.е. должна быть секущая плоскость. В моём коде этот момент не был реализован, чтобы не загромождать главное. Но по построению линий сетки сильное приближение и заход внутрь сетки приведут к тому, что вся сетка не отобразится. Сетку придётся переделать: заменить длинные отрезки во всю длину последовательностями коротких - на длину стороны клетки. Для графика это неактуально - он сразу рисуется небольшими отрезками (надеюсь).
« Последнее редактирование: 02-12-2013 12:56 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Serguntii
Помогающий

ru
Offline Offline

« Ответ #18 : 02-05-2014 16:15 » new

Какую библиотеку использовать что бы на 3D экране рисовать объемные изображение, ну например чтобы можно было нарисовать вырезать вот такой рисунок как на картинке?
http://a-frezer.ru/wp-content/uploads/2013/12/lepnina-3d-261.jpg
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines