Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.88/8: Рейтинг темы: голосов - 8, средняя оценка - 4.88
0 / 0 / 0
Регистрация: 22.01.2013
Сообщений: 5
.NET 4.x

Простая программа отрисовки линий съедает до 50% производительности

14.06.2013, 08:28. Показов 1749. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет Всем!
Создал программу иммитатор обмена и первичной обработки: есть класс для хранения данных DataClass, класс поставщик данных DataMinner, окно-класс рисования WinGraph. Всё реализовано через события.
Упрощенно, датамайнер при создании получает ссылку на датакласс и через определенные интервал времени (20 мс) вырабатывает целое число, передавая через метод датакласса во внутреннюю коллекцию датакласса. Внутри датакласса возникает событие - вызывая обработчик уже WinGraph.Add. Подходя к интересному, у WinGraph есть собственная коллекция и таймер обновления картинки(зачем рябить часто), который перебирает коллекцию и строит картинку. Если оставить класс WinGraph as is, то через некоторое время всё зависает, но если закомментировать строку с добавлением линий или добавлением группы рисования, то всё нормально (меньше 8% ЦП).
Как правильно рисовать в WPF?

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Threading;
 
 public partial class WinGraph : Window
    {
        protected List<double> A = new List<double>();
        Timer updateTimer = null;
        public WinGraph()
        {
            InitializeComponent();
            for (Int32 i = 0; i < 16000; i++) A.Add(0);
        }
 
        public void Add(Int32 value)
        {
            lock (A) this.A[value] += 1.0;
            if (updateTimer == null) updateTimer = new Timer(UpdateWindow, null, 250, 250);
        }
        private void UpdateWindow(object o)
        {
            this.Dispatcher.BeginInvoke((Action)(() =>
            {
                if (this.A.Count == 0) return;
                /// TODO Drawing graphics!!!!
                DrawingGroup drawGroup = new DrawingGroup();
                GeometryGroup lines = new GeometryGroup();
                GeometryDrawing geoDraw = new GeometryDrawing();
                lock (this.A)
                {
                    double deltaY = 1 / this.A.Max();
                    double deltaX = 1.0 / this.A.Count;
                    geoDraw.Brush = Brushes.Black;
                    geoDraw.Pen = new Pen(Brushes.Gray, 0.003);
                    for (Int32 i = 0; i < this.A.Count; i++)
                    {
                        if (A[i] != 0)
                        {
                            LineGeometry line = new LineGeometry();
                            double X = i * deltaX;
                            double Y1 = 1.0 - this.A[i] * deltaY;
                            double Y2 = 1.0;
                            line.StartPoint = new Point(X, Y1);
                            line.EndPoint = new Point(X, Y2);
                            lines.Children.Add(line);
                        }
                    }
                }
                geoDraw.Geometry = lines;
                drawGroup.Children.Add(geoDraw);
                DrawingImage di = new DrawingImage(drawGroup);
                di.Freeze();
                this.image1.Source = (di);
            }));
        }
    }
Вложения
Тип файла: zip HomeProjectWPF_1.zip (79.3 Кб, 9 просмотров)
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
14.06.2013, 08:28
Ответы с готовыми решениями:

Программа для отрисовки линий отрезками нажатием на клавиши мыши(по двум точкам), но при этом надо ограничить ее
Программа для отрисовки линий отрезками нажатием на клавиши мыши(по двум точкам), но при этом надо ограничить ее, что при пересечении с...

Библиотека для отрисовки линий уровня функции от 2-ух переменных
Может быть кто-то знает какие-нибудь библиотеки, которые позволили бы стоить линии уровня функции от двух переменных для windows forms\wpf?...

куда программа съедает первый элемент массива?
Напишу в эту тему. Объясните пожалуйста куда программа съедает первый элемент массива и как ее подправить: n=15 Dim a(n),i,j,k,x as...

8
432 / 433 / 93
Регистрация: 16.07.2012
Сообщений: 886
14.06.2013, 12:05
На самом деле WPF не очень быстр если вам надо отрисовывать ОЧЕНЬ много графики. Для начала можно убрать лишние линии. То есть округлять например координаты до целых и сохранять их в словаре. Потом смотреть по словарю, рисовали ли мы уже на этом месте линию, если рисовали, то пропускать. Я как-то делал на WPF графики с миллионами точек, только такие вот ухищрения помогали. Интервал обновления меньше секунды даже и не пытался ставить. Еще можно сглаживание (антиалиазинг) отключить, но тогда картинка не очень красивая будет. Ну и не забудьте geoDraw.Brush = null поставить. Возможно, тоже значительно ускорит отрисовку.
0
0 / 0 / 0
Регистрация: 22.01.2013
Сообщений: 5
14.06.2013, 12:45  [ТС]
Перешел на класс попроще: Visual (по книге M.McDonald "WPF Windows Presentation Foundation в .NET 4.0 с примерам")
Без пользы. Видимо придется перейти на точки экрана и выбирать только видимые, не знаю пока как(усреднять значения группы линий и только их рисовать или четко попадающую на пиксель линию)
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void UpdateWindow(object o)
        {
            this.Dispatcher.BeginInvoke((Action)(() =>
            {
                if (this.A.Count == 0) return;
                /// TODO Drawing graphics!!!!
                double cW = this.image1.ActualWidth;
                double cH = this.image1.ActualHeight;
                DrawingGroup dGroup = new DrawingGroup();
                using (DrawingContext dc = dGroup.Open())
                {
                    Pen drawPen = new Pen(Brushes.Black, 0.05);
                    lock (this.A)
                    {
                        double deltaY = cH / this.A.Max();
                        double deltaX = cW / this.A.Count;
                        for (Int32 i = 0; i < this.A.Count; i++)
                        {
                            if (A[i] == 0) continue;
                            LineGeometry line = new LineGeometry();
                            double X = i * deltaX;
                            double Y1 = cH - this.A[i] * deltaY;
                            double Y2 = cH;
                            line.StartPoint = new Point(X, Y1);
                            line.EndPoint = new Point(X, Y2);
                            dc.DrawLine(drawPen, new Point(X, Y1), new Point(X, Y2));
                        }
                    }
                    this.image1.Source = new DrawingImage(dGroup);
                }
            }));
        }
0
432 / 433 / 93
Регистрация: 16.07.2012
Сообщений: 886
14.06.2013, 13:47
Смотрите, умножаем координаты на 300 чтобы перейти к целым координатам и толщина линии составила 1 пиксель и заводим HashSet:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
geoDraw.Pen = new Pen(Brushes.Gray, 1);
var hashSet = new HashSet<long>();
int Y2 = (int)(1.0 * 300);
for (Int32 i = 0; i < this.A.Count; i++)
{
    if (A[i] != 0)
    {        
        int X = (int)(i * deltaX * 300);
        int Y1 = (int)((1.0 - this.A[i] * deltaY) * 300);
        var hash = (long)X + ((long)Y1 << 32);
        if (!hashSet.Contains(hash))
        {
            hashSet.Add(hash);   
            LineGeometry line = new LineGeometry();                             
            line.StartPoint = new Point(X, Y1);
            line.EndPoint = new Point(X, Y2);
            lines.Children.Add(line);
        }
    }
}
Это конечно только набросок, идея. Скажем, если две линии стоят рядом, то можно вместо них нарисовать один прямоугольник. Если таких линий будет сотня, то заменив их одним прямоугольником, можно уже радикально скорость увеличить. И т.д.
0
0 / 0 / 0
Регистрация: 22.01.2013
Сообщений: 5
14.06.2013, 14:15  [ТС]
Интересно. Еще не имел дел с хеш-таблицами, но тем не менее - две точки имеющие одинаковые Х и разные У будут иметь разные ключи (Х + У << 32), но видимой будет только большая точка ( с большим У).

Добавлено через 14 минут
Не успел сразу исправить...

Интересно. Еще не имел дел с хеш-таблицами, но тем не менее - две точки имеющие одинаковые Х и разные У будут иметь разные ключи (Х + У << 32), но видимой будет только большая точка ( с большим У) ,остальные(с тем же Х) будут не нужны .

Пока сделал с усреднением: получаю число каналов на точку по Х, усредняю столбики - получаю мега-столбик и в текущем Х рисую линию с его высотой.
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
                Int32 wtd;
                Int32 ave;
                wtd = (Int32)this.image1.Width;
                Int32 clv = (Int32)(this.A.Count / wtd);
                double cH = this.image1.Height;
                DrawingGroup dGroup = new DrawingGroup();
                using (DrawingContext dc = dGroup.Open())
                {
                    Pen drawPen = new Pen(Brushes.Black, 0.1);
                    lock (this.A)
                    {
                        double deltaY = cH / A.Max();
                        Int32 offset = 0; // C начала коллекции
                        for (Int32 i = 0; i < wtd; i++)
                        {
                            ave = 0;
                            for (Int32 j = 0; j < clv; j++)   // пройти по составляющим мегастолбик 
                                ave += this.A[offset + j];   // new Y2
                            offset += clv;
                            if (ave == 0) continue;
                            dc.DrawLine(drawPen, new Point(i, 0), new Point(i, cH - ave * deltaY));
                        }
                    }
                    this.image1.Source = new DrawingImage(dGroup);
                    drawPen = null;
0
432 / 433 / 93
Регистрация: 16.07.2012
Сообщений: 886
14.06.2013, 14:56
Кстати, drawPen.Freeze() неплохо было бы сделать перед тем как ею рисовать.
И зачем вам вообще Image? Можно выставить у окна Background = null и рисовать в OnRender прямо в DrawingContext. А по таймеру просто вызывать InvalidateVisual();
0
0 / 0 / 0
Регистрация: 22.01.2013
Сообщений: 5
17.06.2013, 09:16  [ТС]
Почему-то не рисует на DrawingContent :-(
Только черный фон окна. Попутно хочу узнать:
1) Есть ли метод коллекций - заполняющий количество элеметов дефолтными(0) значениями?
2) Что дает обнуление Background = null?

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 public partial class WinGraph : Window
    {
        protected List<Int32> A = new List<Int32>();
        Timer updateTimer = null;
        public WinGraph()
        {
            InitializeComponent();
            this.Background = null;
            for (Int32 i = 0; i < 16000; i++) A.Add(0);
        }
 
        public void Add(Int32 value)
        {
            lock (A) this.A[value] += 1;
            if (updateTimer == null) updateTimer = new Timer(UpdateWindow, null, 250, 250);
        }
        private void UpdateWindow(object o)
        {  
              this.Dispatcher.BeginInvoke((Action)(() =>
              {
                this.InvalidateVisual();
              }));
        }
        protected override void OnRender(DrawingContext dc)
        {
            dc.DrawLine(new Pen(Brushes.Red, 0.1), new Point(0.0, 0.0), new Point(1.0, 1.0));
            base.OnRender(dc);
        }
    }
0
432 / 433 / 93
Регистрация: 16.07.2012
Сообщений: 886
17.06.2013, 09:39
Почему же, все рисует. Просто когда толщина линии 0.1 пиксель, а длина - 1 пиксель, то это очень трудно заметить )
А фон null - для того чтобы все что вы рисуете в OnRender не залилось потом поверх цветом фона.
Насчет заполнения List нулями не знаю, но раз у вас количество элементов все равно не меняется, то можно вместо List<int> использовать масссив (int []). В массиве после его создания будут дефолтные значения.
0
0 / 0 / 0
Регистрация: 22.01.2013
Сообщений: 5
17.06.2013, 11:15  [ТС]
Спасибо!
Всё стало отлично. Загрузка до 8 -10 % ЦП

Загнал графику в OnRender - рисую в Content, обновляю по таймеру. Как советовали. Единственно только, переформатирую точки отдельно, чтобы побыстрее выйти из lock{...}(освободить ресурс). Поле для оптимизации есть.
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 public partial class WinGraph : Window
    {
        protected Int32[] A = new Int32[16000];
        Timer updateTimer = null;
        Int32 Max_elem = 0;
 
        public WinGraph()
        {
            InitializeComponent();
            this.Background = null;
        }
 
        public void Add(Int32 value)
        {
            lock (A) this.A[value] += 1;
            if (updateTimer == null) updateTimer = new Timer(UpdateWindow, null, 250, 250);
        }
        private void UpdateWindow(object o)
        {
            this.Dispatcher.BeginInvoke((Action)(() =>
            {
                this.InvalidateVisual();
                this.Title = "Graph" + "  Max Element = " + Max_elem.ToString();
            }));
        }
        protected override void OnRender(DrawingContext dc)
        {
            // Хранимые У 
            List<double> Y = new List<double>();
            // Среднее
            double ave;
            Int32 offset = 0;
            // Число значений на линию
            Int32 canalsOnPx;
            double cW = this.Width;
            double cH = this.Height;
            //Масштаб верт. отн. накопленных данных
            double deltaY;
            bool end = false;
            Pen drawPen = new Pen(Brushes.Blue, 1);
            Pen drawPenRed = new Pen(Brushes.Red, 1);
            dc.DrawRectangle(new SolidColorBrush(Colors.WhiteSmoke), new Pen(Brushes.Red, 0.1), new Rect(0, 0, this.Width, this.Height));
 
            canalsOnPx = (Int32)(16000 / cW);
 
            lock (this.A)
            {
                Max_elem = A.Max();
                if (Max_elem != 0)
                {
                    for (int i = 0; i <= cW; i++)
                    {
                        double sum = 0.0;
                        for (int j = 0; j <= canalsOnPx; j++)
                            sum += A[offset + j];
                        ave = sum / (double)canalsOnPx;
                        Y.Add(ave);
                        offset += canalsOnPx;
                    }
                }
                else end = true;
            }
            if (end == true) return;
            deltaY = cH / Y.Max();
            for (int i = 0; i < Y.Count; i++)
            {
                dc.DrawLine(drawPen, new Point((double)i, cH), new Point((double)i, cH - Y[i] * deltaY));
            }
            // сетка
            Int32 step = (Int32)this.Width / 10;
            for (Int32 i = step; i <= this.Width; i += step)
                dc.DrawLine(drawPenRed, new Point(i, 0), new Point(i, cH));
            step = (Int32)this.Height / Max_elem;
            for (Int32 i = step; i <= this.Height; i += step)
                dc.DrawLine(drawPenRed, new Point(0, cH - i), new Point(cW, cH - i));
 
            drawPenRed.Freeze();
            drawPen.Freeze();
            drawPen = null;
            drawPenRed = null;
            base.OnRender(dc);
        }
    }


Буду вводить красоту в виде маркерных линий и подписей.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
17.06.2013, 11:15
Помогаю со студенческими работами здесь

Программа отрисовки винтов LISP
Помогите студенту-заочнику разобраться и написать программу в LISP. Первый раз встречаюсь с этой &quot;штукой&quot;. Спасибо! Условие: ...

Программа для отрисовки в окне произвольного, придуманного Вами, рисунка
Написать программу для отрисовки в окне произвольного, придуманного Вами, рисунка. Рисунок должен содержать не менее 4-5 элементов. В...

Программа "съедает" всю оперативку
Добрый день, уважаемые эксперты! У меня вопрос по поводу выгрузки из dataGridView в Excel очень большой таблицы.Помещаю её в dataGridView...

Программа для улучшения производительности системы
На ХР пользовался Norton Utilities. Для 8 не могу найти NU на русском языке. Где скачать NU для 8 на русском? или подскажите пожалуйста...

Программа для сравнения производительности процессоров?
есть ли какая-нибудь прога сравнения производительности процессоров?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru