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

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

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 функции сильно раскиданы по файлам.

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Заглянул в COM. Там то же самое:
Код - C# [Выбрать]
  1. namespace Autodesk.AECC.Interop.Pipe
  2. {
  3.     [DefaultMember("Item")]
  4.     [Guid("E936001C-0084-4186-8DF9-3D5372B7DC57")]
  5.     [TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)]
  6.     public interface IAeccPipes : IEnumerable
  7.     {
  8.         [DispId(1700)]
  9.         AeccPipe Add(string bstrPartFamilyGuid, AeccPartSizeFilter piPartSizeFilter, object varStartPoint, object varEndPoint);
  10.         [DispId(1703)]
  11.         AeccPipe AddCurvedPipe(string bstrPartFamilyGuid, AeccPartSizeFilter piPartSizeFilter, object varStartPoint, object varEndPoint, double dCurveRadius, bool bClockwise);
  12.         [DispId(1701)]
  13.         void Remove(object varIndexOrName);
  14.         [DispId(0)]
  15.         AeccPipe Item(object Index);
  16.         [DispId(-4)]
  17.         [TypeLibFunc(TypeLibFuncFlags.FRestricted | TypeLibFuncFlags.FHidden)]
  18.         IEnumerator GetEnumerator();
  19.  
  20.         [DispId(1702)]
  21.         int Count { get; }
  22.     }
  23. }

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
А Вы какие файлы исследовали? У Civil функции сильно раскиданы по файлам.
Все arx/dbx/dll/crx.
Кстати, интересный факт. Для PressurePipeNetwork одноимённый метод работает через три точки, что обеспечивает ему однозначность:
Код - C# [Выбрать]
  1. // Autodesk.Civil.DatabaseServices.PressurePipeNetwork
  2. public unsafe ObjectId AddCurvePipe(Point3d startPoint, Point3d secondPoint, Point3d endPoint, PressurePartSize partSize)
  3. {
  4.         string paramName = "partSize";
  5.         if (null == partSize)
  6.         {
  7.                 throw new ArgumentNullException(paramName);
  8.         }
  9.         AcGePoint3d acGePoint3d;
  10.         <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d, startPoint);
  11.         AcGePoint3d acGePoint3d2;
  12.         <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d2, secondPoint);
  13.         AcGePoint3d acGePoint3d3;
  14.         <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d3, endPoint);
  15.         AcDbObjectId ?kNull@AcDbObjectId@@2V1@B = <Module>.?kNull@AcDbObjectId@@2V1@B;
  16.         AeccPressurePart* impObj = partSize.GetImpObj();
  17.         <Module>.Autodesk.Civil.Checker.Check((Acad.ErrorStatus)<Module>.AeccDbPressurePipeNetwork.addCurvePipe(this.GetImpObj(), ref acGePoint3d, ref acGePoint3d2, ref acGePoint3d3, impObj, ref ?kNull@AcDbObjectId@@2V1@B), "Fail to add a curve pipe with the specified start point, second point and end point.");
  18.         return <Module>.ToObjectId(ref ?kNull@AcDbObjectId@@2V1@B);
  19. }
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

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

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Уговорили, сделал. Case ID: 15976503
Отлично! Будет наверно задержка в связи с Autodesk University, но очень интересно услышать результат.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

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

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
При положительных - ведёт себя точно так же как и .NET метод.

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Ееееееееееееехоу!! Кажисть, поборол! Ещё погоняю на разных вариантах, но пока, вроде как, работает как надо.  8)
Код - 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. using System;
  9. using System.Collections.Generic;
  10.  
  11. namespace C3dTest
  12. {
  13.     public class CreateCurvePipeTest
  14.     {
  15.         [CommandMethod("CreatePipeFromArc")]
  16.         public void CreatePipeFromArc()
  17.         {
  18.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  19.             Editor ed = adoc.Editor;
  20.             Database db = adoc.Database;
  21.  
  22.             PromptEntityOptions partOps
  23.                 = new PromptEntityOptions("\nSelect network part: ");
  24.             partOps.SetRejectMessage("It's not part!");
  25.             partOps.AddAllowedClass(typeof(Part), false);
  26.             PromptEntityResult partRes = ed.GetEntity(partOps);
  27.             if (partRes.Status != PromptStatus.OK) return;
  28.  
  29.             PromptEntityOptions arcOpts
  30.                 = new PromptEntityOptions("\nSelect arc: ");
  31.             arcOpts.SetRejectMessage("It's not arc!");
  32.             arcOpts.AddAllowedClass(typeof(Arc), true);
  33.             PromptEntityResult arcRes = ed.GetEntity(arcOpts);
  34.             if (arcRes.Status != PromptStatus.OK) return;
  35.  
  36.             ObjectId
  37.                 netId,
  38.                 famId = default,
  39.                 sizeId = default;
  40.  
  41.             using (Transaction tr = db.TransactionManager.StartTransaction())
  42.             {
  43.                 Part part = tr.GetObject
  44.                     (partRes.ObjectId, OpenMode.ForRead) as Part;
  45.  
  46.                 netId = part.NetworkId;
  47.  
  48.                 Network net = tr.GetObject
  49.                     (netId, OpenMode.ForRead) as Network;
  50.  
  51.                 ObjectId partsListId = net.PartsListId;
  52.  
  53.                 PartsList partsList
  54.                     = tr.GetObject(partsListId, OpenMode.ForRead) as PartsList;
  55.  
  56.                 using (ObjectIdCollection pipeFamsIds
  57.                     = partsList.GetPartFamilyIdsByDomain(DomainType.Pipe))
  58.                 {
  59.                     if (pipeFamsIds.Count > 0)
  60.                     {
  61.                         famId = pipeFamsIds[0];
  62.                     }
  63.                 }
  64.  
  65.                 if (famId.IsValid)
  66.                 {
  67.                     PartFamily partFamily
  68.                         = tr.GetObject(famId, OpenMode.ForRead) as PartFamily;
  69.                     if (partFamily.PartSizeCount > 0)
  70.                     {
  71.                         sizeId = partFamily[0];
  72.                     }
  73.                 }
  74.  
  75.                 tr.Commit();
  76.             }
  77.  
  78.             if (!famId.IsValid || !sizeId.IsValid)
  79.             {
  80.                 return;
  81.             }
  82.  
  83.             ObjectId newPipeId = default;
  84.             Point3d? endPoint = null;
  85.             List<Point3d> tmp = new List<Point3d>();          
  86.  
  87.             using (Transaction tr = db.TransactionManager.StartTransaction())
  88.             {
  89.                 Arc arc = tr.GetObject(arcRes.ObjectId, OpenMode.ForRead) as Arc;              
  90.  
  91.                 CircularArc3d arc3dForCreate;
  92.  
  93.                 bool isClockWise;
  94.  
  95.                 using (CircularArc3d arc3dForSupport = arc.GetGeCurve() as CircularArc3d)
  96.                 {
  97.                     // Если дуга больше полукруга
  98.                     if ((arc3dForSupport.EndAngle - arc3dForSupport.StartAngle) > Math.PI)
  99.                     {
  100.                         // Идея такая. Сперва создаём трубу полукругом, а затем конечную точку
  101.                         // тащим к тому месту, где она должна быть. При этом, капризное API не
  102.                         // даёт сразу перетащить эту точку. Поэтому, перетаскивание делается
  103.                         // через череду промежуточных точек. Причём, чем больше получается итоговый
  104.                         // центральный угол дуги, тем меньше нужно делать шаг между точками.
  105.  
  106.                         // Сохраняем конечную точку. Одновременно это будет флагом того, что
  107.                         // полученную изначально трубу надо будет дотягивать до этой точки
  108.                         endPoint = arc3dForSupport.EndPoint;
  109.                        
  110.                         double
  111.                             // Почему-то иногда метод отказывается строить полный полукруг.
  112.                             // Поэтому чуть-чуть его уменьшаем
  113.                             tmpTotalAngle = 0.99 * Math.PI,
  114.                             tmpEndAngle = arc3dForSupport.StartAngle + tmpTotalAngle;
  115.                        
  116.                         arc3dForCreate = new CircularArc3d
  117.                             (arc3dForSupport.Center,
  118.                             arc3dForSupport.Normal,
  119.                             arc3dForSupport.ReferenceVector,
  120.                             arc3dForSupport.Radius,
  121.                             arc3dForSupport.StartAngle,
  122.                             tmpEndAngle);
  123.  
  124.                         // Начальный угловой шаг для вычисления точек на дуге
  125.                         double
  126.                             step = Math.PI / 6.0,
  127.                             // Пороговое значение центрального угла дуги
  128.                             // для первого уменьшения шага
  129.                             stage1 = Math.PI * 1.5,
  130.                             // Пороговое значение центрального угла дуги
  131.                             // для второго уменьшения шага
  132.                             stage2 = Math.PI * 11.0 / 6.0;
  133.                        
  134.                         while ((arc3dForSupport.EndAngle - tmpEndAngle) > step)
  135.                         {
  136.                             tmpTotalAngle += step;
  137.                             tmpEndAngle += step;
  138.                             tmp.Add(arc3dForSupport.EvaluatePoint(tmpEndAngle));
  139.  
  140.                             // Чем больше итоговая дуга - тем меньше шаг
  141.                             if (tmpTotalAngle > stage2)
  142.                             {
  143.                                 step = Math.PI / 36.0;
  144.                             }
  145.                             else if (tmpTotalAngle > stage1)
  146.                             {
  147.                                 step = Math.PI / 9;
  148.                             }                            
  149.                         }                        
  150.                     }
  151.                     else
  152.                     {
  153.                         arc3dForCreate = arc3dForSupport.Clone() as CircularArc3d;
  154.                     }
  155.  
  156.                     Plane xy = new Plane(Point3d.Origin, Vector3d.ZAxis);
  157.                     Point3d mid = arc3dForSupport.EvaluatePoint
  158.                         ((arc3dForSupport.EndAngle + arc3dForSupport.StartAngle) / 2.0);
  159.                     using (CircularArc2d arc2d = new CircularArc2d
  160.                         (arc3dForSupport.StartPoint.Convert2d(xy),
  161.                         mid.Convert2d(xy),
  162.                         arc3dForSupport.EndPoint.Convert2d(xy)))
  163.                     {
  164.                         isClockWise = arc2d.IsClockWise;
  165.                     }
  166.                 }
  167.  
  168.                 Network net = tr.GetObject(netId, OpenMode.ForWrite) as Network;
  169.  
  170.                 using (arc3dForCreate)
  171.                 {
  172.                     net.AddCurvePipe
  173.                     (famId,
  174.                     sizeId,
  175.                     arc3dForCreate,
  176.                     isClockWise,
  177.                     ref newPipeId,
  178.                     false);
  179.                 }
  180.  
  181.                 tr.Commit();
  182.             }
  183.  
  184.             if (newPipeId.IsValid && endPoint.HasValue)
  185.             {
  186.                 foreach (Point3d tmpPt in tmp)
  187.                 {
  188.                     using (Transaction tr = db.TransactionManager.StartTransaction())
  189.                     {
  190.                         Pipe pipe = tr.GetObject(newPipeId, OpenMode.ForWrite) as Pipe;
  191.                         pipe.EndPoint = tmpPt;
  192.                         tr.Commit();
  193.                     }
  194.                 }
  195.  
  196.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  197.                 {
  198.                     Pipe pipe = tr.GetObject(newPipeId, OpenMode.ForWrite) as Pipe;
  199.                     pipe.EndPoint = endPoint.Value;
  200.                     tr.Commit();
  201.                 }                
  202.             }
  203.         }
  204.     }
  205. }
  206.  

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Красиво!
Да это костыль на костыле: Не даёте построить большую дугу? - Построим поменьше и дотянем! Не даёте сразу дотянуть? - Дотянем кусками! Не даёте большими кусками тянуть под конец? - Дотянем маленькими! Голь на выдумки хитра. :D
Но, в целом, вроде, костыли эти сработали. Спасибо Вам за помощь!

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

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

Отмечено как Решение Дмитрий Загорулькин 22-11-2019, 18:39:09

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Итоговый на данный момент вид метода для добавления криволинейной трубы в сеть:
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Создание дуговой трубы в сети
  3. /// </summary>
  4. /// <param name="network">Сеть, открытая для записи транзакцией</param>
  5. /// <param name="pipeFamilyId">Id семейства трубы</param>
  6. /// <param name="pipeSizeId">Id размера трубы</param>
  7. /// <param name="arc2d">Дуга-прототип</param>
  8. /// <param name="startPointZ">Координата Z начальной точки трубы</param>
  9. /// <param name="endPointZ">Координата Z конечной точки трубы</param>
  10. /// <param name="applyRules">Применить правила</param>
  11. /// <returns>Id новой трубы, при неудаче ObjectId.Null</returns>        
  12. public static ObjectId AddCurvePipe
  13.     (this Network network,
  14.     ObjectId pipeFamilyId,
  15.     ObjectId pipeSizeId,
  16.     CircularArc2d arc2d,
  17.     double startPointZ = 0.0,
  18.     double endPointZ = 0.0,
  19.     bool applyRules = false)
  20. {
  21.     ObjectId newPipeId = default;
  22.     bool needExtend = false;
  23.     List<Point2d> tmpPoints = new List<Point2d>();
  24.  
  25.     Point3d GetPoint3d(Point2d point2d, double z = 0.0)
  26.         => new Point3d(point2d.X, point2d.Y, z);
  27.  
  28.     // Вычисляем точку на дуге, чтобы она оказалась в
  29.     // промежутке до 180 градусов от начальной точки          
  30.     Point2d pointOnArc = arc2d.EvaluatePoint
  31.         ((arc2d.EndAngle - arc2d.StartAngle) * 0.3 + arc2d.StartAngle);
  32.  
  33.     CircularArc3d tmpArc3d;
  34.  
  35.     bool isClockWise = arc2d.IsClockWise;
  36.  
  37.     // Если дуга - полукруг или больше
  38.     if ((arc2d.EndAngle - arc2d.StartAngle) >= Math.PI && !arc2d.IsClosed())
  39.     {
  40.         // Идея такая. Сперва создаём трубу полукругом, а затем конечную точку
  41.         // тащим к тому месту, где она должна быть. При этом, капризное API не
  42.         // даёт сразу перетащить эту точку. Поэтому, перетаскивание делается
  43.         // через череду промежуточных точек. Причём, чем больше получается итоговый
  44.         // центральный угол дуги, тем меньше нужно делать шаг между точками.
  45.  
  46.         // Флаг того, что полученную изначально трубу
  47.         // надо будет дотягивать до конечной точки
  48.         needExtend = true;
  49.  
  50.         double
  51.             // Почему-то иногда метод отказывается строить полный полукруг.
  52.             // Поэтому чуть-чуть его уменьшаем
  53.             tmpTotalAngle = 0.99 * Math.PI,
  54.             tmpEndAngle = arc2d.StartAngle + tmpTotalAngle;
  55.  
  56.         Point2d tmpEndPoint = arc2d.EvaluatePoint(tmpEndAngle);
  57.  
  58.         tmpArc3d = new CircularArc3d
  59.             (GetPoint3d(arc2d.StartPoint),
  60.             GetPoint3d(pointOnArc),
  61.             GetPoint3d(tmpEndPoint));
  62.  
  63.         // Начальный угловой шаг для вычисления точек на дуге
  64.         double
  65.             step = Math.PI / 6.0,
  66.             // Пороговое значение центрального угла дуги
  67.             // для первого уменьшения шага
  68.             stage1 = Math.PI * 1.5,
  69.             // Пороговое значение центрального угла дуги
  70.             // для второго уменьшения шага
  71.             stage2 = Math.PI * 11.0 / 6.0;
  72.  
  73.         while ((arc2d.EndAngle - tmpEndAngle) > step)
  74.         {
  75.             tmpEndAngle += step;
  76.             tmpPoints.Add(arc2d.EvaluatePoint(tmpEndAngle));
  77.  
  78.             // Чем больше итоговая дуга - тем меньше шаг
  79.             tmpTotalAngle = tmpEndAngle - arc2d.StartAngle;
  80.  
  81.             if (tmpTotalAngle > stage2)
  82.             {
  83.                 step = Math.PI / 36.0;
  84.             }
  85.             else if (tmpTotalAngle > stage1)
  86.             {
  87.                 step = Math.PI / 9;
  88.             }
  89.         }
  90.     }
  91.     else
  92.     {
  93.         tmpArc3d = new CircularArc3d
  94.             (GetPoint3d(arc2d.StartPoint),
  95.             GetPoint3d(pointOnArc),
  96.             GetPoint3d(arc2d.EndPoint));
  97.     }
  98.  
  99.     using (tmpArc3d)
  100.     {
  101.         try
  102.         {
  103.             network.AddCurvePipe
  104.                 (pipeFamilyId,
  105.                 pipeSizeId,
  106.                 tmpArc3d,
  107.                 isClockWise,
  108.                 ref newPipeId,
  109.                 applyRules);
  110.         }
  111.         catch (System.Exception ex)
  112.         {
  113.             Debug.WriteLine(ex.Message);
  114.             Debug.WriteLine(ex.StackTrace);
  115.         }
  116.     }
  117.  
  118.     if (newPipeId.IsValid)
  119.     {
  120.         // https://adn-cis.org/forum/index.php?topic=8589.0
  121.         // Раз добавление трубы в сеть не работает если
  122.         // не запущена транзакция - воспользуемся этим
  123.         Pipe pipe = newPipeId.GetObject(OpenMode.ForWrite) as Pipe;
  124.  
  125.         if (needExtend)
  126.         {
  127.             // Задаём промежуточные точки
  128.             foreach (Point2d tmpPt in tmpPoints)
  129.             {
  130.                 pipe.EndPoint = GetPoint3d(tmpPt);
  131.             }
  132.         }
  133.  
  134.         // Задаём конечные точки с отметками
  135.         pipe.StartPoint = GetPoint3d
  136.             (arc2d.StartPoint, startPointZ);
  137.  
  138.         pipe.EndPoint = GetPoint3d
  139.             (arc2d.EndPoint, endPointZ);
  140.     }
  141.  
  142.     return newPipeId;
  143. }
  144.  
Тут полностью твоя заслуга.
Я благодарю за то что помогали в поисках! Так гораздо легче, чем когда один на один с проблемой разбираешься! Так что, не скромничайте  ;) Спасибо!

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
При этом, капризное API не
        // даёт сразу перетащить эту точку. Поэтому, перетаскивание делается
        // через череду промежуточных точек. Причём, чем больше получается итоговый
        // центральный угол дуги, тем меньше нужно делать шаг между точками.
Похоже, придётся ещё один кейс отправлять в DevHelp и ещё пару "обёрток" корректирующих делать. В моём приложении много где используется изменение начальной-конечной точки трубы (около 15 вызовов). И если встречается такая "сильноизогнутая" труба, то изменение свойства Pipe.StartPoint и/или Pipe.EndPoint не срабатывает.  :-\


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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Пришёл ответ: да, метод может строить только дугу до 180 градусов. Направили запрос команде разработчиков на улучшение этого API. Опять просят опросник заполнить... Признаться, что уже нашёлся костыль? :)

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Боюсь я их исправлений. Начнут исправлять - другое поломают :)