Імпорт графічних даних із DXF файлу засобами C#

Імпорт графічних даних із DXF файлу засобами C#

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

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

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

І так далі. Кожні два рядки і є групою. У першому рядку записується код групи, у другому – значення. Код групи є ідентифікатором того, що ця група описує. Наприклад, коди 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 показано малюнку 2.

файлу

Малюнок 2 – Структура секції HEADER.

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

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

Загальна структура секції TABLES показана малюнку 3.

даних

Малюнок 3 – Структура секції TABLES.

У принципі, зі схеми має бути все зрозуміло – кожна таблиця починається з двох груп – 0:TABLE та 2:Ім'я_таблиці, і закінчується групою 0:ENDTAB. Усередині самої таблиці найчастіше присутня група 70:Кількість_в_таблиці, також у нових версіях DXF можуть бути й інші параметри таблиці. Опис даних, що входять до таблиці, ведеться рядково, і група 0: Ім'я_таблиці сигналізує про початок нового рядка. Це схоже на опис змінних в секції HEADER, тут також немає«закриває» групи, всередині одного рядка може бути будь-яка кількість різних даних

А тепер приступаємо до найцікавішого.

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

Загальна структура секції показано малюнку 4.

файлу

Малюнок 4 – Структура секції ENTITIES.

В принципі, ніяких несподіванок у цій секції немає – початок примітиву визначається групою 0:Ім'я_примітиву, закінчується опис примітиву наступною групою 0:Ім'я_примітиву або 0:ENDSEC. Однак є важливий момент, пов'язаний зі складними примітивами – їх треба обробляти за особливими правилами, т.к. група 0:Ім'я_примітиву може зустрічатися всередині них. У цьому випадку кінець складного примітиву припадатиме на першу групу 0:Ім'я_примітиву після групи 0:SEQEND.

А тепер невеликий приклад.

Намалювали в AutoCAD три послідовно з'єднані лінії:

секції

0; Початок секції

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

0 ;Початок примітиву Лінія

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

8 ;Номер шару, на якому розташований примітив