Как одним кликом пользователя получить грань (face) из solid3d?

Автор Тема: Как одним кликом пользователя получить грань (face) из solid3d?  (Прочитано 1199 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн Gr.OmАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 0
Вроде бы тривиальная задача, но убил три дня на нее и толком не решил.
Если через интерфейс легко делаем _solidedit _face, и далее можно мышкой выделять отдельные грани и делать что нужно, то почему через API это оказалось вдруг таким гемором?!
Пытался через .GetNestedEntity (в документации не уточняется, - можно ли его использовать с подобъектами из brep), в сети полно примеров с не brep-подобъектами:

Код - C# [Выбрать]
  1. PromptNestedEntityOptions pneo = new PromptNestedEntityOptions("\nSelect Face: ");
  2. pneo.AllowNone = false;
  3.  
  4. PromptNestedEntityResult pner = ed.GetNestedEntity(pneo);

Но вытащить через нее ничего получилось. Опций у него минимум (AllowNone), а на выходе только сам solid3d без каких-либо вложений. Предварительное переключение переменной SUBOBJSELECTIONMODE на 3 ничего не дает. Пришел к выводу, что метод предназначен только для примитивов, которые могут существовать в модели самостоятельно.

Через .GetEntity тоже не получается:

Код - C# [Выбрать]
  1. PromptEntityOptions opts = new PromptEntityOptions("\nВыберите грань 3D-тела: ");
  2. opts.AddAllowedClass(typeof(Solid3d), false);
  3. opts.AllowNone = false; // Обязательный выбор
  4. PromptEntityResult res = ed.GetEntity(opts);

Если указать AddAllowedClass = Solid3d, на выходе только тело полностью. При попытке подсунуть сюда типы Autodesk.AutoCAD.BoundaryRepresentation.Face или Autodesk.AutoCAD.DatabaseServices.Face и при выборе в модели грани через Ctrl дают PromptStatus.Cancel.

Дальше всего получилось продвинуться через SelectionSet в виде:
         
Код - C# [Выбрать]
  1. Application.SetSystemVariable("SUBOBJSELECTIONMODE", (short)3);
  2. // 1. Запрос выбора грани у пользователя
  3. PromptSelectionOptions pso = new PromptSelectionOptions();
  4. pso.MessageForAdding = "\nSelect Face";
  5. pso.SingleOnly = true;
  6. pso.ForceSubSelections = true;
  7. pso.SinglePickInSpace = true;
  8.  
  9. PromptSelectionResult psrFace = ed.GetSelection(pso);
  10.  
  11. SelectionSet SSet = psrFace.Value;
  12. SelectedObject mainObj = SSet[0];
  13.  
  14. SelectedSubObject[] subs = mainObj.GetSubentities();

Этим способом худо-бедно добрался до подобъекта, но во-первых не понимаю, как сделать так, чтобы команда выполнилась сразу после клика на грани. При выборе грани ничего не происходит, команда продолжается только если что-то сделать еще (зум, еще один клик, Enter). Непонятно тогда, - о чем опции .SingleOnly и .SinglePickInSpace?
И самое бредовое и непроходимое - если тело содержит историю (а запись истории включена по умолчанию), нужную грань уже не выбрать. Под курсором подхватываются солиды или грани из истории, но только не грани текущего brep :o
Чтобы повторно взять грань у предыдущего тела, после выполнения команды попробовал уже все, что можно и в разных конфигурациях (внутри и после транзакции):

Код - C# [Выбрать]
  1. solidAfter.RecordGraphicsModified(true);
  2. solidAfter.Highlight();
  3. solidAfter.Unhighlight();
  4. solidAfter.Draw();
  5. ed.UpdateScreen();
  6. ed.Regen();

Ничего не помогает! Только ручное отключение записи истории солида (с наскока даже не нашел, как это сделать через API).
Но предварительно отключать историю у всех объектов или под курсором - как-то фу! Да и должно быть какое-то штатное решение, просто не могу его найти.
На сайте примеров с ручным выделением и получением идентификатора конкретной грани не нашел.
Может кто поделится примером (достаточно только концепции - какой метод и с какими настройками), - как получить идентификатор грани одним кликом.
« Последнее редактирование: 15-07-2026, 18:43:01 от Gr.Om »

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 870
  • Карма: 175
    • Мои плагины к Автокаду
Отключите историю солида. Это зло от которого надо сразу избавляться.
переключите SUBOBJSELECTIONMODE на Face. иначе всегда будет выбираться ребро, даже если его вообще не видно за гранью.
Далее просто:

Код - C# [Выбрать]
  1. PromptSelectionOptions pso = new()
  2.  {
  3.    SingleOnly = true,
  4.    ForceSubSelections = true,
  5.    MessageForAdding = "....",
  6.    SinglePickInSpace = true,
  7.    RejectObjectsOnLockedLayers = true
  8.  };
  9. PromptSelectionResult psr = ed.GetSelection(pso);
  10. f (psr.Status != PromptStatus.OK) return;
  11. SelectedObject so = psr.Value[0];
  12. ObjectId solidId = so.ObjectId;
  13. SelectedSubObject[] ssubs = so.GetSubentities();
  14. SubentityType subentityType = ssubs[0].FullSubentityPath.SubentId.Type;
  15. if (subentityType != SubentityType.Face) return;
  16. SubentityId id = ssubs[0].FullSubentityPath.SubentId;



Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 870
  • Карма: 175
    • Мои плагины к Автокаду
И кстати в AutoCAD 2025 и новее все "быстрые" визуальные стили путают направление взгляда пользователи и все команды выбора поверхности выбирают самую дальнюю невидимую поверхность на самом дальнем солиде. Надо отключать 3д-ускорение или работать в 2д-каркасе.

Оффлайн Gr.OmАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 0
Т.е., я уже получил максимум из возможного?))
Отключите историю солида
Тут вопрос-то не совсем в том что я могу сделать у себя. Получается, что с NET 8.0 программно можно только переключить переменную SOLIDHIST, чтобы история не писалась у НОВЫХ тел. А что делать с существующими телами, которые удаленному пользователю нужно редактировать плагином? На официальном сайте вижу свойство .RecordHistory под 2018 версией, а в 2025 автокаде его уже нет.
Я думал .NET API - это почти промышленный уровень разработки, почти как C++ и ObjectARX, думал просто многого чего не раскопал в справке:)
Когда твердо решил сменить VBA и VBE на C# и VisualStudio, была надежда на сильный LevelUp (именно в части API), что наконец можно выбросить старый потертый бубен, с которым приходилось танцевать в VBA, чтобы реализовывать нереализуемые вещи.
Теперь понемногу понимаю, что просто поменял его на целую тележку новых блестящих бубнов. ???

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 870
  • Карма: 175
    • Мои плагины к Автокаду
Да, открываете солид на запись и RecordHistory = false. Вся история сбрасывается. Конечно, это свойство солида есть во всех версиях API.
Да, C# это промышленный (без почти) уровень. API полностью повторяет ObjectARX за очень малыми исключениями (динамические свойства создавать нельзя, например). Производительность ниже чем у ObjectARX. Возможность несоизмеримы с примитивным API VBA или скриптами LISP.
Да, проблем и глюков воз и маленькая тележка.

Оффлайн Gr.OmАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 0
Да, открываете солид на запись и RecordHistory = false. Вся история сбрасывается. Конечно, это свойство солида есть во всех версиях API.
Да, действительно. Что-то от усиленного перечитывания дерева вдоль и поперек уже начинаются глюки. Пора отдохнуть.

Оффлайн Gr.OmАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 0
Итак, борьба продолжается:).
На картинке два тела, изначально созданные как штатный примитив Box.



Если после наложения каких-либо простых штатных модификаций (например фаска) неважно какими способом, - программно или через интерфейс, объект может по-прежнему идентифицировать себя как Box, то выбрать субобъект интерактивно не удается. Ни через интерфейс (Ctrl+Space) ни программно через .GetSelection() выделить грань или ребро не получается. Под мышкой принудительно подсвечивается исходный Box (как на картинке справа).
Не помогает ни ручное/программное выключение записи/показа истории, ни всевозможные перерисовки экрана после того же создания фаски программно.
При попытке программно выделить грань и вывести тип грани, выводится тип Class из этого перечисления:

public enum SubentityType {
  Null,
  Face,
  Edge,
  Vertex,
  MlineCache,
  Class
}

Который видимо и характеризует исходный объект Box. Но что с ним делать для доступа к субобъектам текущего или хотя бы исходного тела, из справки не понятно.
Если объект перестал себя идентифицировать как Box, например после наложения Shell (как тело слева), то проблема с принудительным подсовыванием исходного Box при попытке выбора субобъекта исчезает.

Правда, если отключить вручную запись истории для тела ДО применения Chamfer, то проблема тоже решается.

Но в части возможностей API вопрос по-прежнему актуален. Есть ли какой-либо способ через API заставить Box/Frustum/Sphere и прочих забыть о том, кем они были при создании и стать нормальным телом?
« Последнее редактирование: 27-07-2026, 11:19:30 от Gr.Om »

Отмечено как Решение Gr.Om 27-07-2026, 11:29:54

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 870
  • Карма: 175
    • Мои плагины к Автокаду
Картинка в вашем сообщении не отображается.
"штатный примитив Box" - Это называется процедурный бокс. Ни у кого никогда (до этого вашего сообщения) не было проблем с выбором граней на процедурных солидах. Затрудняюсь сказать как вам удалось получить такой спецэффект.
Определить, что солид процедурный можно, например, так:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.Interop.Common;
  2.  
  3.     /// <summary>
  4.     /// процедурный солид. Это солиды, созданные командами: BOX,CYLINDER,CONE,SPHERE,WEDGE,TORUS
  5.     /// Пока пользователь не выполнял никаких операций, эти объекты остаются процедурными, и AutoCAD позволяет редактировать размеры.
  6.     /// После любой операции они превращаются в обычный BREP-солид.
  7.     /// </summary>
  8.     public static bool
  9.     IsProcedural(this Solid3d solid)
  10.     {
  11. #if !BRICS
  12.       if (solid is null || solid.ObjectId.IsNull)
  13.         return false; // солид без Id (созданный программно) никак не может быть процедурным и вызывает Exception на solid.AcadObject
  14.       try
  15.       {
  16.         Acad3DSolid comSolid = solid?.AcadObject as Acad3DSolid;
  17.         return !IsNullOrEmpty(comSolid?.SolidType);
  18.       }
  19.       catch { }
  20. #endif
  21.       return false;
  22.     }

Отключить процедурность у солида легко: практически любая команда модификации ломает процедурность. Например, solid.CleanBody();

Оффлайн Gr.OmАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 0
Затрудняюсь сказать как вам удалось получить такой спецэффект.
Если на только что созданном Box при включенной истории создать фаску, то потом ничего не помогает в части выделения субобъектов. Только отмена фаски, отключение истории и создание фаски повторно. После этого норм.
У вас поведение в интерфейсе отличается от этого?

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 870
  • Карма: 175
    • Мои плагины к Автокаду
История солидов - это отдельная песня. В DWT шаблоне чертежа надо прописать сразу SOLIDHIST=0 и забыть обо всех этих проблемах.

Оффлайн Gr.OmАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 0
Отключить процедурность у солида легко: практически любая команда модификации ломает процедурность. Например, solid.CleanBody();

Штатные методы типа СhamferEdges не ломают, как показал опыт.
А .CleanBody() действительно то, что нужно. Спасибо!

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