Меня зовут Кирилл, я работаю BIM-менеджером, вместе со своей коллегой Анастасией @nobimnolife разрабатываем IDS Core. В этой статье – как мы дошли от просмотрщика IFC браузере до постобработчика.

Интерфейс
Интерфейс

В прошлый раз мы писали о том, как не узнавать о пустом параметре из файла, которого полчаса назад не существовало, — то есть проверять модель до экспорта в IFC. Но бывает и наоборот: файл уже выгружен, отправлен, и проверка нашла мелочь. В маппинге написали для помещений площадь как IfcArea вместо IfcReal. У пары стен пустой RUS_Object. Витраж попал не на тот этаж. Исправить в модели — минута, перевыгрузить — снова час, а иногда и больше.

В профильном чате такие вещи давно лечат Блокнотом: открыть IFC и заменить IFCAREA на IFCREAL. Это работает, пока файл открывается и пока замена не задевает лишнего. Нам захотелось сделать то же самое аккуратно: открыть IFC в браузере, исправить что нужно и скачать файл, который пройдёт строгую проверку по схеме. Ниже — что оказалось сложнее, чем выглядело, и что нашёл валидатор, когда мы были уверены, что всё готово.

Почему в браузере

В принципе, для постобработки идеально подходит Blender с BIM Bonsai внутри. Но нам хотелось сделать такой инструмент, который можно открыть в любом месте и быстро посмотреть/поправить модель, ведь обычно так всегда и получается – нужно срочно здесь и сейчас.

Модель — чужая собственность, часто под соглашением о неразглашении. Отправлять её на сервер ради правки одного параметра не хотелось ни нам, ни тем, кому мы это показывали. Поэтому всё считается на компьютере пользователя: разбор IFC — библиотека web-ifc, скомпилированная в WebAssembly, отображение — three.js. Сервер только один раз отдаёт страницу и библиотеки, около 7 МБ, а модель никуда не уходит.

У такого подхода есть цена: мы целиком зависим от того, как web-ifc читает и пишет файл. Большая часть историй ниже — именно об этом.

Две модели расходятся на семь метров

Первая задача — открыть сразу несколько разделов одного объекта: архитектуру, благоустройство, проектную ситуацию. В web-ifc есть удобная настройка «сдвинуть модель к началу координат» — без неё координаты в общей системе (у нас это сотни метров по одной оси и пять с половиной километров по другой) плохо ложатся во float32, с которым работает видеокарта.

Мы её включили и получили разъехавшиеся модели: архитектура и благоустройство разошлись на 6–7 метров в плане и почти на 4 по высоте. Настройка сдвигает каждую модель к её собственному началу, и у каждой оно своё.

Каждая модель к своему началу — или одна точка отсчёта для всех
Каждая модель к своему началу — или одна точка отсчёта для всех

Каждая модель к своему началу — или одна точка отсчёта для всех

Решение простое, но важна последовательность: все модели читаются в исходных координатах, точка отсчёта берётся от первой модели и вычитается из координат каждой вершины в double — до того, как вершина попадёт в 32-битный буфер. float32 хранит около семи значащих цифр, и если сначала перевести «5545,252» в float32, а потом вычесть, точность уже потеряна.

Свойство — это не строка

Казалось бы, заполнить параметр — значит найти строку со свойством и поменять значение. Но IFC — граф. Значение лежит в IfcPropertySingleValue, оно входит в набор IfcPropertySet, а набор привязан к элементам связью IfcRelDefinesByProperties — и привязан может быть сразу к нескольким.

Это не теория. Когда мы создали новый набор свойств сразу двенадцати стенам, получился один набор и одна связь на всех. Поменять значение у одной стены «на месте» — значит поменять его у всех двенадцати. Поэтому правка устроена так: если набор принадлежит только выбранным элементам — меняем его, если он общий с кем-то ещё — для выбранных делается копия набора со своей связью, а остальные не трогаются. На том же примере: у одной стены значение стало «2», у одиннадцати осталось «1».

IfcArea и почему Блокнот опасен

Требования к ЦИМ для АГР задают у площадей и длин тип IfcReal, у текстов — IfcText. Экспортёры с этим не согласны: Civil 3D пишет тексты как IfcLabel, также может быть неправильный маппинг . Проверка такие значения не засчитывает.

Глобальная замена в Блокноте меняет тип везде, включая стандартные наборы Pset_*, где IfcLabel как раз правильный. Мы меняем тип только у тех свойств, для которых его задаёт IDS — файл требований, по которому идёт проверка, — а если IDS не загружен, только у свойств RUS_*. Значение при этом остаётся тем же числом: было IFCAREA (116.64) — станет IFCREAL(116.64).

Проверяли на копии реальной модели, испорченной так же, как это делает экспортёр: 355 кодов с IfcLabel, 129 площадей и 93 объёма с «измерениями». После исправления все 577 значений и типов совпали с правильной выгрузкой.

Сменить класс — значит сменить и тип

Смена класса выглядела самой простой операцией: переписать запись элемента под тем же номером, сохранив первые восемь атрибутов, общих у всех строительных элементов. Покрытия превратились в плиты, файл открылся, всё выглядело правильно.

Строгая проверка схемы — с правилами WHERE, а не только структурой — нашла нарушение IfcSlab.CorrectTypeAssigned: плита ссылалась на тип IfcCoveringType. Класс типа обязан соответствовать классу элемента. Теперь тип меняет класс вместе с элементами, а если он общий — например, один тип у 39 стен, а перевести нужно две, — для выбранных делается копия типа нужного класса вместе с его наборами свойств. Остальные 37 стен остаются на прежнем типе. Но чаще мы используем его на всеми любимых косоурах внутри системных лестниц.

Сменить этаж и ничего не сдвинуть

Частая ошибка экспорта — элементы привязаны не к тому этажу. Перенести элемент в связь другого IfcBuildingStorey недостаточно: его положение в IFC задано относительно размещения этажа. Поменяйте этаж, не пересчитав положение, — и стена уедет вверх на высоту этажа.

Положение пересчитывается так, чтобы мировое осталось прежним
Положение пересчитывается так, чтобы мировое осталось прежним

Положение пересчитывается так, чтобы мировое осталось прежним

Положение элемента — это цепочка IfcLocalPlacement до корня, у каждого звена своя матрица из точки и двух осей. Перемножив цепочку, получаем мировую матрицу элемента, а новое локальное положение — это она же, выраженная от размещения нового этажа: L′ = P₂⁻¹ · P₁ · L. Помещения, к слову, привязаны к этажу не «содержанием», а агрегацией, а части витражей — к своему витражу, поэтому переносится всегда родитель.

Проверка: все 36 помещений и 48 витражей с частями перенесли на другой этаж, сохранили файл и сравнили габариты всех 349 элементов с исходными. Наибольшее расхождение — 0,0000001 м.

Удалить — значит понять, что ещё уйдёт

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

Что ссылается на элемент и что уходит вместе с ним
Что ссылается на элемент и что уходит вместе с ним

Что ссылается на элемент и что уходит вместе с ним

Первая версия чистила связи и удаляла части и проёмы. Висячих ссылок не осталось — мы проверили это отдельно. А валидатор нашёл 143 нарушения: у каждого удалённого элемента осталось описание формы IfcProductDefinitionShape, а стандарт требует, чтобы оно принадлежало какому-нибудь элементу. Удалили и его — следующий прогон нашёл 161 нарушение правила WR11: теперь бесхозными остались сами представления формы IfcShapeRepresentation. Только после третьего круга — удалять описание формы, его представления и размещение, если они больше никому не нужны, — файл с удалёнными дверью и ещё 75 элементами прошёл проверку чисто.

Вывод, который мы для себя записали: «файл открывается» и «файл корректен» — разные утверждения, и между ними — валидатор с правилами схемы.

Что мы узнали о web-ifc

web-ifc — та часть, которая читает и пишет сам файл IFC; всё, что мы делаем с моделью, проходит через неё. Хорошая и быстрая библиотека, но у неё есть особенности, о которых узнаёшь только на реальном файле. Три из них стоили нам больше всего времени.

«Да» превращалось в «Нет». Мы записывали логический параметр — скажем, «Наружная = да», — и в файле оказывалось IFCBOOLEAN(.F.), то есть «нет». Ошибки при этом никакой: библиотека принимает значение молча и пишет неправильное. Выяснилось, что «да» она понимает только как букву T, а не как привычное программисту true.

Удалённое продолжало числиться в модели. После удаления элемент исчезает из файла при сохранении, но в списке «все связи такого-то типа» его место остаётся — пустой записью. Следующая правка проходила по этому списку, натыкалась на пустоту и падала. Поэтому всё, что перебирает записи модели, у нас сначала отсеивает удалённое.

Записать можно не всё и не любым способом. В библиотеке два способа записать изменение: удобный — «вот элемент, вот его новые поля» — и низкоуровневый, «вот строка файла целиком». Удобный не даёт поменять класс элемента, а без этого не сменить класс. Низкоуровневый не принимает свойства со значением — даже если записать обратно ровно то, что только что прочитал. Пришлось делить: классы и связи — одним способом, свойства — другим.

Отмена, которая не врёт

Отменять правки в библиотеке, которая сама этого не умеет, можно одним способом: на время правки перехватывать запись и запоминать прежний вид каждой затронутой строки, а всё, что правка создала, при отмене удалять. Удалённые элементы держатся в памяти вместе с геометрией, чтобы вернуть их и на экран.

Мы проверили это жёстко: четыре правки подряд — параметр 146 стенам, смена класса, перенос 36 помещений, удаление 204 элементов — и четыре отмены. Сохранённый после этого файл совпал с исходным по составу, этажам и размещениям. Тот же механизм возвращает модель в исходное состояние, если правка упала на середине, — чтобы не оставить файл записанным наполовину.

Проверка по IDS — там же

Раз модель уже открыта, логично проверить её тут же. Проверка повторяет правила, которыми мы пользуемся до экспорта, — применимость спецификации по классу и свойствам, обязательность, допустимые значения, — и добавляет то, чего до экспорта не видно: тип данных. Четыре файла требований АГР — 103 спецификации — проверяются на модели в несколько сотен элементов без заметной задержки, ошибки подсвечиваются в 3D, а неверный тип исправляется кнопкой рядом с причиной. Можно загрузить любые ids, даже несколько сразу.

Что в итоге

Сейчас порядок у нас такой. Если проверка нашла в уже выгруженном файле мелочь — пустой параметр, не тот тип данных, элемент не на своём этаже или забыли что-то скрыть на виде экспорта — мы не возвращаемся в Revit, а открываем IFC в браузере, исправляем и тут же проверяем по тем же IDS. Перевыгрузка остаётся для того, что правкой файла не решается: геометрии и ошибок самой модели. Но исходную модель всё равно поправляем — иначе следующая выгрузка принесёт ту же ошибку обратно (кроме случаев, где Revit не справляется)

Сам просмотр тоже используем: посмотреть файл от заказчика, когда под рукой нет просмотрщика, типа BIM Vision и подобных.

Выводы:

  • IFC — это не текст, а сеть ссылок. Одно значение свойства может принадлежать сразу многим элементам, один тип — десяткам. Поэтому правка «в лоб» почти всегда задевает кого-то ещё — как замена в Блокноте задевает стандартные свойства, которые трогать не надо.

  • «Файл открывается» — ещё не значит «файл правильный». Две первые версии удаления открывались без ошибок и выглядели нормально, но нарушали правила стандарта — это показала только строгая проверка. Поэтому каждую правку мы прогоняем через внутренний валидатор

  • Несколько моделей одного объекта нужно совмещать по одной общей точке. Если каждую сдвигать к своему началу, разделы разъедутся на метры, хотя в самих файлах всё правильно.

  • Отмена нужна, но её тоже надо проверять. Мы считаем отмену рабочей, только когда файл после «правки и отмены» совпадает с исходным — иначе она обещает больше, чем делает.

 Будем рады Вашим комментариям, предложениям по улучшению или фиксам. И также интересны Ваши истории взаимодействия с IFC: мы все делаем на основании собственного опыта и хотим им обмениваться тоже.

Комментарии (0)