Сообщество программистов Autodesk в СНГ

Статьи => Тестирование статей => Тема начата: alz от 15-03-2023, 12:34:27

Название: AutoCAD .NET API: Реальные габариты MText
Отправлено: alz от 15-03-2023, 12:34:27
В связи с тем, что стандартный метод .GeometricExtents в случае с MText возвращает собственно окно в котором этот текст расположен, а сам текст в этом окне может располагаться достаточно свободно пришлось поискать варианты определения реального положения текста, а то были проблемы с определением местоположения текста относительно других объектов, когда текст вроде как внутри какого то контура, а программно определяется что снаружи.
Для решения этой проблемы можно воспользоваться такими параметрами MText как .ActualHeight и .ActualWidth, но просто так их использовать не получится, так как текст у нас все же в пространстве а так же может быть развернут относительно каких либо осей, поэтому было решено использовать немного векторной геометрии. Для начала определяем вектора оси X и оси Y плоскости текста, потом в зависимости от положения текста относительно точки вставки определяем положение минимальной точки через вектор соответствующей оси и реальную высоту/ширину текста, и собственно используя эти же параметры возвращаем и максимальную точку.

На изображении показаны границы: голубым цветом(возвращаемые .GeometricExtents ) зеленым цветом(вычисленные)
(https://i.postimg.cc/XXB4MFqg/Screenshot-78.png) (https://postimg.cc/XXB4MFqg)

(https://i.postimg.cc/v1mw0xCf/Screenshot-79.png) (https://postimg.cc/v1mw0xCf)

Текст функции
Код - C# [Выбрать]
  1.         /// <summary>
  2.         /// возвращаем реальный габарит MText
  3.         /// </summary>
  4.         /// <param name="mText"></param>
  5.         /// <returns></returns>
  6.         private Extents3d? MtextRealExtents(MText mText)
  7.         {            
  8.             if (mText != null)
  9.             {                    
  10.                 //получаем точку вставки текста
  11.                 Point3d point = mText.Location;
  12.                 //получаем вектора оси Х и Y в плоскости текста
  13.                 Plane plane = new Plane(point, mText.Normal);
  14.                 Vector3d vx = plane.Normal.GetPerpendicularVector().GetNormal();
  15.                 Vector3d vy = vx.TransformBy(Matrix3d.Rotation(Math.PI / 2, plane.Normal, point)).GetNormal();
  16.                 //получаем габариты текста
  17.                 double h = mText.ActualHeight;
  18.                 double w = mText.ActualWidth;  
  19.                 //получаем нижний левый угол текста
  20.                 switch (mText.Attachment)
  21.                 {
  22.                     case AttachmentPoint.TopLeft:
  23.                         point = point - vy*h;
  24.                         break;
  25.                     case AttachmentPoint.MiddleCenter:
  26.                         point = point - vy*h/2 - vx*w/2;
  27.                         break;
  28.                     case AttachmentPoint.TopCenter:
  29.                         point = point - vy * h - vx * w / 2;
  30.                         break;
  31.                     case AttachmentPoint.TopRight:
  32.                         point = point - vy * h - vx * w;
  33.                         break;
  34.                     case AttachmentPoint.MiddleLeft:
  35.                         point = point - vy * h / 2;
  36.                         break;
  37.                     case AttachmentPoint.MiddleRight:
  38.                         point = point - vy * h/2 - vx * w;
  39.                         break;
  40.                     case AttachmentPoint.BottomLeft:                        
  41.                         break;
  42.                     case AttachmentPoint.BottomCenter:
  43.                         point = point  - vx * w/2;
  44.                         break;
  45.                     case AttachmentPoint.BottomRight:
  46.                         point = point - vx * w;
  47.                         break;                      
  48.                 }          
  49.                 //возвращаем новые габариты
  50.                 return new Extents3d(point, point + vx*w + vy*h);
  51.             }            
  52.             return null;
  53.         }
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: alz от 15-03-2023, 15:55:48
Хмм, при более углубленных тестах оказалось что не учет поворот текста, пришлось немного переделать, теперь вроде бы все учтено. Жаль нельзя свои сообщения редактировать

Код - C# [Выбрать]
  1.        
  2.         /// <summary>
  3.         /// возвращаем реальный габарит MText
  4.         /// </summary>
  5.         /// <param name="mTextId"></param>
  6.         /// <returns></returns>
  7.         private Extents3d? MtextRealExtents(MText mText)
  8.         {
  9.             if (mText != null)
  10.             {
  11.                 Point3d point = mText.Location;
  12.                 Plane plane = new Plane(point, mText.Normal);
  13.                 Vector3d vx = plane.Normal.GetPerpendicularVector().TransformBy(Matrix3d.Rotation(mText.Rotation, plane.Normal, point)).GetNormal(); ;
  14.                 Vector3d vy = vx.TransformBy(Matrix3d.Rotation(Math.PI / 2, plane.Normal, point)).GetNormal();
  15.                 double h = mText.ActualHeight;
  16.                 double w = mText.ActualWidth;
  17.                 //получаем нижний левый угол текста
  18.                 switch (mText.Attachment)
  19.                 {
  20.                     case AttachmentPoint.TopLeft:
  21.                         point = point - vy * h;
  22.                         break;
  23.                     case AttachmentPoint.MiddleCenter:
  24.                         point = point - vy * h / 2 - vx * w / 2;
  25.                         break;
  26.                     case AttachmentPoint.TopCenter:
  27.                         point = point - vy * h - vx * w / 2;
  28.                         break;
  29.                     case AttachmentPoint.TopRight:
  30.                         point = point - vy * h - vx * w;
  31.                         break;
  32.                     case AttachmentPoint.MiddleLeft:
  33.                         point = point - vy * h / 2;
  34.                         break;
  35.                     case AttachmentPoint.MiddleRight:
  36.                         point = point - vy * h / 2 - vx * w;
  37.                         break;
  38.                     case AttachmentPoint.BottomLeft:
  39.                         break;
  40.                     case AttachmentPoint.BottomCenter:
  41.                         point = point - vx * w / 2;
  42.                         break;
  43.                     case AttachmentPoint.BottomRight:
  44.                         point = point - vx * w;
  45.                         break;
  46.                 }
  47.                 //получаем точки 4 углов в wcs
  48.                 //достаточно перспективные данные, дают 4 реальных угла текста а не прямоугольник области
  49.                 List<Point3d> points = new List<Point3d>
  50.                 {
  51.                     point,
  52.                     point + vx * w + vy * h,
  53.                     point + vx * w,
  54.                     point + vy * h,
  55.                 };
  56.                 //получаем все координаты точек
  57.                 List<double> x = new List<double>();
  58.                 List<double> y = new List<double>();
  59.                 foreach (Point3d p in points)
  60.                 {
  61.                     x.Add(p.X);
  62.                     y.Add(p.Y);
  63.                 }
  64.                 //возвращаем новые габариты
  65.                 return new Extents3d(new Point3d(x.Min(), y.Min(), 0).Project(plane, Vector3d.ZAxis), new Point3d(x.Max(), y.Max(), 0).Project(plane, Vector3d.ZAxis));
  66.             }
  67.             return null;
  68.         }
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: Александр Ривилис от 15-03-2023, 21:42:54
Жаль нельзя свои сообщения редактировать
Это как раз хорошо. Видна "работа над ошибками". :-)
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: Владимир Шу от 27-03-2023, 18:55:00
Я не проверял код, но вот стилистику после 56 строки, что то захотелось поправить... сделать ее более читаемой что ли...
Код - C# [Выбрать]
  1.         var points = new List<Gem.Point3d>();
  2.  
  3.         var minX = points.Select(q => q.X).Min();
  4.         var minY = points.Select(q => q.Y).Min();
  5.        
  6.         var maxX = points.Select(q => q.X).Max();
  7.         var maxY = points.Select(q => q.Y).Max();
  8.  
  9.         var minPoint = new Gem.Point3d(minX, minY, 0).Project(plane, Gem.Vector3d.ZAxis);
  10.         var maxPoint = new Gem.Point3d(maxX, maxY, 0).Project(plane, Gem.Vector3d.ZAxis);
  11.        
  12.         return new Db.Extents3d(minPoint, maxPoint);
И я бы еще избавился от скобочек в if (mText != null) {} заменил бы на if (mText == null) return null;


PS/
И еще хотелось бы уточнить, а вот такой вариант точно не работает?
Код - C# [Выбрать]
  1.           var ext = new Db.Extents3d();
  2.           ext.AddPoint(point.Project(plane, Vector3d.ZAxis));
  3.           ext.AddPoint((point + vx * w + vy * h).Project(plane, Vector3d.ZAxis));
  4.           ext.AddPoint((point + vx * w).Project(plane, Vector3d.ZAxis));
  5.           ext.AddPoint((point + vx * w).Project(plane, Vector3d.ZAxis));
  6.           return ext;
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: alz от 27-03-2023, 21:57:28
Сделайте скидку на мой опыт в программировании в пол года на шарпе (пара часов видеоуроков по программированию в автокаде а дальше как-то сам по примерам с форумов) и пара месяцев на дельфи лет 10 назад)

var  я вообще не использую, задаю предпочитаю сразу задавать нужный тип
Зачем Gem и Db? Вроде программа простейшая и мне хватило этого
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;

Разница как я понял вместо использования списков с координатами они просто вытаскиваются из точек через линк но с линком я знаком крайне поверхностно и такие конструкции пока не делал   

Код - C# [Выбрать]
  1.  var ext = new Db.Extents3d();
  2.           ext.AddPoint(point.Project(plane, Vector3d.ZAxis));
  3.           ext.AddPoint((point + vx * w + vy * h).Project(plane, Vector3d.ZAxis));
  4.           ext.AddPoint((point + vx * w).Project(plane, Vector3d.ZAxis));
  5.           ext.AddPoint((point + vx * w).Project(plane, Vector3d.ZAxis));
  6.           return ext;

тут границы определятся конечно правильно, но только в плане - вид сверху, так как по оси Z граница не лежит в плоскости текста но немного переделанный вариант вполне прошел, вот так выдает все вполне корректно

Код - C# [Выбрать]
  1. var ext = new Extents3d();
  2.                 ext.AddPoint(point);
  3.                 ext.AddPoint((point + vx * w + vy * h));
  4.                 ext.AddPoint((point + vx * w));
  5.                 ext.AddPoint((point + vy * h));
  6.                 return = new Extents3d(ext.MinPoint.Project(plane, Vector3d.ZAxis), ext.MaxPoint.Project(plane, Vector3d.ZAxis));

по поводу - И я бы еще избавился от скобочек в if (mText != null) {} заменил бы на if (mText == null) return null;
тут у меня уже привычка использовать именно такой вариант, поскольку очень много используется транзакций, которые я все же предпочитаю коммитить, так как был у меня пример где я забывал коммитить и программу подвешивало, хотя там был цикл и видимо внутри него транзакция открывалась слишком много раз без закрытия, так что если вариант if (mText == null) return null использовать, то предпочитаю делать так  if (mText == null){ tr.commit(); return null;} а что бы не вспоминать внутри транзакции я или снаружи просто начал везде использовать первый вариант, может зря конечно, в программах не автокадовских обычно да, использую твой вариант с ретурном.
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: alz от 27-03-2023, 22:04:53
В общем результирующий вариант с последними предложениями

Код - C# [Выбрать]
  1. private Extents3d? MtextRealExtents(MText mText)
  2. {            
  3.     if (mText == null)return null;                                
  4.     Point3d point = mText.Location;
  5.     Plane plane = new Plane(point, mText.Normal);
  6.     Vector3d vx = plane.Normal.GetPerpendicularVector().GetNormal().TransformBy(Matrix3d.Rotation(mText.Rotation, plane.Normal, point)).GetNormal(); ;
  7.     Vector3d vy = vx.TransformBy(Matrix3d.Rotation(Math.PI / 2, plane.Normal, point)).GetNormal();
  8.     double h = mText.ActualHeight;
  9.     double w = mText.ActualWidth;  
  10.     //получаем нижний левый угол текста
  11.     switch (mText.Attachment)
  12.     {
  13.         case AttachmentPoint.TopLeft:
  14.             point = point - vy*h;
  15.             break;
  16.         case AttachmentPoint.MiddleCenter:
  17.             point = point - vy*h/2 - vx*w/2;
  18.             break;
  19.         case AttachmentPoint.TopCenter:
  20.             point = point - vy * h - vx * w / 2;
  21.             break;
  22.         case AttachmentPoint.TopRight:
  23.             point = point - vy * h - vx * w;
  24.             break;
  25.         case AttachmentPoint.MiddleLeft:
  26.             point = point - vy * h / 2;
  27.             break;
  28.         case AttachmentPoint.MiddleRight:
  29.             point = point - vy * h/2 - vx * w;
  30.             break;
  31.         case AttachmentPoint.BottomLeft:                        
  32.             break;
  33.         case AttachmentPoint.BottomCenter:
  34.             point = point  - vx * w/2;
  35.             break;
  36.         case AttachmentPoint.BottomRight:
  37.             point = point - vx * w;
  38.             break;                      
  39.     }
  40.     var ext = new Extents3d();            
  41.     ext.AddPoint(point);
  42.     ext.AddPoint((point + vx * w + vy * h));
  43.     ext.AddPoint((point + vx * w));
  44.     ext.AddPoint((point + vy * h));                
  45.     return new Extents3d(ext.MinPoint.Project(plane, Vector3d.ZAxis), ext.MaxPoint.Project(plane, Vector3d.ZAxis));  
  46. }
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: Владимир Шу от 28-03-2023, 07:49:35
Позвольте еще позанудничать...
В 6 строчке точно нужно два раза нормализовать вектор?
Вы вроде получаете нормализованный (единичный) вектор перпендикулярный плоскости и потом просто поворачиваете его... и снова нормализуете и сразу за этим, в 7 строчке Вы опять поворачиваете единичный вектор и снова нормализуете его.
Что то меня сомнения грызут, что все это нужно...

И еще,
Код - C# [Выбрать]
  1. private Extents3d? MtextRealExtents(MText mText)
имеет смысл заменить на
Код - C# [Выбрать]
  1. public static Extents3d? MtextRealExtents(this MText mText)
запихнув все это в статичный класс, что бы удобнее было вызывать:
Код - C# [Выбрать]
  1. var mt = new MText();
  2. var ext = mt.MtextRealExtents();

Цитировать
Зачем Gem и Db? Вроде программа простейшая и мне хватило этого
Код - C# [Выбрать]
  1. using Db = Autodesk.AutoCAD.DatabaseServices;
  2. using Gem = Autodesk.AutoCAD.Geometry;
Таким образом разделяю объекты автокада и других библиотек, просто привык к такому.
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: alz от 28-03-2023, 08:02:31
Не, не нужно, я если честно сомневаюсь что и один раз нужно, просто разворот потом уже добавлял и ляпнул на автомате, заметил уже когда выложил но поздно
Название: Re: AutoCAD .NET API: Реальные габариты MText
Отправлено: alz от 28-03-2023, 19:25:12
имеет смысл заменить на

Код - C# [Выбрать]
  1. public static Extents3d? MtextRealExtents(this MText mText)

Ну думаю конкретно для показа принципа получения этой границы это уже излишне, но за показ такого варианта использования статика большое спасибо, думаю много где может пригодиться в будущем)