diff --git a/AGENTS.md b/AGENTS.md index 3b5620181b..f0a6fea64f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ cmake --build . --config %build_type% --target ALL_BUILD -- -m ## Testing -Catch2 framework. Tests in `tests/` directory. +Catch2 framework. Tests in `tests/`; see [tests/AGENTS.md](tests/AGENTS.md) for where a new test belongs and the conventions to follow. ```bash cd build && ctest --output-on-failure # all tests diff --git a/localization/i18n/ru/OrcaSlicer_ru.po b/localization/i18n/ru/OrcaSlicer_ru.po index d1c4b42014..c9e8f5dc62 100644 --- a/localization/i18n/ru/OrcaSlicer_ru.po +++ b/localization/i18n/ru/OrcaSlicer_ru.po @@ -19,13 +19,13 @@ msgstr "" "X-Generator: Poedit 3.8\n" msgid "Main Extruder" -msgstr "" +msgstr "Основной экструдер" msgid "Main extruder" -msgstr "" +msgstr "Основной экструдер" msgid "main extruder" -msgstr "" +msgstr "основной экструдер" msgid "Auxiliary Extruder" msgstr "" @@ -162,22 +162,23 @@ msgid "AMS does not support 'Bambu Lab PET-CF'." msgstr "AMS не поддерживает Bambu Lab PET-CF." msgid "The current filament doesn't support the E3D high-flow nozzle and can't be used." -msgstr "" +msgstr "Невозможно использовать материал, несовместимый с высокопроизводительным соплом E3D." msgid "The current filament doesn't support the TPU high-flow nozzle and can't be used." -msgstr "" +msgstr "Невозможно использовать материал, несовместимый с высокопроизводительным соплом для TPU." msgid "Auto dynamic flow calibration is not supported for TPU filament." -msgstr "" +msgstr "TPU не поддерживает автоматическую калибровку динамики потока." +# Опустил бы "High Flow", но у бамбуков же есть какие-то узкоспециализированные сопла msgid "Bambu TPU 85A is not supported for printing with 0.4 mm Standard or High Flow nozzles." -msgstr "" +msgstr "Материал Bambu TPU 85A не предназначен для печати через стандартные и высокопроизводительные сопла диаметром 0,4 мм." msgid "How to feed TPU filament." -msgstr "" +msgstr "Заправка TPU." msgid "How to feed TPU filament on X2D." -msgstr "" +msgstr "Заправка TPU в X2D." msgid "Please cold pull before printing TPU to avoid clogging. You may use cold pull maintenance on the printer." msgstr "Перед печатью TPU выполните холодную протяжку, чтобы избежать засорения. Вы можете воспользоваться функцией холодной протяжки на принтере." @@ -191,8 +192,9 @@ msgstr "Влажный PVA становится гибким и может за msgid "The rough surface of PLA Glow can accelerate wear on the AMS system, particularly on the internal components of the AMS Lite." msgstr "Шероховатая поверхность PLA Glow может ускорить износ системы AMS, особенно внутренних компонентов AMS Lite." +# Без понятия, почему они называют фидер (механизм подачи) приводом, но так написано в материалах Bambu msgid "PLA Glow may wear the AMS first stage feeder. Use an external spool instead." -msgstr "" +msgstr "Использование PLA Glow может привести к износу привода первой ступени AMS. Воспользуйтесь внешним держателем." msgid "CF/GF filaments are hard and brittle, it's easy to break or get stuck in AMS, please use with caution." msgstr "Стекло-/угленаполненные материалы твёрдые и хрупкие, легко ломаются и застревают в AMS, поэтому используйте их с осторожностью." @@ -204,23 +206,24 @@ msgid "PPA-CF is brittle and could break in bended PTFE tube above Toolhead." msgstr "PPA-CF – это хрупкий материал, который может сломаться в изогнутом тефлоновом канале подачи." msgid "Default settings may affect print quality. Adjust as needed for best results." -msgstr "" +msgstr "Стандартные настройки могут повлиять на качество печати. Для улучшения результата рекомендуется тонкая настройка параметров." #, c-format, boost-format msgid "%s has a risk of nozzle clogging when using 0.4mm high-flow nozzles. Use with caution." -msgstr "" +msgstr "%s может привести к затору в высокопроизводительном сопле диаметром 0,4 мм. Используйте его с осторожностью." #, c-format, boost-format msgid "%s filaments are hard and brittle and could break in AMS, and there is also a risk of nozzle clogging when using 0.4mm high-flow nozzles. Use with caution." -msgstr "" +msgstr "Материалы %s жёсткие и ломкие, их использование может привести к поломке внутри AMS и затору в высокопроизводительном сопле диаметром 0,4 мм. Используйте его с осторожностью." #, c-format, boost-format msgid "%s has a risk of nozzle clogging when using 0.4, 0.6, 0.8mm high-flow nozzles. Use with caution." -msgstr "" +msgstr "%s может привести к затору в высокопроизводительных соплах диаметром 0,4, 0,6 или 0,8 мм. Используйте его с осторожностью." +# Ошибка во втором предложении; вероятно, подразумевался вопрос. В таком случае в окне будет кнопка подтверждения, и его можно опустить. Сам Filament Track Switch можно перевести общим "переключатель AMS". #, c-format, boost-format msgid "%s may fail to load or unload due to the Filament Track Switch. If you wish to continue." -msgstr "" +msgstr "Filament Track Switch может не справиться с загрузкой/выгрузкой %s." # подставляется tag_type и extruder_name #, c-format, boost-format @@ -229,19 +232,19 @@ msgstr "%s не подходит для печати через %s экстру #, c-format, boost-format msgid "There may be critical print quality issues when printing '%s' with %s Bowden extruder. Use with caution!" -msgstr "" +msgstr "При печати %s через %s экструдер с внешней подачей могут возникнуть серьёзные проблемы с качеством. Действуйте с осторожностью." #, c-format, boost-format msgid "There may be critical print quality issues when printing '%s' with %s extruder. Use with caution!" -msgstr "" +msgstr "При печати %s через %s экструдер могут возникнуть серьёзные проблемы с качеством. Действуйте с осторожностью." #, c-format, boost-format msgid "There may be print quality issues when printing '%s' with %s Bowden extruder. Use with caution." -msgstr "" +msgstr "При печати %s через %s экструдер с внешней подачей могут возникнуть проблемы с качеством. Действуйте с осторожностью." #, c-format, boost-format msgid "There may be print quality issues when printing '%s' with %s extruder. Use with caution." -msgstr "" +msgstr "При печати %s через %s экструдер могут возникнуть проблемы с качеством. Действуйте с осторожностью." msgid "High Flow" msgstr "Высокий расход" @@ -273,19 +276,19 @@ msgid "Warning" msgstr "Предупреждение" msgid "Hotend information may be inaccurate. Would you like to re-read the hotend? (Hotend information may change during power-off)." -msgstr "" +msgstr "Информация о хотэнде может быть неточной (например, из-за отключения питания). Обновить сведения о нём?" msgid "I confirm all" -msgstr "" +msgstr "Продолжить" msgid "Re-read all" -msgstr "" +msgstr "Обновить сведения" msgid "Reading the hotends, please wait." -msgstr "" +msgstr "Чтение сведений, подождите." msgid "During the hotend upgrade, the toolhead will move. Don't reach into the chamber." -msgstr "" +msgstr "В процессе обновления печатающая голова будет перемещаться. Не вмешивайтесь в процесс." msgid "Update" msgstr "Обновление" @@ -339,16 +342,16 @@ msgid "Induction Hotend Rack" msgstr "" msgid "Hotends Info" -msgstr "" +msgstr "Информация о хотэндах" msgid "Read All" -msgstr "" +msgstr "Прочитать" msgid "Reading " -msgstr "" +msgstr "Чтение " msgid "Please wait" -msgstr "" +msgstr "Подождите" msgid "Reading" msgstr "" @@ -388,7 +391,7 @@ msgstr "Версия" #, c-format, boost-format msgid "Used Time: %s" -msgstr "" +msgstr "Время использования: %s" msgid "Dynamic nozzles are allocated on the current plate. Picking hotend is not supported." msgstr "" @@ -630,7 +633,7 @@ msgid "Move" msgstr "Перемещение" msgid "Please select at least one object." -msgstr "Пожалуйста, выберите хотя бы один объект." +msgstr "Сначала выберите объект." msgid "Gizmo-Move" msgstr "Инструмент перемещения" @@ -954,7 +957,8 @@ msgstr "Верхняя часть" msgid "Lower part" msgstr "Нижняя часть" -# ?????8 к чему относится? +# Andy: к чему относится? +# Felix: это галочки, какие из частей сохранять в инструменте резки msgid "Keep" msgstr "Сохранить" @@ -970,8 +974,9 @@ msgstr "Объединить в сборку" msgid "Reset cutting plane and remove connectors" msgstr "Сброс позиции секущей плоскости и удаление всех соединений" +# Запись в истории действий при нажатии кнопки сброса резки (TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset Cut"), UndoRedo::SnapshotType::GizmoAction) msgid "Reset Cut" -msgstr "" +msgstr "Сброс плоскости резки" msgid "Perform cut" msgstr "Разрезать" @@ -1008,12 +1013,16 @@ msgstr "текущее положение секущей плоскости с msgid "Connector" msgstr "Соединение" +# Подставляется список моделей, через запятую #, boost-format msgid "" "Objects(%1%) have duplicated connectors. Some connectors may be missing in slicing result.\n" "Please report to PrusaSlicer team in which scenario this issue happened.\n" "Thank you." msgstr "" +"У моделей (%1%) образовались дубликаты соединений, что может привести к их пропуску при нарезке.\n" +"Пожалуйста, сообщите команде разработчиков условия, при которых это произошло.\n" +"Заранее спасибо." msgid "Cut by Plane" msgstr "Разрез по плоскости" @@ -1371,7 +1380,7 @@ msgid "" msgstr "" "При смене стиля на «%1%» изменения текущего стиля будут утеряны.\n" "\n" -"Хотите продолжить?" +"Продолжить?" msgid "Not valid style." msgstr "Недопустимый стиль." @@ -1506,11 +1515,11 @@ msgstr "Выровнять текст по направлению к камер #, boost-format msgid "Font \"%1%\" can't be used. Please select another." -msgstr "Невозможно использовать шрифт \"%1%\". Пожалуйста, выберите другой." +msgstr "Невозможно использовать шрифт «%1%». Пожалуйста, выберите другой." #, boost-format msgid "Can't load exactly same font (\"%1%\"). Application selected a similar one (\"%2%\"). You have to specify font for enable edit text." -msgstr "Т.к. не удалось загрузить тот же шрифт (\"%1%\"), приложение выбрало похожий шрифт (\"%2%\"). Выберите шрифт для включения редактирования текста." +msgstr "Не удалось загрузить шрифт («%1%»), был выбран похожий («%2%»). Выберите шрифт для изменения текста." msgid "No symbol" msgstr "Нет символов" @@ -1804,7 +1813,7 @@ msgid "Measure" msgstr "Измерения" msgid "Please confirm explosion ratio = 1, and please select at least one object." -msgstr "Сначала отключите разнесение и выберите хотя бы одну модель." +msgstr "Отключите разнесение и выберите хотя бы одну модель." msgid "Edit to scale" msgstr "Редактировать масштаб" @@ -1887,10 +1896,10 @@ msgid "Reverse rotation" msgstr "Обратное вращение" msgid "Rotate around center:" -msgstr "Вращение вокруг центра:" +msgstr "Вращение по оси:" msgid "Parallel distance:" -msgstr "Расстояние между параллельными гранями:" +msgstr "Расстояние между гранями:" msgid "Flip by Face 2" msgstr "Перевернуть грань 2" @@ -1906,10 +1915,10 @@ msgid "Assemble" msgstr "Объединить в сборку" msgid "Please confirm explosion ratio = 1 and select at least two volumes." -msgstr "Отключите разнесение и выберите сборку из нескольких частей." +msgstr "Отключите разнесение и выберите несколько частей." msgid "Please select at least two volumes." -msgstr "Выберите хотя бы две модели." +msgstr "Выберите минимум две модели." msgid "(Moving)" msgstr "(подвижная)" @@ -2135,7 +2144,7 @@ msgid "" "Do you want to continue?" msgstr "" "%s\n" -"Хотите продолжить?" +"Продолжить?" msgid "Remember my choice" msgstr "Запомнить выбор" @@ -2154,11 +2163,11 @@ msgid "Info" msgstr "Информация" msgid "Loading Plugins" -msgstr "" +msgstr "Загрузка плагинов" #, c-format, boost-format msgid "Plugin %s is no longer available." -msgstr "" +msgstr "Плагин «%s» больше недоступен." #, c-format, boost-format msgid "Plugin %s access is unauthorized." @@ -2298,16 +2307,16 @@ msgid "Resolve cloud sync conflict" msgstr "Решение проблем синхронизации" msgid "Syncing your account…" -msgstr "" +msgstr "Синхронизация учётной записи…" msgid "Migrating presets…" -msgstr "" +msgstr "Перенос профилей…" msgid "Fetching plugins…" -msgstr "" +msgstr "Запрос плагинов…" msgid "Loading user presets…" -msgstr "" +msgstr "Загрузка пользовательских профилей…" msgid "Retrieving printer information, please try again later." msgstr "Получение информации о принтере, попробуйте позднее." @@ -2410,7 +2419,7 @@ msgid "Loading user preset" msgstr "Загрузка пользовательского профиля" msgid "There is an update available. Open the preset bundle dialog to update it." -msgstr "" +msgstr "Доступно обновление. Проверьте меню пакета профилей для его загрузки." #, c-format, boost-format msgid "%s has been removed." @@ -2427,33 +2436,37 @@ msgstr "Язык" #, c-format, boost-format msgid "Switching Orca Slicer to language %s failed." -msgstr "" +msgstr "Не удалось переключить язык на %s." msgid "" "\n" "You may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n" msgstr "" +"\n" +"Возможно, потребуется восстановить отсутствующие файлы локализации (например, при помощи команд \"locale-gen\" и \"dpkg-reconfigure locales\").\n" msgid "Orca Slicer - Switching language failed" -msgstr "" +msgstr "Orca Slicer – ошибка смены языка" msgid "*" msgstr "*" msgid "Plugins" -msgstr "" +msgstr "Плагины" #, c-format, boost-format msgid "" "Failed to open the Plugins dialog:\n" "%s" msgstr "" +"Не удалось открыть меню плагинов:\n" +"%s" msgid "Failed to open the Plugins dialog (unknown error)." -msgstr "" +msgstr "Не удалось открыть меню плагинов (неизвестная ошибка)." msgid "Plugin Terminal" -msgstr "" +msgstr "Консоль плагина" msgid "Changing application language" msgstr "Изменение языка приложения" @@ -2779,7 +2792,7 @@ msgid "Flush into objects' support" msgstr "Прочистка в поддержку" msgid "Edit in Parameter Table" -msgstr "Редактировать таблицу параметров" +msgstr "Просмотр сводки настроек" msgid "Convert from Inches" msgstr "Преобразовать дюймы в миллиметры" @@ -2842,7 +2855,7 @@ msgid "Invalidate cut info" msgstr "Удалить информацию о разрезе" msgid "Add Primitive" -msgstr "Добавить примитив" +msgstr "Добавить фигуру" msgid "Add Handy models" msgstr "Добавить тестовую модель" @@ -3059,44 +3072,57 @@ msgstr "Перекрасить модель" msgid "Click the icon to shift this object to the bed" msgstr "Вытолкнуть на стол" +# Запись в истории действий msgid "Rename Object" -msgstr "" +msgstr "Переименование модели" +# Запись в истории действий msgid "Rename Part" -msgstr "" +msgstr "Переименование части" +# Запись в истории действий msgid "Paste settings" -msgstr "" +msgstr "Вставка настроек" +# Запись в истории действий msgid "Shift objects to bed" -msgstr "" +msgstr "Смещение моделей к столу" +# Запись в истории действий msgid "Object order changed" -msgstr "" +msgstr "Смена порядка моделей" +# Запись в истории действий msgid "Layer setting added" -msgstr "" +msgstr "Добавление настройки слоя" +# Запись в истории действий msgid "Part setting added" -msgstr "" +msgstr "Добавление настройки части" +# Запись в истории действий msgid "Object setting added" -msgstr "" +msgstr "Добавление настройки модели" +# Запись в истории действий msgid "Height range settings added" -msgstr "" +msgstr "Добавление настроек диапазона слоёв" +# Запись в истории действий msgid "Part settings added" -msgstr "" +msgstr "Добавление настроек части" +# Запись в истории действий msgid "Object settings added" -msgstr "" +msgstr "Добавление настроек модели" +# Запись в истории действий msgid "Load Part" -msgstr "" +msgstr "Загрузка части" +# Запись в истории действий msgid "Load Modifier" -msgstr "" +msgstr "Загрузка модификатора" msgid "Loading file" msgstr "Загрузка файла" @@ -3108,7 +3134,7 @@ msgid "Failed to get the model data in the current file." msgstr "Не удалось получить данные модели из текущего файла." msgid "Add primitive" -msgstr "" +msgstr "Добавить фигуру" # Базовый примитив – опасно оставлять узкоспециализированный перевод (чисто # для фигур), т.к. Generic уже сейчас используется в названии начала профилей. @@ -3129,8 +3155,9 @@ msgstr "" msgid "Remove paint-on fuzzy skin" msgstr "Удалить покраску нечеткой оболочки" +# Запись в истории действий msgid "Delete Settings" -msgstr "" +msgstr "Удаление настроек" msgid "Remove height range" msgstr "Удаление диапазона высот слоёв" @@ -3164,8 +3191,9 @@ msgstr "Удалить все соединения" msgid "Deleting the last solid part is not allowed." msgstr "Удаление последней твердотельной части не допускается." +# Запись в истории действий msgid "Delete part" -msgstr "" +msgstr "Удаление части" msgid "The target object contains only one part and can not be split." msgstr "Заданная модель состоит только из одной части и не может быть разделена." @@ -3177,34 +3205,38 @@ msgid "Assembly" msgstr "Сборка" msgid "Merge parts to an object" -msgstr "" +msgstr "Сборка моделей" +# Запись в истории действий msgid "Add layers" -msgstr "" +msgstr "Добавление слоёв" msgid "Cut Connectors information" msgstr "Информация о вырезанных соединениях" msgid "Object manipulation" -msgstr "Манипуляция с моделями" +msgstr "Действие с моделями" msgid "Group manipulation" -msgstr "Групповые манипуляции" +msgstr "Действие с группами" +# Requires refactoring msgid "Object Settings to Modify" msgstr "Параметры модели для изменения" +# Requires refactoring msgid "Part Settings to Modify" msgstr "Параметры элемента для изменения" +# Requires refactoring msgid "Layer Range Settings to Modify" msgstr "Изменение параметров диапазона слоёв" msgid "Part manipulation" -msgstr "Манипуляция с частями" +msgstr "Действие с частями" msgid "Instance manipulation" -msgstr "Манипуляция с копиями" +msgstr "Действие с экземплярами" msgid "Height ranges" msgstr "Диапазон высот слоёв" @@ -3213,7 +3245,7 @@ msgid "Settings for height range" msgstr "Настройки для диапазона высот слоёв" msgid "Delete selected" -msgstr "" +msgstr "Удалить выбранное" msgid "Layer" msgstr "Слой" @@ -3237,7 +3269,7 @@ msgid "Choose part type" msgstr "Выберите тип элемента" msgid "Instances to Separated Objects" -msgstr "" +msgstr "Отделение экземпляра" msgid "Enter new name" msgstr "Введите новое имя" @@ -3271,8 +3303,9 @@ msgstr "«%s» после сглаживания будет иметь боле msgid "\"%s\" part's mesh contains errors. Please repair it first." msgstr "Сетка модели «%s» содержит ошибки. Выполните её восстановление." +# Запись в истории действий msgid "Change Filaments" -msgstr "" +msgstr "Смена материала" msgid "Additional process preset" msgstr "Доп. профиль настроек" @@ -3458,16 +3491,16 @@ msgid "code" msgstr "код" msgid "Failed to connect to cloud service" -msgstr "Не удалось подключиться к облачному сервису" +msgstr "Не удалось подключиться к облаку." msgid "Please click on the hyperlink above to view the cloud service status" -msgstr "Для просмотра состояния статуса сервиса нажмите на вышерасположенную ссылку" +msgstr "Откройте ссылку выше, чтобы проверить статус сервиса." msgid "Failed to connect to the printer" msgstr "Не удалось подключиться к принтеру" msgid "Connection to printer failed" -msgstr "Не удалось подключиться к принтеру" +msgstr "Ошибка подключения к принтеру" msgid "Please check the network connection of the printer and Orca." msgstr "Убедитесь, что принтер и OrcaSlicer имеют доступ к сети." @@ -3493,7 +3526,7 @@ msgid "Filament type is unknown which is required to perform this action. Please msgstr "Для выполнения этого действия требуется указать тип материала. Пожалуйста, дополните информацию об этом материале." msgid "AMS has not been initialized. Please initialize it before use." -msgstr "" +msgstr "AMS не проинициализирована. Перед использованием требуется инициализация." msgid "Changing fan speed during printing may affect print quality, please choose carefully." msgstr "Будьте осторожны, изменение охлаждения в процессе печати может повлиять на её качество." @@ -3573,7 +3606,7 @@ msgctxt "air_duct" msgid "Left(Aux)" msgstr "Левый (вспом.)" -# FAN_HEAT_BREAK_0_IDX +# FAN_HEAT_BREAK_0_IDX - охлаждение термобарьера в первом хотэнде msgid "Hotend" msgstr "Термобарьер" @@ -3583,7 +3616,7 @@ msgstr "Основной" msgid "Aux" msgstr "Вспом." -# FAN_HEAT_BREAK_1_IDX +# FAN_HEAT_BREAK_1_IDX - охлаждение термобарьера во втором хотэнде msgid "Nozzle1" msgstr "2 термобарьер" @@ -3703,7 +3736,7 @@ msgid "Please set nozzle count" msgstr "" msgid "Error: Can not set both nozzle count to zero." -msgstr "" +msgstr "Ошибка: оба значения не могут быть нулевыми." #, c-format, boost-format msgid "Error: Nozzle count can not exceed %d." @@ -3750,7 +3783,7 @@ msgid "Ignore" msgstr "Игнорировать" msgid "Done." -msgstr "" +msgstr "Готово." msgid "" "All the selected objects are on a locked plate.\n" @@ -3866,7 +3899,7 @@ msgid "Failed to upload file to ftp. Please try again." msgstr "Не удалось загрузить файл на FTP. Пожалуйста, попробуйте ещё раз." msgid "Check the current status of the Bambu Lab server by clicking on the link above." -msgstr "Проверить текущий статус сервера Bambu можно перейдя по указанной выше ссылке." +msgstr "Можно проверить текущий статус сервера Bambu, перейдя по ссылке выше." msgid "The size of the print file is too large. Please adjust the file size and try again." msgstr "Размер файла для печати слишком велик. Пожалуйста, уменьшите размер файла и повторите попытку." @@ -3991,7 +4024,7 @@ msgid "Remaining time: Calculating..." msgstr "Осталось: [оценка...]" msgid "The heated bed's thermal preconditioning helps optimize the first layer print quality. Printing will start once preconditioning is complete." -msgstr "Термоподготовка нагревательного стола помогает улучшить качество печати первого слоя. Печать начнётся после завершения подготовки." +msgstr "Преднагрев стола помогает улучшить качество печати первого слоя. Печать начнётся после завершения подготовки." #, c-format, boost-format msgid "Remaining time: %dmin%ds" @@ -4125,7 +4158,7 @@ msgid "Are you sure you want to clear the filament information?" msgstr "Вы действительно хотите удалить информацию о материале?" msgid "You need to select the material type and color first." -msgstr "Сначала необходимо выбрать тип материала и цвет." +msgstr "Сначала нужно выбрать тип материала и цвет." #, c-format, boost-format msgid "Please input a valid value (K in %.1f~%.1f)" @@ -4135,12 +4168,13 @@ msgstr "Пожалуйста, введите допустимое значени msgid "Please input a valid value (K in %.1f~%.1f, N in %.1f~%.1f)" msgstr "Пожалуйста, введите допустимое значение (K в диапазоне %.1f~%.1f, N в диапазоне %.1f~%.1f)" +# Дичь какая-то бамбуковская с терминами, требуется проверка. msgid "" "The nozzle flow is not set. Please set the nozzle flow rate before editing the filament.\n" "'Device -> Print parts'" msgstr "" -"Поток сопла не задан. Пожалуйста, установите расход сопла перед редактированием филамента.\n" -"«Устройство -> Компоненты для печати»" +"Расход сопла не задан. Пожалуйста, установите расход сопла перед изменением материала.\n" +"«Принтер → Print parts»" msgid "AMS" msgstr "AMS" @@ -4220,6 +4254,7 @@ msgstr "Шаг" msgid "Unmapped" msgstr "Не назначен" +# Requires refactoring msgid "" "Upper half area: Original\n" "Lower half area: The filament from original project will be used when unmapped.\n" @@ -4270,7 +4305,7 @@ msgstr "Правый AMS" #, c-format, boost-format msgid "Printing with the current nozzle may produce an extra %0.2f g of waste." -msgstr "" +msgstr "Печать текущим соплом может привести к дополнительным затратам %0.2f г материала." #, c-format, boost-format msgid "Tips: the filament type(%s) does not match with the filament type(%s) in the slicing file. If you want to use this slot, you can install %s instead of %s and change slot information on the 'Device' page." @@ -4617,23 +4652,23 @@ msgid "A maximum temperature below %d℃ is recommended for %s.\n" msgstr "Рекомендуется температура не более %d℃ для %s.\n" msgid "The recommended minimum temperature cannot be higher than the recommended maximum temperature.\n" -msgstr "Минимально рекомендуемая температура не может быть выше максимально рекомендуемой.\n" +msgstr "Минимально допустимая температура сопла не может быть выше максимально допустимой.\n" msgid "Please check.\n" -msgstr "Пожалуйста, проверьте.\n" +msgstr "Проверьте введённые значения.\n" msgid "" "The nozzle may become clogged when the temperature is out of the recommended range.\n" "Please make sure whether to use this temperature to print.\n" "\n" msgstr "" -"Сопло может забиться, если температура превышает рекомендуемый диапазон.\n" -"Пожалуйста, убедитесь, что вы задали нужную температуру для печати.\n" +"Сопло может забиться, если температура превышает допустимый диапазон.\n" +"Убедитесь в корректности указанных значений температуры для печати.\n" "\n" #, c-format, boost-format msgid "The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius." -msgstr "Диапазон рекомендуемых температур печати данным типом материала составляет [%d, %d] градусов Цельсия." +msgstr "Диапазон допустимых температур печати данным типом материала составляет [%d, %d] градусов Цельсия." msgid "Adaptive Pressure Advance model validation failed:\n" msgstr "Обнаружена ошибка в настройке адаптивного PA:\n" @@ -4643,7 +4678,7 @@ msgid "" "Value was reset to 0.5" msgstr "" "Слишком низкий предел объёмного расхода.\n" -"Значение будет сброшено на 0,5." +"Значение будет сброшено до 0,5." #, c-format, boost-format msgid "Current chamber temperature is higher than the material's safe temperature; this may result in material softening and nozzle clogs. The maximum safe temperature for the material is %d" @@ -4651,21 +4686,21 @@ msgstr "Текущая температура внутри термокамер #, c-format, boost-format msgid "The minimal chamber temperature (%d℃) is higher than the target chamber temperature (%d℃). The minimal value is the threshold at which printing starts while the chamber keeps heating toward the target, so it should not exceed it. It will be clamped to the target." -msgstr "Минимальная температура внутри термокамеры (%d℃) превышает целевую (%d℃). Печать начинается по достижении минимальной температуры, поэтому она не должна быть больше целевой. Значение будет уменьшено." +msgstr "Стартовая температура внутри термокамеры (%d℃) превышает целевую (%d℃). Подразумевается, что печать начинается заранее, поэтому стартовая температура не должна превышать её. Значение будет уменьшено." msgid "" "Layer height too small\n" "It has been reset to 0.2" msgstr "" "Слишком маленькая высота слоя.\n" -"Значение будет сброшено на 0,2." +"Значение будет сброшено до 0,2." msgid "" "Ironing spacing too small\n" "It has been reset to 0.1" msgstr "" "Слишком маленькое расстояние между линиями разглаживания.\n" -"Значение будет сброшено на 0,1." +"Значение будет сброшено до 0,1." msgid "" "Zero initial layer height is invalid.\n" @@ -4686,7 +4721,7 @@ msgstr "" "Этот параметр используется только для точной подгонки размера модели и соблюдения допусков.\n" "Для более значительной корректировки размеров используйте функцию масштабирования модели.\n" "\n" -"Это значение будет сброшено на 0." +"Это значение будет сброшено до 0." msgid "" "The elephant foot compensation value is too large.\n" @@ -4699,7 +4734,7 @@ msgstr "" "Если возникает сильный дефект слоновьей ноги, проблема может исходить из других настроек.\n" "Например, завышенная температура стола или некорректный прижим сопла на первом слое.\n" "\n" -"Значение будет сброшено на 0." +"Значение будет сброшено до 0." msgid "Alternate extra wall does't work well when ensure vertical shell thickness is set to All." msgstr "Чередующаяся дополнительная стенка не работает, если для «Сохранение толщины вертикальной оболочки» установлено значение «Все»." @@ -4758,7 +4793,7 @@ msgid "" "Reset to 0." msgstr "" "Начальная высота не должна превышать высоту слоя.\n" -"Значение сброшено на 0." +"Значение сброшено до 0." #, no-c-format, no-boost-format msgid "" @@ -5259,26 +5294,26 @@ msgid "N/A" msgstr "–" msgid "System agents" -msgstr "" +msgstr "Системные агенты" msgid "No plugin selected" -msgstr "" +msgstr "Плагины не выбраны" msgid "Add plugin" -msgstr "" +msgstr "Добавить плагин" msgid "Select plugin" -msgstr "" +msgstr "Выбрать плагин" msgid "Remove plugin" -msgstr "" +msgstr "Удалить плагин" msgid "Configure" -msgstr "" +msgstr "Настроить" #, c-format, boost-format msgid "Configure (%d)" -msgstr "" +msgstr "Настроить (%d)" msgid "Pick" msgstr "Выбрать" @@ -5304,8 +5339,9 @@ msgstr "Рывки" msgid "Fan Speed" msgstr "Охлаждение" +# В интерфейсе бамбуков не вмещается оба слова msgid "Flow" -msgstr "Объёмный расход" +msgstr "Расход" msgid "Actual Flow" msgstr "Действительный расход" @@ -5394,7 +5430,7 @@ msgstr "Нижняя поверхность" msgid "Internal bridge" msgstr "Внутренний мост" -# Подложка связующего слоя +# Подложка интерфейса msgid "Support transition" msgstr "Переходный слой" @@ -5428,10 +5464,10 @@ msgid "Color: " msgstr "Материал: " msgid "Acceleration: " -msgstr "" +msgstr "Ускорение: " msgid "Jerk: " -msgstr "Jerk: " +msgstr "Рывок: " msgid "PA: " msgstr "PA: " @@ -5579,10 +5615,10 @@ msgid "Actual Speed (mm/s)" msgstr "Фактическая скорость (мм/с)" msgid "Acceleration (mm/s²)" -msgstr "" +msgstr "Ускорение (мм/с²)" msgid "Jerk (mm/s)" -msgstr "Jerk (mm/s)" +msgstr "Рывок (мм/с)" msgid "Fan speed (%)" msgstr "Скорость вентилятора (%)" @@ -5831,8 +5867,9 @@ msgstr "Статистика" msgid "Slice" msgstr "Нарезать" +# Отображается на превьюшке стола при неудачной нарезке msgid "Review" -msgstr "" +msgstr "Исправить" msgid "Assembly Return" msgstr "Выйти из сборки" @@ -5970,8 +6007,9 @@ msgstr "Другие модели скрыты на время редактир msgid "Filaments %s cannot be printed directly on the surface of this plate." msgstr "Для печати %s требуется подложка или основание из другого материала." +# В конце подставляется "руководством." msgid "PLA and PETG filaments detected in the mixture. Adjust parameters according to the Wiki to ensure print quality." -msgstr "Обнаружено совместное использование PLA и PETG. Для повышения качества рекомендуется настроить параметры в соответствии с руководством." +msgstr "Обнаружено совместное использование PLA и PETG. Для повышения качества рекомендуется настроить печать в соответствии с" msgid "The prime tower extends beyond the plate boundary." msgstr "Башня очистки выходит за пределы области печати." @@ -5981,7 +6019,7 @@ msgid "Partial flushing volume set to 0. Multi-color printing may cause color mi msgstr "Обнаружен нулевой объём прочистки сопла, что может вызвать смешивание цветов в цветной модели. Рекомендуется изменить настройки прочистки:" msgid "Click Wiki for help." -msgstr "Открыть руководство" +msgstr "руководством." msgid "Click here to regroup" msgstr "Перегруппировать" @@ -6119,7 +6157,7 @@ msgid "No" msgstr "Нет" msgid "will be closed before creating a new model. Do you want to continue?" -msgstr "будет закрыт перед созданием новой модели. Хотите продолжить?" +msgstr "будет закрыт перед созданием новой модели. Продолжить?" # Возможно, имеет смысл убрать тут "стол". Подавляющее большинство проектов – # это размещение моделей и их печать на одном столе, поэтому намёк на работу с @@ -6562,7 +6600,7 @@ msgid "Select profile to load:" msgstr "Выберите профиль для загрузки:" msgid "Config files (*.json;*.zip;*.orca_printer;*.orca_bundle;*.orca_filament)|*.json;*.zip;*.orca_printer;*.orca_bundle;*.orca_filament" -msgstr "" +msgstr "Файлы настроек (*.json;*.zip;*.orca_printer;*.orca_bundle;*.orca_filament)|*.json;*.zip;*.orca_printer;*.orca_bundle;*.orca_filament" #, c-format, boost-format msgid "There is %d config imported. (Only non-system and compatible configs)" @@ -6594,6 +6632,11 @@ msgid "" "2. The Filament presets\n" "3. The Printer presets" msgstr "" +"Загрузить личные сведения из Orca Cloud?\n" +"Содержимое сведений:\n" +"1. Профили настроек печати\n" +"2. Профили настроек материала\n" +"3. Профили настроек принтеров" msgid "Synchronization" msgstr "Синхронизация" @@ -6653,7 +6696,7 @@ msgid "" "Do you want to install them?" msgstr "" "Для этой задачи требуется Virtual Camera Tools!\n" -"Хотите установить?" +"Установить?" msgid "Downloading Virtual Camera Tools" msgstr "Загрузка Virtual Camera Tools" @@ -6663,13 +6706,13 @@ msgid "" "Orca Slicer supports only a single virtual camera.\n" "Do you want to stop this virtual camera?" msgstr "" -"Уже работает одна виртуальная камера.\n" -"Orca Slicer поддерживает только одну виртуальную камеру.\n" -"Хотите остановить эту виртуальную камеру?" +"Уже работает одна виртуальная камера,\n" +"Orca Slicer поддерживает только одну.\n" +"Остановить её?" #, c-format, boost-format msgid "Virtual camera initialize failed (%s)!" -msgstr "Не удалось инициализировать виртуальную камеру (%s)!" +msgstr "Не удалось инициализировать виртуальную камеру (%s)." msgid "Network unreachable" msgstr "Сеть недоступна" @@ -6755,9 +6798,9 @@ msgstr "Просмотр локальных файлов не поддержив #, c-format, boost-format msgid "You are going to delete %u file from printer. Are you sure to continue?" msgid_plural "You are going to delete %u files from printer. Are you sure to continue?" -msgstr[0] "Вы собираетесь удалить %u файл с принтера. Вы действительно хотите это сделать?" -msgstr[1] "Вы собираетесь удалить %u файла с принтера. Вы действительно хотите это сделать?" -msgstr[2] "Вы собираетесь удалить %u файлов с принтера. Вы действительно хотите это сделать?" +msgstr[0] "Вы собираетесь удалить %u файл с принтера. Продолжить?" +msgstr[1] "Вы собираетесь удалить %u файла с принтера. Продолжить?" +msgstr[2] "Вы собираетесь удалить %u файлов с принтера. Продолжить?" msgid "Delete files" msgstr "Удалить файлы" @@ -6899,16 +6942,16 @@ msgid "Invert Z axis" msgstr "Инвертировать ось Z" msgid "Invert Yaw axis" -msgstr "Инвертировать ось рыскания (Y)" +msgstr "Инвертировать ось вращения (Y)" msgid "Invert Pitch axis" -msgstr "Инвертировать ось тангажа (X)" +msgstr "Инвертировать ось наклона (X)" msgid "Invert Roll axis" -msgstr "Инвертировать ось крена (Z)" +msgstr "Инвертировать ось поворота (Z)" msgid "(LAN)" -msgstr "" +msgstr "(локальная сеть)" msgid "Search" msgstr "Поиск" @@ -7169,10 +7212,10 @@ msgid "Delete Photo" msgstr "Удалить фото" msgid "Select Images" -msgstr "" +msgstr "Выбор изображений" msgid "Image files (*.png;*.jpg;*jpeg)|*.png;*.jpg;*.jpeg" -msgstr "" +msgstr "Файлы изображений (*.png;*.jpg;*jpeg)|*.png;*.jpg;*.jpeg" msgid "Submit" msgstr "Отправить" @@ -7240,10 +7283,10 @@ msgid "" msgstr "" "\n" "\n" -"Хотите перейти на страницу для выставления оценки?" +"Открыть страницу для выставления оценки?" msgid "Some of your images failed to upload. Would you like to redirect to the webpage to give a rating?" -msgstr "Некоторые из ваших изображений не удалось загрузить. Хотите перейти на страницу для выставления оценки?" +msgstr "Некоторые из ваших изображений не удалось загрузить. Перейти на страницу для выставления оценки?" msgid "You can select up to 16 images." msgstr "Допускается выбор до 16 изображений." @@ -7317,7 +7360,7 @@ msgid "The 3MF file version is in Beta and it is newer than the current OrcaSlic msgstr "Этот файл 3MF был сохранён в более новой бета-версии OrcaSlicer." msgid "If you would like to try Orca Slicer Beta, you may click to" -msgstr "Нажмите, если хотите попробовать бета-версию Orca Slicer:" +msgstr "Нажмите, чтобы попробовать бета-версию Orca Slicer:" msgid "Download Beta Version" msgstr "Скачать бета-версию" @@ -7847,7 +7890,7 @@ msgid "" "Are you sure to continue syncing?" msgstr "" "Текущий выбранный профиль принтера не соответствует типу подключённого принтера.\n" -"Вы уверены, что хотите продолжить синхронизацию?" +"Продолжить синхронизацию?" msgid "There are unset nozzle types. Please set the nozzle types of all extruders before synchronizing." msgstr "Типы сопел не заданы. Перед синхронизацией необходимо указать типы всех установленных сопел." @@ -7917,7 +7960,7 @@ msgstr "Информация о типе и цвете филамента син #, boost-format msgid "Do you want to save changes to \"%1%\"?" -msgstr "Вы хотите сохранить изменения в \"%1%\"?" +msgstr "Сохранить изменения в «%1%»?" #, c-format, boost-format msgid "Successfully unmounted. The device %s (%s) can now be safely removed from the computer." @@ -7928,7 +7971,7 @@ msgid "Ejecting of device %s (%s) has failed." msgstr "Не удалось извлечь устройство %s (%s)." msgid "Previously unsaved items have been detected. Do you want to restore them?" -msgstr "Обнаружен несохранённый проект. Хотите восстановить его?" +msgstr "Обнаружен несохранённый проект. Восстановить его?" msgid "Restore" msgstr "Восстановить" @@ -7954,7 +7997,7 @@ msgstr "Свернуть боковую панель" # Название кнопки на клавиатуре, на момент 2.3.2 используется в 5 местах в # значении названия кнопки msgid "Tab" -msgstr "" +msgstr "Tab" #, c-format, boost-format msgid "Loading file: %s" @@ -7964,7 +8007,7 @@ msgid "Load 3MF" msgstr "Загрузка 3mf" msgid "BambuStudio Project" -msgstr "" +msgstr "Проект BambuStudio" msgid "The 3MF is not supported by OrcaSlicer, loading geometry data only." msgstr "Этот 3MF не поддерживается OrcaSlicer, поэтому загрузятся только данные геометрии." @@ -7973,10 +8016,10 @@ msgid "The 3MF file was generated by an old OrcaSlicer version, loading geometry msgstr "Этот файл 3MF был сохранён в старой версии OrcaSlicer, будут загружены только данные геометрии." msgid "The 3MF file was generated by an older version, loading geometry data only." -msgstr "" +msgstr "Файл 3MF был создан в старой версии, будут загружены только данные геометрии." msgid "The 3MF file was generated by BambuStudio, loading geometry data only." -msgstr "" +msgstr "Файл 3MF был создан в BambuStudio, будут загружены только данные геометрии." msgid "This project was created with an OrcaSlicer 2.3.1-alpha and uses infill rotation template settings that may not work properly with your current infill pattern. This could result in weak support or print quality issues." msgstr "Этот проект был создан в OrcaSlicer 2.3.1-alpha и использует шаблон поворота заполнения, который может некорректно работать с вашим текущим шаблоном заполнения. Это может привести к снижению прочности или проблемам с качеством печати." @@ -7998,14 +8041,14 @@ msgstr "Этот файл 3MF был сохранён в более новой #, c-format, boost-format msgid "The 3MF was created by BambuStudio (version %s), which is newer than the compatible version %s. Found unrecognized settings:" -msgstr "" +msgstr "Этот файл 3MF был создан в BambuStudio (версии %s), которая новее совместимой с ней версией OrcaSlicer %s. Обнаружены следующие нераспознанные настройки:" #, c-format, boost-format msgid "The 3MF was created by BambuStudio (version %s), which is newer than the compatible version %s. Some settings may not be fully compatible." -msgstr "" +msgstr "Этот файл 3MF был создан в BambuStudio (версии %s), которая новее совместимой с ней версией OrcaSlicer %s. Некоторые настройки могут не поддерживаться." msgid "The 3MF was created by BambuStudio. Some settings may differ from OrcaSlicer." -msgstr "" +msgstr "Этот файл 3MF был создан в BambuStudio. Некоторые настройки могут отличаться от OrcaSlicer." msgid "Invalid values found in the 3MF:" msgstr "В файле 3MF найдены недопустимые значения:" @@ -8023,7 +8066,7 @@ msgid "Modified G-code" msgstr "Модифицированный G-код" msgid "The 3MF has the following customized filament or printer presets:" -msgstr "В этом 3MF файле содержатся следующие пользовательские профили материала или принтера:" +msgstr "В этом 3MF файле содержатся следующие пользовательские профили материалов или принтера:" msgid "Please confirm that the G-code within these presets is safe to prevent any damage to the machine!" msgstr "Во избежание повреждения принтера убедитесь, что G-код в этих профилях безопасен!" @@ -8083,8 +8126,9 @@ msgstr "Загрузить эти файлы как единую модель, msgid "An object with multiple parts was detected" msgstr "Обнаружена модель, состоящая из нескольких частей" +# Настройка – Притягивать к столу, но тут это галочка при импорте нескольких файлов разом msgid "Auto-Drop" -msgstr "" +msgstr "Опустить на стол" #, c-format, boost-format msgid "Connected printer is %s. It must match the project preset for printing.\n" @@ -8098,9 +8142,8 @@ msgstr "Файл не содержит никаких геометрически msgid "Your object appears to be too large, do you want to scale it down to fit the print bed automatically?" msgstr "" -"Похоже, ваша модель слишком большая.\n" -"Хотите автоматически уменьшить её масштаб,\n" -"чтобы она уместилась на столе?" +"Размер модели слишком велик.\n" +"Вписать её в область печати?" msgid "Object too large" msgstr "Модель слишком большая" @@ -8423,30 +8466,30 @@ msgid "The file %s has been sent to the printer's storage space and can be viewe msgstr "Файл %s отправлен в память принтера и может быть просмотрен на нём." msgid "The nozzle type is not set. Please set the nozzle and try again." -msgstr "Тип сопла не задан, укажите его и попробуйте ещё раз." +msgstr "Не указан тип сопла. Укажите его и попробуйте ещё раз." msgid "The nozzle type is not set. Please check." -msgstr "Тип сопла не задан, проверьте настройки." +msgstr "Не указан тип сопла. Проверьте настройки." msgid "Unable to perform boolean operation on model meshes. Only positive parts will be kept. You may fix the meshes and try again." msgstr "Невозможно выполнить булеву операцию над сетками модели. Будут сохранены только положительные части. Попробуйте починить сетку модели и попробовать снова." #, boost-format msgid "Reason: part \"%1%\" is empty." -msgstr "Причина: часть \"%1%\" пустая." +msgstr "Причина: часть «%1%» пустая." # ??? не формирует объем, не имеет замкнутой геометрии #, boost-format msgid "Reason: part \"%1%\" does not bound a volume." -msgstr "Причина: часть \"%1%\" не формирует замкнутый объём." +msgstr "Причина: часть «%1%» не формирует замкнутый объём." #, boost-format msgid "Reason: part \"%1%\" has self intersection." -msgstr "Причина: часть \"%1%\" имеет самопересечение." +msgstr "Причина: часть «%1%» имеет самопересечение." #, boost-format msgid "Reason: \"%1%\" and another part have no intersection." -msgstr "Причина: \"%1%\" и другая часть не пересекаются." +msgstr "Причина: «%1%» не имеет пересечений с другой частью." msgid "Unable to perform boolean operation on model meshes. Only positive parts will be exported." msgstr "Невозможно выполнить булеву операцию над сетками модели. Будут экспортированы только положительные части." @@ -8454,6 +8497,7 @@ msgstr "Невозможно выполнить булеву операцию н msgid "Flashforge host is not available." msgstr "" +# Авторизация принтера Flashforge не удалась. msgid "Unable to log in to the Flashforge printer." msgstr "" @@ -8580,9 +8624,7 @@ msgstr "Объём: %1% дюйм³\n" #, boost-format msgid "Volume: %1% mm³\n" -msgstr "" -"Объём: %1% мм³\n" -"\n" +msgstr "Объём: %1% мм³\n" #, boost-format msgid "Triangles: %1%\n" @@ -8593,7 +8635,7 @@ msgstr "" #, c-format, boost-format msgid "Plate %d: %s is not suggested for use printing filament %s (%s). If you still want to do this print job, please set this filament's bed temperature to a number that is not zero." -msgstr "Покрытие %d-го стола (%s) не рекомендуется использовать для печати %s-м материалом (%s). Установите положительную температуру для этого покрытия в настройках материала, чтобы продолжить нарезку." +msgstr "Покрытие «%2$s» (стол %1$d) не поддерживает печать %4$s (материал %3$s). Смените покрытие или настройте его температуру в профиле материала, чтобы продолжить нарезку." msgid "Currently, the object configuration form cannot be used with a multiple-extruder printer." msgstr "Сводка настроек моделей пока не поддерживает многоэкструдерные принтеры." @@ -8661,6 +8703,7 @@ msgstr "Интервал резервного копирования в секу msgid "Bed Temperature Difference Warning" msgstr "Предупреждение о разнице температур" +# В конце подставляется "руководством." msgid "" "Using filaments with significantly different temperatures may cause:\n" "• Extruder clogging\n" @@ -8669,12 +8712,11 @@ msgid "" "\n" "Continue with enabling this feature?" msgstr "" -"Печать материалами со значительно отличающимися температурами может вызвать:\n" +"Печать материалами с большим перепадом температур может вызвать:\n" "• засорение сопла;\n" "• повреждение сопла;\n" "• проблемы с адгезией.\n" -"Включить эту функцию и продолжить?\n" -" " +"Включить эту функцию и продолжить? Рекомендуется ознакомиться с " # Сканирование IP принтеров в сети и поиск файла сертификата в файловом # менеджере @@ -8829,7 +8871,7 @@ msgid "If enabled, a parameter settings dialog will appear during STEP file impo msgstr "Отображать диалоговое окно с настройками импорта при открытии файлов STEP." msgid "STEP importing: linear deflection" -msgstr "Линейное отклонение при импорте STEP" +msgstr "Импорт STEP: линейное отклонение" msgid "" "Linear deflection used when meshing imported STEP files.\n" @@ -8845,7 +8887,7 @@ msgstr "" "Значение по умолчанию: 0.003 мм." msgid "STEP importing: angle deflection" -msgstr "Угловое отклонение при импорте STEP" +msgstr "Импорт STEP: угловое отклонение" msgid "" "Angle deflection used when meshing imported STEP files.\n" @@ -8965,7 +9007,7 @@ msgid "Delay in seconds before auto slicing starts, allowing multiple edits to b msgstr "Ожидание перед запуском нарезки, позволяет вносить несколько изменений подряд. 0 – запускать без ожидания." msgid "Remove mixed temperature restriction" -msgstr "Снять ограничение перепада температур" +msgstr "Игнорировать перепад температур" msgid "With this option enabled, you can print materials with a large temperature difference together." msgstr "Активировать возможность совместной печати разными материалами с большим перепадом температур." @@ -9553,11 +9595,11 @@ msgstr "Перезапись системного профиля запреще #, boost-format msgid "Preset \"%1%\" already exists." -msgstr "Профиль \"%1%\" уже существует." +msgstr "Профиль «%1%» уже существует." #, boost-format msgid "Preset \"%1%\" already exists and is incompatible with the current printer." -msgstr "Профиль \"%1%\" уже существует и несовместим с текущим принтером." +msgstr "Профиль «%1%» уже существует и несовместим с текущим принтером." msgid "Please note that saving will overwrite the current preset." msgstr "Обратите внимание, что при сохранении произойдёт перезапись текущего профиля." @@ -9574,23 +9616,23 @@ msgstr "Копировать" #, boost-format msgid "Printer \"%1%\" is selected with preset \"%2%\"" -msgstr "Принтер \"%1%\" выбран с профилем \"%2%\"" +msgstr "Принтер «%1%» выбран с профилем «%2%»" #, boost-format msgid "Please choose an action with \"%1%\" preset after saving." -msgstr "Пожалуйста, выберите действие с профилем \"%1%\" после сохранения." +msgstr "Пожалуйста, выберите действие с профилем «%1%» после сохранения." #, boost-format msgid "For \"%1%\", change \"%2%\" to \"%3%\" " -msgstr "Для \"%1%\" сменить \"%2%\" на \"%3%\" " +msgstr "Для «%1%» сменить «%2%» на «%3%» " #, boost-format msgid "For \"%1%\", add \"%2%\" as a new preset" -msgstr "Для \"%1%\" добавить \"%2%\" как новый профиль" +msgstr "Для «%1%» добавить «%2%» как новый профиль" #, boost-format msgid "Simply switch to \"%1%\"" -msgstr "Просто переключиться на \"%1%\"" +msgstr "Просто переключиться на «%1%»" msgid "Task canceled" msgstr "Задание отменено" @@ -9722,10 +9764,10 @@ msgstr "" #, c-format, boost-format msgid "Filament %s does not match the filament in AMS slot %s. Please update the printer firmware to support AMS slot assignment." -msgstr "Материал %s не соответствует материалу в слоте AMS %s. Обновите прошивку принтера для возможности назначения слота AMS." +msgstr "Материал %s не соответствует материалу в слоте AMS %s. Для назначения слота AMS необходимо обновить прошивку принтера." msgid "Filament does not match the filament in AMS slot. Please update the printer firmware to support AMS slot assignment." -msgstr "Материал %s не соответствует материалу в слоте %s AMS. Обновите прошивку принтера, чтобы получить поддержку функции назначения слотов AMS." +msgstr "Материал %s не соответствует материалу в слоте %s AMS. Для назначения слота AMS необходимо обновить прошивку принтера." #, c-format, boost-format msgid "The selected printer (%s) is incompatible with the print file configuration (%s). Please adjust the printer preset in the prepare page or choose a compatible printer on this page." @@ -9843,18 +9885,18 @@ msgstr "" #, c-format, boost-format msgid "The nozzle flow setting of %s(%s) doesn't match with the slicing file(%s). Please make sure the nozzle installed matches with settings in printer, then set the corresponding printer preset while slicing." -msgstr "Настройка потока сопла %s(%s) не совпадает с файлом нарезки (%s). Убедитесь, что установленное сопло соответствует настройкам принтера, затем выберите соответствующий профиль принтера при нарезке." +msgstr "Настройка потока сопла %s(%s) не совпадает с файлом нарезки (%s). Убедитесь, что настройки принтера соответствуют установленному соплу, затем выберите соответствующий профиль принтера при нарезке." msgid "Tips: If you changed your nozzle of your printer lately, please go to 'Device -> Printer parts' to change your nozzle setting." msgstr "Совет: после замены сопла в принтере необходимо обновить его настройки («Принтер» → «Части принтера»)." #, c-format, boost-format msgid "The %s diameter(%.1fmm) of current printer doesn't match with the slicing file (%.1fmm). Please make sure the nozzle installed matches with settings in printer, then set the corresponding printer preset when slicing." -msgstr "Диаметр %s (%.1f мм) текущего принтера не совпадает с файлом нарезки (%.1f мм). Убедитесь, что установленное сопло соответствует настройкам принтера, и выберите соответствующий профиль принтера при нарезке." +msgstr "Диаметр %s (%.1f мм) текущего принтера не совпадает с файлом нарезки (%.1f мм). Убедитесь, что настройки принтера соответствуют установленному соплу, и выберите соответствующий профиль принтера при нарезке." #, c-format, boost-format msgid "The current nozzle diameter (%.1fmm) doesn't match with the slicing file (%.1fmm). Please make sure the nozzle installed matches with settings in printer, then set the corresponding printer preset when slicing." -msgstr "Текущий диаметр сопла (%.1f мм) не совпадает с файлом нарезки (%.1f мм). Убедитесь, что установленное сопло соответствует настройкам принтера, и выберите соответствующий профиль принтера при нарезке." +msgstr "Текущий диаметр сопла (%.1f мм) не совпадает с файлом нарезки (%.1f мм). Убедитесь, что настройки принтера соответствуют установленному соплу, и выберите соответствующий профиль принтера при нарезке." msgid "both extruders" msgstr "обоих экструдерах" @@ -10190,13 +10232,13 @@ msgid "A prime tower is required for clumping detection. There may be flaws on t msgstr "Черновая башня необходима для обнаружения налипаний на сопле, без неё на модели могут образоваться дефекты. Вы действительно хотите отключить черновую башню?" msgid "Enabling both precise Z height and the prime tower may cause slicing errors. Do you still want to enable?" -msgstr "Одновременное включение точной высоты Z и башни очистки может вызвать ошибки нарезки. Вы всё равно хотите включить?" +msgstr "Включение черновой башни совместно с «Точной высотой по Z» может привести к ошибкам нарезки. Продолжить?" msgid "A prime tower is required for clumping detection. There may be flaws on the model without prime tower. Do you still want to enable clumping detection?" msgstr "Для обнаружения налипаний на сопле требуется черновая башня, без неё на модели могут образоваться дефекты. Включить обнаружение налипаний?" msgid "Enabling both precise Z height and the prime tower may cause slicing errors. Do you still want to enable precise Z height?" -msgstr "Одновременное включение точной высоты Z и башни очистки может вызвать ошибки нарезки. Вы всё равно хотите включить точную высоту Z?" +msgstr "Включение «Точной высоты по Z» вместе с черновой башней может привести к ошибкам нарезки. Продолжить?" msgid "A prime tower is required for smooth timelapse mode. There may be flaws on the model without prime tower. Do you want to enable the prime tower?" msgstr "Для сглаженного таймлапса требуется черновая башня, без неё на модели могут возникнуть дефекты. Включить черновую башню?" @@ -10247,13 +10289,14 @@ msgstr "" msgid "Enabling this option will modify the model's shape. If your print requires precise dimensions or is part of an assembly, it's important to double-check whether this change in geometry impacts the functionality of your print." msgstr "Включение этой настройки приведёт к изменению формы модели. Если модель требует соблюдения точности размеров или является частью сборки, убедитесь, что изменение геометрии не повлияет на функциональность напечатанного." +# Дописывается в диалоговых окнах (sparse_infill_rotate_template и make_overhang_printable). msgid "Are you sure you want to enable this option?" -msgstr "Вы действительно хотите включить эту опцию?" +msgstr "Вы действительно хотите задействовать эту функцию?" # Слайсер дописывает "Вы действительно хотите включить эту опцию?" в следующей # строчке. Т.е. последнее предложение переводить не нужно, оно дублируется. msgid "Infill patterns are typically designed to handle rotation automatically to ensure proper printing and achieve their intended effects (e.g., Gyroid, Cubic). Rotating the current sparse infill pattern may lead to insufficient support. Please proceed with caution and thoroughly check for any potential printing issues. Are you sure you want to enable this option?" -msgstr "Шаблоны заполнения обычно разработаны так, чтобы автоматически обрабатывать поворот для обеспечения правильной печати и достижения желаемого эффекта (например, «гироид», «куб»). Поворот текущего узора заполнения может привести к недостаточной поддержке. Будьте осторожны и тщательно проверьте наличие возможных проблем с печатью." +msgstr "Многие шаблоны заполнения разработаны на основе автоматического поворота по определённым правилам для поддержания правильной печати и желаемого эффекта (например, «Гироид» или «Куб»). Изменение правила поворота текущего шаблона может привести к его провисанию. Будьте осторожны и внимательно проверяйте результат на наличие потенциальных проблем с печатью." msgid "" "Layer height is too small.\n" @@ -10263,7 +10306,7 @@ msgstr "" "Будет установлено значение min_layer_height\n" msgid "Layer height exceeds the limit in Printer Settings -> Extruder -> Layer height limits, this may cause printing quality issues." -msgstr "Высота слоя не может превышать ограничения, установленные в настройках принтера -> Экструдер -> Ограничение высоты слоя. Это может вызвать проблемы с качеством печати." +msgstr "Высота слоя не может превышать ограничения, установленные в настройках принтера → Экструдер → Ограничение высоты слоя. Это может вызвать проблемы с качеством печати." msgid "Adjust to the set range automatically?\n" msgstr "Автоматически подстроиться под заданный в настройках диапазон?\n" @@ -10282,7 +10325,7 @@ msgid "" "by right-click the empty position of build plate and choose \"Add Primitive\"->\"Timelapse Wipe Tower\"." msgstr "" "При записи таймлапса со скрытием головы рекомендуется добавить черновую башню таймлапса.\n" -"Нажмите по столу правой кнопкой мыши и выберите «Добавить примитив» → «Черновая башня таймлапса»." +"Нажмите по столу правой кнопкой мыши и выберите «Добавить фигуру» → «Черновая башня таймлапса»." msgid "A copy of the current system preset will be created, which will be detached from the system preset." msgstr "Будет создана независимая копия текущего системного профиля." @@ -10488,13 +10531,14 @@ msgid "Chamber temperature" msgstr "Температура термокамеры" msgid "Target chamber temperature, and the minimal chamber temperature at which printing should start" -msgstr "Целевая (основная) и минимальная температура термокамеры, с которой начинается печать" +msgstr "Целевая (основная) и стартовая температуры термокамеры (с которой начинается печать)" msgid "Target" msgstr "Целевая" +# "Минимальная" не влезает msgid "Minimal" -msgstr "Минимальная" +msgstr "Стартовая" msgid "Print temperature" msgstr "Температура печати" @@ -10772,9 +10816,9 @@ msgstr "Отсоединён" #, c-format, boost-format msgid "%d Filament Preset and %d Process Preset is attached to this printer. Those presets would be deleted if the printer is deleted." msgstr "" -"С этим принтером связано\n" -"• профилей материала: %d\n" -"• профилей настроек: %d\n" +"С этим принтером связаны\n" +"• профили материалов: %d\n" +"• профили настроек: %d\n" "\n" "Эти профили будут удалены при удалении принтера." @@ -11043,7 +11087,7 @@ msgid "" "Note: New modified presets will be selected in settings tabs after close this dialog." msgstr "" "Перенос выбранных параметров из левого профиля в правый.\n" -"Примечание: новые изменённые профили будут выбраны после закрытия этого диалогового окна." +"Примечание: после закрытия окна будут выбраны изменённые профили." msgid "Transfer values from left to right" msgstr "Перенести значения из левого профиля" @@ -11052,10 +11096,10 @@ msgid "If enabled, this dialog can be used for transfer selected values from lef msgstr "При включении активируется режим выбора значений из левого профиля для переноса в правый." msgid "One of the presets does not exist" -msgstr "" +msgstr "Один из профилей не существует" msgid "Compared presets has different printer technology" -msgstr "" +msgstr "Принтеры печатают по разным технологиям" msgid "Add File" msgstr "Добавить файл" @@ -12062,13 +12106,14 @@ msgid " is too close to clumping detection area, and collisions will be caused.\ msgstr " находится слишком близко к зоне обнаружения налипаний, столкновения неизбежны.\n" msgid "Selected nozzle temperatures are incompatible. Each filament's nozzle temperature must fall within the recommended nozzle temperature range of the other filaments. Otherwise, nozzle clogging or printer damage may occur." -msgstr "" +msgstr "Обнаружен недопустимый перепад температур. Каждый из используемых материалов должен иметь в профиле температуру печати в пределах допустимого диапазона других материалов. В противном случае сопло может забиться и повредить принтер." msgid "Invalid recommended nozzle temperature range. The lower bound must be lower than the upper bound." -msgstr "" +msgstr "Диапазон допустимых температур сопла задан некорректно. Минимально допустимая температура сопла не может быть выше максимально допустимой." +# В подсказках стрелочки "→" не работают msgid "If you still want to print, you can enable the option in Preferences / Control / Slicing / Remove mixed temperature restriction." -msgstr "" +msgstr "Если вас это не пугает, эту проверку можно отключить настройкой слайсера «Игнорировать перепад температур» (Настройки > Управление > Нарезка)." msgid "No extrusions under current settings." msgstr "При текущих настройках экструзия отсутствует." @@ -12147,6 +12192,7 @@ msgstr "Для использования черновой башни требу msgid "The prime tower is only supported if all objects have the same variable layer height." msgstr "Для черновой башни требуется, чтобы все модели имели одинаковую переменную высоту слоя." +# Для одного или нескольких моделей назначен несуществующий экструдер – может так будет лучше? Надо по контексту употребления смотреть. msgid "One or more object were assigned an extruder that the printer does not have." msgstr "Для одного или нескольких моделей был назначен экструдер, который у принтера отсутствует." @@ -12671,8 +12717,10 @@ msgstr "" "\n" "Повышение плотности:\n" "• создаёт опору на предыдущие линии при печати каждой новой;\n" -"• требует точного подбора потока, хорошего охлаждения и низкой скорости печати;\n" -"• может вызвать переэкструзию, усадочную деформацию слоя и ухудшить отделяемость поддержек.\n" +"• требует точного подбора потока, хорошего охлаждения и низкой\n" +"   скорости печати;\n" +"• может вызвать переэкструзию, усадочную деформацию слоя и\n" +"   ухудшить отделяемость поддержек.\n" "\n" "Понижение плотности:\n" "• улучшает эффективность охлаждения за счёт увеличения отступа;\n" @@ -12703,7 +12751,8 @@ msgstr "" "\n" "Повышение плотности:\n" "• позволяет быстро набрать прочность перед печатью внешних слоёв;\n" -"• требует точного подбора потока, хорошего охлаждения и/или низкой скорости печати;\n" +"• требует точного подбора потока, хорошего охлаждения и/или низкой\n" +"   скорости печати;\n" "• может вызвать усадочную деформацию слоя моста при охлаждении.\n" "\n" "Понижение плотности:\n" @@ -13399,17 +13448,17 @@ msgstr "" "Шаблон заполнения верхней поверхности.\n" "\n" "🞄 Аккуратный зигзаг: лучше зигзага (см. ниже) сохраняет\n" -" однородность поверхности при наличии отверстий.\n" +"   однородность поверхности при наличии отверстий.\n" "🞄 Аккуратная штриховка: идентичен предыдущему, но состоит из\n" -" отдельных штрихов.\n" +"   отдельных штрихов.\n" "🞄 Зигзаг: самый простой вариант обхода отверстий.\n" "🞄 Ровный зигзаг: создаёт минимум пор в сплошном заполнении,\n" -" полезно для прозрачных материалов и повышения когезии.\n" +"   полезно для прозрачных материалов и повышения когезии.\n" "🞄 Эквидистанты: повторяют контур периметров.\n" "🞄 Кривая Гильберта: декоративный шаблон, хорошо распределяет\n" -" усадку и рекордно долго печатается.\n" +"   усадку и рекордно долго печатается.\n" "🞄 Спираль Архимеда: декоративный шаблон, в отличие от эквидистант\n" -" не создаёт горизонтального шва.\n" +"   не создаёт горизонтального шва.\n" "🞄 Спиральная октаграмма: декоративный шаблон." msgid "Monotonic" @@ -13445,24 +13494,27 @@ msgid "Density of top surface layer. A value of 100% creates a fully solid, smoo msgstr "Плотность верхней поверхности. Если установить 100%, поверхность будет сплошной и гладкой. Уменьшение этого параметра создаст текстурированную поверхность в соответствии с выбранным шаблоном заполнения верхней поверхности. При значении 0% останутся только стенки верхнего слоя. Эта функция предназначена для улучшения внешнего вида или функциональности объекта, но не для решения проблем, таких как чрезмерная экструзия." msgid "Top surface expansion" -msgstr "" +msgstr "Расширение поверхностей" msgid "" "Expands the top surfaces by this distance to connect distinct top surfaces and fill gaps.\n" "Useful for cases where the top surface is interrupted by a raised feature, such as text on a plane. Expanding it removes the holes beneath these features and creates a continuous path with a better finish for printing on top. The expansion is applied to the original top surface, before any other processing such as bridging or overhang detection." msgstr "" +"Позволяет сместить границу заполнения плоских поверхностей относительно опирающихся на них периметров, чтобы закрыть небольшие разрывы в шаблоне.\n" +"Полезно в случаях, когда на поверхности расположен текст или иные выступы." msgid "Top expansion wall margin" -msgstr "" +msgstr "Отступ от края" msgid "" "Using “Top surface expansion” may cause a surface that did not previously touch the model's outer walls to now do so.\n" "This can cause contraction marks (such as the hull line) on the outer walls.\n" "By adding a small margin, this contraction will not occur directly on the walls, thereby preventing a visible mark." msgstr "" +"Расширение закрытой поверхности (например, окружённой стенками впадины) может привести к её сцеплению с внешним контуром модели, что повышает риск возникновения дефектов усадки. Добавление отступа позволяет избежать этого." msgid "Top expansion direction" -msgstr "" +msgstr "Направление расширения" msgid "" "Direction in which the top surface expansion grows.\n" @@ -13470,15 +13522,19 @@ msgid "" " - Outward grows the outer edge of the surface, connecting surfaces separated by features that can divide a surface, such as a lattice pattern.\n" " - Inward and Outward does both." msgstr "" +"Направление смещения контура поверхности. Применяется к разным типам объектов на ней.\n" +"• Внутрь: объекты целиком внутри площади поверхности.\n" +"• Наружу: объекты частично или полностью за её пределами.\n" +"• Оба: расширять поверхность во всех доступных направлениях." msgid "Inward and Outward" -msgstr "" +msgstr "Оба" msgid "Inward" -msgstr "" +msgstr "Внутрь" msgid "Outward" -msgstr "" +msgstr "Наружу" msgid "Bottom surface pattern" msgstr "Шаблон заполнения нижней поверхности" @@ -13488,17 +13544,17 @@ msgstr "" "Шаблон заполнения нижней поверхности, кроме мостов.\n" "\n" "🞄 Аккуратный зигзаг: лучше зигзага (см. ниже) сохраняет\n" -" однородность поверхности при наличии отверстий.\n" +"   однородность поверхности при наличии отверстий.\n" "🞄 Аккуратная штриховка: идентичен предыдущему, но состоит из\n" -" отдельных штрихов.\n" +"   отдельных штрихов.\n" "🞄 Зигзаг: самый простой вариант обхода отверстий.\n" "🞄 Ровный зигзаг: создаёт минимум пор в сплошном заполнении,\n" -" полезно для прозрачных материалов и повышения когезии.\n" +"   полезно для прозрачных материалов и повышения когезии.\n" "🞄 Эквидистанты: повторяют контур периметров.\n" "🞄 Кривая: Гильберта: декоративный шаблон, хорошо распределяет\n" -" усадку и рекордно долго печатается.\n" +"   усадку и рекордно долго печатается.\n" "🞄 Спираль Архимеда: декоративный шаблон, в отличие от эквидистант\n" -" не создаёт горизонтального шва.\n" +"   не создаёт горизонтального шва.\n" "🞄 Спиральная октаграмма: декоративный шаблон." msgid "Bottom surface density" @@ -13512,22 +13568,30 @@ msgstr "" "Внимание: уменьшение этого значения может негативно повлиять на адгезию к печатной платформе." msgid "Top surface fill order" -msgstr "" +msgstr "Направление печати" msgid "" "Direction in which top surfaces are filled when using a center-based pattern (Concentric, Archimedean Chords, Octagram Spiral).\n" "Outward starts at the center of the surface, so any excess material is pushed towards the edge where it is least visible. Inward starts at the edge and ends with the tight curves at the center.\n" "Default uses shortest-path ordering, which may run in either direction." msgstr "" +"Направление печати спирали/эквидистант на верхних поверхностях. Позволяет управляемо распределять избыток материала.\n" +"• По умолчанию: использовать кратчайший путь.\n" +"• Наружу: от центра шаблона к краю модели.\n" +"• Внутрь: от края модели к центру шаблона." msgid "Bottom surface fill order" -msgstr "" +msgstr "Направление печати" msgid "" "Direction in which bottom surfaces are filled when using a center-based pattern (Concentric, Archimedean Chords, Octagram Spiral).\n" "Inward starts each surface with the wider outer curves, which improves first layer adhesion on build plates where the tight curves at the center may not stick. Outward starts at the center, pushing any excess material towards the edge.\n" "Default uses shortest-path ordering, which may run in either direction." msgstr "" +"Направление печати спирали/эквидистант на нижних поверхностях. Позволяет управляемо распределять избыток материала.\n" +"• По умолчанию: использовать кратчайший путь.\n" +"• Наружу: от центра шаблона к краю модели.\n" +"• Внутрь: от края модели к центру шаблона." msgid "Internal solid infill pattern" msgstr "Шаблон сплошного заполнения" @@ -13537,17 +13601,17 @@ msgstr "" "Шаблон печати внутреннего сплошного заполнения.\n" "\n" "🞄 Аккуратный зигзаг: лучше зигзага (см. ниже) сохраняет\n" -" однородность поверхности при наличии отверстий.\n" +"   однородность поверхности при наличии отверстий.\n" "🞄 Аккуратная штриховка: идентичен предыдущему, но состоит из\n" -" отдельных штрихов.\n" +"   отдельных штрихов.\n" "🞄 Зигзаг: самый простой вариант обхода отверстий.\n" "🞄 Ровный зигзаг: создаёт минимум пор в сплошном заполнении,\n" -" полезно для прозрачных материалов и повышения когезии.\n" +"   полезно для прозрачных материалов и повышения когезии.\n" "🞄 Эквидистанты: повторяют контур периметров.\n" "🞄 Кривая: Гильберта: декоративный шаблон, хорошо распределяет\n" -" усадку и рекордно долго печатается.\n" +"   усадку и рекордно долго печатается.\n" "🞄 Спираль Архимеда: декоративный шаблон, в отличие от эквидистант\n" -" не создаёт горизонтального шва.\n" +"   не создаёт горизонтального шва.\n" "🞄 Спиральная октаграмма: декоративный шаблон.\n" "\n" "Внимание: при включении «Оптимизации заполнения узких мест» в узких участках для заполнения будут использоваться эквидистанты." @@ -13735,7 +13799,7 @@ msgid "Enable pressure advance, auto calibration result will be overwritten once msgstr "Включить Pressure Advance (коррекцию давления в сопле). Результат автокалибровки будет перезаписан после включения." msgid "Pressure advance (Klipper) AKA Linear advance factor (Marlin)." -msgstr "Pressure Advance в прошивке Klipper – то же самое, что и Linear Advance в прошивке Marlin." +msgstr "Pressure Advance в прошивке Klipper – то же самое, что и Linear Advance в прошивке Marlin." msgid "Enable adaptive pressure advance (beta)" msgstr "Адаптивный PA (beta)" @@ -13786,14 +13850,14 @@ msgstr "" "Выбор тестируемых ускорений:\n" " • наиболее низкое ускорение из настроек печати\n" " • наиболее высокое ускорение (не должно превышать\n" -" рекомендуемый предел калибровщика Input Shaper в Klipper)\n" +"    рекомендуемый предел калибровщика Input Shaper в Klipper)\n" "\n" "2. Выпишите коэффициенты PA по примеру выше для каждой пары расхода/ускорения. Удельный расход можно посмотреть в «Просмотре нарезки», выбрав режим отображения «Объёмный расход». Значение отображается над горизонтальной шкалой печати слоя. Особенности:\n" "• Как правило, значение PA должно снижаться с повышением расхода.\n" -" Если это не так, проверьте экструдер и корректность тестов.\n" +"   Если это не так, проверьте экструдер и корректность тестов.\n" "• Диапазон PA растёт со снижением скоростей и ускорений.\n" "• Если разницы между тестами не наблюдается, выбирайте PA из\n" -" наиболее быстрого теста.\n" +"   наиболее быстрого теста.\n" "\n" "3. Проверьте порядок введённых значений (PA, расход, ускорения) и сохраните профиль материала." @@ -13883,7 +13947,7 @@ msgid "Filament map to extruder." msgstr "Назначение филамента на экструдер." msgid "Auto For Flush" -msgstr "Авто для промывки" +msgstr "Авто для прочистки" msgid "Auto For Match" msgstr "Авто для сопоставления" @@ -13895,7 +13959,7 @@ msgid "Flush temperature" msgstr "Температура прочистки" msgid "Temperature when flushing filament. 0 indicates the upper bound of the recommended nozzle temperature range." -msgstr "Температура при прочистке. 0 – использовать верхний предел рекомендуемой температуры." +msgstr "Температура при прочистке. 0 – использовать максимально допустимую температуру." msgid "Flush temperature used in fast purge mode." msgstr "" @@ -13915,13 +13979,13 @@ msgid "Filament load time" msgstr "Время загрузки прутка" msgid "Time to load new filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only." -msgstr "Время загрузки прутка при смене материала. Применяется для комбинированной печати через общий экструдер. Для принтеров со сменой инструмента или многоголовых принтеров обычно равно 0. Используется при подсчёте статистики." +msgstr "Время загрузки прутка при смене материала. Применяется для совместной печати через общий экструдер. Для принтеров со сменой инструмента или многоголовых принтеров обычно равно 0. Используется при подсчёте статистики." msgid "Filament unload time" msgstr "Время выгрузки прутка" msgid "Time to unload old filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only." -msgstr "Время выгрузки старого прутка при смене материала. Применяется для комбинированной печати через общий экструдер. Для принтеров со сменой инструмента или многоголовых принтеров обычно равно 0. Используется при подсчёте статистики." +msgstr "Время выгрузки старого прутка при смене материала. Применяется для совместной печати через общий экструдер. Для принтеров со сменой инструмента или многоголовых принтеров обычно равно 0. Используется при подсчёте статистики." msgid "Tool change time" msgstr "Время смены инструмента" @@ -14095,10 +14159,10 @@ msgid "mm²" msgstr "мм²" msgid "Interface layer purge length" -msgstr "Длина промывки слоя интерфейса" +msgstr "Прочистка на слое интерфейса" msgid "Purge length for prime tower interface layer (where different materials meet)." -msgstr "Длина промывки слоя интерфейса башни очистки (где соприкасаются разные материалы)." +msgstr "Длина прочистки на слое интерфейса башни (где соприкасаются разные материалы)." msgid "Interface layer print temperature" msgstr "Температура при прочистке" @@ -14107,7 +14171,7 @@ msgid "Print temperature for prime tower interface layer (where different materi msgstr "" "Целевая температура при печати слоёв прочистки в черновой башне (место контакта разных материалов).\n" "\n" -"При значении -1 используется максимальная рекомендуемая температура." +"При значении -1 используется максимально допустимая температура." msgid "Speed of the last cooling move" msgstr "Скорость последнего охлаждающего движения" @@ -14247,12 +14311,13 @@ msgid "Density of internal sparse infill, 100% turns all sparse infill into soli msgstr "Плотность внутреннего заполнения, выраженная в процентах. 100% означает сплошное заполнение." msgid "Align directions to model" -msgstr "" +msgstr "Относительные углы" msgid "" "Aligns infill, bridge, ironing, and top/bottom surface directions to follow the model's orientation on the build plate.\n" "When enabled, these directions rotate together with the model so the printed features keep their intended orientation relative to the part, preserving optimal strength and surface characteristics regardless of how the model is placed." -msgstr "" +msgstr "Учитывать вращение модели при расчёте угла мостов, заполнения и разглаживания.\n" +"Позволяет сохранять направленность линий относительно модели независимо от её поворота на столе." msgid "Insert solid layers" msgstr "Правило сплошных слоёв" @@ -14289,43 +14354,43 @@ msgstr "" "🞄 Зигзаг: самый простой и быстрый вариант заполнения.\n" "🞄 Ровный зигзаг: идентичен зигзагу, но сохраняет постоянный угол.\n" "🞄 Аккуратный зигзаг: идентичен зигзагу, но сохраняет согласованность\n" -" между слоями и поддерживает зеркальный режим.\n" +"   между слоями и поддерживает зеркальный режим.\n" "🞄 Смещённый зигзаг: идентичен зигзагу, но смещается с каждым слоем.\n" "🞄 Двойной зигзаг: зигзаг в оболочке с возможностью её настройки.\n" "🞄 Нити: похожи на зигзаг, но содержат вдвое меньше углов и\n" -" печатаются быстрее. Заполнение напоминает изонить.\n" +"   печатаются быстрее. Заполнение напоминает изонить.\n" "🞄 Сетка: простая сетка из двух перпендикулярных линий в основании.\n" "🞄 Треугольники: сетка из трёх линий в основании.\n" "🞄 Звёзды: идентичен треугольной сетке, но линии смещены для\n" -" снижения переэкструзии в её узлах.\n" +"   снижения переэкструзии в её узлах.\n" "🞄 Куб: заполнение из вертикально расположенных кубов, отлично\n" -" распределяет нагрузку во всех направлениях у разных форм.\n" +"   распределяет нагрузку во всех направлениях у разных форм.\n" "🞄 Динамический куб: идентичен кубу, но экономит материал и время\n" -" в наименее нагруженных областях. Идеален для крупных моделей.\n" +"   в наименее нагруженных областях. Идеален для крупных моделей.\n" "🞄 Четверной куб: алгоритм идентичен кубу, но имеет в основании\n" -" 4 линии. Лучше распределяет нагрузку в ортогональных формах.\n" +"   4 линии. Лучше распределяет нагрузку в ортогональных формах.\n" "🞄 Кубическая поддержка: экономичный шаблон заполнения,\n" -" рекомендуется исключительно для ненагруженных деталей.\n" +"   рекомендуется исключительно для ненагруженных деталей.\n" "🞄 Молния: предельно экономичный шаблон для быстрой печати\n" -" тестовых и декоративных моделей.\n" +"   тестовых и декоративных моделей.\n" "🞄 Соты: заполнение вертикальными шестигугольными сотами.\n" "🞄 3D-соты: заполнение усечёнными октаэдрами, состоящими из\n" -" квадратов и наклонных шестиугольников.\n" +"   квадратов и наклонных шестиугольников.\n" "🞄 Боковые соты: идентичен сотам, но расположен горизонтально.\n" -" Хорошо сопротивляется кручению.\n" +"   Хорошо сопротивляется кручению.\n" "🞄 Боковая сетка: более слабый аналог боковых сот с настройкой углов.\n" "🞄 Переходный зигзаг: упрощённый \"гироид\" для слабой электроники,\n" -" уступает ему в прочности.\n" +"   уступает ему в прочности.\n" "🞄 ТПМП Шварца D: похож на 3D-соты, но не создаёт концентраций\n" -" напряжения. Заполнение изотропно, как и гироид.\n" +"   напряжения. Заполнение изотропно, как и гироид.\n" "🞄 ТПМП Фишера-Коха S: схож с костной тканью и хорошо гасит удары.\n" "🞄 Гироид: как и 3 предыдущих, делит пространство на 2 объёма и\n" -" хорошо подходит для заливки смолой.\n" +"   хорошо подходит для заливки смолой.\n" "🞄 Эквидистанты: повторяют контур слоя. Подходят для сплошного\n" -" заполнения и гибких деталей.\n" +"   заполнения и гибких деталей.\n" "🞄 Кривая Гильберта: создаёт непрерывный фрактальный лабиринт.\n" "🞄 Спираль Архимеда: как и кривая Гильберта, равномерно заполняет\n" -" весь объём без его деления, подходит для заливки смолой.\n" +"   весь объём без его деления, подходит для заливки смолой.\n" "🞄 Спиральная октаграмма: декоративный шаблон." msgid "Zig Zag" @@ -14526,7 +14591,6 @@ msgstr "" msgid "Support interface fan speed" msgstr "Обдув связующего слоя" -# ????? Установите значение -1, чтобы запретить переопределять этот параметр. msgid "" "This part cooling fan speed is applied when printing support interfaces. Setting this parameter to a higher than regular speed reduces the layer binding strength between supports and the supported part, making them easier to separate.\n" "Set to -1 to disable it.\n" @@ -14534,8 +14598,7 @@ msgid "" msgstr "" "Скорость вращения вентилятора при печати связующих слоёв поддержки. Увеличение этой скорости уменьшает сцепление между поддержками и основной деталью, что облегчает их отделение.\n" "\n" -"Чтобы отключить, установите значение -1.\n" -"Установите значение -1, чтобы запретить переопределять этот параметр.\n" +"Установите -1 для отключения настройки.\n" "Если включена опция «Не обдувать первые слои», то она перекрывает эту настройку." msgid "Internal bridges fan speed" @@ -14794,9 +14857,9 @@ msgid "" "\n" "Note: For Klipper machines, this option is recommended to be disabled. Klipper does not benefit from arc commands as these are split again into line segments by the firmware. This results in a reduction in surface quality as line segments are converted to arcs by the slicer and then back to line segments by the firmware." msgstr "" -"Включите, если хотите чтобы программа пыталась заменить последовательности из коротких прямолинейных участков дугами (используя команды G2 и G3). Функция должна поддерживаться прошивкой принтера. Значение допуска траектории такое же как разрешение G-кода.\n" +"Распознавать движение по дуге окружности и кодировать траекторию командами G2/G3. Позволяет значительно сократить размер файла при печати некоторых моделей. Отклонения от дуги меньше указанного выше разрешения игнорируются.\n" "\n" -"Примечание: для устройств с прошивкой Klipper рекомендуется отключить эту опцию. Klipper не получает преимуществ от этой опции, поскольку прошивка снова разбивает дуги на линейные сегменты. Это приводит к снижению качества поверхности, поскольку линейные сегменты сначала преобразуются в дуги с помощью слайсера, а затем снова в линейные сегменты с помощью прошивки." +"Примечание: не рекомендуется для принтеров с прошивкой на базе Klipper. Klipper не поддерживает G2/G3 нативно и разбивает дуги обратно на линейные сегменты, что приводит к снижению качества поверхности из-за двойной потери точности. Также G2/G3 не поддерживается некоторыми старыми/проприетарными прошивками." msgid "Add line number" msgstr "Нумеровать строки" @@ -15364,6 +15427,7 @@ msgid "" "A reasonable value is 35. Set to 0 to disable." msgstr "" "Прижимать периметры к предыдущему слою на поверхностях с наклоном менее заданного значения.\n" +"\n" "Внимание: в настоящий момент действие настройки инвертировано (затрагивается всё, кроме верхних периметров с указанным углом), что делает её вредоносной." msgid "Don't alternate fill direction" @@ -16144,13 +16208,13 @@ msgstr "" "Выбор поверхностей, при печати которых следует приподнимать голову перед совершением холостого перемещения. Этот приём позволяет избежать случайных столкновений и следов подтёков на печатаемом слое.\n" "\n" "• Везде: приподнимать на всех слоях; помогает избежать столкновений\n" -" при переэкструзии и печати материалами, склонными к деформации.\n" +"   при переэкструзии и печати материалами, склонными к деформации.\n" "• Поверхности: приподнимать только при печати лицевых\n" -" горизонтальных поверхностей, помогает улучшить внешний вид.\n" +"   горизонтальных поверхностей, помогает улучшить внешний вид.\n" "• Первый слой: приподнимать только у стола, помогает избежать\n" -" столкновений с локальной переэкструзией на первом слое.\n" +"   столкновений с локальной переэкструзией на первом слое.\n" "• Первый слой и поверхности: приподнимать и у стола, и на\n" -" горизонтальных поверхностях.\n" +"   горизонтальных поверхностях.\n" "\n" "Внимание: работа этой функции может быть ограничена определённой высотой в настройках «Приподнимать не ниже» и «Приподнимать не выше»." @@ -16651,7 +16715,7 @@ msgid "Manual Filament Change" msgstr "Ручная смена прутка" msgid "Enable this option to omit the custom Change filament G-code only at the beginning of the print. The tool change command (e.g., T0) will be skipped throughout the entire print. This is useful for manual multi-material printing, where we use M600/PAUSE to trigger the manual filament change action." -msgstr "Полезно использовать для смены материала вручную при комбинированной печати через общий экструдер, где для этого используются команды M600/PAUSE. Отключает выполнение «G-кода смены материала» в самом начале печати (обычно это не требуется, так как пруток уже заправлен). Команда смены инструмента (например, T0) будет пропускаться на протяжении всей печати." +msgstr "Полезно для смены материала вручную при совместной печати через общий экструдер, где для этого используются команды M600/PAUSE. Отключает выполнение «G-кода смены материала» в самом начале печати (обычно это не требуется, так как пруток уже заправлен). Команда смены инструмента (например, T0) будет пропускаться на протяжении всей печати." msgid "Wipe tower type" msgstr "Тип башни очистки" @@ -16679,13 +16743,14 @@ msgstr "Менять материал над башней" msgid "Force the toolhead to travel to the wipe tower before issuing the tool change command (Tx). Only relevant for multi-extruder (multi-toolhead) printers using a Type 2 wipe tower. By default Orca skips the travel on multi-toolhead machines because the firmware handles the head swap, which can result in the Tx command being issued above the printed part. Enable this option if you want the tool change to always be issued above the wipe tower instead." msgstr "" -"Перемещать печатающую голову в позицию над башней перед вызовом команды смены инструмента (T). Предполагается, что прошивка принтера выполняет смену материала с учётом позиции детали и учитывает возможные подтёки, но если это не так – данная настройка позволит принудительно перемещать сопло к черновой башне заранее.\n" +"Перемещать печатающую голову в позицию над башней перед вызовом команды смены инструмента (T). Предполагается, что прошивка принтера настроена на минимизацию подтёков над деталью при смене материала, но если это не так – данная настройка позволит принудительно перемещать сопло к черновой башне заранее.\n" "\n" "Внимание: применимо только к многоэкструдерным принтерам с черновой башней 2 типа." msgid "No sparse layers (beta)" msgstr "Без разреженных слоёв (beta)" +# Requires refactoring msgid "If enabled, the wipe tower will not be printed on layers with no tool changes. On layers with a tool change, extruder will travel downward to print the wipe tower. User is responsible for ensuring there is no collision with the print." msgstr "Если включено, черновая башня не будет печататься на слоях, где не происходит смена материала/инструмента. На слоях, где происходит смена материала, экструдер будет опускаться вниз до верхней части черновой башни, чтобы напечатать её. Слайсер не проверяет столкновения при перемещении, и пользователь сам несет ответственность за правильную настройку всех соответствующих параметров." @@ -16696,20 +16761,26 @@ msgid "If enabled, all printing extruders will be primed at the front edge of th msgstr "Если этот параметр включён, все печатающие экструдеры в начале печати будут подготавливаться на переднем крае стола." msgid "Toolchange ordering" -msgstr "" +msgstr "Порядок смены материала" msgid "" "Determines the order of tool changes on each layer.\n" "- Default: Starts with the last used extruder to minimize tool changes.\n" "- Cyclic: Uses a fixed tool sequence each layer. This sacrifices speed for better surface quality, as the extra toolchanges allow layers more time to cool." msgstr "" +"Политика смены материала на каждом слое.\n" +"• По умолчанию: минимизировать количество смен материала.\n" +"• Цикличный: менять материал в строгом порядке. Это обеспечивает\n" +"   равномерное охлаждение всех материалов ценой увеличения\n" +"   времени печати." msgid "Cyclic" -msgstr "" +msgstr "Цикличный" msgid "Slice gap closing radius" msgstr "Радиус закрытия зазоров полигональной сетки" +# Requires refactoring msgid "Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low." msgstr "Часто в импортируемых в программу моделях, встречаются небольшие зазоры между полигонами. Если зазор окажется меньше двукратного значения этого параметра, то он будет автоматически закрыт перед нарезкой. Это операция может уменьшить окончательное разрешение печати (детализацию модели), поэтому рекомендуется выставлять это значение достаточно низким." @@ -16899,7 +16970,7 @@ msgstr "" "Шаблон заполнения поддержек.\n" "\n" "• По умолчанию: «Зигзаг» для обычных и «Полость» для древовидных\n" -" поддержек.\n" +"   поддержек.\n" "• Зигзаг: быстрый и лёгкий для удаления шаблон.\n" "• Чередующийся зигзаг: прочная структура для тонких поддержек.\n" "• Соты: заполнение вертикальными шестигугольными сотами.\n" @@ -16907,7 +16978,7 @@ msgstr "" "• Полость: классический вид древовидных поддержек без заполнения.\n" "\n" "Внимание:\n" -"2 периметра у органического стиля древовидных поддержек можетиметь только с шаблон «Полость» (по умолчанию).\n" +"2 периметра у органического стиля древовидных поддержек может иметь только с шаблон «Полость» (по умолчанию).\n" "Шаблон заполнения «Молния» несовместим с обычным типом поддержек и органическим стилем древовидных поддержек, при\n" "выборе несовместимых настроек он будет заменён на зигзаг." @@ -16925,15 +16996,15 @@ msgstr "" "Шаблон печати связующего слоя поддержек.\n" "\n" "• По умолчанию: «Зигзаг» для обычных и «Эквидистанты» для\n" -" растворимых материалов поддержки.\n" +"   растворимых материалов поддержки.\n" "• Зигзаг: быстрый и лёгкий для удаления шаблон.\n" "• Эквидистанты: хорошо поддерживают поверхности неправильной\n" -" формы, подходят для растворимых материалов в сочетании с\n" -" небольшим зазором и отступом.\n" +"   формы, подходят для растворимых материалов в сочетании с\n" +"   небольшим зазором и отступом.\n" "• Чередующийся зигзаг: лучше формирует контактную поверхность\n" -" в промежутках между точками опоры основной части поддержек.\n" +"   в промежутках между точками опоры основной части поддержек.\n" "• Сетка: хорошо сопротивляется деформации при печати длинных\n" -" нависающих элементов." +"   нависающих элементов." # То же самое, что и Rectilinear grid, просто повёрнуто на 45° msgid "Rectilinear Interlaced" @@ -17046,7 +17117,7 @@ msgstr "Плотность ветвей" #. TRN PrintSettings: "Organic supports" > "Branch Density" msgid "Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces instead of a high branch density value if dense interfaces are needed." -msgstr "Регулирует плотность создания ветвей в месте контакта с моделью. Большее значение приводит к улучшению качества печати нависаний, но такие поддержки сложнее удалять, поэтому рекомендуется вместо высокого значения плотности ветвей включать связующие слои поддержки." +msgstr "Регулирует плотность создания ветвей в месте контакта с моделью. Бóльшие значения приводят к улучшению качества печати нависаний. Однако такие поддержки сложнее удалять, поэтому рекомендуется вместо высокого значения плотности ветвей включать связующие слои поддержки." msgid "Auto brim width" msgstr "Автоширина каймы" @@ -17155,7 +17226,7 @@ msgstr "" "В отличие от целевой температуры не приводит к автоматической генерации команд M141/M191 и нужна только для использования в секции G-кода." msgid "Chamber minimal temperature" -msgstr "Минимальная температура термокамеры" +msgstr "Стартовая температура термокамеры" msgid "Nozzle temperature after the first layer" msgstr "Температура при печати последующих слоёв." @@ -17200,33 +17271,42 @@ msgid "The number of top solid layers is increased when slicing if the thickness msgstr "Минимальная толщина оболочки сверху в мм. Если толщина оболочки, рассчитанная по количеству сплошных слоёв сверху, меньше этого значения, количество сплошных слоёв сверху будет автоматически увеличено при нарезке, для удовлетворения минимальной толщины оболочки. Это позволяет избежать слишком тонкой оболочки при небольшой высоте слоя. 0 означает, что этот параметр отключён, а толщина оболочки сверху задаётся количеством сплошных слоёв сверху." msgid "Separated infills" -msgstr "" +msgstr "Независимые шаблоны" +# Тут сразу несколько изменений. 1 - на Вики речь идёт в целом даже про отдельные куски одного меша, а не только про части в составе сборки. 2 - логически переупорядочил предложения. 3 - touch and overlap заменено на "прямой контакт". 4 - не вижу смысла приводить примеры, не давая полного списка шаблонов. Какой-то логики по структуре шаблонов тут нет, т.к. ограничение исходит из алгоритма центровки, так что это только будет путать. msgid "" "Centers the internal infill of each part on itself, as if it were sliced on its own, instead of on the whole assembly. Parts that touch or overlap are treated as one body and share a center; separate parts (or distinct 3D objects) each get their own.\n" "Useful when an assembly groups several objects that should each keep a consistent, self-centered infill.\n" "Affects line and grid patterns and rotation-template infills.\n" "Patterns locked to global coordinates (Gyroid, Honeycomb, TPMS, ...) are unaffected." msgstr "" +"Разделить шаблоны заполнения у независимых частей модели/сборки. Это позволяет получать симметричное расположение шаблона в случаях, когда отдельные части вызывают его смещение относительно центра модели.\n" +"Примечание: не влияет на части с прямым контактом между собой и не затрагивает шаблоны с жёсткой привязкой к координатам стола." msgid "Center surface pattern on" -msgstr "" +msgstr "Центровать шаблон по" +# Работает только со спиралями msgid "" "Chooses where the centering point of centered top/bottom surface patterns (Archimedean Chords, Octagram Spiral) is placed.\n" " - Each Surface: centers the pattern on every individual surface region, so each island is symmetric on its own.\n" " - Each Model: centers the pattern on each connected body. Parts that touch or overlap share one center; parts detached from the rest each get their own.\n" " - Each Assembly: uses a single shared center for the whole object or assembly." msgstr "" +"Настройка выравнивания центра спирали на поверхностях.\n" +"Позволяет создать согласованный узор на разных горизонтальных уровнях.\n" +"• По поверхности: центр задаётся у каждой поверхности индивидуально.\n" +"• По модели: поверхности на разных уровнях сохраняют общий центр.\n" +"• По сборке: поверхности всех её частей сохраняют общий центр." msgid "Each Surface" -msgstr "" +msgstr "Поверхности" msgid "Each Model" -msgstr "" +msgstr "Модели" msgid "Each Assembly" -msgstr "" +msgstr "Сборке" msgid "This is the speed at which traveling is done." msgstr "Ограничение скорости холостых перемещений печатающей головы." @@ -18557,8 +18637,8 @@ msgid "" "Within the same extruder, the name(%s) must be unique when the filament type, nozzle diameter, and nozzle flow are the same.\n" "Are you sure you want to override the historical result?" msgstr "" -"Для одного экструдера имя (%s) должно быть уникальным при одинаковых типе филамента, диаметре и потоке сопла.\n" -"Вы уверены, что хотите перезаписать предыдущий результат?" +"Для одного экструдера имя (%s) должно быть уникальным, если тип материала, диаметр сопла и поток одинаковы.\n" +"Перезаписать прошлый результат?" #, c-format, boost-format msgid "This machine type can only hold %d historical results per nozzle. This result will not be saved." @@ -18577,7 +18657,7 @@ msgid "Internal Error" msgstr "Внутренняя ошибка" msgid "Please select at least one filament for calibration" -msgstr "Выберите хотя бы один пруток для калибровки" +msgstr "Выберите хотя бы один материал для калибровки" msgid "Flow rate calibration result has been saved to preset." msgstr "Результат калибровки динамики потока был сохранён в профиль." @@ -18624,6 +18704,7 @@ msgstr "" msgid "When to use Flow Rate Calibration" msgstr "В каких случаях необходима калибровка скорости потока" +# Requires refactoring msgid "" "After using Flow Dynamics Calibration, there might still be some extrusion issues, such as:\n" "1. Over-Extrusion: Excess material on your printed object, forming blobs or zits, or the layers seem thicker than expected and not uniform\n" @@ -18680,10 +18761,10 @@ msgid "Part of the calibration failed! You may clean the plate and retry. The fa msgstr "Часть калибровки выполнена неудачно! Вы можете очистить стол и повторить попытку. Результат неудачного теста будет удалён." msgid "*We recommend you to add brand, materia, type, and even humidity level in the Name" -msgstr "*Мы рекомендуем добавить к названию материала, производителя, тип и даже уровень влажности" +msgstr "*Рекомендуется указывать в названии производителя, тип материала и даже уровень влажности" msgid "Please enter the name you want to save to printer." -msgstr "Введите имя, которое хотите сохранить на принтере." +msgstr "Введите имя для сохранения на принтере." msgid "The name cannot exceed 40 characters." msgstr "Максимальная длина имени 40 символов." @@ -18719,7 +18800,7 @@ msgid "Please input a valid value (0.0 < flow ratio < 2.0)" msgstr "Введите допустимое значение (0.0 < коэффициент потока < 2.0)" msgid "Please enter the name of the preset you want to save." -msgstr "Введите имя профиля, которое хотите сохранить." +msgstr "Введите имя профиля для сохранения." msgid "Calibration1" msgstr "Калибровка 1" @@ -18918,8 +18999,8 @@ msgid "" "There are several IP addresses resolving to hostname %1%.\n" "Please select one that should be used." msgstr "" -"Существует несколько IP-адресов, соответствующих имени хоста %1%.\n" -"Пожалуйста, выберите тот, который хотите использовать." +"По имени хоста %1% обнаружено несколько IP-адресов.\n" +"Выберите адрес для использования." # В заголовке окна куча места msgid "PA Calibration" @@ -19269,7 +19350,7 @@ msgstr "После загрузки переключиться на вкладк #, c-format, boost-format msgid "Upload filename doesn't end with \"%s\". Do you wish to continue?" -msgstr "Имя загружаемого файла не заканчивается на \"%s\". Хотите продолжить?" +msgstr "Имя загружаемого файла не заканчивается на \"%s\". Продолжить?" msgid "Upload" msgstr "Загрузить" @@ -19467,9 +19548,6 @@ msgstr "Скопировать текущий профиль материала msgid "Basic Information" msgstr "Основная информация" -# ??? Создать профиль для этого прутка, Создание профиля прутка для данный -# прутка -# ??? было Добавление профиля прутка под текущий пруток msgid "Add Filament Preset under this filament" msgstr "Создание профиля для данного материала" @@ -19535,8 +19613,8 @@ msgid "" "The Filament name %s you created already exists.\n" "If you continue, the preset created will be displayed with its full name. Do you want to continue?" msgstr "" -"Материал с таким именем %s уже существует.\n" -"Если продолжить создание, то созданный профиль будет отображаться с полным именем. Хотите продолжить?" +"Материал с именем «%s» уже существует.\n" +"Если продолжить создание, то созданный профиль будет отображаться с полным именем. Продолжить?" # ??? Создание некоторых профилей завершилось с ошибкой. Список проблемных # профилей: @@ -19548,7 +19626,7 @@ msgid "" "Do you want to rewrite it?" msgstr "" "\n" -"Хотите перезаписать его?" +"Перезаписать?" msgid "" "We would rename the presets as \"Vendor Type Serial @printer you selected\".\n" @@ -19651,8 +19729,8 @@ msgid "" "and filament and process presets without the same preset name will be reserved.\n" "\tCancel: Do not create a preset; return to the creation interface." msgstr "" -"Профиль принтера с таким именем уже существует. Хотите перезаписать его?\n" -"\tДа: перезаписать профиль принтера. Профили материала и настроек печати с теми же именами будут созданы заново, а профили без имени будут зарезервированы.\n" +"Профиль принтера с таким именем уже существует. Перезаписать его?\n" +"\tДа: перезаписать профиль принтера. Профили материалов и настроек печати с теми же именами будут созданы заново, а профили без имени будут зарезервированы.\n" "\tОтмена: не создавать профиль и вернуться на экран создания." msgid "You need to select at least one filament preset." @@ -19743,6 +19821,7 @@ msgstr "" "Обратите внимание, что на качество печати существенно влияют температура сопла,\n" "температура стола и максимальный объёмный расход. Подбирайте их с осторожностью." +# Orca has detected that – вообще можно не переводить msgid "" "\n" "\n" @@ -19758,19 +19837,19 @@ msgid "Printer Setting" msgstr "Настроить принтер" msgid "Printer config bundle(.orca_printer)" -msgstr "Printer config bundle(.orca_printer) - Пакет профилей принтера" +msgstr "Пакет профилей принтера (.orca_printer)" msgid "Filament bundle(.orca_filament)" -msgstr "Filament bundle(.orca_filament) - Пакет профилей материалов" +msgstr "Пакет профилей материалов (.orca_filament)" msgid "Printer presets(.zip)" -msgstr "Printer presets(.zip) - Профили принтеров" +msgstr "Профили принтеров (.zip)" msgid "Filament presets(.zip)" -msgstr "Filament presets (.zip) - Профили материалов" +msgstr "Профили материалов (.zip)" msgid "Process presets(.zip)" -msgstr "Process presets (.zip) - Профили настроек печати" +msgstr "Профили настроек печати (.zip)" msgid "initialize fail" msgstr "ошибка инициализации" @@ -19795,7 +19874,7 @@ msgid "" "The '%s' folder already exists in the current directory. Do you want to clear it and rebuild it?\n" "If not, a time suffix will be added, and you can modify the name after creation." msgstr "" -"Файл '%s' уже существует в текущем каталоге. Хотите перезаписать его?\n" +"Файл \"%s\" уже существует в текущем каталоге. Перезаписать его?\n" "Если нет, то к имени будет добавлен временной суффикс." #, c-format, boost-format @@ -19852,7 +19931,7 @@ msgid "Please select at least one printer or filament." msgstr "Выберите хотя бы один принтер или материал." msgid "Please select a preset type you want to export" -msgstr "Выберите тип профилей, который вы хотите экспортировать" +msgstr "Выберите тип профилей, которые вы хотите экспортировать" msgid "Failed to create temporary folder, please try Export Configs again." msgstr "Не удалось создать временную папку, пожалуйста, попробуйте экспортировать конфигурацию ещё раз." @@ -20366,7 +20445,7 @@ msgid "Compared with the default profile of a 0.2 mm nozzle, it has a bigger lay msgstr "В отличие от стандартного профиля для сопла 0.2 мм имеет увеличенную высоту слоя, что обеспечивает малозаметные слои и сокращает время печати." msgid "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer height. This results in almost invisible layer lines and higher print quality but longer print time." -msgstr "В отличие от стандартного профиля для сопла 0.2 мм имеет меньшую высоту слоя, что обеспечивает практически незаметные слои и более высокое качество ценой увеличения времени печати." +msgstr "В отличие от стандартного профиля для сопла 0.2 мм имеет меньшую высоту слоя, что обеспечивает практически незаметные слои и более высокое качество ценой увеличения времени печати." msgid "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. This results in almost invisible layer lines and much higher print quality but much longer print time." msgstr "В отличие от стандартного профиля для сопла 0.2 мм имеет меньшую высоту слоя, более низкие скорости и ускорения, а также гироидный шаблон заполнения. Это обеспечивает практически невидимые слои и более высокое качество ценой значительного увеличения времени печати." @@ -20536,7 +20615,7 @@ msgid "AMS Status" msgstr "Статус AMS" msgid "Please select the devices you would like to manage here (up to 6 devices)" -msgstr "Пожалуйста, выберите устройства, которыми хотите управлять (до 6 устройств)" +msgstr "Укажите устройства для управления (до 6 штук)" msgid "Printing Options" msgstr "Параметры печати" @@ -21688,9 +21767,6 @@ msgstr "" #~ msgid "Cancel" #~ msgstr "Отмена" -#~ msgid "Align infill direction to model" -#~ msgstr "Вращать заполнение с моделью" - #~ msgid "Print" #~ msgstr "Печать" diff --git a/src/slic3r/GUI/DeviceCore/DevManager.cpp b/src/slic3r/GUI/DeviceCore/DevManager.cpp index 0b9e67adbd..2d54b5c85f 100644 --- a/src/slic3r/GUI/DeviceCore/DevManager.cpp +++ b/src/slic3r/GUI/DeviceCore/DevManager.cpp @@ -592,6 +592,7 @@ namespace Slic3r } selected_machine = dev_id; + record_user_last_machine(selected_machine); return true; } @@ -854,21 +855,39 @@ namespace Slic3r } } + void DeviceManager::record_user_last_machine(const std::string& dev_id) + { + if (Slic3r::GUI::wxGetApp().app_config) { + Slic3r::GUI::wxGetApp().app_config->set("user_last_selected_machine", dev_id); + } + } + + std::string DeviceManager::get_user_last_machine() const + { + if (Slic3r::GUI::wxGetApp().app_config) { + const auto& user_last_machine = Slic3r::GUI::wxGetApp().app_config->get("user_last_selected_machine"); + if (!user_last_machine.empty()) { + return user_last_machine; + } else if (m_agent) { + return m_agent->get_user_selected_machine(); + } + } + + return ""; + } + void DeviceManager::load_last_machine() { - // Get all available machines, include cloud machines and lan machines that have access right - auto all_machines = get_my_machine_list(); - if (all_machines.empty()) - return; - - // Then connect to the machine we last selected if available - const std::string last_monitor_machine = m_agent ? m_agent->get_user_selected_machine() : ""; - const auto last_machine = all_machines.find(last_monitor_machine); - if (last_machine != all_machines.end()) { - this->set_selected_machine(last_machine->second->get_dev_id()); + if (userMachineList.empty()) return; + else if (userMachineList.size() == 1) { + this->set_selected_machine(userMachineList.begin()->second->get_dev_id()); } else { - // If not, then select the first available one - this->set_selected_machine(all_machines.begin()->second->get_dev_id()); + const auto& last_monitor_machine = get_user_last_machine(); + if (userMachineList.find(last_monitor_machine) != userMachineList.end()) { + set_selected_machine(last_monitor_machine); + } else { + this->set_selected_machine(userMachineList.begin()->second->get_dev_id()); + } } } diff --git a/src/slic3r/GUI/DeviceCore/DevManager.h b/src/slic3r/GUI/DeviceCore/DevManager.h index bab53d02b9..1f7f87b7fb 100644 --- a/src/slic3r/GUI/DeviceCore/DevManager.h +++ b/src/slic3r/GUI/DeviceCore/DevManager.h @@ -48,6 +48,9 @@ public: MachineObject* get_selected_machine(); bool set_selected_machine(std::string dev_id); + void record_user_last_machine(const std::string& dev_id); + std::string get_user_last_machine() const; + // local machine void set_local_selected_machine(std::string dev_id) { local_selected_machine = dev_id; }; MachineObject* get_local_selected_machine() const { return get_local_machine(local_selected_machine); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 9a846b1aaa..29aad072f0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2637,7 +2637,6 @@ void GUI_App::on_start_subscribe_again(std::string dev_id) if ( (dev_id == obj->get_dev_id()) && obj->is_connecting() && obj->subscribe_counter > 0) { obj->subscribe_counter--; - if(wxGetApp().getAgent()) wxGetApp().getAgent()->set_user_selected_machine(dev_id); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": dev_id=" << obj->get_dev_id(); } }); diff --git a/tests/AGENTS.md b/tests/AGENTS.md new file mode 100644 index 0000000000..c62a1d12ec --- /dev/null +++ b/tests/AGENTS.md @@ -0,0 +1,57 @@ +# Test suite rules + +Rules for writing tests under `tests/`. [CATCH2.md](CATCH2.md) is the Catch2 reference. Building and running the suites is covered on the wiki, at . + +## The suites + +- `libslic3r`: the core library. Geometry, meshes, file formats, config and presets, Clipper, algorithms, data structures. +- `fff_print`: the FFF slicing pipeline, from a `Model` plus config through `Print` and `PrintObject` to emitted G-code. +- `sla_print`: SLA support-tree and pad geometry, support-point generation, raycast. +- `libnest2d`: 2D nesting and packing. +- `slic3rutils`: the Python plugin system and its slicing-pipeline bindings. +- `filament_group`: filament-to-extruder grouping, checked against golden files. + +## Building and running + +Tests are off by default, so the build has to be told to include them. + +- Windows: `build_release_vs.bat tests`, then `ctest --test-dir build/tests -C Release` +- macOS: `./build_release_macos.sh -s -a arm64 -T`, which builds and runs them +- Linux: `./build_linux.sh -t`, then `ctest --test-dir build/tests` + +Rebuild a single suite with `cmake --build build --config Release --target _tests`. Visual Studio and Xcode are multi-configuration generators, so `ctest` needs `-C` there; on Linux it does not. + +## Where a test goes + +- Pick the suite by the production code the test exercises, not by how the test is written. +- A property of a class that holds with no `Print` involved belongs in `libslic3r`. Behavior that depends on print settings, or produces or consumes G-code or slicing state, belongs in `fff_print`. +- One file per subsystem, named `test_.cpp`. It owns every test for that subsystem, whether the test reads in-memory state or generated output. +- When you add a file, list it in that suite's `CMakeLists.txt` in the same change. + +## Use the existing helpers + +Check these before writing your own setup or output-parsing code. + +- `tests/test_utils.hpp` is shared by every suite. `load_model()` loads a mesh from `tests/data/`, and `ScopedTemporaryFile` gives a temp path that removes itself. +- `fff_print/test_helpers.hpp` builds and slices a `Print` and parses the emitted G-code. Read it before writing an fff_print test rather than assembling a `Print` by hand. +- The other suites have their own: `sla_print/sla_test_utils.hpp`, `libnest2d/libnest2d_test_utils.hpp`, `slic3rutils/plugin_test_utils.hpp`, `filament_group/fg_test_utils.hpp`. `libslic3r` has none and uses the shared header. +- Test data lives in `tests/data/` and is reached through the `TEST_DATA_DIR` define. Wrap it in `std::string(...)` before joining a path onto it. + +## Writing the test + +- Name the test case as a plain behavioral sentence in the present tense. No `Subsystem:` prefix. +- Tag it with the subsystem it covers, matching the file, in PascalCase. That tag is what people filter on, so every test needs one. +- Add further tags where they help: a narrower one to slice a large file (`[Rotcalip]`, `[Placer]`), a shared one for something spanning files (`[Python]`, `[H2C]`, `[Regression]`), or `[NotWorking]` / `[.]` to disable or hide a test. Say why in a comment if you disable or hide. +- Prefer a flat `TEST_CASE` per behavior, with `GENERATE` for parameterized cases. Reserve `SCENARIO` / `GIVEN` / `WHEN` / `THEN` for genuine shared setup that branches into a few close variations. +- Set the config keys your test depends on, and derive the expected values from what you set. A 20mm cube sliced at `layer_height` 2 is 10 layers, and the test should state both parts. If a number in your assertion comes from a key you never set, the test is also testing that default. +- Assert the defining property, not an incidental value. "Skirt present" or "at least 2 brim loops" survives a refactor; exact coordinates and byte counts do not. +- Name a regression test for the behavior it protects, never for an issue or PR number. +- When asserting on G-code, match the meaningful token such as `; skirt` rather than whole lines, whitespace or comment wording. Depend on ordering only when ordering is the contract. + +## Catch2 rules that cause real breakage + +- Never reuse a `SECTION` name inside a loop. Use `DYNAMIC_SECTION` so each iteration is unique. +- Never assert from a spawned thread. Catch2 assertions are not thread-safe. Collect results in the thread and assert on the main thread. +- Never combine conditions with `&&` or `||` inside one assertion. Split them so Catch2 can print both operands on failure. +- Compare floats with `WithinAbs` or `WithinRel`, never `==`. Prefer these over `Approx` in new tests. +- Keep tests self-contained: no shared state, green under `--order rand`. diff --git a/tests/CATCH2.md b/tests/CATCH2.md new file mode 100644 index 0000000000..1b528b3c01 --- /dev/null +++ b/tests/CATCH2.md @@ -0,0 +1,369 @@ +# Catch2 reference + +How to write and structure test code with Catch2 in OrcaSlicer. For where a test belongs, how to name and tag it, and how to build and run the suites, see [AGENTS.md](AGENTS.md). + +OrcaSlicer uses **Catch2 v3.11.0**, vendored in `tests/catch2/`. Include it with the single-header convenience include: + +```cpp +#include +``` + +## Critical rules + +These three mistakes produce undefined behavior, crashes, or useless failure output rather than a normal test failure. Avoid them everywhere. + +### 1. Never reuse a section name inside a loop + +A repeated `SECTION` name in a loop makes Catch2's section tracking behave unpredictably. Use `DYNAMIC_SECTION` so each iteration is unique. + +```cpp +// WRONG: same name every iteration +for (int i = 0; i < 3; ++i) + SECTION("Same name") { REQUIRE(i >= 0); } + +// CORRECT +for (int i = 0; i < 3; ++i) + DYNAMIC_SECTION("Section " << i) { REQUIRE(i >= 0); } +``` + +### 2. Assertions are not thread-safe + +Catch2 assertions are not thread-safe by default. A `REQUIRE`/`CHECK` from a spawned thread corrupts internal state or terminates the process. Collect results in the thread, assert on the main thread. + +```cpp +// WRONG +std::thread t([&]{ REQUIRE(work() == expected); }); + +// CORRECT +std::atomic passed{0}; +std::thread t([&]{ if (work() == expected) passed++; }); +t.join(); +REQUIRE(passed == 1); +``` + +> Catch2 v3.9.0+ has opt-in thread-safe assertions via `CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS`. OrcaSlicer does not enable that flag, so assertions remain non-thread-safe. See [Thread safety](#thread-safety) below for the full rule list. + +### 3. Do not combine conditions with binary operators + +Catch2 decomposes a single comparison to show both operands on failure. A `&&`/`||` inside one assertion collapses to `false` with no values. Split it. + +```cpp +REQUIRE(a > 0 && b < 10); // WRONG: prints "false" +REQUIRE(a > 0); // CORRECT: each prints its operands +REQUIRE(b < 10); +``` + +## Test structure + +```cpp +#include +#include "libslic3r/Point.hpp" + +using namespace Slic3r; + +TEST_CASE("Behavioral description", "[SubsystemTag]") { + // ... +} +``` + +## Assertions + +```cpp +// Stop the test on failure +REQUIRE(expression); +REQUIRE_FALSE(expression); + +// Continue the test after failure (report all failures in the case) +CHECK(expression); +CHECK_FALSE(expression); + +// Record the result without failing (for assumptions that may be violated) +CHECK_NOFAIL(expression); +``` + +### Exceptions + +```cpp +REQUIRE_NOTHROW(function_call()); +REQUIRE_THROWS(risky_function()); +REQUIRE_THROWS_AS(function_call(), SpecificException); +REQUIRE_THROWS_WITH(function_call(), "Expected error message"); +REQUIRE_THROWS_MATCHES(function_call(), SpecificException, + Catch::Matchers::Message("contains this")); +``` + +Prefer these over a hand-rolled `try`/`catch` with a bool flag. + +## Matchers + +```cpp +#include + +// String matchers +using Catch::Matchers::StartsWith; +using Catch::Matchers::EndsWith; +using Catch::Matchers::ContainsSubstring; // v2's "Contains" no longer exists +using Catch::Matchers::Equals; +using Catch::Matchers::Matches; // regex + +REQUIRE_THAT(result, StartsWith("Expected prefix")); +REQUIRE_THAT(result, ContainsSubstring("middle part")); +REQUIRE_THAT(result, Matches(".*pattern.*")); + +// Float matchers - always prefer these over Approx +using Catch::Matchers::WithinAbs; +using Catch::Matchers::WithinRel; +using Catch::Matchers::WithinULP; + +REQUIRE_THAT(v, WithinAbs(expected, 0.001)); +REQUIRE_THAT(v, WithinRel(expected, 0.01)); +REQUIRE_THAT(v, WithinULP(expected, 4)); + +// Combine: relative OR absolute (useful when the value can be near zero) +REQUIRE_THAT(v, WithinRel(expected, 0.001) || WithinAbs(0.0, 0.000001)); +``` + +## Sections + +Each `SECTION` re-runs the enclosing `TEST_CASE` body from the top, so setup declared before the sections is fresh for each one. + +```cpp +TEST_CASE("Complex feature", "[Feature]") { + SomeObject obj; // rebuilt for every section + + SECTION("First scenario") { REQUIRE(obj.method1() == expected_value); } + SECTION("Second scenario") { REQUIRE(obj.method2() == other_expected); } +} +``` + +## BDD-style tests + +`SCENARIO` / `GIVEN` / `WHEN` / `THEN` are aliases for `TEST_CASE` and `SECTION` with prefixed names. New tests should prefer a flat `TEST_CASE`; reserve BDD for genuine shared setup that branches into closely related variations (see the test-design guidance in [AGENTS.md](AGENTS.md)). + +```cpp +SCENARIO("User performs an operation", "[UserStory]") { + GIVEN("A setup condition") { + GCodeWriter writer; + WHEN("The user acts") { + auto result = writer.some_operation(); + THEN("The outcome holds") { + REQUIRE(result.size() > 0); + } + } + } +} +``` + +## Generators + +```cpp +// Value list +auto v = GENERATE(1, 3, 5, 7, 11, 13); + +// Range +auto i = GENERATE(range(1, 10)); // 1..9 + +// From a variable (use GENERATE_REF / GENERATE_COPY for captured references) +std::vector values = {1, 2, 3, 4, 5}; +auto x = GENERATE_REF(from_range(values)); + +// Random +auto r = GENERATE(take(100, random(-1000, 1000))); +``` + +## Fixtures + +```cpp +class GeometryFixture { +public: + Point origin{0, 0}; + Point unit_x{1, 0}; +}; + +TEST_CASE_METHOD(GeometryFixture, "Point operations", "[Geometry]") { + REQUIRE(origin.distance_to(unit_x) == 1.0); +} +``` + +Persistent (`TEST_CASE_PERSISTENT_FIXTURE`, one instance for the whole case) and type-parameterized (`TEMPLATE_TEST_CASE_METHOD`) variants also exist; neither is used in the suite today. + +## Advanced features + +### Logging and control + +```cpp +INFO("Persists until end of scope"); +UNSCOPED_INFO("Survives beyond its scope"); // v2.7.0+ +CAPTURE(some_variable, another_var); // logs names and values + +WARN("Warns without failing"); +SKIP("Reason"); // marks the test skipped (v3.3.0+) +FAIL("Stops the test"); +SUCCEED("Explicit success marker"); +``` + +### Other macros + +Available but currently unused in the suite; see the upstream docs for details. + +- **Compile-time asserts**: `STATIC_REQUIRE` / `STATIC_CHECK` (v3.0.1+) check type traits at compile time. +- **Conditional blocks**: `CHECKED_IF` / `CHECKED_ELSE` record a branch condition without counting it as a failure. +- **Benchmarking** (v2.9.0+): `BENCHMARK("name") { return work(); };`, or `BENCHMARK_ADVANCED` when setup must be excluded from the measurement. + +## Usage patterns in OrcaSlicer + +Concrete shapes for exercising the codebase's own types. Test data is reached through the `TEST_DATA_DIR` define; always wrap it in `std::string(...)` before concatenating a path. + +```cpp +// Geometry, with epsilon tolerance +TEST_CASE("Line operations", "[Geometry]") { + Line line{{100000, 0}, {0, 0}}; + Line rotated(line); + rotated.rotate(0.9 * EPSILON, {0, 0}); + REQUIRE(line.parallel_to(rotated)); +} + +// Config from an ini +TEST_CASE("Config loading", "[Config]") { + DynamicPrintConfig config; + REQUIRE_NOTHROW(config.load_from_ini(std::string(TEST_DATA_DIR) + "/test_config/sample.ini", + ForwardCompatibilitySubstitutionRule::Disable)); + REQUIRE(config.has("layer_height")); +} + +// File I/O +TEST_CASE("STL file parsing", "[FileFormat]") { + TriangleMesh mesh; + REQUIRE_NOTHROW(mesh.ReadSTLFile((std::string(TEST_DATA_DIR) + "/test_stl/20mmbox.stl").c_str())); + REQUIRE_FALSE(mesh.empty()); + REQUIRE(mesh.volume() > 0); +} + +// G-code emission, matched by token (see test_gcodewriter.cpp) +TEST_CASE("z_hop lifts the nozzle", "[GCodeWriter]") { + GCodeWriter writer; + writer.set_extruders({0}); + writer.set_extruder(0); + writer.travel_to_z(10.0); + writer.config.z_hop.values = {1.0}; + REQUIRE_THAT(writer.eager_lift(LiftType::NormalLift), Catch::Matchers::ContainsSubstring("Z11")); +} +``` + +### Custom string conversions + +Give Catch2 a way to print a custom type on failure. The usual case is an `operator<<` overload: + +```cpp +std::ostream& operator<<(std::ostream& os, const Point& p) { + return os << "Point(" << p.x << ", " << p.y << ")"; +} +``` + +When you cannot add `operator<<`, specialize `Catch::StringMaker`. Enums can be registered with `CATCH_REGISTER_ENUM` (at global scope) and exceptions translated with `CATCH_TRANSLATE_EXCEPTION`; see the upstream docs for those. + +## Command line + +[AGENTS.md](AGENTS.md) covers the everyday commands (CTest, per-suite runs, tag filtering as CTest labels). The flags below are Catch2's own, available when you run a suite executable directly. + +```bash +# Filtering +suite_tests "[Geometry]" # by tag +suite_tests "*geometry*" # by name pattern +suite_tests "~[Performance]" # exclude a tag +suite_tests "[Geometry][Config],[Algorithm]" # (Geometry AND Config) OR Algorithm + +# Discovery +suite_tests --list-tests +suite_tests --list-tags +suite_tests --list-reporters + +# Debugging a failure +suite_tests --break # break into the debugger on failure +suite_tests --success # show passing assertions too +suite_tests --durations yes # per-test timing +suite_tests --abort # stop at the first failure +``` + +### Ordering and sharding + +Run in random order so tests stay independent. For parallel shards, all shards must share one seed. + +```bash +suite_tests --order rand --warn NoAssertions + +suite_tests --order rand --shard-index 0 --shard-count 4 --rng-seed 0xBEEF +suite_tests --order rand --shard-index 1 --shard-count 4 --rng-seed 0xBEEF +# ...one invocation per shard index +``` + +### Reporters + +```bash +suite_tests --reporter console # default, human-readable +suite_tests --reporter compact +suite_tests --reporter xml # Catch2 XML +suite_tests --reporter junit # JUnit XML (CI) +suite_tests --reporter tap +suite_tests --reporter console --reporter junit::out=results.xml # multiple at once +``` + +## Common pitfalls + +### Floating-point comparison + +Compare floats with the float matchers, never with `==`. New tests should prefer the `Within*` matchers over `Approx`. Many existing tests still use `Approx`, which works but is: + +- **Asymmetric**: `Approx(10).epsilon(0.1) != 11.1` yet `Approx(11.1).epsilon(0.1) == 10`. +- **Double-only**: all math is done in `double`, which misbehaves for `float` inputs. +- **Relative by default**: `Approx(0) == X` holds only for `X == 0`. + +Use `WithinAbs` near zero, `WithinRel` across magnitudes, `WithinULP` for the tightest check, or combine them. `Catch::StringMaker::precision = 15;` widens printed precision. + +### Exception testing + +Use `REQUIRE_THROWS` / `REQUIRE_THROWS_AS` rather than a `try`/`catch` with a bool flag. + +### Thread safety + +Assertions are not thread-safe (see [Critical rule 2](#2-assertions-are-not-thread-safe)). The full list of macros that must stay on the main thread: + +- **`REQUIRE` family**: throws in a spawned thread with no handler, terminating the process. +- **`CHECK` family**: can corrupt internal state. +- **`SKIP`, `FAIL`, `SUCCEED`**: unsafe even with v3's opt-in thread-safe assertions. +- **Message macros** (`INFO`, `CAPTURE`, `WARN`): unsafe. +- **`STATIC_REQUIRE` / `STATIC_CHECK`**: unsafe (rely on runtime registration). + +### Path handling + +Wrap `TEST_DATA_DIR` in `std::string(...)` before concatenating, or use `boost::filesystem`: + +```cpp +std::string path = std::string(TEST_DATA_DIR) + "/model.obj"; +``` + +### Memory + +Prefer RAII and smart pointers so a failing assertion cleans up automatically. + +## Compilation and performance flags + +```cpp +#define CATCH_CONFIG_FAST_COMPILE // ~20% faster compile, disables some features +#define CATCH_CONFIG_DISABLE_STRINGIFICATION // works around the VS2017 raw-string bug +#define CATCH_CONFIG_WINDOWS_CRTDBG // memory-leak detection (whole build) +``` + +The test build already defines `CATCH_CONFIG_FAST_COMPILE` (via `test_common` in `tests/CMakeLists.txt`). + +## Platform-specific workarounds + +- **MinGW/Cygwin** slow linking: build with `-fuse-ld=lld`. +- **Visual Studio 2017** raw-string-literal bug: define `CATCH_CONFIG_DISABLE_STRINGIFICATION` (disables expression stringification). +- **Visual Studio 2022** spaceship operator: `REQUIRE((a <=> b) == 0)` may not compile; use clang-cl or avoid `<=>` in assertions. + +## Catch2 v3 notes + +Available on v3.11.0: `SKIP()` (v3.3.0+), opt-in thread-safe assertions (v3.9.0+, not enabled here), built-in `BENCHMARK`, multiple simultaneous reporters (v3.0.1+), `STATIC_CHECK` (v3.0.1+), built-in sharding (`--shard-*`). + +Two behavior notes: the string matcher is `ContainsSubstring` (v2's `Contains` is gone), and a section is re-run when a later sibling section fails (unchanged from v2). diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md index dcbeab7cbd..43c994c2d3 100644 --- a/tests/CLAUDE.md +++ b/tests/CLAUDE.md @@ -1,964 +1 @@ -# CLAUDE.md - Testing Guide for OrcaSlicer - -This guide provides comprehensive instructions for Claude Code when writing, maintaining, and understanding tests in the OrcaSlicer codebase. - -> **Adding or organizing `fff_print` tests?** See -> [fff_print/README.md](fff_print/README.md) for where a test belongs and how to -> name it. This guide covers Catch2 mechanics; that README is the suite's -> organizing contract. - -## ⚠️ CRITICAL RULES - MUST FOLLOW - -### 1. **SECTIONS IN LOOPS - NEVER REUSE NAMES** -❌ **WRONG**: Will cause unpredictable behavior -```cpp -TEST_CASE("Bad loop sections") { - for (int i = 0; i < 3; ++i) { - SECTION("Same name") { // WRONG! Same name used multiple times - REQUIRE(i >= 0); - } - } -} -``` - -✅ **CORRECT**: Use DYNAMIC_SECTION or incorporate counter -```cpp -TEST_CASE("Good loop sections") { - for (int i = 0; i < 3; ++i) { - DYNAMIC_SECTION("Section " << i) { // Unique name per iteration - REQUIRE(i >= 0); - } - } -} -``` - -### 2. **THREAD SAFETY - ASSERTIONS ARE NOT THREAD-SAFE** -❌ **WRONG**: Will cause undefined behavior or crashes -```cpp -TEST_CASE("Multi-threaded test") { - std::vector threads; - for (int i = 0; i < 4; ++i) { - threads.emplace_back([]() { - REQUIRE(some_calculation() == expected); // NOT THREAD-SAFE! - }); - } -} -``` - -✅ **CORRECT**: Synchronize results, test on main thread -```cpp -TEST_CASE("Multi-threaded test") { - std::vector threads; - std::atomic passed{0}; - - for (int i = 0; i < 4; ++i) { - threads.emplace_back([&passed]() { - if (some_calculation() == expected) { - passed++; - } - }); - } - - for (auto& t : threads) t.join(); - REQUIRE(passed == 4); // Test results on main thread -} -``` - -### 3. **EXPRESSION DECOMPOSITION - AVOID BINARY OPERATORS** -❌ **WRONG**: Cannot decompose properly -```cpp -REQUIRE(a > 0 && b < 10); // Shows "false" on failure, not individual values -``` - -✅ **CORRECT**: Split into separate assertions -```cpp -REQUIRE(a > 0); -REQUIRE(b < 10); // Each shows individual values on failure -``` - -### 4. **FLOATING POINT - NEVER USE APPROX** -❌ **WRONG**: Approx is deprecated and asymmetric -```cpp -REQUIRE(calculated_value == Catch::Approx(expected)); // Deprecated! -``` - -✅ **CORRECT**: Use floating point matchers -```cpp -REQUIRE_THAT(calculated_value, WithinAbs(expected, 0.001)); -REQUIRE_THAT(calculated_value, WithinRel(expected, 0.01)); // 1% tolerance -REQUIRE_THAT(calculated_value, WithinULP(expected, 4)); // 4 ULPs apart -``` - -### 5. **TEST ORDERING - ALWAYS USE RANDOM ORDER** -✅ **REQUIRED**: For CI/CD and development -```bash -# Essential flags for running tests -./tests --order rand --warn NoAssertions - -# For test sharding (parallel execution), share random seed -./tests --order rand --shard-index 0 --shard-count 3 --rng-seed 0xBEEF -./tests --order rand --shard-index 1 --shard-count 3 --rng-seed 0xBEEF -./tests --order rand --shard-index 2 --shard-count 3 --rng-seed 0xBEEF -``` - -## Overview of OrcaSlicer's Testing Framework - -OrcaSlicer uses **Catch2 v3** (currently v3.11.0, vendored in `tests/catch2/`) as its primary testing framework. The test suite is organized into several modules that mirror the project's architectural components: - -> **Note**: Test files include the framework via `#include ` (the v3 single-header convenience include). All v3 features described in this guide are available. - -### Test Structure -``` -tests/ -├── CMakeLists.txt # Main test configuration -├── catch_main.hpp # Custom test reporter -├── libslic3r/ # Core library tests (21 test files) -├── fff_print/ # FFF printing tests (12 test files) -├── sla_print/ # SLA printing tests (4 test files) -├── libnest2d/ # 2D nesting tests -├── slic3rutils/ # Utility tests -├── data/ # Test data files and meshes -└── catch2/ # Catch2 framework files -``` - -### Build Integration -- Tests are built using CMake with `catch_discover_tests()` integration -- Each test module creates a separate executable (e.g., `libslic3r_tests`, `fff_print_tests`) -- Test data directory is available via `TEST_DATA_DIR` preprocessor definition -- Custom verbose console reporter provides detailed test output - -## Test Suite Organization - -### libslic3r Tests -Core slicing engine tests covering: -- **Geometry operations**: Points, polygons, lines, Voronoi diagrams -- **File formats**: STL, 3MF, AMF parsing and validation -- **Algorithms**: Clipper operations, mesh boolean operations, optimization -- **Configuration**: Print settings validation and parsing -- **Utilities**: String processing, time utilities, data structures - -### fff_print Tests -Fused Filament Fabrication specific tests: -- **G-code generation**: Writer functionality, cooling, lift/unlift -- **Slicing algorithms**: Layer generation, infill patterns -- **Print mechanics**: Flow calculations, extrusion, support material -- **Model processing**: Print objects, skirt/brim generation - -### sla_print Tests -Stereolithography specific tests: -- **SLA print processing**: Layer curing, support generation -- **Raycast operations**: Light path calculations -- **Test utilities**: SLA-specific helper functions - -## Writing New Tests - Best Practices - -### File Organization -1. **Naming Convention**: `test_.cpp` (e.g., `test_geometry.cpp`) -2. **Header Structure**: Include `` first, then relevant headers -3. **Namespace Usage**: Use `using namespace Slic3r;` for convenience -4. **File Placement**: Add to appropriate test directory and update CMakeLists.txt - -### Test Naming and Structure -```cpp -#include -#include "libslic3r/Point.hpp" - -using namespace Slic3r; - -TEST_CASE("Feature description", "[category_tag]") { - // Test implementation -} -``` - -### Tagging System -Use descriptive tags for test categorization: -- `[Geometry]` - Geometric operations and calculations -- `[GCodeWriter]` - G-code generation functionality -- `[Config]` - Configuration and settings tests -- `[FileFormat]` - File I/O operations (STL, 3MF, etc.) -- `[Algorithm]` - Core algorithms and processing -- `[Performance]` - Performance benchmarks (if applicable) - -## Catch2 Features Guide - -### Basic Assertions -```cpp -// Primary assertions - stop test on failure -REQUIRE(expression); -REQUIRE_FALSE(expression); - -// Continuing assertions - continue test after failure -CHECK(expression); -CHECK_FALSE(expression); - -// Non-failing checks - record result but don't fail test -CHECK_NOFAIL(expression); // Useful for assumptions that might be violated -``` - -### Exception Testing -```cpp -// Verify no exception is thrown -REQUIRE_NOTHROW(function_call()); - -// Verify any exception is thrown -REQUIRE_THROWS(risky_function()); - -// Verify specific exception type -REQUIRE_THROWS_AS(function_call(), SpecificException); - -// Verify exception message -REQUIRE_THROWS_WITH(function_call(), "Expected error message"); - -// Verify exception with matchers (for partial matching) -REQUIRE_THROWS_MATCHES(function_call(), SpecificException, - Catch::Matchers::Message("contains this")); -``` - -### Complex Assertions with Matchers -```cpp -#include - -// String matchers -using Catch::Matchers::StartsWith; -using Catch::Matchers::EndsWith; -using Catch::Matchers::ContainsSubstring; -using Catch::Matchers::Equals; -using Catch::Matchers::Matches; // Regex matching - -REQUIRE_THAT(result_string, StartsWith("Expected prefix")); -REQUIRE_THAT(result_string, ContainsSubstring("middle part")); -REQUIRE_THAT(result_string, Matches(".*pattern.*")); - -// Floating point matchers - ALWAYS use these instead of Approx! -using Catch::Matchers::WithinAbs; -using Catch::Matchers::WithinRel; -using Catch::Matchers::WithinULP; - -REQUIRE_THAT(float_value, WithinAbs(expected, 0.001)); // Absolute tolerance -REQUIRE_THAT(float_value, WithinRel(expected, 0.01)); // Relative tolerance (1%) -REQUIRE_THAT(float_value, WithinULP(expected, 4)); // ULP difference (requires IEEE-754) - -// Combining matchers -REQUIRE_THAT(value, WithinRel(expected, 0.001) || WithinAbs(0.0, 0.000001)); -``` - -### Sections for Test Organization -```cpp -TEST_CASE("Complex feature testing", "[Feature]") { - // Common setup code - SomeObject obj; - - SECTION("First scenario") { - // Specific test case - REQUIRE(obj.method1() == expected_value); - } - - SECTION("Second scenario") { - // Another test case with same setup - REQUIRE(obj.method2() == other_expected); - } -} -``` - -### BDD-Style Tests -Use for complex scenarios and user story testing: - -> **Note**: BDD macros are aliases for TEST_CASE and SECTION with prefixed names -```cpp -SCENARIO("User performs complex operation", "[UserStory]") { - GIVEN("A specific setup condition") { - GCodeWriter writer; - // Setup code - - WHEN("User performs action") { - auto result = writer.some_operation(); - - THEN("Expected outcome occurs") { - REQUIRE(result.size() > 0); - - AND_WHEN("Follow-up action occurs") { - auto next_result = writer.next_operation(); - - THEN("Final outcome is correct") { - REQUIRE(next_result == expected); - } - } - } - } - } -} -``` - -### Data Generators for Parameterized Tests -```cpp -TEST_CASE("Function works with various inputs", "[Algorithm]") { - auto test_value = GENERATE(1, 3, 5, 7, 11, 13); - - REQUIRE(is_odd(test_value)); - REQUIRE(test_value > 0); -} - -// Range-based generators -TEST_CASE("Range testing", "[Algorithm]") { - auto i = GENERATE(range(1, 10)); // 1 to 9 - REQUIRE(process_value(i) > i); -} - -// Using variables in generators (requires GENERATE_COPY or GENERATE_REF) -TEST_CASE("Generator with variables", "[Algorithm]") { - std::vector values = {1, 2, 3, 4, 5}; - auto test_value = GENERATE_REF(from_range(values)); // Use GENERATE_REF for references - - REQUIRE(test_value > 0); -} - -// Custom generators -TEST_CASE("Random values", "[Algorithm]") { - auto random_int = GENERATE(take(100, random(-1000, 1000))); // 100 random values - REQUIRE(process_random_value(random_int)); -} -``` - -### Test Fixtures -```cpp -class GeometryFixture { -public: - Point origin{0, 0}; - Point unit_x{1, 0}; - Point unit_y{0, 1}; - - mutable double tolerance = EPSILON; // Use mutable for data that might change -}; - -// Standard fixture - new instance per test run -TEST_CASE_METHOD(GeometryFixture, "Point operations", "[Geometry]") { - REQUIRE(origin.distance_to(unit_x) == 1.0); -} - -// Persistent fixture - single instance for entire test case (v3.2.0+) -TEST_CASE_PERSISTENT_FIXTURE(GeometryFixture, "Persistent operations", "[Geometry]") { - static int call_count = 0; - ++call_count; - INFO("This fixture persists across sections, call: " << call_count); - - SECTION("First section") { - REQUIRE(origin.distance_to(unit_x) == 1.0); - } - - SECTION("Second section") { - REQUIRE(origin.distance_to(unit_y) == 1.0); - // call_count will be 2 here with persistent fixture - } -} - -// Template fixtures for type-parameterized tests -template -class NumericFixture { -public: - T zero = T{0}; - T one = T{1}; -}; - -TEMPLATE_TEST_CASE_METHOD(NumericFixture, "Numeric operations", "[Template]", int, float, double) { - REQUIRE(TestType{} == this->zero); - REQUIRE(TestType{1} == this->one); -} -``` - -### Advanced Testing Features - -#### Logging and Information Macros -```cpp -TEST_CASE("Advanced logging", "[Logging]") { - INFO("This info persists until end of scope"); - - SECTION("Section A") { - INFO("Section A specific info"); - CAPTURE(some_variable, another_var); // Captures variable names and values - CHECK(some_condition); - } - - SECTION("Section B") { - UNSCOPED_INFO("This survives beyond its scope"); // v2.7.0+ - CHECK(other_condition); - } -} - -// Warning and explicit control -TEST_CASE("Explicit test control", "[Control]") { - WARN("This warns but doesn't fail the test"); - - if (precondition_not_met) { - SKIP("Reason"); // Marks the test as skipped (v3.3.0+, available) - return; - } - - if (critical_failure) { - FAIL("Critical condition failed"); // Fails and stops test - } - - SUCCEED("Reached successful completion"); // Explicit success marker -} -``` - -#### Static Assertions (Compile-time Testing) -```cpp -TEST_CASE("Compile-time checks", "[Static]") { - STATIC_REQUIRE(sizeof(int) >= 4); // Checked at compile time - STATIC_REQUIRE_FALSE(std::is_void_v); - - // For traits and template metaprogramming - STATIC_CHECK(std::is_trivially_copyable_v); // v3.0.1+ -} -``` - -#### Conditional Testing -```cpp -TEST_CASE("Conditional blocks", "[Conditional]") { - int value = get_test_value(); - - // These record the expression but don't count as test failures (v3.0.1+) - CHECKED_IF(value > 0) { - // This block runs if value > 0 - REQUIRE(value <= 100); - } CHECKED_ELSE(value > 0) { - // This block runs if value <= 0 - REQUIRE(value >= -100); - } -} -``` - -#### Benchmarking (v2.9.0+) -```cpp -TEST_CASE("Performance testing", "[Benchmark]") { - // Simple benchmarking - BENCHMARK("Algorithm performance") { - return expensive_algorithm(); - }; - - // Advanced benchmarking with setup - BENCHMARK_ADVANCED("Advanced benchmark")(Catch::Benchmark::Chronometer meter) { - std::vector data = setup_test_data(); // Setup not measured - - meter.measure([&] { - return process_data(data); // Only this is measured - }); - }; -} -``` - -## OrcaSlicer-Specific Testing Patterns - -### Geometry Testing -```cpp -TEST_CASE("Line operations", "[Geometry]") { - Line line{{100000, 0}, {0, 0}}; - Line parallel{{200000, 0}, {0, 0}}; - - REQUIRE(line.parallel_to(line)); - REQUIRE(line.parallel_to(parallel)); - - // Test with epsilon tolerance - Line rotated(parallel); - rotated.rotate(0.9 * EPSILON, {0, 0}); - REQUIRE(line.parallel_to(rotated)); -} -``` - -### Configuration Testing -```cpp -TEST_CASE("Config loading", "[Config]") { - DynamicPrintConfig config; - std::string config_path = std::string(TEST_DATA_DIR) + "/test_config/sample.ini"; - - REQUIRE_NOTHROW(config.load_from_ini(config_path)); - REQUIRE(config.has("layer_height")); -} -``` - -### File I/O Testing -```cpp -TEST_CASE("STL file parsing", "[FileFormat]") { - std::string stl_path = std::string(TEST_DATA_DIR) + "/test_stl/20mmbox.stl"; - - TriangleMesh mesh; - REQUIRE_NOTHROW(mesh.ReadSTLFile(stl_path.c_str())); - REQUIRE(!mesh.empty()); - REQUIRE(mesh.volume() > 0); -} -``` - -### G-code Generation Testing -```cpp -TEST_CASE("G-code writer functionality", "[GCodeWriter]") { - GCodeWriter writer; - - // Load test configuration - std::string config_path = std::string(TEST_DATA_DIR) + "/fff_print_tests/test_config.ini"; - writer.config.load(config_path, ForwardCompatibilitySubstitutionRule::Disable); - - // Test specific G-code generation - std::string result = writer.lift(); - REQUIRE(!result.empty()); - REQUIRE_THAT(result, Catch::Matchers::ContainsSubstring("G1")); -} -``` - -### Performance Testing Patterns -```cpp -TEST_CASE("Algorithm performance", "[Performance][Algorithm]") { - // Large test data - std::vector points = generate_large_point_set(10000); - - // Time the operation (manual timing example; the BENCHMARK macro is also available) - auto start = std::chrono::high_resolution_clock::now(); - auto result = convex_hull(points); - auto end = std::chrono::high_resolution_clock::now(); - - auto duration = std::chrono::duration_cast(end - start); - - REQUIRE(result.size() > 0); - REQUIRE(duration.count() < 1000); // Should complete in < 1 second -} -``` - -### Custom String Conversions - -#### For Custom Types -```cpp -// Method 1: operator<< overload (preferred) -std::ostream& operator<<(std::ostream& os, const Point& point) { - os << "Point(" << point.x << ", " << point.y << ")"; - return os; -} - -// Method 2: StringMaker specialization -namespace Catch { - template<> - struct StringMaker { - static std::string convert(const MyCustomType& value) { - return "MyCustomType{" + std::to_string(value.data) + "}"; - } - }; -} - -// Method 3: Enum registration (v2.8.0+) -enum class Status { Ready, Processing, Complete, Error }; - -// Must be at global scope! -CATCH_REGISTER_ENUM(Status, Status::Ready, Status::Processing, Status::Complete, Status::Error); - -// Method 4: Exception translation -CATCH_TRANSLATE_EXCEPTION(MyCustomException const& ex) { - return "MyCustomException: " + std::string(ex.what()); -} - -// Method 5: Disable range iteration for problematic types -namespace Catch { - template<> - struct is_range { - static const bool value = false; - }; -} -``` - -## Running and Debugging Tests - -### Building Tests -```bash -# Build all tests -cd build && make - -# Build specific test suite -cd build && make libslic3r_tests - -# Build and run tests -cd build && make && ctest -``` - -### Running Tests - -#### Essential Test Execution Patterns -```bash -# REQUIRED: Random order with assertion warnings (best practice) -cd build && ./tests/libslic3r/libslic3r_tests --order rand --warn NoAssertions - -# Run all tests with verbose output via CTest -cd build && ctest --output-on-failure - -# Run specific test suite with best practices -cd build && ./tests/libslic3r/libslic3r_tests --order rand --warn NoAssertions - -# Filter tests with specific tags -cd build && ./tests/libslic3r/libslic3r_tests "[Geometry]" --order rand - -# Filter by test name patterns -cd build && ./tests/libslic3r/libslic3r_tests "*geometry*" --order rand - -# Exclude tests (negation) -cd build && ./tests/libslic3r/libslic3r_tests "~[Performance]" --order rand - -# Combine filters: (Geometry AND Config) OR Algorithm -cd build && ./tests/libslic3r/libslic3r_tests "[Geometry][Config],[Algorithm]" --order rand - -# List available tests, tags, and reporters -cd build && ./tests/libslic3r/libslic3r_tests --list-tests -cd build && ./tests/libslic3r/libslic3r_tests --list-tags -cd build && ./tests/libslic3r/libslic3r_tests --list-reporters - -# Debug failing tests -cd build && ./tests/libslic3r/libslic3r_tests --break # Break into debugger on failure -cd build && ./tests/libslic3r/libslic3r_tests --success # Show passing tests too -cd build && ./tests/libslic3r/libslic3r_tests --durations yes # Show timing info - -# Abort on first failure -cd build && ./tests/libslic3r/libslic3r_tests --abort - -# Test sharding for parallel execution (MUST share random seed) -cd build && ./tests/libslic3r/libslic3r_tests --order rand --shard-index 0 --shard-count 4 --rng-seed 0xBEEF & -cd build && ./tests/libslic3r/libslic3r_tests --order rand --shard-index 1 --shard-count 4 --rng-seed 0xBEEF & -cd build && ./tests/libslic3r/libslic3r_tests --order rand --shard-index 2 --shard-count 4 --rng-seed 0xBEEF & -cd build && ./tests/libslic3r/libslic3r_tests --order rand --shard-index 3 --shard-count 4 --rng-seed 0xBEEF & -wait # Wait for all to complete -``` - -#### Reporter Options for CI Integration -```bash -# Different output formats for CI systems -cd build && ./tests/libslic3r/libslic3r_tests --reporter console # Default human-readable -cd build && ./tests/libslic3r/libslic3r_tests --reporter compact # Minimal output -cd build && ./tests/libslic3r/libslic3r_tests --reporter xml # Catch2 XML format -cd build && ./tests/libslic3r/libslic3r_tests --reporter junit # JUnit XML (widely supported) -cd build && ./tests/libslic3r/libslic3r_tests --reporter tap # Test Anything Protocol -cd build && ./tests/libslic3r/libslic3r_tests --reporter teamcity # TeamCity integration -cd build && ./tests/libslic3r/libslic3r_tests --reporter sonarqube # SonarQube integration -cd build && ./tests/libslic3r/libslic3r_tests --reporter automake # Automake integration - -# Multiple reporters simultaneously (if supported) -cd build && ./tests/libslic3r/libslic3r_tests --reporter console --reporter junit::out=results.xml -``` - -### Test Output Control -The custom `VerboseConsoleReporter` provides enhanced output: -- Test case start/end notifications with timing -- Section execution tracking -- Color-coded success/failure indicators -- Duration reporting for performance analysis - -## Test Data Management - -### Using TEST_DATA_DIR -All test data is accessible via the `TEST_DATA_DIR` preprocessor definition: - -```cpp -std::string mesh_path = std::string(TEST_DATA_DIR) + "/20mm_cube.obj"; -std::string config_path = std::string(TEST_DATA_DIR) + "/test_config/printer.ini"; -``` - -### Available Test Assets - -#### 3D Models -- **Basic shapes**: `20mm_cube.obj`, `pyramid.obj`, `sphere.obj` -- **Complex geometry**: `extruder_idler.obj`, `ipadstand.obj`, `bridge.obj` -- **Edge cases**: `cube_with_hole.obj`, `sloping_hole.obj`, `small_dorito.obj` - -#### File Format Tests -- **STL variants**: ASCII/binary, different line endings, Unicode names -- **3MF files**: Multi-material, complex assemblies -- **Configuration files**: Various printer/material profiles - -#### Test Utilities -The `Test` namespace provides helper functions: -```cpp -using namespace Slic3r::Test; - -// Load standard test meshes -TriangleMesh mesh = mesh(TestMesh::cube_20x20x20); - -// Standard test configurations -DynamicPrintConfig config = config(TestConfig::PLA_default); -``` - -## Common Pitfalls and Solutions - -### Floating-Point Comparisons - -> **CRITICAL**: Never use Approx - it's deprecated due to asymmetry and other issues - -❌ **Incorrect**: -```cpp -REQUIRE(calculated_volume == expected_volume); // Exact equality -REQUIRE(calculated_volume == Catch::Approx(expected)); // Deprecated! Asymmetric! -``` - -✅ **Correct**: Always use floating point matchers -```cpp -// Absolute tolerance - good when values are near zero -REQUIRE_THAT(calculated_volume, WithinAbs(expected_volume, 0.001)); - -// Relative tolerance - good for values with different magnitudes -REQUIRE_THAT(calculated_volume, WithinRel(expected_volume, 0.01)); // 1% tolerance - -// ULP (Units in Last Place) - most precise, requires IEEE-754 -REQUIRE_THAT(calculated_volume, WithinULP(expected_volume, 4)); - -// Combined approach - relative OR absolute -REQUIRE_THAT(calculated_volume, - WithinRel(expected_volume, 0.001) || WithinAbs(0.0, 0.000001)); - -// Precision control for output -Catch::StringMaker::precision = 15; // Show more decimal places -``` - -### Why Approx is Problematic: -- **Asymmetric**: `Approx(10).epsilon(0.1) != 11.1` but `Approx(11.1).epsilon(0.1) == 10` -- **Double-only**: All computation done in `double`, causes issues with `float` inputs -- **Default behavior**: Only uses relative comparison, so `Approx(0) == X` only works for `X == 0` - -### Path Handling -❌ **Incorrect**: -```cpp -std::string path = TEST_DATA_DIR + "/model.obj"; // May have path separator issues -``` - -✅ **Correct**: -```cpp -std::string path = std::string(TEST_DATA_DIR) + "/model.obj"; -// or use boost::filesystem for complex path operations -``` - -### Exception Testing -❌ **Incorrect**: -```cpp -bool threw_exception = false; -try { - risky_function(); -} catch (...) { - threw_exception = true; -} -REQUIRE(threw_exception); -``` - -✅ **Correct**: -```cpp -REQUIRE_THROWS(risky_function()); -// or for specific exceptions -REQUIRE_THROWS_AS(risky_function(), SpecificException); -``` - -### Thread Safety - -⚠️ **CRITICAL**: Catch2 assertions are **NOT thread-safe** by default! - -> **Note**: Catch2 v3.9.0+ has opt-in thread-safe assertions via `CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS`. OrcaSlicer is on v3.11.0 but does not enable this flag, so assertions remain non-thread-safe by default. - -❌ **Incorrect**: Will cause undefined behavior or crashes -```cpp -std::thread t([&]() { - REQUIRE(threaded_operation() == expected); // NOT THREAD-SAFE! - CHECK(other_operation()); // NOT THREAD-SAFE! -}); -``` - -✅ **Correct**: Collect results, assert on main thread -```cpp -std::atomic success{false}; -std::atomic error_count{0}; - -std::thread t([&]() { - // Do work in thread, collect results - bool result1 = (threaded_operation() == expected); - bool result2 = other_operation(); - - if (result1 && result2) { - success = true; - } else { - error_count++; - } -}); - -t.join(); - -// Assert results on main thread -REQUIRE(success); -REQUIRE(error_count == 0); -``` - -#### Thread Safety Rules: -- **REQUIRE family**: Would terminate process in spawned threads (throws exception with no try-catch) -- **CHECK family**: Not thread-safe, can corrupt internal state -- **SKIP, FAIL, SUCCEED**: Not thread-safe even with v3 thread-safe assertions -- **Message macros**: INFO, CAPTURE, WARN - not thread-safe -- **STATIC_REQUIRE/CHECK**: Not thread-safe (relies on runtime registration) - -### Memory Management -Use RAII and smart pointers in tests: -```cpp -TEST_CASE("Resource management", "[Memory]") { - auto model = std::make_unique(); - // Automatic cleanup on test completion/failure - - REQUIRE(model->objects.empty()); -} -``` - -## Performance Considerations - -### Compilation Optimizations -```cpp -// In CMakeLists.txt or as preprocessor definition -#define CATCH_CONFIG_FAST_COMPILE // 20% faster compilation, disables some features - -// For faster test iteration during development -#define CATCH_CONFIG_DISABLE_STRINGIFICATION // Workaround for VS2017 raw string bug -``` - -### Runtime Performance -```cpp -TEST_CASE("Performance-sensitive test", "[Performance]") { - // Manual timing example (Catch2's built-in BENCHMARK macro is also available) - auto start = std::chrono::high_resolution_clock::now(); - - auto result = expensive_operation(); - - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - REQUIRE(result.is_valid()); - REQUIRE(duration.count() < 1000); // Should complete in < 1 second - - INFO("Operation took " << duration.count() << "ms"); -} -``` - -### Memory Leak Detection -```cpp -// For Windows builds - detects memory leaks -#define CATCH_CONFIG_WINDOWS_CRTDBG // Must be defined for whole build -``` - -## Integration with CMake - -### Adding New Test Files -1. Create test file: `test_new_feature.cpp` -2. Add to appropriate `CMakeLists.txt`: -```cmake -add_executable(${_TEST_NAME}_tests - ${_TEST_NAME}_tests.cpp - test_existing_feature.cpp - test_new_feature.cpp # Add here -) -``` - -### Advanced Test Discovery -```cmake -# Basic test discovery -catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") - -# Advanced test discovery with customization -catch_discover_tests(${_TEST_NAME}_tests - TEST_PREFIX "${_TEST_NAME}: " - TEST_SUFFIX " (auto)" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - EXTRA_ARGS --order rand --warn NoAssertions - PROPERTIES - TIMEOUT 300 - LABELS "unit;core" - DISCOVERY_MODE PRE_TEST # or POST_BUILD - REPORTER junit - OUTPUT_DIR ${CMAKE_BINARY_DIR}/test-results - OUTPUT_PREFIX "results_" - OUTPUT_SUFFIX ".xml" -) - -# Test sharding for parallel execution -include(CatchShardTests) # If available -catch_shard_tests(${_TEST_NAME}_tests - SHARD_COUNT 4 - TEST_PREFIX "${_TEST_NAME}_shard: " -) -``` - -### Conditional Test Compilation -```cmake -# Feature-dependent tests -if (TARGET OpenVDB::openvdb) - target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp) -endif() - -# Platform-specific tests -if(WIN32) - target_sources(${_TEST_NAME}_tests PRIVATE test_windows_specific.cpp) -elseif(UNIX) - target_sources(${_TEST_NAME}_tests PRIVATE test_unix_specific.cpp) -endif() - -# Compiler-specific workarounds -if(MSVC) - target_compile_definitions(${_TEST_NAME}_tests PRIVATE CATCH_CONFIG_DISABLE_STRINGIFICATION) -endif() - -# Fast compile mode for development -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_definitions(${_TEST_NAME}_tests PRIVATE CATCH_CONFIG_FAST_COMPILE) -endif() -``` - -## Known Issues and Workarounds - -### Platform-Specific Issues -```cpp -// MinGW/CygWin slow linking workaround -// Use: -fuse-ld=lld flag to speed up linking significantly - -// Visual Studio 2017 raw string literal bug -#define CATCH_CONFIG_DISABLE_STRINGIFICATION -// This disables expression stringification but works around the compiler bug - -// Visual Studio 2022 spaceship operator issue -// REQUIRE((a <=> b) == 0); // May not compile with MSVC -// Workaround: use clang-cl or avoid spaceship in assertions - -// QNX/VxWorks C stdlib issues -#include // Use C++ headers -std::foo_function(); // Always call qualified -// NOT: #include and foo_function(); -``` - -### Catch2 v3 Features Available -```cpp -// OrcaSlicer is on Catch2 v3.11.0 - all of these ARE available: -// SKIP() macro - v3.3.0+ -// Opt-in thread-safe assertions - v3.9.0+ (NOT enabled here; see Thread Safety) -// Built-in BENCHMARK / BENCHMARK_ADVANCED - v3.x -// testCasePartial events - v3.0.1+ -// Multiple reporters simultaneously - v3.0.1+ -// STATIC_CHECK macro - v3.0.1+ -// Built-in test sharding (--shard-*) - v3.x - -// v3 notes to remember: -// - String matcher is "ContainsSubstring" (v2's "Contains" no longer exists) -// - Sections can still be re-run if a later section fails (unchanged from v2) -``` - -### Test Organization Best Practices - -#### Project Structure Rules -1. **1:1 correspondence**: One test binary per library/module -2. **Hidden tests**: Use `[.]` or `[!benchmark]` tags for tests that shouldn't run by default -3. **Tag hierarchy**: Use consistent tagging scheme across the project -4. **File naming**: Follow `test_.cpp` pattern - -#### CI/CD Integration -```bash -# Essential CI test command -./tests --order rand --warn NoAssertions --reporter junit::out=results.xml - -# For coverage analysis -./tests --order rand --warn NoAssertions --reporter console --success - -# For performance tracking -./tests --order rand --warn NoAssertions --durations yes -``` - -This comprehensive guide ensures robust, maintainable, and efficient testing practices for OrcaSlicer development with Claude Code, incorporating all critical knowledge from the official Catch2 documentation. \ No newline at end of file +@AGENTS.md diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000000..bd490ae38f --- /dev/null +++ b/tests/README.md @@ -0,0 +1,8 @@ +# OrcaSlicer tests + +Building, running and writing tests is documented on the wiki, under [How to Test](https://www.orcaslicer.com/wiki/developer_reference/how_to_test.html). + +Two files here rather than there, because coding agents only read what is in the repository: + +- [AGENTS.md](AGENTS.md) is the same guidance in short form, and is what an agent working under `tests/` picks up. +- [CATCH2.md](CATCH2.md) is the Catch2 reference, including the mistakes that break a test at runtime. diff --git a/tests/fff_print/README.md b/tests/fff_print/README.md deleted file mode 100644 index c8ed85e367..0000000000 --- a/tests/fff_print/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# fff_print test suite - -Component- and pipeline-level tests for FFF slicing: the path from a `Model` plus config, through `Print` / `PrintObject`, to emitted G-code. - -For Catch2 mechanics (assertions, generators, matchers, random ordering, thread-safety), see [../CLAUDE.md](../CLAUDE.md). This document is the organizing contract for the suite: where a test goes, and how it is named. - -## Organizing principle - -**One file per subsystem. A subsystem is usually a single production class (`Flow`, `PrintObject`), but may be a cohesive feature that spans several (skirt/brim lives in `Brim.cpp`, `Print.cpp`, and `GCode.cpp`). That file owns every test for the subsystem: in-memory-state assertions and emitted-G-code assertions alike.** - -A test's home is decided by *what production code it exercises*, never by *how it observes the result*. A skirt test that inspects `print.skirt()` and one that greps the G-code for `; skirt` live in the same file. - -If you touched code in a subsystem, its test file is where your test goes. If a subsystem has no file yet, add `test_.cpp` and list it in `CMakeLists.txt`. - -## File ownership - -### Building blocks (one class, exercised through its API) - -| File | Source (`src/libslic3r/`) | Covers | -|---|---|---| -| `test_trianglemesh` | `TriangleMesh.{c,h}pp` | mesh stats, transforms, slicing, split/merge/cut | -| `test_flow` | `Flow.{c,h}pp` | extrusion width / area math | -| `test_extrusion_entity` | `ExtrusionEntity.{c,h}pp` | extrusion-collection geometry | -| `test_gcodewriter` | `GCodeWriter.{c,h}pp`, `GCode.cpp` | low-level G-code emit primitives, origin | -| `test_model` | `Model.{c,h}pp` | object / volume / instance construction | - -### Slicing pipeline (build a `Print`, then assert state or G-code) - -| File | Source (`src/libslic3r/`) | Covers | -|---|---|---| -| `test_printobject` | `PrintObject.cpp` | layer heights, perimeter generation | -| `test_fill` | `Fill/` | infill patterns and infill G-code | -| `test_skirt_brim` | `Brim.cpp`, `Print.cpp` | skirt/brim loop counts, grouping, brim ears, emission order | -| `test_support_material` | `Support/` | support & raft layers, contact distance | -| `test_cooling` | `GCode/CoolingBuffer.cpp` | fan control, speed-marker consumption | -| `test_multifilament` | `GCode/ToolOrdering.cpp` | per-feature and per-object filament routing | -| `test_print` | `Print.{c,h}pp` | `validate()`, solid-shell behavior, sequential printing, custom G-code & config comments, default-slice smoke | - -Paths are under `src/libslic3r/`. A trailing `/` is a directory of related files; otherwise it is a single class. `{c,h}pp` means the `.cpp`/`.hpp` pair. - -## Naming and tags - -- **File:** `test_.cpp`. -- **Test name:** a plain behavioral sentence, present tense, stating the contract the test pins down. No `Subsystem:` prefix (the tag carries that). - - Good: `TEST_CASE("Skirt is emitted once per layer it spans", "[SkirtBrim]")` - - Avoid: `TEST_CASE("Print: Skirt generation", "[Print]")` -- **Tags:** - - Exactly one **subsystem** tag, PascalCase, matching the file (`[SkirtBrim]`, `[PrintObject]`, `[Fill]`). This is the grouping / filter key. - - Optional **cross-cutting** tags for a concern that genuinely spans files (`[validate]`, `[Regression]`). - - **Status** tags: `[NotWorking]` marks a test disabled for a known, documented reason; CI excludes it via `~[NotWorking]` (it does not hide itself). Use `[.]` to hide a test from default runs entirely. Either way, say why in a one-line comment. - -## Test style - -Prefer a flat `TEST_CASE` per behavior, with `GENERATE` for parameterized cases and shared setup factored into helpers. The test name carries the behavior, so the BDD scaffolding is usually redundant. Reserve `SCENARIO` / `GIVEN` / `WHEN` / `THEN` for a test with genuine shared setup that branches into a few closely related variations, and never let a `SCENARIO` accumulate unrelated `WHEN`s: that grab-bag is what this contract exists to prevent (and it hides failures behind a single coarse test case). - -## Robust tests - -A test should fail only when the behavior it names breaks, not from unrelated changes (the "change-detector" anti-pattern). Test behavior, not incidentals, and aim for one reason to fail. Concretely: - -- Don't depend on or assert defaults: set the config keys the behavior needs, and derive expected values from those inputs (a 20mm cube at 0.2mm = 100 layers), not from a default that may change. -- Assert the defining property, not an incidental value: prefer "skirt present", "at least 2 brim loops", or "ears vs none" over exact coordinates, extrusion amounts, line counts, or byte sizes. -- Compare floats with a tolerance (`WithinAbs` / `WithinRel`), never `==`. -- Match the meaningful G-code token (`; skirt`), not whole lines, whitespace, or comment wording. -- Rely on ordering only when it is the contract (as `role_sequence` does). -- Keep tests self-contained: no shared state, green under `--order rand`. - -## Helpers - -Reuse these instead of building a `Print` or parsing G-code by hand. - -- **Global** (`tests/test_utils.hpp`, available to every suite): - - `load_model("file.obj")`: load a `TriangleMesh` from `tests/data/`. - - `ScopedTemporaryFile`: an RAII temp-file path, removed on scope exit. -- **Suite harness** (`fff_print/test_helpers.{hpp,cpp}`): - - Build and run: `init_print(...)`, `init_and_process_print(...)`, `slice(...)` (returns the G-code string), and `gcode(print)`. - - Two-cube placement: `slice_two_cubes_arranged(...)` (arranger-positioned), and `place_two_cubes_apart(...)` / `slice_two_cubes_apart(...)` (a fixed gap, not arranged). - - Meshes: `cube(size)` / `make_cube(...)` for simple shapes; the `TestMesh` enum with `mesh(...)` for named fixtures. - - G-code analysis: `layers_with_role(gcode, role)`, `max_z(gcode)`, `role_passes(gcode, role)`, `role_sequence(gcode, roles)`. Subsystem-specific checks stay local (for example `brim_count` in `test_skirt_brim`). - -Promote a helper into the suite harness when it is a general test primitive (not tied to one subsystem's logic), even if only one file uses it today; keep genuinely subsystem-specific helpers local (file-static). Reuse potential, not current usage count, is the test. - -## Adding a test (checklist) - -1. Find the subsystem's file in the tables; create `test_.cpp` if missing. -2. Build the print with a harness helper; set only the config keys the behavior needs. -3. Assert the behavior, in-memory or via parsed G-code, whichever is clearest. -4. Name it as a behavioral sentence and tag it `[Subsystem]`. -5. For a bug fix, add the regression test in the owning file. Name it for the behavior it protects; the test must stand on its own without relying on an external issue or PR for meaning. - -## Running - - ctest --test-dir build/tests/fff_print - build/tests/fff_print//fff_print_tests --order rand "~[NotWorking]"