AutoCAD .NET API: Реальные габариты MText

Автор Тема: AutoCAD .NET API: Реальные габариты MText  (Прочитано 21293 раз)

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

Оффлайн alzАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 97
  • Карма: 11
В связи с тем, что стандартный метод .GeometricExtents в случае с MText возвращает собственно окно в котором этот текст расположен, а сам текст в этом окне может располагаться достаточно свободно пришлось поискать варианты определения реального положения текста, а то были проблемы с определением местоположения текста относительно других объектов, когда текст вроде как внутри какого то контура, а программно определяется что снаружи.
Для решения этой проблемы можно воспользоваться такими параметрами MText как .ActualHeight и .ActualWidth, но просто так их использовать не получится, так как текст у нас все же в пространстве а так же может быть развернут относительно каких либо осей, поэтому было решено использовать немного векторной геометрии. Для начала определяем вектора оси X и оси Y плоскости текста, потом в зависимости от положения текста относительно точки вставки определяем положение минимальной точки через вектор соответствующей оси и реальную высоту/ширину текста, и собственно используя эти же параметры возвращаем и максимальную точку.

На изображении показаны границы: голубым цветом(возвращаемые .GeometricExtents ) зеленым цветом(вычисленные)




Текст функции
Код - 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.         }

Оффлайн alzАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 97
  • Карма: 11
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #1 : 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.         }

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

  • Administrator
  • *****
  • Сообщений: 13886
  • Карма: 1788
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #2 : 15-03-2023, 21:42:54 »
Жаль нельзя свои сообщения редактировать
Это как раз хорошо. Видна "работа над ошибками". :-)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 624
  • Карма: 158
    • ПГСу Бложик
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #3 : 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;

Оффлайн alzАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 97
  • Карма: 11
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #4 : 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;} а что бы не вспоминать внутри транзакции я или снаружи просто начал везде использовать первый вариант, может зря конечно, в программах не автокадовских обычно да, использую твой вариант с ретурном.

Оффлайн alzАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 97
  • Карма: 11
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #5 : 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. }

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 624
  • Карма: 158
    • ПГСу Бложик
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #6 : 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;
Таким образом разделяю объекты автокада и других библиотек, просто привык к такому.

Оффлайн alzАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 97
  • Карма: 11
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #7 : 28-03-2023, 08:02:31 »
Не, не нужно, я если честно сомневаюсь что и один раз нужно, просто разворот потом уже добавлял и ляпнул на автомате, заметил уже когда выложил но поздно

Оффлайн alzАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 97
  • Карма: 11
Re: AutoCAD .NET API: Реальные габариты MText
« Ответ #8 : 28-03-2023, 19:25:12 »
имеет смысл заменить на

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

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