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

Получу длины воздуховодов из модели.

Беру все воздуховоды из модели и получаю их длины
Беру все воздуховоды из модели и получаю их длины

Видно, что длина разная. Чтобы округлить есть два нода Round:

Называются одинаково, но работают по-разному
Называются одинаково, но работают по-разному

Первый нод просто берёт и округляет до целых. Второму можно указать количество знаков после запятой (digits). Пусть мне нужно округлить до двух знаков.

Округляет стандартно: до 5 — в меньшую сторону, 5 и больше — в большую
Округляет стандартно: до 5 — в меньшую сторону, 5 и больше — в большую
Иногда такое округление работает не очень правильно
Иногда такое округление работает не очень правильно

Если вам очень критичны эти ошибки округления, то решить проблему можно только с помощью дополнительного геморроя или Питон-кода.

Пример геморроя
Пример геморроя

Для площадей квартира эти копейки важны, конечно, но и сам Ревит округляет так же, как в Динамо, так что только геморрой!

Будьте осторожны, а мы переходим к переводу чисел в текст, ведь тут тоже есть заморочки. Начнём с нода: для этого есть нод String from Object. Проще всего его найти по запросу «tostring».

Вот так его можно найти в библиотеке
Вот так его можно найти в библиотеке

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

Вот так перевёл имена элементов в строку
Вот так перевёл имена элементов в строку

Теперь давайте переведём число в текст и посмотрим, что будет. А будет вот так:

Перевод чисел в строку
Перевод чисел в строку

Как видите, основная проблема тут в лишних нулях, их называют трейлинг нули. Они появляются всегда, если вы подаёте дробное число, то есть переменную с типом данных double. С учётом того, что дробная часть везде разная, нужно не просто отрезать сколько-то знаков после точки, а в каждом случае определить, сколько нулей надо убирать.

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

Довольно большой блок, но это из-за того, что количество нулей на конце чисел разное
Довольно большой блок, но это из-за того, что количество нулей на конце чисел разное

Расскажу алгоритм:

String.Split (Строка.Разделить)— нод делит строку по указанному разделителю на два значения: до разделителя и после. В итоге, если у меня есть два и более нолей, то я получаю на каждое число подсписок, в котором первой позицией идёт число до разделителя, а второй позицией — то, что после разделителя. Проще всего понять, если посмотреть на исходные числа и на результат. Далее нодом List.FirstItem (Список.ПервоеЗначение) я получаю первые значения из каждого подсписка. А что @L2 в нём — читайте в статье про уровни в списках.

Dynamo: округление и перевод чисел в текст
Dynamo: округление и перевод чисел в текст

По сути нод String.Split вышвыривает все лишние нули, оставляя только реально дробную часть. Теперь остаётся только убрать точки там, где они не нужны. Не нужны они там, где текст точкой заканчивается, поэтому использую нод String.EndsWith (Строка.ЗаканчиваетсяНа) для поиска позиций, которые оканчиваются точкой.

Подаю список с числами текстом и ищу те, которые оканчиваются на точку
Подаю список с числами текстом и ищу те, которые оканчиваются на точку

В результате получаю список из выражений true и false. True — текст заканчивается точкой, false — не закачивается. Где true — там надо убрать последний символ, то есть точку. Для этого буду использовать нод String.Remove (Строка.Удалить).

String.Remove работает так: нужно подать строку, которой делаем обрезание (ой-вэй), индекс начала (с какого символа начинаем удалять) и сколько символов удаляем. Логика такая: если строка оканчивается на точку, то надо удалить один символ, если не оканчивается, то ноль символов.

Поэтому надо получить нули и единицы, а также длину строки. Для этого использую нод String.Length (Строка.Длина). Подаю на него мою строку с текстом и получаю длину каждой строки. Длина в данном случае — это количество символов.

Нули и единицы получаю с помощью нода If (Если). На вход «test» подаю свой список true и false, на вход «true» — 1, на «false» — 0. В результате получаю список, в котором по сути все false заменились на нули, а true — на единицы.

Вот так получаю нули и единицы
Вот так получаю нули и единицы

Теперь важный момент: длина строки не равна индексу последнего символа. Если я сейчас подам длину строки на вход нода String.Remove, то получу ошибку. Дело в том, что индексы в программировании всегда начинают свой отсчёт от нуля, а не от единицы. Поэтому длина будет как бы на единицу больше, чем максимальное значение индекса. Разберём на примере.

Вот есть текст «3315.5». Его длина — 6 символов, то есть 6 знаков. Но индекс каждого символа будет отсчитываться с нуля. Обозначу индекс в виде [i], получится вот так:

[0]: 3
[1]: 3
[2]: 1
[3]: 5
[4]: .
[5]: 5

Таким образом, если я подам на String.Remove длину строки, то приду в несуществующий символ и получу ошибку. Поэтому я вычитаю один знак, тем самым как бы прихожу в последний символ строки.

А далее нод смотрит на количество знаков, которые надо удалить. Где точка на конце — будет удаляться один знак, где нет точки — ноль знаков, то есть строка останется без изменений.

В итоге получаю свои числа в текстовом виде с нужным количеством знаком. Длинно? Геморрно? Ну да, а что поделать. Более изящным способом было бы написать код в Питоне, но речь в статье про Динамо и ноды.

Подавал данные с левого нода, результат в правом
Подавал данные с левого нода, результат в правом
Весь скрипт
Весь скрипт

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

Варианты решения с помощью Питона

Иван Волощенко поделился примером кода в Питоне, которым можно округлить числа до нужной величины. Текст после знака решётки — это комментарии, они не влияют на код, а просто поясняют его.

spis = IN[0] # список значений

kr = 0.01 # округлить до значения

spis = spis if type(spis)==list else [spis] # проверка списка

mas = [] # создаем пустой список для записи значений

for s in spis:

mas.append(round(s/kr)*kr)

OUT = mas

Или тоже самое, но в одну строку:

OUT = [round(s/kr)*kr for s in spis]

Чтобы отбросить трейлинг нули, я нашёл вот такой метод: rstrip().

Если у вас есть список из «бывших» чисел с нулями, то можно использовать такой цикл:

Накидал в Notepad++
Накидал в Notepad++

Понравилась статья?

Тогда можете сделать небольшое пожертвование (как это ни звучало) — любую удобную вам сумму.

Новые статьи удобно получать в Телеграм-канале «Блог Муратова про Revit MEP». Подписывайтесь и приглашайте коллег. Можно обсудить статью и задать вопросы в специальном чате канала. В Телеграме много полезных чатов по Ревиту, присоединяйтесь!

Бесплатные обзоры ваших моделей

Раз в две недели провожу «Ревит-линчи» — разбираю файлы семейств и проектов пользователей и отвечаю на вопросы по Ревиту и БИМ-технологиям. Дату и ссылку на Ревит-линч публикую в Телеграм-канале. Приходите, там интересно.