Некорректное построение 3D тела. В чём причина?

Автор Тема: Некорректное построение 3D тела. В чём причина?  (Прочитано 4381 раз)

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Здравствуйте!
Столкнулся с непонятным поведением автокада при построении трёхмерного тела с помощью вращения. Почему-то, при определённых исходных значениях построение выполняется некорректно. Я вырезал из своего приложения кусок кода, оформил в виде двух отдельных команд, которые выполняются с одинаковой процедурой, но разными исходными данными. Эти данные я взял из значений, полученных при отладке моего приложения на реальном чертеже. В первой команде тело рисуется как надо, а во второй - очень странным образом. Я проверял и перепроверял входные данные - никакого криминала не заметил. Что может быть не так?
Тестовый код:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using Autodesk.AutoCAD.Geometry;
  3. using Autodesk.AutoCAD.Runtime;
  4. using System;
  5. using System.Diagnostics;
  6.  
  7. #pragma warning disable CS0618
  8.  
  9. namespace AcadTest2020
  10. {
  11.     public class TestSolid
  12.     {
  13.         private readonly double diameter = 0.1;
  14.         private readonly Vector3d vertDir = new Vector3d(0, 0, 1);
  15.  
  16.         [CommandMethod("RunSolidTest1")]
  17.         public void RunTest1()
  18.         {
  19.             Vector3d insideDirection = new Vector3d
  20.                 (0.968490925903539,
  21.                 0.249048843487588,
  22.                 0);
  23.  
  24.             Point3d circleCenter = new Point3d
  25.                 (2232732.26355686,
  26.                 163570.797977758,
  27.                 134.681813969545);
  28.  
  29.             CreateSolid(insideDirection, circleCenter);
  30.         }
  31.  
  32.         [CommandMethod("RunSolidTest2")]        
  33.         public void RunTest2()
  34.         {
  35.             Vector3d insideDirection = new Vector3d
  36.                 (-0.968490925903539,
  37.                 -0.249048843487588,
  38.                 0);
  39.  
  40.             Point3d circleCenter = new Point3d
  41.                 (2232741.17367338,
  42.                 163573.089227118,
  43.                 134.820753838578);
  44.  
  45.             CreateSolid(insideDirection, circleCenter);
  46.         }
  47.  
  48.  
  49.         void CreateSolid
  50.             (Vector3d insideDirection,            
  51.             Point3d circleCenter)
  52.         {
  53.             Vector3d axisDir = vertDir.CrossProduct(insideDirection);
  54.             Point3d axisPoint = circleCenter - vertDir.MultiplyBy(diameter / 2.0);
  55.             using (Circle circle = new Circle(circleCenter, insideDirection, diameter / 2.0))
  56.             using (Solid3d rev = RevolveContour(circle, axisPoint, axisDir, Math.PI / 2.0))
  57.             {
  58.                 if (rev != null)
  59.                 {
  60.                     ObjectId spaceId
  61.                         = SymbolUtilityServices.GetBlockModelSpaceId
  62.                         (HostApplicationServices.WorkingDatabase);
  63.                     using (BlockTableRecord space
  64.                         = spaceId.Open(OpenMode.ForWrite) as BlockTableRecord)
  65.                     {
  66.                         space.AppendEntity(rev);
  67.                     }
  68.                 }
  69.             }
  70.         }
  71.  
  72.         /// <summary>
  73.         ///
  74.         /// </summary>
  75.         /// <param name="contour"></param>
  76.         /// <param name="axisPoint"></param>
  77.         /// <param name="axisDir"></param>
  78.         /// <param name="angleOfRev"></param>
  79.         /// <returns></returns>
  80.         static Solid3d RevolveContour
  81.                 (Entity contour, Point3d axisPoint,
  82.                 Vector3d axisDir, double angleOfRev = 2 * Math.PI)
  83.         {
  84.             RevolveOptionsBuilder
  85.                 builder = new RevolveOptionsBuilder();
  86.             builder.CloseToAxis = false;
  87.             builder.DraftAngle = 0;
  88.             builder.TwistAngle = 0;
  89.  
  90.             // Вращаем полусечение
  91.             Solid3d solid = new Solid3d();
  92.             try
  93.             {
  94.                 solid.CreateRevolvedSolid
  95.                     (contour,
  96.                     axisPoint,
  97.                     axisDir,
  98.                     angleOfRev,
  99.                     0.0,
  100.                     builder.ToRevolveOptions());
  101.                 return solid;
  102.             }
  103.             catch (System.Exception ex)
  104.             {
  105.                 solid.Dispose();
  106.                 Debug.WriteLine(ex.Message);
  107.                 Debug.WriteLine(ex.StackTrace);
  108.                 return null;
  109.             }
  110.         }
  111.     }
  112. }
  113.  
Видео проверки:

Оффлайн Александр Пекшев aka Modis

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
В первом случае значения в списке возрастают, а во втором убывают. Если список перевернуть, поведение изменится?

Оффлайн Александр Пекшев aka Modis

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
CreateRevolvedSolid
Извиняюсь за бредятину - смотрел с телефона и не обратил внимания, что это вектор, а не список значений  :D

Отмечено как Решение Дмитрий Загорулькин 18-06-2019, 23:01:30

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

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

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Решение вижу вот в таком изменении кода:

Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using Autodesk.AutoCAD.Geometry;
  3. using Autodesk.AutoCAD.Runtime;
  4. using System;
  5. using System.Diagnostics;
  6.  
  7. #pragma warning disable CS0618
  8.  
  9. namespace AcadTest2020
  10. {
  11.   public class TestSolid
  12.   {
  13.     private readonly double diameter = 0.1;
  14.     private readonly Vector3d vertDir = new Vector3d(0, 0, 1);
  15.  
  16.     [CommandMethod("RunSolidTest1")]
  17.     public void RunTest1()
  18.     {
  19.       Vector3d insideDirection = new Vector3d
  20.           (0.968490925903539,
  21.           0.249048843487588,
  22.           0);
  23.  
  24.       Point3d circleCenter = new Point3d
  25.           (2232732.26355686,
  26.           163570.797977758,
  27.           134.681813969545);
  28.  
  29.       CreateSolid(insideDirection, Point3d.Origin, Matrix3d.Displacement(new Vector3d(circleCenter.ToArray())));
  30.     }
  31.  
  32.     [CommandMethod("RunSolidTest2")]
  33.     public void RunTest2()
  34.     {
  35.       Vector3d insideDirection = new Vector3d
  36.           (-0.968490925903539,
  37.           -0.249048843487588,
  38.           0);
  39.  
  40.       Point3d circleCenter = new Point3d
  41.           (2232741.17367338,
  42.           163573.089227118,
  43.           134.820753838578);
  44.  
  45.       CreateSolid(insideDirection, Point3d.Origin, Matrix3d.Displacement(new Vector3d(circleCenter.ToArray())));
  46.     }
  47.  
  48.     void CreateSolid
  49.         (Vector3d insideDirection,
  50.         Point3d circleCenter, Matrix3d mat)
  51.     {
  52.       Vector3d axisDir = vertDir.CrossProduct(insideDirection);
  53.       Point3d axisPoint = circleCenter - vertDir.MultiplyBy(diameter / 2.0);
  54.       using (Circle circle = new Circle(circleCenter, insideDirection, diameter / 2.0))
  55.       using (Solid3d rev = RevolveContour(circle, axisPoint, axisDir, Math.PI / 2.0))
  56.       {
  57.         if (rev != null)
  58.         {
  59.           ObjectId spaceId
  60.               = SymbolUtilityServices.GetBlockModelSpaceId
  61.               (HostApplicationServices.WorkingDatabase);
  62.           using (BlockTableRecord space
  63.               = spaceId.Open(OpenMode.ForWrite) as BlockTableRecord)
  64.           {
  65.             rev.TransformBy(mat);
  66.             space.AppendEntity(rev);
  67.           }
  68.         }
  69.       }
  70.     }
  71.  
  72.     /// <summary>
  73.     ///
  74.     /// </summary>
  75.     /// <param name="contour"></param>
  76.     /// <param name="axisPoint"></param>
  77.     /// <param name="axisDir"></param>
  78.     /// <param name="angleOfRev"></param>
  79.     /// <returns></returns>
  80.     static Solid3d RevolveContour
  81.             (Entity contour, Point3d axisPoint,
  82.             Vector3d axisDir, double angleOfRev = 2 * Math.PI)
  83.     {
  84.       RevolveOptionsBuilder
  85.           builder = new RevolveOptionsBuilder();
  86.       builder.CloseToAxis = false;
  87.       builder.DraftAngle = 0;
  88.       builder.TwistAngle = 0;
  89.  
  90.       // Вращаем полусечение
  91.       Solid3d solid = new Solid3d();
  92.       try
  93.       {
  94.         solid.CreateRevolvedSolid
  95.             (contour,
  96.             axisPoint,
  97.             axisDir,
  98.             angleOfRev,
  99.             0.0,
  100.             builder.ToRevolveOptions());
  101.         return solid;
  102.       }
  103.       catch (System.Exception ex)
  104.       {
  105.         solid.Dispose();
  106.         Debug.WriteLine(ex.Message);
  107.         Debug.WriteLine(ex.StackTrace);
  108.         return null;
  109.       }
  110.     }
  111.   }
  112. }
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Увы, но ты выскочил за пределы точности. Если координаты уменьшить в 1000 раз, то всё строится нормально.
Вот же блин!! Я сегодня полдня убил на поиски!! Всю голову сломал, каждое значение проверил и перепроверил!  :-\ Эх :D
Спасибо Вам большое! Сам бы я ещё долго искал бы причину!

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Увы, но ты выскочил за пределы точности. Если координаты уменьшить в 1000 раз, то всё строится нормально.
Вот же блин!! Я сегодня полдня убил на поиски!! Всю голову сломал, каждое значение проверил и перепроверил!  :-\ Эх :D
Спасибо Вам большое! Сам бы я ещё долго искал бы причину!

Вообще я ориентируюсь на то, что в операциях с Region и Solid3d относительные значения не должны превышать 1e+6. А у тебя мало что координаты 1e+7, так еще и радиус 1e-1, т.е. в результате имеем 1e+8. Удивительно то, что у тебя первый Solid3d строится нормально, а не то, что второй ненормален.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Это пользователи почему-то решили создавать объекты в космосе...

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Говорят, что объект в Москве. Не знаю по каким координатам это Москва :)

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Сегодня снова отловил фокусы с большими координатами. На этот раз из-за потери точности в десятом значащем знаке (да-да, координаты объектов - десятизначные). При таких координатах, точка пересечения объектов методом Entity.IntersectWith вычисляется неточно. Для примера, такой вызов метода:
Код - C# [Выбрать]
  1. curve1.IntersectWith(curve2, Intersect.OnBothOperands, AcParams.PlaneXY, col, IntPtr.Zero, IntPtr.Zero);
В коллекцию col попадает одна точка пересечения:
X = 2194767480.69967, Y = 352987762.951604)Проверка показала, что эта точка действительно есть на объекте curve1. Но она не находится на втором объекте curve2! И это рушит дальнейшие вычисления.
Методом Curve.GetClosestPointTo нашел ближайшую к найденной на объекте curve2:
X = 2194767480.15021, Y = 352987763.018291Итого, разница по X = 0.55, по Y = 0.07. В относительных величинах - мелочь, а вот в абсолютных - уже довольно большая разница (т.к. единицы чертежа - метры).

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Не ошибся, проверял  :)
До Луны раз в 7 поближе будет, так что подальше.
Я не знаю, что заставило пользователя создавать объекты в этих координатах. Может быть, действительно, изначально чертёж был в мм.