Проблемы с созданием дуговой трубы с центральным углом дуги больше 180 градусов.

Автор Тема: Проблемы с созданием дуговой трубы с центральным углом дуги больше 180 градусов.  (Прочитано 12762 раз)

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

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

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Есть подозрение, что в API Civil 3D по трубам всплыл весьма неприятный баг. У меня не получается построить трубу по дуге, если эта дуга больше полукруга. Метод AddCurvePipe упорно строит не то, что требуется. Беда ещё в том, что уже построенную дуговую трубу никак не исправить - вообще нет методов/свойств для изменения радиуса или центра дуговой трубы.
Для проверки наваял такой код:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using Autodesk.Civil.DatabaseServices;
  7. using Autodesk.Civil.DatabaseServices.Styles;
  8.  
  9. namespace C3dTest
  10. {
  11.     public class CreateCurvePipeTest
  12.     {
  13.         [CommandMethod("CreatePipeFromArc")]
  14.         public void CreatePipeFromArc()
  15.         {
  16.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  17.             Editor ed = adoc.Editor;
  18.             Database db = adoc.Database;
  19.  
  20.             PromptEntityOptions partOps
  21.                 = new PromptEntityOptions("\nSelect network part: ");
  22.             partOps.SetRejectMessage("It's not part!");
  23.             partOps.AddAllowedClass(typeof(Part), false);
  24.             PromptEntityResult partRes = ed.GetEntity(partOps);
  25.             if (partRes.Status != PromptStatus.OK) return;
  26.  
  27.             PromptEntityOptions arcOpts
  28.                 = new PromptEntityOptions("\nSelect arc: ");
  29.             arcOpts.SetRejectMessage("It's not arc!");
  30.             arcOpts.AddAllowedClass(typeof(Arc), true);
  31.             PromptEntityResult arcRes = ed.GetEntity(arcOpts);
  32.             if (arcRes.Status != PromptStatus.OK) return;
  33.  
  34.             PromptKeywordOptions cWiseOpts
  35.                 = new PromptKeywordOptions("\nClockwise?: ");
  36.             cWiseOpts.Keywords.Add("Yes");
  37.             cWiseOpts.Keywords.Add("No");
  38.             PromptResult cWiseRes = ed.GetKeywords(cWiseOpts);
  39.             if (cWiseRes.Status != PromptStatus.OK) return;
  40.  
  41.             ObjectId
  42.                 netId,
  43.                 famId = default,
  44.                 sizeId = default;
  45.  
  46.             using (Transaction tr = db.TransactionManager.StartTransaction())
  47.             {
  48.                 Part part = tr.GetObject
  49.                     (partRes.ObjectId, OpenMode.ForRead) as Part;
  50.  
  51.                 netId = part.NetworkId;
  52.  
  53.                 Network net = tr.GetObject
  54.                     (netId, OpenMode.ForRead) as Network;
  55.  
  56.                 ObjectId partsListId = net.PartsListId;
  57.  
  58.                 PartsList partsList
  59.                     = tr.GetObject(partsListId, OpenMode.ForRead) as PartsList;
  60.  
  61.                 using (ObjectIdCollection pipeFamsIds
  62.                     = partsList.GetPartFamilyIdsByDomain(DomainType.Pipe))
  63.                 {
  64.                     if (pipeFamsIds.Count > 0)
  65.                     {
  66.                         famId = pipeFamsIds[0];
  67.                     }
  68.                 }
  69.  
  70.                 if (famId.IsValid)
  71.                 {
  72.                     PartFamily partFamily
  73.                         = tr.GetObject(famId, OpenMode.ForRead) as PartFamily;
  74.                     if (partFamily.PartSizeCount > 0)
  75.                     {
  76.                         sizeId = partFamily[0];
  77.                     }
  78.                 }
  79.  
  80.                 tr.Commit();
  81.             }
  82.  
  83.             if (!famId.IsValid || !sizeId.IsValid)
  84.             {
  85.                 return;
  86.             }
  87.  
  88.             using (Transaction tr = db.TransactionManager.StartTransaction())
  89.             {
  90.                 Arc arc = tr.GetObject(arcRes.ObjectId, OpenMode.ForRead) as Arc;
  91.  
  92.                 Network net = tr.GetObject(netId, OpenMode.ForWrite) as Network;
  93.  
  94.                 ObjectId pipeId = default;
  95.  
  96.                 using (Curve3d curve3d = arc.GetGeCurve())
  97.                 {
  98.                     net.AddCurvePipe
  99.                     (famId,
  100.                     sizeId,
  101.                     curve3d,
  102.                     cWiseRes.StringResult == "Yes",
  103.                     ref pipeId,
  104.                     false);
  105.                 }
  106.  
  107.                 tr.Commit();
  108.             }
  109.         }
  110.     }
  111. }
  112.  
Проверка с дугой меньше полукруга:

Проверка с дугой больше полукруга:

Ну и, для кучи - проверка построения трубы по дуге с помощью базовой команды Civil 3D:

Может быть, я что-то не так делаю?

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Попробовал использовать другой тип кривой для построения, выдало ошибку: The curve type should be CircularArc3d.
Попробовал создать CircularArc3d в виде полной окружности. Глупость, конечно. Потому что в таком случае неоткуда получить начальную и конечную точку трубы. Что интересно, в таком варианте метод отрабатывает без ошибок и даже создаёт трубу. Но эта труба не имеет геометрии - начало и конец в начале координат, длина трубы нулевая.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Дмитрий Загорулькин,
Это или баг в методе AddCurvePipe или баг в методе Arc.GetGeCurve
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
На всякий случай попробуй такую функцию преобразования Arc в CircularArc3d:
Код - C# [Выбрать]
  1. public static CircularArc3d ArcToGeArc(ref Arc arc)
  2. {
  3.       double parm1 = arc.StartParam, parm2 = arc.EndParam;
  4.       return new CircularArc3d(
  5.         arc.StartPoint,
  6.         arc.GetPointAtParameter(0.5 * (parm1 + parm2)),
  7.         arc.EndPoint
  8.       );
  9. }
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Это или баг в методе AddCurvePipe или баг в методе Arc.GetGeCurve
Первое.
Я уже пробовал CircularArc3d с нуля создавать по точкам - не меняется поведение. Вообще, изначально я именно так и создавал кривую для трубы. Просто в демонстрационном примере взял дугу и этот метод для наглядности.
Что ещё попробовал:
1. Если исходная дуга больше 180 градусов - создавать трубу полукругом, отсчитывая полукруг от начальной точки исходной дуги. А потом изменять конечную точку, задавая значение конечной точки исходной дуги. Несмотря на то, что свойство Pipe.EndPoint доступно для изменения, попытки задать ему новое значение для дуговой трубы просто игнорируются. Ошибки не возникает, но и значение не меняется.
2. Снова строю трубу - полукруг, как в предыдущем опыте. Затем создаю колодец в той точке, куда надо привести трубу и пытаюсь с помощью метода Pipe.ConnectToStructure присоединить трубу к этому колодцу. При этом, параметр force, естественно, задаю true. Присоединение "как бы" происходит - никаких ошибок, в свойствах трубы появляется имя присоединённого на конце колодца. Но конец трубы физически в эту точку не подтягивается. Получилось создать вот такой вот искусственный баг модели (который не даже аудит не детектит!).
3. Сейчас пробую после манипуляций из опыта 2 применять различные методы обновления трубы, чтобы она поняла, что её конечная точка должна быть на колодце. Иногда получается, но стабильного поведения пока не удалось добиться...

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Дмитрий Загорулькин,
Я думаю, что тебе есть прямой смысл отправить это как баг в ADN DevHelp. Если по правильной CircularArc3d труба не строится, то это явный баг в API.
Есть несколько соображений:
1. Глянь исходник метода AddCurvePipe - может быть будет что-то понятно.
2. Попробуй воспользоваться метод Curve3d.GetReverseParameterCurve для обращения CircularArc3d.
3. Какой-то вообще это странный метод AddCurvePipe. Зачем ему дополнительно задавать clockwise Curve3d или нет, если Curve3d явно задана? 
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Дмитрий Загорулькин,
Я думаю, что тебе есть прямой смысл отправить это как баг в ADN DevHelp. Если по правильной CircularArc3d труба не строится, то это явный баг в API.
Есть несколько соображений:
1. Глянь исходник метода AddCurvePipe - может быть будет что-то понятно.
2. Попробуй воспользоваться метод Curve3d.GetReverseParameterCurve для обращения CircularArc3d.
3. Какой-то вообще это странный метод AddCurvePipe. Зачем ему дополнительно задавать clockwise Curve3d или нет, если Curve3d явно задана?
1. Там не за что зацепиться - внутри идёт вызов нативного метода.
2. Не получилось. Метод выворачивает дугу - конечные точки меняются местами, вектор нормали - на противоположный и т.д. При этом приходится менять clockwise на обратное и приходим к тому же самому результату.
3. Золотые слова, Александр Наумович! Я тоже не понимаю этого прикола! Видимо, какой-то в этом непостижимый "индусский" замысел.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
3. Золотые слова, Александр Наумович! Я тоже не понимаю этого прикола! Видимо, какой-то в этом непостижимый "индусский" замысел.
У меня под руками только Civil 3D 2016. В нём этот метод выглядит так:
Код - C# [Выбрать]
  1. // Autodesk.Civil.DatabaseServices.Network
  2. public unsafe void AddCurvePipe(ObjectId pipeFamilyId, ObjectId pipeSizeId, Curve3d curve, [MarshalAs(UnmanagedType.U1)] bool clockwise, ref ObjectId newPipeId, [MarshalAs(UnmanagedType.U1)] bool applyRules)
  3. {
  4.         CircularArc3d circularArc3d = null;
  5.         ObjectId objectId = pipeFamilyId;
  6.         AcDbObjectId oid;
  7.         cpblk(ref oid, &objectId, 8);
  8.         ObjectId objectId2 = pipeSizeId;
  9.         AcDbObjectId acDbObjectId;
  10.         cpblk(ref acDbObjectId, &objectId2, 8);
  11.         <Module>.Autodesk.Civil.Checker.CheckArgOid<class AeccDbPartFamilyItem>(oid, "A part family id of Autodesk.Civil.DatabaseServices.Styles.PartFamily is expected.");
  12.         <Module>.Autodesk.Civil.Checker.CheckArgOid<class AeccDbPartSizeFilter>(acDbObjectId, "A part size id of Autodesk.Civil.DatabaseServices.Styles.PartSize is expected.");
  13.         AeccDbTreeOid aeccDbTreeOid;
  14.         cpblk(ref aeccDbTreeOid, ref oid, 8);
  15.         AeccDbTreeOid styleRootOid;
  16.         AcDbObjectId styleOid;
  17.         string message;
  18.         try
  19.         {
  20.                 styleRootOid = aeccDbTreeOid;
  21.                 styleOid = acDbObjectId;
  22.                 message = "Part size doesn't belong to the part family.";
  23.         }
  24.         catch
  25.         {
  26.                 <Module>.___CxxCallUnwindDtor(ldftn(AeccDbTreeOid.{dtor}), (void*)(&aeccDbTreeOid));
  27.                 throw;
  28.         }
  29.         <Module>.Autodesk.Civil.Checker.CheckArgIsChildOid(styleRootOid, styleOid, message);
  30.         DbObjectReader<AeccDbPartFamilyItem,1,0> dbObjectReader<AeccDbPartFamilyItem,1,0>;
  31.         <Module>.DbObjectPointer<AeccDbPartFamilyItem,0,1,0>.{ctor}(ref dbObjectReader<AeccDbPartFamilyItem,1,0>, ref oid);
  32.         try
  33.         {
  34.                 try
  35.                 {
  36.                         circularArc3d = (CircularArc3d)curve;
  37.                 }
  38.                 catch (InvalidCastException)
  39.                 {
  40.                         throw new ArgumentException("The curve type should be CircularArc3d.");
  41.                 }
  42.                 AcDbObjectId ?kNull@AcDbObjectId@@2V1@B = <Module>.?kNull@AcDbObjectId@@2V1@B;
  43.                 Point3d endPoint = circularArc3d.EndPoint;
  44.                 Point3d startPoint = circularArc3d.StartPoint;
  45.                 AecRmCString aecRmCString;
  46.                 AecRmCString* ptr = <Module>.AeccDbPartFamilyItem.getGuid((0 == *(ref dbObjectReader<AeccDbPartFamilyItem,1,0> + 24)) ? (*(ref dbObjectReader<AeccDbPartFamilyItem,1,0> + 16)) : 0L, (AecRmCString*)(&aecRmCString));
  47.                 AeccDbGeo* impObj;
  48.                 AcGePoint3d* ptr2;
  49.                 AcGePoint3d* ptr3;
  50.                 double radius;
  51.                 try
  52.                 {
  53.                         impObj = base.GetImpObj();
  54.                         AcGePoint3d acGePoint3d;
  55.                         ptr2 = <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d, startPoint);
  56.                         AcGePoint3d acGePoint3d2;
  57.                         ptr3 = <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d2, endPoint);
  58.                         radius = circularArc3d.Radius;
  59.                 }
  60.                 catch
  61.                 {
  62.                         <Module>.___CxxCallUnwindDtor(ldftn(AecRmCString.{dtor}), (void*)(&aecRmCString));
  63.                         throw;
  64.                 }
  65.                 AeccNetworkErrorCode es;
  66.                 <Module>.AeccDbNetwork.addCurvePipe(impObj, &es, (AecRmCString*)ptr, ref acDbObjectId, ptr2, ptr3, radius, clockwise, ref ?kNull@AcDbObjectId@@2V1@B, applyRules);
  67.                 <Module>.Autodesk.Civil.Checker.Check(es, "Fail to add curve pipe.");
  68.                 ObjectId objectId3 = <Module>.ToObjectId(ref ?kNull@AcDbObjectId@@2V1@B);
  69.                 newPipeId = objectId3;
  70.         }
  71.         catch
  72.         {
  73.                 <Module>.___CxxCallUnwindDtor(ldftn(DbObjectReader<AeccDbPartFamilyItem,1,0>.{dtor}), (void*)(&dbObjectReader<AeccDbPartFamilyItem,1,0>));
  74.                 throw;
  75.         }
  76.         <Module>.DbObjectPointer<AeccDbPartFamilyItem,0,1,0>.{dtor}(ref dbObjectReader<AeccDbPartFamilyItem,1,0>);
  77.         try
  78.         {
  79.         }
  80.         catch
  81.         {
  82.                 <Module>.___CxxCallUnwindDtor(ldftn(DbObjectReader<AeccDbPartFamilyItem,1,0>.{dtor}), (void*)(&dbObjectReader<AeccDbPartFamilyItem,1,0>));
  83.                 throw;
  84.         }
  85. }
  86.  
В нативном методе передаются начальная и конечная точка, радиус и clockwise - тут всё понятно. И вроде вычисляются они правильно.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
В нативном методе передаются начальная и конечная точка, радиус и clockwise - тут всё понятно. И вроде вычисляются они правильно.
Ох, видимо я не туда смотрел... Пора домой.
В общем, причина такого поведения метода понятна - они не учли, что по таким параметрам можно построить две дуги.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
В общем, причина такого поведения метода понятна - они не учли, что по таким параметрам можно построить две дуги.
Точно. Или решили, что трубы с углом > 180 никто делать не станет. :)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Или решили, что трубы с углом > 180 никто делать не станет.
Если бы такую трубу в принципе нельзя было бы построить - тогда да, такое решение оправдано. Но когда с помощью встроенных команд да и просто ручками можно так выгнуть трубу, а в API нет способа этого сделать - это уже недоработка, ятд.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Или решили, что трубы с углом > 180 никто делать не станет.
Если бы такую трубу в принципе нельзя было бы построить - тогда да, такое решение оправдано. Но когда с помощью встроенных команд да и просто ручками можно так выгнуть трубу, а в API нет способа этого сделать - это уже недоработка, ятд.
Я видимо не тот смайлик поставил. ;) Конечно это баг. И судя по тому что в интернете ничего по этому поводу не пишут - он для Autodesk неизвестен. Так что рекомендую всё-таки написать в ADN DevHelp.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Я видимо не тот смайлик поставил.
Да не, всё в порядке. Я понял что это шутка. Просто я сильно расстроен этим багом. Получается, что довольно много функционала моего приложения он затрагивает. И теперь надо либо как-то его обходить, либо добавлять "оповещалку": извините, но эта труба слишком изогнутая, с помощью API мы её построить не можем, предлагаем два варианта на выбор: 1. разбиваем на две, 2. строим как получится и потом вы сами ручками подправите. Но с точки зрения пользователей - это какая дичь будет.
В ADN отправлю, но позже. После того как разберусь что делать с моим приложением.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
В ADN отправлю, но позже.
У тебя всё готово - и код и видео. Отправляй сразу. Может всё-таки есть какой-то простой workaround, о котором мы не догадываемся. Подозреваю, что в самом Civil 3D используется если не тот-же native код, то какой-нибудь его перегруженный метод. К сожалению найти его я не смог. Причина в том, что практически для всех native-методов в Autodesk Architecture, Autodesk MEP и Autodesk Civil 3d (в отличие от AutoCAD) не видны их имена и параметры (сигнатуры):



Для AutoCAD это выглядит так:





Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
У тебя всё готово - и код и видео. Отправляй сразу.
Уговорили, сделал. Case ID: 15976503
К сожалению найти его я не смог.
А Вы какие файлы исследовали? У Civil функции сильно раскиданы по файлам.