Некорректное определение конечных точек объекта EllipticalArc3d

Автор Тема: Некорректное определение конечных точек объекта EllipticalArc3d  (Прочитано 11537 раз)

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
Похоже, я столкнулся с неприятным багом. Есть, конечно, шанс, что это я что-то не так делаю. Если вдруг найдёте ошибку у меня в логике или коде - ткните носом, буду благодарен!
В общем, мне понадобилось вычислить геометрию эллиптической дуги. Для этого в пространстве Autodesk.AutoCAD.Geometry есть подходящий вспомогательный объект EllipticalArc3d. Задал необходимые параметры, создал этот объект, но при попытке использования этой дуги, постоянно выдавались неверные результаты. В итоге, я провёл такой эксперимент: написал код, который требует выбрать на чертеже все необходимые параметры, по ним создаёт EllipticalArc3d, по данным этой дуги создаёт Ellipse, выводит указанные точки и конечные точки полученного EllipticalArc3d на чертёж.
В результате, я получил такую картину:

То есть, конечные точки эллиптической дуги не соответствуют заданным параметрам!
Видео опыта:

Код, которым проверял:
Код - 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 System;
  7.  
  8. namespace AcadTest
  9. {
  10.     public class EllipseArcTest
  11.     {
  12.         public class EllipseTest
  13.         {
  14.             [CommandMethod("EllipticalArc3dTest")]
  15.             public void RunEllipticalArc3dTest()
  16.             {
  17.                 Document adoc = Application.DocumentManager.MdiActiveDocument;
  18.                 Editor ed = adoc.Editor;
  19.                 Database db = adoc.Database;
  20.  
  21.                 PromptPointResult centerPtRes = ed.GetPoint("\nCenter: ");
  22.                 if (centerPtRes.Status != PromptStatus.OK) return;
  23.  
  24.                 PromptPointResult leftPtRes = ed.GetPoint("\nLeft point on Ellipse: ");
  25.                 if (leftPtRes.Status != PromptStatus.OK) return;
  26.  
  27.                 PromptPointResult rightPtRes = ed.GetPoint("\nRight point on Ellipse: ");
  28.                 if (rightPtRes.Status != PromptStatus.OK) return;
  29.              
  30.                 PromptDoubleResult horRadRes = ed.GetDistance("\nHorizontal radius length: ");
  31.                 if (horRadRes.Status != PromptStatus.OK) return;
  32.  
  33.                 PromptDoubleResult vertRadRes = ed.GetDistance("\nVertical radius length: ");
  34.                 if (vertRadRes.Status != PromptStatus.OK) return;
  35.  
  36.                 EllipticalArc3d elArc3d = GetEllipseArc
  37.                     (leftPtRes.Value, rightPtRes.Value,
  38.                     centerPtRes.Value, horRadRes.Value,
  39.                     vertRadRes.Value);
  40.  
  41.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  42.                 {
  43.                     Ellipse ellipse = new Ellipse(elArc3d.Center, Vector3d.ZAxis,
  44.                         elArc3d.MajorAxis * elArc3d.MajorRadius,
  45.                         elArc3d.MinorRadius / elArc3d.MajorRadius,
  46.                         elArc3d.StartAngle, elArc3d.EndAngle);
  47.  
  48.                     ellipse.ColorIndex = 1;
  49.                     ellipse.LineWeight = LineWeight.LineWeight050;
  50.  
  51.                     BlockTableRecord space = tr.GetObject
  52.                         (db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
  53.  
  54.                     space.AppendEntity(ellipse);
  55.                     tr.AddNewlyCreatedDBObject(ellipse, true);
  56.  
  57.                     double rad = ellipse.MinorRadius / 10;
  58.  
  59.                     CreateCircle(tr, space, leftPtRes.Value, rad, 1);
  60.                     CreateCircle(tr, space, rightPtRes.Value, rad, 1);
  61.                     CreateCircle(tr, space, elArc3d.StartPoint, rad, 5);
  62.                     CreateCircle(tr, space, elArc3d.EndPoint, rad, 5);
  63.  
  64.                     tr.Commit();
  65.                 }
  66.  
  67.                 ed.WriteMessage
  68.                     ("\nSelected points. Start: {0}, end: {1}",
  69.                     leftPtRes.Value, rightPtRes.Value);
  70.                 ed.WriteMessage
  71.                     ("\nPoints from arc. Start: {0}, end: {1}",
  72.                     elArc3d.StartPoint, elArc3d.EndPoint);
  73.             }
  74.  
  75.             static void CreateCircle(Transaction tr,
  76.                 BlockTableRecord space, Point3d center,
  77.                 double radius, byte color)
  78.             {
  79.                 Circle circle = new Circle(center, Vector3d.ZAxis, radius);
  80.                 circle.ColorIndex = color;
  81.                 circle.LineWeight = LineWeight.LineWeight050;
  82.                 space.AppendEntity(circle);
  83.                 tr.AddNewlyCreatedDBObject(circle, true);
  84.             }
  85.  
  86.             static EllipticalArc3d GetEllipseArc
  87.                 (Point3d startPt, Point3d endPt,
  88.                 Point3d centerPt, double horRadius,
  89.                 double vertRadius)
  90.             {
  91.                 double
  92.                         firstAngle = new Vector2d
  93.                         (startPt.X - centerPt.X,
  94.                         startPt.Y - centerPt.Y).Angle,
  95.                         secondAngle = new Vector2d
  96.                         (endPt.X - centerPt.X,
  97.                         endPt.Y - centerPt.Y).Angle;
  98.  
  99.                 if (centerPt.Y < startPt.Y)
  100.                 {
  101.                     double tmp = firstAngle;
  102.                     firstAngle = secondAngle;
  103.                     secondAngle = tmp;
  104.                 }
  105.  
  106.                 Vector3d major, minor;
  107.                 double majorRad, minorRad;
  108.                 if (horRadius > vertRadius)
  109.                 {
  110.                     major = Vector3d.XAxis * horRadius;
  111.                     minor = Vector3d.YAxis * vertRadius;
  112.                     majorRad = horRadius;
  113.                     minorRad = vertRadius;
  114.                 }
  115.                 else
  116.                 {
  117.                     major = Vector3d.YAxis * vertRadius;
  118.                     minor = Vector3d.XAxis * horRadius;
  119.                     majorRad = vertRadius;
  120.                     minorRad = horRadius;
  121.                     firstAngle -= Math.PI / 2;
  122.                     secondAngle -= Math.PI / 2;
  123.                 }
  124.  
  125.                 return new EllipticalArc3d(centerPt,
  126.                     major, minor, majorRad, minorRad, firstAngle, secondAngle);
  127.             }          
  128.         }
  129.     }
  130. }
  131.  

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Похоже ты неправильно вычисляешь конечный и начальный углы. Их следует вычислять через refVec(). Посмотри эту старейшую тему на форуме Autodesk:
https://forums.autodesk.com/t5/objectarx/acgecircarc3d-start-angle-and-end-angle/td-p/328915
Ну и эту поновее:
https://forums.autodesk.com/t5/objectarx/ellipticalarc-in-subworlddraw/td-p/6795181
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

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

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Ну и еще чтобы особенно не заморачиваться если у тебя получается по параметрам создать правильный Ellipse, то EllipticalArc3d можно из него получить при помощи метода GetGeCurve.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
Похоже ты неправильно вычисляешь конечный и начальный углы. Их следует вычислять через refVec(). Посмотри эту старейшую тему на форуме Autodesk:
https://forums.autodesk.com/t5/objectarx/acgecircarc3d-start-angle-and-end-angle/td-p/328915
Ну и эту поновее:
https://forums.autodesk.com/t5/objectarx/ellipticalarc-in-subworlddraw/td-p/6795181
Спасибо! Завтра гляну. Смущает, что Ellipse-то правильный получается по этим параметрам. Хотя, не сильно удивлюсь, если для них по разному вычисляются углы :)
Ну и еще чтобы особенно не заморачиваться если у тебя получается по параметрам создать правильный Ellipse, то EllipticalArc3d можно из него получить при помощи метода GetGeCurve.
Да в том-то и дело, что я не хотел эллипс-примитив создавать. А вот, кстати, интересно будет посмотреть на параметры полученной таким образом дуги! Спасибо, тоже попробую!

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

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
Похоже ты неправильно вычисляешь конечный и начальный углы. Их следует вычислять через refVec(). Посмотри эту старейшую тему на форуме Autodesk:
https://forums.autodesk.com/t5/objectarx/acgecircarc3d-start-angle-and-end-angle/td-p/328915
Ну и эту поновее:
https://forums.autodesk.com/t5/objectarx/ellipticalarc-in-subworlddraw/td-p/6795181
Посмотрел. Может я что-то не так понимаю, но, вроде как, там рассматривается произвольное положение кривых в пространстве. Там надо и нормали вычислять, и референс-векторы. Я же пока что просто на плоскости XY пытаюсь получить адекватный результат. В данном случае всё просто: нормаль - ось Z МСК, референс-вектор (от которого отсчитывается угол) либо ось X, либо ось Y. По крайней мере, у Ellipse всегда отсчёт угла идёт от вектора большой полуоси. У этой же EllipticalArc3d я никак не могу понять, какие углы она использует. Явно эти углы должны задаваться в плоскости XY, значит, и вектор отсчёта должен находиться в ней.
Я получил уже из готового объекта Ellipse через метод GetGeCurve его аналог EllipticalArc3d и сравнил его характеристики с исходным объектом EllipticalArc3d и с Ellipse.
Исходный EllipticalArc3d (углы совпадают с Ellipse, конечные точки - нет):
+ Center {(216.954781481876,2250.75091595517,0)} Autodesk.AutoCAD.Geometry.Point3d
EndAngle 2.7208968453904023 double
+ EndPoint {(174.475239341363,2257.73437348297,0)} Autodesk.AutoCAD.Geometry.Point3d
+ MajorAxis {(1,0,0)} Autodesk.AutoCAD.Geometry.Vector3d
MajorRadius 46.5373681299061 double
+ MinorAxis {(0,1,0)} Autodesk.AutoCAD.Geometry.Vector3d
MinorRadius 17.099733701097648 double
+ Normal {(0,0,1)} Autodesk.AutoCAD.Geometry.Vector3d
StartAngle 0.62407641452031326 double
+ StartPoint {(254.720002818984,2260.74311165246,0)} Autodesk.AutoCAD.Geometry.Point3d
Полученный по нему Ellipse:
+ Center {(216.954781481876,2250.75091595517,0)} Autodesk.AutoCAD.Geometry.Point3d
EndAngle 2.7208968453904019 double
IsNull false bool
+ MajorAxis {(46.5373681299061,0,0)} Autodesk.AutoCAD.Geometry.Vector3d
MajorRadius 46.5373681299061 double
+ MinorAxis {(0,17.0997337010976,0)} Autodesk.AutoCAD.Geometry.Vector3d
MinorRadius 17.099733701097648 double
+ Normal {(0,0,1)} Autodesk.AutoCAD.Geometry.Vector3d
RadiusRatio 0.3674409273288689 double
StartAngle 0.62407641452031326 double
Полученный из эллипса EllipticalArc3d (конечные точки совпадают с Ellipse, углы - нет):
+ Center {(216.954781481876,2250.75091595517,0)} Autodesk.AutoCAD.Geometry.Point3d
EndAngle 2.2583711173560803 double
+ EndPoint {(187.419148368747,2263.96537609417,0)} Autodesk.AutoCAD.Geometry.Point3d
+ MajorAxis {(1,0,0)} Autodesk.AutoCAD.Geometry.Vector3d
MajorRadius 46.5373681299061 double
+ MinorAxis {(0,1,0)} Autodesk.AutoCAD.Geometry.Vector3d
MinorRadius 17.099733701097648 double
+ Normal {(0,0,1)} Autodesk.AutoCAD.Geometry.Vector3d
StartAngle 1.0989608044276178 double
+ StartPoint {(238.10703982062,2265.98225586905,0)} Autodesk.AutoCAD.Geometry.Point3d
Вывел на чертёж конечные точки этого итогового EllipticalArc3d - да, они совпадают с конечными точками Ellipse. Но значения углов - просто какой-то случайный набор чисел. Отложил эти углы от значений точек - получил такую картину:

С помощью геометрических зависимостей удалось свести их к одной точке отсчёта, но это какая-то дичь получилась:

В общем, наверное, придётся воспользоваться Вашим советом:
Ну и еще чтобы особенно не заморачиваться если у тебя получается по параметрам создать правильный Ellipse, то EllipticalArc3d можно из него получить при помощи метода GetGeCurve.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Из спортивного интереса можешь еще проверить StartParam и EndParam. Они как-то взаимосвязаны с StartAngle и EndEngle.
Из спортивного интереса глянь этот код: http://adn-cis.org/kak-vosstanovit-poteryannyie-graniczyi-shtrixovki.html
Насколько я помню эллиптические дуги у меня получались правильные.

В общем, наверное, придётся воспользоваться Вашим советом:
Цитата: Александр Ривилис от 01-02-2018, 23:43:56

    Ну и еще чтобы особенно не заморачиваться если у тебя получается по параметрам создать правильный Ellipse, то EllipticalArc3d можно из него получить при помощи метода GetGeCurve.
Плохого не посоветую! :-)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
Из спортивного интереса глянь этот код: http://adn-cis.org/kak-vosstanovit-poteryannyie-graniczyi-shtrixovki.html
Насколько я помню эллиптические дуги у меня получались правильные.
Взял из Вашего кода:
Код - C# [Выбрать]
  1. //-------------------------------------------------------------------------------------------
  2. // Ошибка: Нельзя присвоить StartParam и EndParam примитиву  эллипс:
  3. // Ellipse ent = new Ellipse(new Point3d(plane, e2d.Center), plane.Normal,
  4. //      new Vector3d(plane,e2d.MajorAxis) * e2d.MajorRadius,
  5. //      e2d.MinorRadius / e2d.MajorRadius, e2d.StartAngle, e2d.EndAngle);
  6. // ent.StartParam = e2d.StartAngle;
  7. // ent.EndParam = e2d.EndAngle;
  8. // error CS0200: Property or indexer 'Autodesk.AutoCAD.DatabaseServices.Curve.StartParam' cannot be assigned to -- it is read only
  9. // error CS0200: Property or indexer 'Autodesk.AutoCAD.DatabaseServices.Curve.EndParam' cannot be assigned to -- it is read only
  10. //---------------------------------------------------------------------------------------------
  11. // Обходим эту ошибку используя «отражение» (Reflection)
  12. //
  13. using (Ellipse ent = new Ellipse(new Point3d(plane, ellipse2d.Center), plane.Normal,
  14.      new Vector3d(plane, ellipse2d.MajorAxis) * ellipse2d.MajorRadius,
  15.      ellipse2d.MinorRadius / ellipse2d.MajorRadius, ellipse2d.StartAngle, ellipse2d.EndAngle))
  16. {
  17.     ent.GetType().InvokeMember("StartParam", BindingFlags.SetProperty, null,
  18.       ent, new object[] { ellipse2d.StartAngle });
  19.     ent.GetType().InvokeMember("EndParam", BindingFlags.SetProperty, null,
  20.       ent, new object[] { ellipse2d.EndAngle });
  21.     btr.AppendEntity(ent);
  22.     tr.AddNewlyCreatedDBObject(ent, true);
  23. }
  24.  
Сперва Вы создали эллипс и задали ему сразу в конструкторе начальный и конечный угол (ellipse2d.StartAngle, ellipse2d.EndAngle). Насколько я понимаю, у эллипса параметр = угол. И, скорее всего, последующее задание параметров здесь избыточно.
К тому же, здесь объект EllipticallArc2d, он отличается от 3d довольно сильно. У него, например, есть свойство IsClockWise. У Ellipse такого нет, поэтому, наверное, стоит дополнительно проверять его и, при необходимости, менять местами начальные и конечные свойства.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
И, скорее всего, последующее задание параметров здесь избыточно.
В том то и дело, что оно было необходимо. Без него эллиптические дуги были совсем неправильные.
К тому же, здесь объект EllipticallArc2d, он отличается от 3d довольно сильно.
Тут возможно ты прав. Я пробовал в твоём коде сделать тоже самое (т.е. задавать параметры по углам), но результат не улучшился.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
В том то и дело, что оно было необходимо. Без него эллиптические дуги были совсем неправильные.
Тогда я чего-то сильно недопонимаю... Параметр != угол у этих EllipticalArc2d(3d)? А что тогда у них параметр? И как тогда вычисляется их угол? Какой-то пока непостижимый объект...

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Тогда я чего-то сильно недопонимаю... Параметр != угол у этих EllipticalArc2d(3d)?
Речь шла про Ellipse. Это ему после создания по углам EllipticalArc2d следует установить начальный и конечный параметр в начальный и конечный углы EllipticalArc2d.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
А, да, действительно. Но это другая проблема, наверное.

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
Ну и еще чтобы особенно не заморачиваться если у тебя получается по параметрам создать правильный Ellipse, то EllipticalArc3d можно из него получить при помощи метода GetGeCurve.
В общем, пока остановлюсь на этом варианте. Работает как надо, результат есть, тормозов и глюков нет. Появится свободное время и желание - буду разбираться с этими EllipticalArc, а пока не до них. И так уже из-за них кучу времени на месте топчусь :(
Ещё раз спасибо, Александр Наумович, за эту идею!

Отмечено как Решение Дмитрий Загорулькин 17-04-2018, 01:12:53

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Код - C# [Выбрать]
  1.     EllipticalArc3d ea = (EllipticalArc3d)curve.NativeCurve;
  2.     double ratio = ea.MinorRadius / ea.MajorRadius;
  3.     double startAngle = Math.Atan2(Math.Sin(ea.StartAngle) * ratio, Math.Cos(ea.StartAngle));
  4.     double endAngle = Math.Atan2(Math.Sin(ea.EndAngle) * ratio, Math.Cos(ea.EndAngle));
  5.     Ellipse el = new Ellipse(ea.Center, ea.Normal, ea.MajorAxis * ea.MajorRadius, ratio, startAngle, endAngle));
Это подсказка от Gilles Chanteau
Обсуждалось здесь: http://www.theswamp.org/index.php?topic=54014.0
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение