С Новым годом! Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.57/177: Рейтинг темы: голосов - 177, средняя оценка - 4.57
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11

Простой векторный графический редактор (разбор полётов)

16.11.2018, 09:22. Показов 36025. Ответов 127

Студворк — интернет-сервис помощи студентам
Всем привет! Меня зовут ashsvis и я программист...
Я пытался бороться с этим "недугом", но эта привычка затягивает меня всё глубже... (шутка)
Короче, для тех кто не в теме: я пытаюсь сделать простой векторный графический редактор.
Сначала я выпустил цикл статей по этому поводу (см. https://www.cyberforum.ru/blog... tegory389/)

Получилась начальная версия редактора (можно забрать по ссылке в статье https://www.cyberforum.ru/blog... g5533.html)
Первая версия, она известно какая и, благодаря критике моих более опытных коллег, была несколько переработана,
с целью улучшить её (а с какой-же ещё?) и получилась версия вторая (можно забрать по ссылке в статье https://www.cyberforum.ru/blog... g5536.html)

После второй версии мне намекнули, что я начал движение вообще не в ту сторону и предложили выложить в отдельную тему,
на стол прозектора, так сказать. Что я и делаю.

Подключайтесь к обсуждению, господа программисты. Давайте выведем меня на чистую воду...

Добавлено через 3 часа 28 минут
[для "затравки" дискуссии]
У меня тут появилась мысль, как ни странно, что если попытаться перевести логику работы редактора из WinForms в WPF,
то с текущей моделью это будет сделать сложновато. Там ведь (в WPF) всё другое, даже точки Point на основе double...
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
16.11.2018, 09:22
Ответы с готовыми решениями:

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

Простой графический редактор, как сохранять изображение
Здравствуйте! Мне задали на C# сделать какое-то подобие Paint. Всё уже есть, все инструменты. Всё рисуется методом Graphics.Draw...(), и...

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

127
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
16.11.2018, 12:19
Во-первых, давайте определимся с ТЗ.
Раз вы его не задали, сформулирую я.
Нужно создать простой(?) векторный редактор, который позволяет:
1) Строить геометрические фигуры произвольной формы, включая: квадраты, окружности, прямоугольники, правильные n-угольники, произвольные многоугольники, ломаные линии, кривые Базье и текст.
2) Менять стиль границы и заливки фигур.
3) Менять местоположение, размеры фигур, а также поддерживать вращение фигур. Трансформации должны поддерживаться как для одиночной фигуры, так и для набора выделенных фигур.
4) Поддерживать минимальную инфраструктуру - чтение и сохранение файла, экспорт.

Также, на вырост, по возможности нужно поддерживать undo/redo, слои, спецэффекты (свечение, размытие, тени).

Далее, предлагаю подумать о модели данных.
Центральное понятие - фигура. Фигура - это то что рисуется, имеет цвет, границу, форму. Фигуру можно перемещать и менять ее размеры.
Для пользователя это выглядит как единое целое, но с программной точки зрения - это довольно сложный объект, который имеет ряд разных обязанностей. Можно выделить четыре разных обязанности:
1) Хранить форму фигуры
2) Хранить трансформацию фигуры (позицию, масштаб, вращение)
3) Хранить стиль фигуры (цвет границы, цвет заливки и т.д.)
4) Уметь рисовать себя на канве

Если запихнуть все обязанности в один класс, мы получим большой слабоуправляемый, сильно связанный антипаттерн God Object. Это нам не хотелось бы.

Поэтому предлагаю сделать класс Figure композитным объектом, который в себе содержит отдельные объекты, которые умеют выполнять вышеназванные функции:

C#
1
2
3
4
5
6
7
    class Figure
    {
        public Matrix Transform { get; set; }
        public Geometry Geometry { get; set; }
        public Style Style { get; set; }
        public Renderer Renderer { get; set; }
    }
Geometry - будет содержать в себе то что рисуется. Transform - будет содержать где рисуется. Style - будет содержать информацию о том, как рисуется. Ну и Renderer будет собственно рисовать.

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

Далее, объект Geometry должен нам вернуть GraphicsPath. Нам пока без разницы внутренне содержимое объекта Geometry, но на выходе он должен нам отдать сформированный объект GraphicsPath, который содержит форму того, что мы рисуем, и который можно просто отрисовать средствами GDI+. Поэтому класс Geometry сделаем абстрактным с одним публичным свойством Path:
C#
1
2
3
4
    abstract class Geometry
    {
        public abstract GraphicsPath Path { get; }
    }
Далее объект Style. На самом деле, стиль это довольно сложная штука. Как минимум, он распадается на два несвязанных объекта - стиль границы и стиль заливки. Поэтому стиль тоже сделаем композитным, состоящим из двух объектов: Border и Fill:
C#
1
2
3
4
5
    class Style
    {
        public Border BorderStyle { get; set; }
        public Fill FillStyle { get; set; }
    }
Вот эти два композитных класса Figure и Style реализуют нечто похожее на паттерн Мост. Смысл в том, что с одной стороны, фигура и стиль - это объекты, выступающие как единое целое, но с другой стороны, они реализуют разнообразный функционал, который мы хотим сделать независимыми классами. Таким образом, Figure и Style выступают мостом между внешней абстракцией и внутренней реализацией.

Ну и наконец Renderer - это рисовальщик объекта на канве. Он должен содержать один метод Render. Его реализация достаточно проста и мы можем ее сразу написать:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    class Renderer
    {
        public virtual void Render(Graphics gr, Figure fig)
        {
            using (var path = fig.GetTransformedPath())
            {
                if (fig.Style.FillStyle.Draw)
                using (var brush = fig.Style.FillStyle.GetBrush(fig)) 
                    gr.FillPath(brush, path);
 
                if (fig.Style.BorderStyle.Draw)
                using (var pen = fig.Style.BorderStyle.GetPen(fig))
                    gr.DrawPath(pen, path);
            }
        }
    }
В принципе мы бы могли сделать единый рендерер на все фигуры, и не вносить его внутрь Figure. Но в будущем, может быть такая ситуация, что фигура потребует какого-то особого рендеринга, и поэтому лучше внести его внутрь Figure.

Плюсы данной модели:
1) Нет GodObject-ов, форма, стиль, трансформации и рендеринг разнесены по разным классам.
2) Абстрактный класс Geometry может содержать любой графический контент, включая полигоны, линии, текст или даже внешний векторный рисунок. При этом, вся геометрия инкапсулирована, наружу дается только то, что нужно для рендеринга - GraphicsPath.
3) Трансформации хранятся отдельно, легко модифицируются. При этом, сама геометрия не меняется.
4) Классы хорошо изолированы друг от друга (кроме класса Renderer, который понятное дело, должен соединять все компоненты вместе).
5) Бонусом получаем то, что мы можем менять компоненты фигуры, независимо друг от друга. Например, вместо геометрии Квадрат, можно подставить геометрию Круг. При этом, трансформации и стиль фигуры останутся прежними, просто вместо квадрата, в фигуре будет рисоваться круг. Такого трудно было бы добиться, если бы мы наследовали фигуры от базового класса, как это обычно делают в детских учебниках (аля класс Square наследуется от Figure). Кроме того, мы можем легко присваивать стиль рисования, копировать стили из одной фигуры в другую, и даже использовать один стиль для нескольких фигур.

Нужно отметить, что эта модель наверное не идеальна. И вообще возможно великое множество разных подходов и разных реализаций. Но если прикинуть, то все заявленные в ТЗ требования в ней могут быть выполнены. Фигуры и текст может рендерить? Может. Вращать объекты может? Может. Стили задавать можно? Можно. Кисти типа GradientBrush делать можно? Можно. Сериализвать объекты - можно, генерировать различные маркеры для разных геометрий - можно.

Могут возникнуть какие-то проблемы, но в целом такая модель справляется с ТЗ. То есть она как минимум приемлема.

ashsvis, Все таки, нужно сделать репозиторий на github. Это реально бы упростило дело. Если хотите, могу я создать.

Цитата Сообщение от ashsvis Посмотреть сообщение
У меня тут появилась мысль, как ни странно, что если попытаться перевести логику работы редактора из WinForms в WPF,
то с текущей моделью это будет сделать сложновато. Там ведь (в WPF) всё другое, даже точки Point на основе double...
А давайте сначала разберемся с простыми вещами. Иначе все это гарантированно утонет.
6
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
16.11.2018, 12:53  [ТС]
Storm23, спасибо за развёрнутый ответ!

Цитата Сообщение от Storm23 Посмотреть сообщение
нужно сделать репозиторий
Я вчера пытался, создал аккаунт, завёл один репозиторий на сайте, второй при помощи VS, что-то уже залил,
потом какой-то репозиторий пытался удалить. В общем, как обезьяна перед .... Всё на английском, идеологии пока не пойму.
Вот что получилось: https://github.com/ashsvis/Sim... tor-editor.
В VS вроде как подключил и даже сделал 2 коммита. Хотел один из тестовых репозиториев удалить и не знаю, как.
Но всё на автомате делал, нужно разбираться конкретно...

А вообще бы я хотел заливать файлы как-то без VS, если это возможно. Посоветуйте, как поступить.

Но, что очень важно для меня, я хотел бы код править сам и заливать сам, ибо материала в Вашем этом посте уже пока достаточно для начала работы.

Спасибо за ТЗ, отличное получилось, мог ли я себе позволить выдать Вам ТЗ... В общем, я приступаю.
1
Эксперт .NET
 Аватар для Rius
13068 / 7629 / 1669
Регистрация: 25.05.2015
Сообщений: 23,180
Записей в блоге: 14
16.11.2018, 13:17
ashsvis, качайте Git for Windows и читайте https://git-scm.com/book/ru/v2
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
16.11.2018, 13:26
Цитата Сообщение от ashsvis Посмотреть сообщение
А вообще бы я хотел заливать файлы как-то без VS, если это возможно. Посоветуйте, как поступить.
Хороший менеджер для github это SourceTree. Но может показаться сложноватым для новичка. В таком случае скачайте любой клиент для githib. Их полно, например вот этот.
Цитата Сообщение от ashsvis Посмотреть сообщение
Но, что очень важно для меня, я хотел бы код править сам и заливать сам
Как хотите. Но я чувствую, это будет долго

И да, разбейте код по папкам. Эта мешанина их контролов, вспомогательных файлов и файлов модели - режет глаз.
А модель так и вообще можно в отдельный проект вынести.
2
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
16.11.2018, 14:08  [ТС]
Цитата Сообщение от Storm23 Посмотреть сообщение
Но я чувствую
Если я сам ручками это не сделаю, не пропущу через себя, то для меня это не будет опытом.
Постараюсь не затягивать. Я уже делаю, делаю...
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
16.11.2018, 17:49
Цитата Сообщение от ashsvis Посмотреть сообщение
Я уже делаю, делаю
Гм... а что вы собственно делаете?
Мы же только начали модель строить. А вы уже пошли что-то писать? Вы пытаетесь натянуть сову на глобус? Ну то есть просто впихнуть эту модель в вашу программу?

PS Посмотрел, уже налеплено куча классов, уже пошло поехало... Брррр....
Вам не лень сейчас снова будет все переписывать?
0
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
16.11.2018, 18:08  [ТС]
Цитата Сообщение от Storm23 Посмотреть сообщение
что вы собственно делаете?
Делаю классы для модели в отдельный проект dll.
Цитата Сообщение от Storm23 Посмотреть сообщение
Посмотрел, уже налеплено
Я нового ничего не выкладывал. Я не буду работать в существующем проекте, а создам новый, с новым названием.
Старый проект я не менял, только рассовал файлы по папкам и тренировался с гитхабом.

Будьте уверены, паровоз я не обгоняю!
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
16.11.2018, 18:33
Цитата Сообщение от ashsvis Посмотреть сообщение
Старый проект я не менял, только рассовал файлы по папкам и тренировался с гитхабом.
А, ок, значит я в старый проект посмотрел.

Цитата Сообщение от ashsvis Посмотреть сообщение
Делаю классы для модели в отдельный проект
Просто перед тем как что то писать лучше это обдумать и обсудить.
1
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
16.11.2018, 18:37  [ТС]
Цитата Сообщение от Storm23 Посмотреть сообщение
перед тем как
План такой. Я готовлю текущую модель по посту. Потом где-то я её должен выложить, что бы обсудить.
Завести ещё один черновой репозиторий. (Я кажется начал понимать, как работать с репозиторием со страницы самого гитхаба).
А вот через VS первый раз закидываю при создании проекта, а потом при обновлении ругается на отсутствие лицензии какой-то:
Bash
1
2
3
4
5
6
7
8
Получение из origin
Произошла ошибка при принесении: Git failed with a fatal error.
unable to access 'https://github.com/ashsvis/VectorGraphicsEditor/': SSL certificate problem: self signed certificate in certificate chain
 
Отправка master
Произошла ошибка при отправке ветви в удаленный репозиторий: Git failed with a fatal error.
unable to access 'https://github.com/ashsvis/VectorGraphicsEditor/': SSL certificate problem: self signed certificate in certificate chain
Pushing to https://github.com/ashsvis/VectorGraphicsEditor
0
Эксперт .NET
 Аватар для Rius
13068 / 7629 / 1669
Регистрация: 25.05.2015
Сообщений: 23,180
Записей в блоге: 14
16.11.2018, 18:42
Цитата Сообщение от ashsvis Посмотреть сообщение
Там ведь (в WPF) всё другое, даже точки Point на основе double...
В книге МакДональда в том числе рассмотрено создание векторного графического редактора на WPF.
0
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
16.11.2018, 19:22  [ТС]
Rius,
у меня уже есть хорошая книга: Петцольд. WPF базовый курс

Добавлено через 38 минут
Storm23,
Рабочий репозиторий: https://github.com/ashsvis/VectorGraphicsEditor
Содержит два проекта: заготовка с моделью и заготовка с интерфейсом формы (форма без кода).

Я остановился и весь внимание...
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
16.11.2018, 21:06
Цитата Сообщение от ashsvis Посмотреть сообщение
Я остановился и весь внимание...
Эмм.. а я думал вы что то будете предлагать
Обратите внимание, я понятия не имею как делать это приложение. У меня нет готовых решений, которые я бы мог выложить. Я просто рассуждаю, как бы я делал это приложение.

Ну ок, пойдем дальше.
Во-первых по коду, который вы написали. Там уже есть ошибки. Возможно вы просто неправильно поняли смысл некоторых методов.
Вот здесь:
C#
1
2
3
4
        public Pen GetPen(Figure figure)
        {
            return figure.Style.BorderStyle.GetPen(figure);
        }
Произойдет просто зацикливание. Вы же вызываете сами себя. Аналогично в классе Fill.

Далее, вот здесь:
C#
1
2
3
4
        public GraphicsPath GetTransformedPath()
        {
            return Geometry.Path;
        }
нужно возвращать не просто Geometry.Path, а Path умноженный на Transform.
Ведь смысл каков - Geometry возвращает путь без учета преобразований. А GetTransformedPath должен вернуть путь уже с учетом преобразований. На самом деле этот путь можно получить и без этого метода (просто перемножив путь на трансформ), но поскольку получать этот путь нужно будет часто в программе, то лучше сделать отдельный метод для удобства. Аналогично нужно сделать метод GetBounds который вернет RectangleF вмещающего прямоугольника для фигуры.
Т.о. GetTransformedPath должен выглядеть так:
C#
1
2
3
4
5
6
        public GraphicsPath GetTransformedPath()
        {
            var path = (GraphicsPath)Geometry.Path.Clone();
            path.Transform(Transform);
            return path;
        }
Далее, классы Border и Fill должны выглядеть примерно так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    class Fill
    {
        public Color Color { get; set; } = Color.White;
        public bool Draw { get; set; } = true;
 
        public virtual Brush GetBrush(Figure fig)
        {
            return new SolidBrush(Color);
        }
    }
 
    class Border
    {
        public Color Color { get; set; } = Color.Black;
        public bool Draw { get; set; } = true;
        public float Width { get; set; } = 1;
 
        public virtual Pen GetPen(Figure fig)
        {
            return new Pen(Color, Width);
        }
    }
Только наверное нужно переименовать Draw в IsVisible. Я немного ступил, когда давал название Draw. IsVisible - лучше.

Далее. На этом этапе лучше сделать небольшие unit-тесты. Тогда бы вы выяснили например, что ваша реализация GetPen - зациклена. Как делать простейшие unit-тесты смотрите в параллельной ветке здесь.

Добавлено через 54 минуты
Вернемся к нашей модели.

Рассмотрим такую важную часть как класс Geometry.

Мы бы могли сделать на каждую фигуру наследник от Geometry. Например class Sqaure: Geometry {...}. Но код этих классов будет очень простой и почти у всех одинаков. Поэтому смысла плодить кучу классов - нет.
Вместо этого воспользуемся паттерном Строитель.

Суть в том, что мы сделаем отдельный класс, назовем его FigureBuilder, который будет иметь ряд методов, которые будут создавать нужную геометрию для фигуры:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    /// <summary>
    /// Строит компоненты фигуры
    /// </summary>
    class FigureBuilder
    {
        public void BuildSquareGeometry(Figure fig)
        {
            var path = new GraphicsPath();
            path.AddRectangle(new RectangleF(-0.5f, -0.5f, 1, 1));
 
            fig.Geometry = new PrimitiveGeometry(path);
        }
 
        //и т.д. для всех примитивных фигур
        //todo
    }
Класс PrimitiveGeometry это наследник Geomtery, который просто содержит фиксированный GraphicsPath:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
    /// <summary>
    /// Содержит геометрию фиксированной формы
    /// </summary>
    class PrimitiveGeometry : Geometry
    {
        GraphicsPath path;
        public override GraphicsPath Path {get { return path; }}
 
        internal PrimitiveGeometry(GraphicsPath path)
        {
            this.path = path;
        }
    }
(У этого класса есть небольшая проблема: GraphicsPath - не сериализуемый класс. Это плохо, будем решать эту проблему позже.)

Методы для создания остальных фигур - думаю сделаете сами.
Методы FigureBuilder могут быть вызваны прямо из интерфейса, для создания фигуры необходимой формы.

Однако PrimitiveGeometry годится не для всех фигур. Например, нам нужна фигура типа Текст. Но строка текста задается (и меняется) пользователем. Поэтому GraphicsPath постоянно меняется и не может быть фиксированным, как в PrimitiveGeometry. Кроме того, текст зависит от шрифта и его размера.

Поэтому, для текста создаем отдельный класс TextGeometry. Примерно такой:
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
    /// <summary>
    /// Содержит текст
    /// </summary>
    class TextGeometry : Geometry
    {
        public string Text { get; set; }
        public string FontName { get; set; } = "Arial";
        public float FontSize { get; set; } = 14;
 
        private GraphicsPath path = new GraphicsPath();
 
        public override GraphicsPath Path
        {
            get
            {
                path.Reset();
                path.AddString(Text ?? "", new FontFamily(FontName), 0, FontSize, Point.Empty, StringFormat.GenericTypographic);
                return path;
            }
        }
 
        internal TextGeometry()
        {
        }
    }
Аналогично, создается класс для полигонов(ломаных) с произвольными точками, задаваемыми пользователем. Думаю, вы его можете написать сами.

Не забываем, что классы геометрии создаются только в FigureBuilder, поэтому там нужно создать соответствующие методы.
Замечу также, что конструкторы PrimitiveGeometry, TextGeometry и т.д. объявлены как internal. Это сделано для того, что бы экземпляры этих классов можно было создавать только внутри FigureBuilder, и нельзя было создавать снаружи, из пользовательского кода.
0
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
16.11.2018, 21:43  [ТС]
вы что то будете предлагать
Сначала вы меня притормаживали, когда я говорил, что делаю,
а когда остановился, то ждете предложений от меня
Да, зацикливание я пропустил, а думал - как все сошлось, все свойства
нужные нашел и подцепил.
Я поработаю над новым материалом и выложу, что получается.
Потом попробую продвинуться чуть далее самостоятельно.
И потом обсудим. Спасибо!
Ваши посты - как глоток свежего воздуха...
0
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
17.11.2018, 10:06  [ТС]
Storm23, нужен совет.

Добавил в решение юнит тесты, начал добавлять примитивные класс и тут такое в тестах:
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
[TestMethod]
public void CreateCircleFigureTestMethod()
{
    var builder = new FigureBuilder();
    var circle = new Figure()
    {
        Style = new Style()
        {
            BorderStyle = new Border(),
            FillStyle = new Fill()
        },
        Transform = new Matrix(),
        Renderer = new Renderer()
    };
    // настраиваем геометрию на круг
    builder.BuildCircleGeometry(circle);
    // проверим, что все внутренние классы были подключены
    CheckInternalClassesConnection(circle);
    // пробуем отрисовывать
    using (var pictbox = new PictureBox())
    {
        using (var canvas = pictbox.CreateGraphics())
        {
            circle.Renderer.Render(canvas, circle);
        }
    }
}
Здесь CheckInternalClassesConnection()

C#
1
2
3
4
5
6
7
8
9
10
private static void CheckInternalClassesConnection(Figure square)
{
    // проверим, что все внутренние классы были подключены
    Assert.AreNotEqual(square.Geometry, null, "Класс Figure.Geometry не подключен");
    Assert.AreNotEqual(square.Style, null, "Класс Figure.Style не подключен");
    Assert.AreNotEqual(square.Style.BorderStyle, null, "Класс Style.BorderStyle не подключен");
    Assert.AreNotEqual(square.Style.FillStyle, null, "Класс Style.BorderStyle не подключен");
    Assert.AreNotEqual(square.Transform, null, "Класс Figure.Transform не подключен");
    Assert.AreNotEqual(square.Renderer, null, "Класс Figure.Renderer не подключен");
}

При создании фигуры я всегда вынужден создавать внутренние классы:
C#
1
2
3
4
5
6
7
8
9
10
    var circle = new Figure()
    {
        Style = new Style()
        {
            BorderStyle = new Border(),
            FillStyle = new Fill()
        },
        Transform = new Matrix(),
        Renderer = new Renderer()
    };
Вопрос: необходимо создание этих классов с настройками по умолчанию. В каком месте модели это лучше сделать?

Пока остановился...
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
17.11.2018, 10:48
Цитата Сообщение от ashsvis Посмотреть сообщение
В каком месте модели это лучше сделать?
Значения по умолчанию можно сделать прямо в Figure:
C#
1
2
3
4
5
6
7
    class Figure
    {
        public Matrix Transform { get; set; } = new Matrix();
        public Geometry Geometry { get; set; }
        public Style Style { get; set; } = new Style();
        public Renderer Renderer { get; set; } = new Renderer();
    }
Если же нужны специфические параметры, то FigureBuilder их переприсвоит.

Цитата Сообщение от ashsvis Посмотреть сообщение
C#
1
2
3
4
5
6
7
8
// пробуем отрисовывать
      using (var pictbox = new PictureBox())
      {
            using (var canvas = pictbox.CreateGraphics())
            {
                  circle.Renderer.Render(canvas, circle);
            }
      }
Зачем же здесь PictureBox? Создавайте Bitmap. Затем Graphics.CreateFromImage(). А сам битмап можно сохранить на диск и посмотреть глазками.
0
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
17.11.2018, 13:31  [ТС]
Storm23,
для сериализации пути нашёл такую штуку:
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
/// <summary>
/// Сериализуемая обертка над GraphicsPath
/// </summary>
[Serializable]
public class SerializableGraphicsPath : ISerializable
{
    public GraphicsPath Path = new GraphicsPath();
 
    public SerializableGraphicsPath()
    {
    }
 
    private SerializableGraphicsPath(SerializationInfo info, StreamingContext context)
    {
        if (info.MemberCount > 0)
        {
            var points = (PointF[])info.GetValue("p", typeof(PointF[]));
            var types = (byte[])info.GetValue("t", typeof(byte[]));
            Path = new GraphicsPath(points, types);
        }
        else
            Path = new GraphicsPath();
    }
 
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (Path.PointCount <= 0) return;
        info.AddValue("p", Path.PathPoints);
        info.AddValue("t", Path.PathTypes);
    }
}
Буду применять её так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
/// Свойство возвращает путь, построенный по данным строки и свойств шрифта
/// </summary>
public override GraphicsPath Path
{
    get
    {
        // сброс пути. TODO: зачем?
        //_path.Reset();
        _serializablePath.Path.Reset();
        // добавляем в путь текстовую строку
        //_path.AddString(Text ?? "", 
        //    new FontFamily(FontName), 0, FontSize, Bounds,
        //                   StringFormat.GenericTypographic);
        _serializablePath.Path.AddString(Text ?? "",
            new FontFamily(FontName), 0, FontSize, Bounds,
                            StringFormat.GenericTypographic);
        // возвращаем настроенный путь
        //return _path;
        return _serializablePath.Path;
    }
}
Добавлено через 1 минуту
Надо будет ещё тест сейчас написать...
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
17.11.2018, 13:47
SerializableGraphicsPath
Где нашли? Сильно смахивает на код, который я сам когда то и писал )

Только не нужно делать публичным полем Path. Сделайте его свойством. А еще лучше сделайте операторы приведения типа. Что бы можно было писать:

C#
1
var path = (GraphicsPath)serializablePath;
0
 Аватар для ashsvis
923 / 503 / 202
Регистрация: 08.10.2018
Сообщений: 1,553
Записей в блоге: 11
17.11.2018, 13:52  [ТС]
Мдя... Тест провалился. Свойство Matrix тоже не сериализуется...
Цитата Сообщение от Storm23 Посмотреть сообщение
Где нашли?
Где конкретно, не помню, но уже давненько и таскаю в коде с собой
вот это:
Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*************************************************************************
Diagrams library
Copyright (c) 2010 Pavel Torgashov.
 
>>> LICENSE >>>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (www.fsf.org); either version 2 of the
License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
A copy of the GNU General Public License is available at
http://www.fsf.org/licensing/licenses
 
>>> END OF LICENSE >>>
*************************************************************************/
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
17.11.2018, 13:54
Цитата Сообщение от ashsvis Посмотреть сообщение
уже давненько и таскаю в коде с собой
Ясно.

Цитата Сообщение от ashsvis Посмотреть сообщение
Мдя... Тест провалился. Свойство Matrix тоже не сериализуется...
Не нужно пока заморачиваться техническими деталями. Нужно модель до кондиции довести сначала.
Технические детали будут только отвлекать.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
17.11.2018, 13:54
Помогаю со студенческими работами здесь

Векторный редактор карт
Здравствуйте, я создаю векторный редактор карт, пишу на шарпе в студии,с помощью GDI+ не понимаю как можно создать некоторые...

Как создать векторный редактор?
Добрый день! Какие элементы формы нужно использовать чтобы создать векторное изображение? Можно ли использовать для этого PictureBox?...

Графический редактор
Всем привет! Надо написать графический редактор на C# ( курсовая работа ) Минимальный набор функций - 1. Создание 3d объектов ...

Графический редактор
Помогите пожалуйста.Графический редактор. Напишите программу - редактор графики, аналог Paint. Добавьте инструменты &quot;Кисть&quot;,...

Графический редактор на С#
Можете выложить пример графического редактора типапаинт на си шарп. Р.S Не выкладывайте исходники паинт нета!


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
Модель микоризы: классовый агентный подход
anaschu 02.01.2026
Раньше это было два гриба и бактерия. Теперь три гриба, растение. И на уровне агентов добавится между грибами или бактериями взаимодействий. До того я пробовал подход через многомерные массивы,. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru