Добавить в корзинуПозвонить
Найти в Дзене
Хроники Георга

В мире open-source. Xbim Toolkit. Часть 2. Создание и валидация файла IFC (Xbim Essentials, Xbim Xplorer)

Введение: продолжим рассмотрение пакета Xbim Essentials (см. предыдущую часть тут), и сегодня рассмотрим его опции по созданию новых элементов (генерацию IFC файла), коснемся немного темы структуры файла IFC. В качестве рабочего кейса попробуем реализовать создание элемента типа "поверхность" как геометрическое представление структуры IfcSite. 1. Готовим исходные данные Перед тем как генерировать новый IFC-элемент типа поверхности, давайте сперва посмотрим, как в целом может выглядеть поверхность в IFC-файлах. Глобально, это 2 случая - плоское представление и выдавленное 3D-тело; нас, естественно, будет интересовать первый вариант. В качестве примера обратимся к простой поверхности в среде Civil 3D: Примечание: здесь и далее будем рассматривать простейшую поверхность только для того, чтобы ускорить процессы обработки данных. От размера поверхности разрабатываемая процедура никак не зависит (мы так делаем сугубо для удобства отладки). Экспортируем данную поверхность в LandXML-представле
Оглавление

Введение: продолжим рассмотрение пакета Xbim Essentials (см. предыдущую часть тут), и сегодня рассмотрим его опции по созданию новых элементов (генерацию IFC файла), коснемся немного темы структуры файла IFC. В качестве рабочего кейса попробуем реализовать создание элемента типа "поверхность" как геометрическое представление структуры IfcSite.

Официальный логотип https://docs.xbim.net/img/xBim_toolkit_logo.png
Официальный логотип https://docs.xbim.net/img/xBim_toolkit_logo.png

1. Готовим исходные данные

Перед тем как генерировать новый IFC-элемент типа поверхности, давайте сперва посмотрим, как в целом может выглядеть поверхность в IFC-файлах. Глобально, это 2 случая - плоское представление и выдавленное 3D-тело; нас, естественно, будет интересовать первый вариант. В качестве примера обратимся к простой поверхности в среде Civil 3D:

Пример поверхности из 8 треугольников (объектов триангуляции)
Пример поверхности из 8 треугольников (объектов триангуляции)

Примечание: здесь и далее будем рассматривать простейшую поверхность только для того, чтобы ускорить процессы обработки данных. От размера поверхности разрабатываемая процедура никак не зависит (мы так делаем сугубо для удобства отладки).

Экспортируем данную поверхность в LandXML-представление, а также в Revit как набор 3D-граней

Создаем в Revit топо-поверхность по этим данным
Создаем в Revit топо-поверхность по этим данным
Формируем IFC с базовыми настройками для данной геометрии (из-под 3D-вида)
Формируем IFC с базовыми настройками для данной геометрии (из-под 3D-вида)
Открываем сформированный IFC в среде XBim Xplorer
Открываем сформированный IFC в среде XBim Xplorer

2. Формируем структуру элементов (теоретически)

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

2.1 Анализ в Xbim Xplorer

Для этого давайте опустимся до конца (до исходников поверхности), и пойдем к началу - для лучшего восприятия материала:

1. Группа из трех точек образует треугольник (3D-грань), и этот связующий элемент имеет тип IfcPolyLoop
1. Группа из трех точек образует треугольник (3D-грань), и этот связующий элемент имеет тип IfcPolyLoop
2. Каждый треугольник (элемент IfcPolyLoop) входит в представление IfcFaceOuterBound - как внешняя грань структуры
2. Каждый треугольник (элемент IfcPolyLoop) входит в представление IfcFaceOuterBound - как внешняя грань структуры
3. Элемент IfcFace является определением грани (по сути, дублирование IfcFaceOuterBound)
3. Элемент IfcFace является определением грани (по сути, дублирование IfcFaceOuterBound)
4. Набор граней поверхности (нескольких IfcFace) формируют структуру IfcConnectedFaceSet (связанные грани)
4. Набор граней поверхности (нескольких IfcFace) формируют структуру IfcConnectedFaceSet (связанные грани)
5.1 Данное геометрическое представление IfcConnectedFaceSet является частью [геометрической] элемента IfcFaceBasedSurfaceModel, где помимо него также присутствует "стиль" поверхности заданный элементом IfcStyledItem
5.1 Данное геометрическое представление IfcConnectedFaceSet является частью [геометрической] элемента IfcFaceBasedSurfaceModel, где помимо него также присутствует "стиль" поверхности заданный элементом IfcStyledItem
5.2 Стиль IfcStyledItem состоит из двух выражений - первое #200 - это фактически указатель, к ЧЕМУ применяется данный стиль; а второе - IfcPresentationStyleAssignment -- само описание стиля
5.2 Стиль IfcStyledItem состоит из двух выражений - первое #200 - это фактически указатель, к ЧЕМУ применяется данный стиль; а второе - IfcPresentationStyleAssignment -- само описание стиля
5.3 Описание стиля IfcPresentationStyleAssignment ссылается на стиль с текстурой поверхности - элемент IfcSurfaceStyle
5.3 Описание стиля IfcPresentationStyleAssignment ссылается на стиль с текстурой поверхности - элемент IfcSurfaceStyle
5.4 Стиль поверхности IfcSurfaceStyle содержит описание для "отображаемого стиля" - земли -- элемента IfcSurfaceStyleRendering
5.4 Стиль поверхности IfcSurfaceStyle содержит описание для "отображаемого стиля" - земли -- элемента IfcSurfaceStyleRendering
5.5 И, наконец, стиль для отображения - содежит зависимый элемент IfcColourRgb (цветовой код текстуры поверхности)
5.5 И, наконец, стиль для отображения - содежит зависимый элемент IfcColourRgb (цветовой код текстуры поверхности)
5.6 Элемент IfcColourRgb - цветовая текстура поверхности
5.6 Элемент IfcColourRgb - цветовая текстура поверхности
6. Набор граней (геометрия поверхности) + стиль - это суммарно элемент  IfcFaceBasedSurfaceModel (выделен); он является частью структуры IfcShapeRepresentation. Помимо него здесь есть отсылка к типу модели (IfcGeometricRepresentationSubContext) - 3D-вид, упоминание слоя данных - IfcPresentationLayerAssignment с именем "C-TOPO-____-OTLN" - то что пришло из Civil 3D;
6. Набор граней (геометрия поверхности) + стиль - это суммарно элемент IfcFaceBasedSurfaceModel (выделен); он является частью структуры IfcShapeRepresentation. Помимо него здесь есть отсылка к типу модели (IfcGeometricRepresentationSubContext) - 3D-вид, упоминание слоя данных - IfcPresentationLayerAssignment с именем "C-TOPO-____-OTLN" - то что пришло из Civil 3D;
7. Выделенная структура IfcShapeRepresentation (описание поверхности) вместе с другим элементом IfcShapeRepresentation (видимо, плоская граница) входят в состав "результирующего набора" IfcProductDefinitionShape
7. Выделенная структура IfcShapeRepresentation (описание поверхности) вместе с другим элементом IfcShapeRepresentation (видимо, плоская граница) входят в состав "результирующего набора" IfcProductDefinitionShape
8. И, наконец, этот IfcProductDefinitionShape и является "представлением" элемента IfcSite!
8. И, наконец, этот IfcProductDefinitionShape и является "представлением" элемента IfcSite!

2.2 Составление блок-схемы

Теперь давайте составим вспомогательную блок-схему, иллюстрирующую логику подчиненности элементов внутри файла IFC

Схема структуры файла (для описания поверхности). Составлена в MS Visio
Схема структуры файла (для описания поверхности). Составлена в MS Visio

3. Закладываем структуру кода

Теперь, предварительно определившись с структурой файла, перейдем к написанию методов для генерации отдельных элементов. Работа будет состоять из трех глобальных частей - создание нового файла IFC, реализация чтение файла LabdXML и формирование на его основе геометрического представления поверхности, добавление данного геометрического представления в элемент поверхности.

Примечание: в общем случае, пакет Xbim Essentials не предназначен под генерацию IFC-файлов; в нём есть набор базовых типов элементов, доступных для создания, прочие же имеют тип ReadOnly и предназначены только для считывания. В силу этого - нам потребуется реализовать свои методы для генерации отдельных элементов файла IFC.

3.1 Создание файла

Добавляем общие параметры проекта - фиксированный файловый путь до файла LandXML и пока не созданный путь до файла IFC; также есть счетчик строк файла IFC, который нужен будет в дальнейшем
Добавляем общие параметры проекта - фиксированный файловый путь до файла LandXML и пока не созданный путь до файла IFC; также есть счетчик строк файла IFC, который нужен будет в дальнейшем
Метод для создания пустого IFC файла с элементом IFCPROJECT и единицами проекта, а также структурой IFCGEOMETRICREPRESENTATIONCONTEXT
Метод для создания пустого IFC файла с элементом IFCPROJECT и единицами проекта, а также структурой IFCGEOMETRICREPRESENTATIONCONTEXT
Вид сгенерированного файла (в Notepad++)
Вид сгенерированного файла (в Notepad++)

3.2 Создание точек в файле

Теперь будем изменять данный файл, добавляя в него информацию о точках (элемент IfcCartesianPoint). В силу того, что точки в файле LandXML уже пронумерованы, возникнет ситуация, что при добавлении граней, надо будет парсить IFC-файл с целью поиска, какой номер стал у той или иной точки. Чтобы этого не делать, создадим временный список типа List <string> PointsNums, который будет хранить номер точки из файла LandXML и новый номер точки (позицию строки в IFC), разделенные к примеру, символом "|".

Корректнее двигаться по описанию отдельных граней (какими точками она определяется), и данные точки заносить в IFC-файл, причем обязательно фиксируя номера точек в список (я для удобства сделал второй временный список PointsNumbers)

Метод для формирования точек
Метод для формирования точек

Дадим разъяснение для блока коды выше.

Имеется внутренний метод AddInfoAboutPoint, принимающий на вход номер точки по структуре файла LandXML. Номер сразу же фиксируется во временном списке PointsNumbers (чтобы не добавить эту же точку ещё раз в файл, если она будет присутствовать в другой грани). Далее идёт запрос к структуре <Pnts> файла LandXML с целью найти точку с таким порядковым номером, и из её значения извлекаются координаты этой точки (X,Y,Z) в виде строчного массива PointCoordinates. Далее точки преобразуются в тип Double, умножаются на 1000 (так как в файле LandXML единицы измерения - метры, а в IFC - мм) и записываются в структуру элемента IfcCartesianPoint. После этого в другой временный список PointsNums записывается номер точки по файлу LandXML + табулятор '|' + новый номер точки (строка IFC - метод EntityLabel).

Имеется внутренний метод IsContain, возвращающий булеву истину/ложь, если заданное значение уже имеется во временном списке PointsNumbers (только номера точек).

Активируются оба этих метода при переборе граней в рамках блока <Faces> в файле LandXML. Из каждой грани берется набор точек, которые её образуют, каждая точка сперва ищется в списке уже имеющихся точек PointsNumbers, если её там нет - то запускается метод AddInfoAboutPoint, добавляющий её в IFC-файл.

По окончанию, транзакция на изменение закрывается. и файл сохраняется. Причем несмотря на тип "SaveAs", он просто дозаписывается.

3.3 Создание набора граней

Здесь уже наступают сложности, так как элементы IfcPolyLoop, IfcFaceOuterBound, IfcFace, IfcConnectedFaceSet в рамках элементов геометрического представления поверхности не имеют методов создания (только чтения), поэтому элементы выше нам предстоит генерировать вручную.

Перед тем как это делать, необходимо в файле IFC избавиться от "формального окончания" - строк ниже:

ENDSEC;
END-ISO-10303-21;

Для этого мы введём 2 вспомогательных метода:

Метод DeleteEndOfFile создает временный файл, в котором отсутствуют последние строки, и подменяет оригинальный файл данным временным; Метод RepairEndOdFile добавляет в конец файла эти строки - для возможности повторной инициализации файла как IFC-структуры
Метод DeleteEndOfFile создает временный файл, в котором отсутствуют последние строки, и подменяет оригинальный файл данным временным; Метод RepairEndOdFile добавляет в конец файла эти строки - для возможности повторной инициализации файла как IFC-структуры
Метод AddFacesToIfc - создает геометрическое представление поверхности IFCFACEBASEDSURFACEMODEL
Метод AddFacesToIfc - создает геометрическое представление поверхности IFCFACEBASEDSURFACEMODEL

Дадим разъяснение методу выше:

Есть вспомогательный метод GetNewNum, анализирующий раннее сформированный временный список PointsNums, на предмет получения строки с новым именем строки (элемент IfcCartesianPoint) для данной точки - вершины триангуляции, на вход он принимает номер точки по файлу LandXML.

В рамках цикла foreach по переборку каждой грани происходит процесс формирования массива из номеров строк IFC для данных точек; далее эти точки записываются в структуру IFCPOLYLOOP, которая записывается в с IFCFACEOUTERBOUND, которая входит в IFCFACE. А вот набор разных IFCFACE - это уже структура для IFCCONNECTEDFACESET.

По завершению действий наш временный список можно очистить:

PointsNums.Clear();
Сформированная структура файла IFC (вид в Notepad++)
Сформированная структура файла IFC (вид в Notepad++)

3.4 Создание стиля поверхности

На самом деле, стиль не является обязательным, но тем не менее в рамках знакомства, его всё равно реализуем. К слову, методы Xbim Essentials также не позволяют данные элементы создавать (только читать).

Здесь будет проще всего взять за основу то, как стиль задан для поверхности в рамках существующего IFC-файла, и подменить в нем номера строк для нашего случая.

Методы для формирования текстуры поверхности
Методы для формирования текстуры поверхности

3.5 Размещаем поверхность в структуре IfcSite

Теперь осталось лишь встроить нашу поверхность в площадку IfcSite (которую, к слову, потребуется сперва ещё и создать).

Перед этим я добавил в метод CreateBasicIfc_File() операцию по определению номера для элемента IfcRepresentationContext

Вспомогательный метод для определения IfcRepresentationContext (представление модели)
Вспомогательный метод для определения IfcRepresentationContext (представление модели)
Метод по окончательному формированию файла, добавление геометрических представлений IfcShapeRepresentation и IfcProductDefinitionShape
Метод по окончательному формированию файла, добавление геометрических представлений IfcShapeRepresentation и IfcProductDefinitionShape

Здесь мы уже работает с IFC как со структурой (вернули ему привычный вид методом RepairEndOdFile()). Создаем элемент IfcSite и указываем, что его Representation = нашей созданной структуре IfcProductDefinitionShape.

Теперь запускаем наш код и пробуем подгрузить файл в Xbim Xplorer:

Вид загруженной модели в среде Xbim Xporer
Вид загруженной модели в среде Xbim Xporer
Вид загруженной модели в среде IFC++
Вид загруженной модели в среде IFC++

4. Важные комментарии

Структуру-то мы сформировали, но теперь необходимо дать комментарий к сделанным изменениям. Я миновал вопрос привязки площадки IfcLocalPlacement к нашей структуре IfcSite. Она должна быть как минимум быть, но по нулям:

Добавляем элемент IfcLocalPlacement
Добавляем элемент IfcLocalPlacement

Примечание: выставление для IfcLocalPlacement ненулевой точки оправдано при сборке модели в общих координатах (например, в Revit в параметры IfcLocalPlacement идут координаты точки съемки, а вся внутренняя геометрия разбивается относительно внутренних координат). При этом если импортировать итоговый IFC в САПР, то координаты IfcLocalPlacement останутся без внимания, и объект встанет только во внутренних координатах. Поэтому для изменения координат файла мы будем руководствоваться принципом предварительного пересчета каждой точки (см. статью за дополнительными пояснениями).

5. Выводы:

В данной статье мы рассмотрели итеративный процесс по формированию IFC-файла с помощью Nuget-пакета Xbim Essentials со структурой поверхности. В большей степени мы генерировали элементы IFC вручную из-за отсутствия их поддержки в пакете, но отдельные вещи пробовали создавать методами пакета. В качестве проверки - мы использовали среду Xbim Xplorer.

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

Не пропускайте публикации, подписывайтесь на Telegram-канал с тизерами статей.