Клуб "Трёх инженеров"
 

Email:

Пароль:

Забыли пароль?

Вступить в клуб?

Микроконтроллеры AVR

MS Visual Studio & C#

MODBUS-RTU & RS485

SolidWorks & Cosmos

Компьютерная техника

Мехатроника & Авиация

Силовая электроника

Всего статей:

Категорий/рубрик:

Комментариев:

Пользователей:

33

7

239

2125

 
видео прикол из Городка
На стрелке
Для корректного отображения этого элемента вам необходимо установить FlashPlayer и включить в браузере Java Script.

Импорт графических данных из DXF файла средствами C#

Автор: Меньшиков П. В.

Дата: 2010-05-29


При написании инженерных программ часто возникает необходимость работать с графическими данными, например чертежами деталей или планами зданий. Да, иногда можно использовать предложенные разработчиком возможности для расширения функционала самой CAD-системы (например, ObjectARX для AutoCAD), но все же часто возникает необходимость работать с чертежами в отдельной самописной программе. В том случае, если программа пишется «на один раз» и чертеж не сложный, вполне можно жестко задать его в самом коде. Также можно создать свой формат исходных данных, читаемый программой, и заполняемый пользователем вручную, возможно даже с помощью какого-то удобного пользовательского интерфейса. Но что, если чертеж состоит из сотни примитивов – линий, сплайнов, дуг окружностей, или использовать программу придется регулярно с разными чертежами? Возможно, быстрее (ну и как минимум интереснее) будет сделать в программе импорт графических данных из файлов чертежей какого-либо из существующих форматов.

Формат DXF, разработанный Autodesk в качестве универсального формата обмена чертежами между разными CAD-системами, наиболее подходит для этой задачи – этот формат открыт и бесплатен (в отличие, например, от DWG – SDK RealDWG для разработки приложений, работающих с этим форматом, стоит 9000$), широко распространен (практически все CAD-системы умеют делать экспорт в DXF) и удобен для программного считывания. Полную спецификацию формата можно найти на сайте Autodesk. Существуют также готовые модули для различных сред программирования, позволяющие осуществлять импорт из различных графических форматов, в том числе и DXF, однако они тоже в большинстве своем стоят денег, и включение их в свой проект может быть сопряжено с некоторыми сложностями – такие модули часто слабо документированы, спроектированы не самым лучшим образом, не обладают достаточным количеством функций, или наоборот, имеют в своем распоряжении столько возможностей, что поиск нужных превращается в тот еще квест. Рассмотрев все эти возможности, автор решил самостоятельно написать импорт из DXF, чего и Вам рекомендует.

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

0

SECTION

2

HEADER

9

$ACADVER

1

AC1009

И так далее. Каждые две строчки и являются группой. В первой строчке записывается код группы, во второй – значение. Код группы – это идентификатор того, что эта группа описывает. Например, коды 0-9 используются для обозначения строковых данных, а коды 10-59 – данных в формате с плавающей запятой двойной точности (double). Но коды используются не только для указания типа значения группы; они также обозначают, что именно содержится в значении группы. Например, код 0 используется в строго определенных случаях – начало секции, конец секции, начало таблицы и т.д., код 2 означает, что в значении группы описано имя (секции, таблицы, примитива…), код 9 используется только в секции HEADER и означает имя переменной. Полный список групповых кодов с описанием можно найти в DXF Reference, здесь и здесь.

Для удобства в дальнейшем в тексте статьи группы будут записываться в виде «Код_группы»:«Значение», например 2:HEADER.

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

Рисунок 1 – Структура DXF файла.

Как видно из схемы, DXF-файл состоит из разделов, которые именуются секциями (SECTION). Каждая такая секция начинается с двух групп – 0:SECTION и 2:Имя_секции, и заканчивается группой 0:ENDSEC. Количество и порядок секций в различных версиях DXF могут меняться, в новых версиях добавляются новые разделы, поэтому при чтении DXF файла программными средствами не стоит делать ставку на то, что в файле будут именно такие секции и именно в таком порядке. Лучше потратить чуть больше времени и написать гибкий парсер, который сможет прочитать любую версию формата DXF без ошибок и «выудить» оттуда нужные данные.

Рассмотрим некоторые секции, которые можно встретить практически в любой относительно современной версии формата DXF – секции HEADER (заголовок), TABLES (таблицы), ENTITIES (примитивы) и BLOCKS (блоки примитивов). Разобравшись в структуре этих секций, работа с любыми другими секциями из более новых форматов не составит труда – принцип везде один и тот же.

Секция HEADER

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

Рисунок 2 – Структура секции HEADER.

Поясню: в этой секции может содержаться произвольное количество переменных, каждая из которых может содержать произвольное количество данных разных типов. К примеру, переменная $EXTMIN содержит три числа с плавающей запятой двойной точности (double) – координаты X, Y и Z. Каждая переменная начинается с группы с 9:Имя_переменной. Специальной группы, обозначающей конец описания переменной, нет – переменная «кончается», когда встречается следующая группа с кодом 9 или группа 0:ENDSEC.

Секция TABLES

Здесь все несколько сложнее. В этой секции хранятся массивы данных, например, таблица слоев со всеми их свойствами, таблица стилей и т.д. Мне эта секция также не пригодилась, но для более серьезных приложений здесь явно находится довольно важная информация.

Общая структура секции TABLES показана на рисунке 3.

Рисунок 3 – Структура секции TABLES.

В принципе, из схемы должно быть все понятно – каждая таблица начинается с двух групп – 0:TABLE и 2:Имя_таблицы, и заканчивается группой 0:ENDTAB. Внутри самой таблицы чаще всего присутствует группа 70:Кол-во_строк_в_таблице, также в новых версиях DXF могут быть и другие параметры таблицы. Описание данных, входящих в таблицу, ведется построчно, и группа 0:Имя_таблицы сигнализирует о начале новой строки. Это похоже на описание переменных в секции HEADER, здесь также нет «закрывающей» группы, внутри одной строки может быть любое количество различных данных

А теперь приступаем к самому интересному.

Секция ENTITIES

Вообще-то, секция ENTITIES обычно располагается после секции BLOCKS, однако эти две секции мало чем отличаются, и начать лучше с более простой. В этом разделе файла хранятся данные о примитивах – собственно, это и есть графические данные чертежа. Примитив – это какая-то геометрическая фигура, например точка, линия, окружность, дуга и т.д. Существуют также сложные примитивы, состоящие из других примитивов, например полилиния (POLYLINE), состоящая из вертексов, соединенных прямыми или дугами.

Общая структура секции показана на рисунке 4.

Рисунок 4 – Структура секции ENTITIES.

В принципе, никаких неожиданностей в этой секции нет – начало примитива определяется группой 0:Имя_примитива, заканчивается описание примитива следующей группой 0:Имя_примитива или 0:ENDSEC. Однако есть важный момент, связанный со сложными примитивами - их надо обрабатывать по особым правилам, т.к. группа 0:Имя_примитива может встречаться внутри них. В этом случае конец сложного примитива будет приходиться на первую группу 0:Имя_примитива после группы 0:SEQEND.

А теперь небольшой пример.

Нарисовали в AutoCAD три последовательно соединенные линии:

Сохранили в формате DXF, открываем текстовым редактором, смотрим, как это выглядит в тексте (для удобства чтения группы разделены пустыми строками):

0 ;Начало секции

SECTION


2 ;Секция Примитивы

ENTITIES


0 ;Начало примитива Линия

LINE


5 ;Уникальный идентификатор примитива

1EC


8 ;Номер слоя, на котором расположен примитив

0


10 ;Координата Х начальной точки

0.0


20 ;Координата Y начальной точки

0.0


30 ;Координата Z начальной точки

0.0


11 ;Координата X конечной точки

452.55480855023052


21 ;Координата Y конечной точки

601.16555418995085


31 ;Координата Z конечной точки

0.0


0 ;Начало примитива Линия

LINE


5 ;и т.д.

1ED


8

0


10

452.55480855023052


20

601.16555418995085


30

0.0


11

291.779542423983


21

601.16555418995085


31

0.0


0

LINE


5

1EE


8

0


10

291.779542423983


20

601.16555418995085


30

0.0


11

672.97250179134153


21

321.2681517584133


31

0.0


0 ;Конец секции

ENDSEC

На вид ничего сложного, не так ли? В принципе, так оно и есть, хотя даже в примере с линиями в описании примитивов может присутствовать намного больше параметров. Полный список всех групповых кодов для примитивов и их значений можно, опять же, посмотреть в DXF Reference, тут.

Секция BLOCKS

В этой секции также содержится описание примитивов, однако здесь они объединены в блоки. Каждый блок имеет уникальный идентификатор и название и может использоваться в секции ENTITIES с помощью примитива INSERT. Таким образом, можно, например, один раз нарисовать какую-то сложную фигуру, объединить входящие в нее примитивы в блок, назвать, скажем «ФИГУРНОЕ_ОТВЕРСТИЕ» и использовать его несколько раз в секции ENTITIES, вместо того, чтобы несколько раз рисовать одну и ту же фигуру. Преимуществом перед обычным копированием-вставкой является в основном то, что при изменении описания этого блока в секции BLOCKS на чертеже автоматически изменятся все вставки этого блока. Общая структура секции BLOCKS показана на рисунке 5.

Рисунок 5 – Структура секции BLOCKS.

Несколько странным в данном случае является то, что часть параметров блока определяются после группы 0:BLOCK, а остальные – после, казалось бы, закрывающей группы 0:ENDBLK. Какая в этом логика, для меня осталось загадкой. Описания параметров и групповых кодов для секции BLOCKS можно подсмотреть в DXF Reference тут.

Пример использования блоков. Описание блока в секции BLOCKS:

0

BLOCK


8

0


2

ВНЕШНИЙ_КОНТУР


70

0


10

0.0


20

0.0


30

0.0


3

ВНЕШНИЙ_КОНТУР


1

0


LINE


5

24C


8

0


10

0.0


20

0.0


30

0.0


11

0.0000000000000568


21

520.92016302072079


31

0.0


0

LINE


5

24D


8

0


10

0.0000000000000568


20

520.92016302072079


30

0.0


11

259.3183916651933


21

520.92016302072079


31

0.0


0

ARC


5

24E


8

0


10

259.3183916651933


20

355.05503667966877


30

0.0


40

165.86512634105199


50

0.0


51

90.0


0

LINE


5

24F


8

0


10

425.18351800624532


20

355.05503667966877


30

0.0


11

578.28001640457023


21

209.9230504388434


31

0.0


0

LINE


5

250


8

0


10

578.28001640457023


20

209.9230504388434


30

0.0


11

578.28001640457023


21

0.0000000000000568


31

0.0


0

LINE


5

251


8

0


10

578.28001640457023


20

0.0000000000000568


30

0.0


11

0.0


21

0.0


31

0.0


0

ENDBLK


5

24B


8

0

Использование этого блока в секции ENTITIES:

0

INSERT


5

252


8

0


2

ВНЕШНИЙ_КОНТУР


10

260.65144090254489


20

503.97895235368458


30

0.0

И вот как это выглядит на чертеже:

На этом первая часть статьи, посвященная краткому описанию формата DXF, заканчивается. Во второй части будут рассмотрены уже более практические вопросы написания импорта из DXF файла на языке C#.

Рейтинг:

Просмотров: 77166

Комментарии:

Автор: Dmitry (2011-02-21, 13:20)

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

Автор: KnZ (2011-02-17, 23:55)

RE: Dmitry. Лично я, когда искал информацию по этому вопросу (искал, правда, под C#) ничего вразумительного не нашел, пришлось разбираться и писать самому с нуля. Все, кстати, оказалось проще, чем я думал, главное - хорошо разобраться со спецификацией DXF формата (ссылка в статье есть). Чтение файла построчно никаких проблем не доставляет, отрисовка примитивов на форме, в общем-то, тоже. Есть пара примеров под С++ на codeproject.com, например вот: http://www.codeproject.com/KB/cs/dxfreader.aspx
Но, насколько я вижу по коду, сделано там все довольно халтурно, реализовано только чтение примитивов, причем не без косяков. Взять за основу можно, как образец для подражания - не стоит. Хочу, кстати, извиниться перед читателями за то, что вторая часть статьи так и не увидела свет - безвозвратно погиб винт со всей моей рабочей информацией, а проект, в котором использовался DXF формат, к тому времени уже был не актуален, и заново я все это писать не стал. [Ответить]

Автор: Dmitry (2011-02-17, 15:32)

огромное спасибо автору за доступные объяснения! не подскажете, где можно еще почерпать информацию на тему обработки формата dxf средствами C++? [Ответить]

Гости не имеют права добавлять комментарии и проставлять рейтинг.