Написать скрипт на LISP для определения моментов инерции

Автор Тема: Написать скрипт на LISP для определения моментов инерции  (Прочитано 8435 раз)

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

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

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

  • ADN OPEN
  • Сообщений: 15
  • Карма: 1
Скрипт уже написан на C# (см. приложение), но проблема в том, что на C# он выдаёт неверные значения. Насколько мне известно это баг самого API для C#, который признали даже разработчики.
Проверял похожий скрипт на LISP (см. приложение) - там значения верные получались, но этот скрипт немного под другие цели, но если хотите можно допилить его, для меня это китайская грамота.
От скрипта нужно чтобы:
1) Пользователь выбирал объекты.
2) В выбранных объектах для каждой области скрипт находит какими бы были моменты инерции по осям X и Y у области, если её изменить так, что
   2а) область уменьшить в 10 раз (перевод из миллиметров в сантиметры)
   2б) центр масс станет находиться в начале координат
3) Для каждой из областей скрипт находит имя как содержимое первого попавшегося однострочного текста из выбранных объектов, точка вставки которого лежит в пределах ограничивающей рамки области.
Если текст не найден, то присваивается имя "noname"
4) После получения всей информации нужно вывести в текстовое окно (которое ctrl+F2) информацию в следующем виде:
Наименование\tA\tJx\tWx\tJy\tWy\n
<Имя1>\t<значение A1>\t<значение Jx1>\t<значение Wx1>\t<значение Jy1>\t<значение Wy1>\n
<Имя2>\t<значение A2>\t<значение Jx2>\t<значение Wx2>\t<значение Jy2>\t<значение Wy2>\n
...
<Имяn>\t<значение An>\t<значение Jxn>\t<значение Wxn>\t<значение Jyn>\t<значение Wyn>\n

где: \t - символ табуляции
   \n - символ новой строки
   <Имя1> - имя полученное по п.3
   <значение A1> - значение площади области в сантиметрах
   <значение Jx1> - значение момента инерции в сантиметрах (см. п.2)
   <значение Wx1> - значение момента сопротивления в сантиметрах, полученное как Jx1 / Макс (Y1, Y2), округлённое до 4 знаков после запятой
   где Y1, Y2 - это абсолютные значения расстояний от центра масс до края ограничивающей рамки по оси Y.
   Другими словами момент инерции по оси X равен моменту инерции по оси X, делённому на наибольшее расстояние от центра масс до крайнего волокна по оси Y
   <значение Jy1> - значение момента инерции в сантиметрах (см. п.2)
   <значение Wy1> - значение момента сопротивления в сантиметрах, полученное как Jy1 / Макс (X1, X2)
   
Общая идея такая, чтобы скриптом получать уже вычисленные значения, копировать их из текстового окна и вставлять в Excel

Во вложении тестовый файл, при отработке скрипта должен получиться следующий результат:



Скрипт на C#:
Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using Autodesk.AutoCAD.ApplicationServices;
  5. using Autodesk.AutoCAD.DatabaseServices;
  6. using Autodesk.AutoCAD.EditorInput;
  7. using Autodesk.AutoCAD.Geometry;
  8. using FSImark.Services;
  9. using Region = Autodesk.AutoCAD.DatabaseServices.Region;
  10.  
  11. namespace FSImark.GetGeometryParameters
  12. {
  13.         public class GetGeometryParametersModel
  14.         {
  15.                 public List<Data> Datas { get; set; } = new List<Data>();
  16.                 public bool IsSearchName { get; set; } = true;
  17.                 public void Do()
  18.                 {
  19.                         var doc = Application.DocumentManager.MdiActiveDocument;
  20.                         var db = doc.Database;
  21.                         var ed = doc.Editor;
  22.  
  23.                         var types = new List<TypedValue> { new TypedValue((int)DxfCode.Start, "REGION") };
  24.                         var filter = new SelectionFilter(types.ToArray());
  25.                         var pso = new PromptSelectionOptions
  26.                         {
  27.                                 MessageForAdding = $"Выберите области{(IsSearchName ? " и наименования" : "")} для добавления их в набор",
  28.                                 MessageForRemoval = $"Выберите области{(IsSearchName ? " и наименования" : "")}, подлежащие удалению из набора",
  29.                                 SingleOnly = false,
  30.                                 RejectObjectsFromNonCurrentSpace = true,
  31.                                 AllowDuplicates = false
  32.                         };
  33.                         var psr = ed.GetSelection(pso, filter);
  34.                         if (psr.Status != PromptStatus.OK)
  35.                         {
  36.                                 ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  37.                                 return;
  38.                         }
  39.  
  40.                         Datas.Clear();
  41.  
  42.                         using (var tr = db.TransactionManager.StartTransaction())
  43.                         {
  44.                                 var blockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
  45.                                 var blockTableRecord = tr.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
  46.                                 foreach (var regionId in psr.Value.GetObjectIds())
  47.                                 {
  48.                                         if (!(tr.GetObject(regionId, OpenMode.ForWrite) is Region region)) continue;
  49.  
  50.                                         using (region)
  51.                                         using (var origin = new Solid3d())
  52.                                         using (var geom = new Solid3d())
  53.                                         {
  54.                                                 origin.Extrude(region, 1, 0); // экструдируем солид, чтобы не нужно было вертеть сечение
  55.                                                 var center = origin.MassProperties.Centroid;
  56.                                                 region.TransformBy(Matrix3d.Scaling(1 / 10D, center));
  57.                                                 region.TransformBy(Matrix3d.Displacement(new Vector3d(-center.X, -center.Y, -center.Z)));
  58.  
  59.                                                 geom.Extrude(region, 1, 0); // экструдируем солид, чтобы не нужно было вертеть сечение
  60.  
  61.                                                 var x1 = geom.GeometricExtents.MinPoint.X.Abs();
  62.                                                 var x2 = geom.GeometricExtents.MaxPoint.X.Abs();
  63.                                                 var y1 = geom.GeometricExtents.MinPoint.Y.Abs();
  64.                                                 var y2 = geom.GeometricExtents.MaxPoint.Y.Abs();
  65.  
  66.                                                 var data = new Data
  67.                                                 {
  68.                                                         Center = center,
  69.                                                         A = geom.MassProperties.Volume,
  70.                                                         Jx = geom.MassProperties.MomentsOfIntertia.X,
  71.                                                         Jy = geom.MassProperties.MomentsOfIntertia.Y,
  72.                                                         Wx = geom.MassProperties.MomentsOfIntertia.X / Math.Max(y1, y2),
  73.                                                         Wy = geom.MassProperties.MomentsOfIntertia.Y / Math.Max(x1, x2),
  74.                                                 };
  75.                                                
  76.                                                 foreach (var textId in blockTableRecord)
  77.                                                 {
  78.                                                         if (!(tr.GetObject(textId, OpenMode.ForRead) is DBText text)) continue;
  79.                                                         if (text.Position.X < origin.GeometricExtents.MinPoint.X) continue;
  80.                                                         if (text.Position.X > origin.GeometricExtents.MaxPoint.X) continue;
  81.                                                         if (text.Position.Y < origin.GeometricExtents.MinPoint.Y) continue;
  82.                                                         if (text.Position.Y > origin.GeometricExtents.MaxPoint.Y) continue;
  83.                                                         data.Name = text.TextString;
  84.                                                         break;
  85.                                                 }
  86.                                                
  87.                                                 Datas.Add(data);
  88.                                         }
  89.                                 }
  90.                         }
  91.                 }
  92.  
  93.                 public class Data
  94.                 {
  95.                         public string Name { get; set; }
  96.                         public Point3d Center { get; set; }
  97.                         public double A { get; set; }
  98.                         public double Jx { get; set; }
  99.                         public double Wx { get; set; }
  100.                         public double Jy { get; set; }
  101.                         public double Wy { get; set; }
  102.                 }
  103.         }
  104. }
Скрипт на LISP

Код - Auto/Visual Lisp [Выбрать]
  1. (defun C:mihar (/ *error* pt1 pt2 ent1)
  2.               (defun *error* (msg) (princ))
  3.   (vl-load-com)
  4.         (setq pt1 (getpoint "\nУкажите рамкой (с права на лево) расчетный участок: "))
  5.          (setq pt2 (getcorner pt1 "\nУкажите вторую точку: "))
  6.         (command "_section" "_f" pt1 pt2 "" "" "_yz" "_m2p" pt1 pt2 )
  7.        (command  "_extrude" "Р" pt2 pt1 "" 0.0001)
  8.        (command "_union" "Р" pt2 pt1 "")
  9.   (setq ent1(entlast))
  10.  
  11.              (defun centroid (entity /)
  12.     (vlax-safearray->list (vlax-variant-value (vla-get-centroid (vlax-ename->vla-object entity))))
  13.     ) ;_ end of defun
  14.   (defun mass (entity /)
  15.     (vla-get-volume (vlax-ename->vla-object entity))
  16.     ) ;_ end of defun
  17.   (if (setq ents (ssget "_L"))
  18.     (progn
  19.       (setq ents (vl-remove-if-not 'atom (mapcar 'cadr (ssnamex ents))))
  20.       (setq firstpoint (centroid (car ents)))
  21.       (setq firstpointmass (mass (car ents)))
  22.       (if (setq ents (cdr ents))
  23.   (progn
  24.     (foreach n ents
  25.       (setq secondpoint (centroid n))
  26.       (setq secondpointmass (mass n))
  27.       (setq length12 (sqrt (apply '+ (mapcar '(lambda (x) (* x x)) (mapcar '- firstpoint secondpoint)))))
  28.       (setq length1 (/ (* secondpointmass length12) (+ firstpointmass secondpointmass)))
  29.  
  30. (if (and (= length1 length12)(= length1 0))(setq length12 1))
  31.  
  32.       (setq delta (mapcar '(lambda (x) (/ (* length1 x) length12)) (mapcar '- firstpoint secondpoint)))
  33.       (setq firstpoint (mapcar '- firstpoint delta))
  34.       (setq firstpointmass (+ firstpointmass secondpointmass))
  35.       ) ;_ end of foreach
  36.     ) ;_ end of progn
  37.   ) ;_ end of if
  38.  
  39. (setq osmode (getvar "osmode")
  40.       firstpoint (trans firstpoint 0 1))
  41.  
  42. (vla-startundomark (vla-get-activedocument (vlax-get-acad-object)))
  43.  
  44. (setvar "osmode" 0)
  45.  
  46. (COMMAND "_ucs" "_o" firstpoint
  47.       )
  48. (setvar "osmode" osmode)
  49. ))
  50.  
  51. (setvar "cmdecho" 1)
  52. (vla-endundomark (vla-get-activedocument (vlax-get-acad-object)))
  53. (command "_massprop" "_last" "" "Д" "" "_.undo" "5")  
  54.  
  55.      
  56.       (princ)
  57.       ) ;_ end of progn
  58.     ) ;_ end of if
  59.   ) ;_ end of defun[/quote]




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

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Скрипт уже написан на C# (см. приложение), но проблема в том, что на C# он выдаёт неверные значения. Насколько мне известно это баг самого API для C#, который признали даже разработчики.
Слегка подправил код и для тестового файла получил точно такие же значения, что и на картинке. Следовательно, нормально работает API, это у Вас код кривой.
« Последнее редактирование: 23-01-2023, 18:52:52 от Владимир Шу »

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

  • ADN OPEN
  • Сообщений: 15
  • Карма: 1
Слегка подправил код
Подскажите, пожалуйста, где именно в коде ошибка, сам я вряд ли найду :)

Отмечено как Решение Serg34 24-01-2023, 09:47:36

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

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
не нужно регион преобразовывать в солид, для того, что бы получить центройд и моменты инерции, у региона есть эти свойства.

Я когда комент писал, то только проверил идею на Вашем коде, а когда картинку вставлял, уже основательно код почекрыжил, вот то, что получилось:
Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7.  
  8. using Autodesk.AutoCAD.ApplicationServices;
  9. //using Autodesk.AutoCAD.ApplicationServices.Application;
  10. using Autodesk.AutoCAD.DatabaseServices;
  11. using Autodesk.AutoCAD.EditorInput;
  12. using Autodesk.AutoCAD.Geometry;
  13. using Autodesk.AutoCAD.Runtime;
  14.  
  15. [assembly: CommandClass(typeof(TrackLineCreator.GetGeometryParametersModel))]
  16.  
  17. namespace TrackLineCreator
  18. {
  19.   public class Data
  20.   {
  21.     public string Name { get; set; }
  22.     public Point3d Center { get; set; }
  23.     public double A { get; set; }
  24.     public double Jx { get; set; }
  25.     public double Wx { get; set; }
  26.     public double Jy { get; set; }
  27.     public double Wy { get; set; }
  28.   }
  29.  
  30.  
  31.   public static class GetGeometryParametersModel
  32.   {
  33.     public static List<Data> Datas { get; set; } = new List<Data>();
  34.     public static bool IsSearchName { get; set; } = true;
  35.  
  36.  
  37.  
  38.  
  39.     [CommandMethod("ppp")]
  40.     public static void Do()
  41.     {
  42.       var doc = Application.DocumentManager.MdiActiveDocument;
  43.       var db = doc.Database;
  44.       var ed = doc.Editor;
  45.  
  46.       var types = new List<TypedValue> { new TypedValue((int)DxfCode.Start, "REGION") };
  47.       var filter = new SelectionFilter(types.ToArray());
  48.       var pso = new PromptSelectionOptions
  49.       {
  50.         MessageForAdding = $"Выберите области{(IsSearchName ? " и наименования" : "")} для добавления их в набор",
  51.         MessageForRemoval = $"Выберите области{(IsSearchName ? " и наименования" : "")}, подлежащие удалению из набора",
  52.         SingleOnly = false,
  53.         RejectObjectsFromNonCurrentSpace = true,
  54.         AllowDuplicates = false
  55.       };
  56.       var psr = ed.GetSelection(pso, filter);
  57.       if (psr.Status != PromptStatus.OK)
  58.       {
  59.         ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  60.         return;
  61.       }
  62.  
  63.       Datas.Clear();
  64.  
  65.       var textList = new List<(string, Point3d)>();
  66.       using (var ms = db.CurrentSpaceId.Open(OpenMode.ForRead) as BlockTableRecord)
  67.       {
  68.         foreach (var textId in ms)
  69.         {
  70.           if (textId.ObjectClass != RXObject.GetClass(typeof(DBText))) continue;
  71.           using (var text = textId.Open(OpenMode.ForRead) as DBText)
  72.           {
  73.             textList.Add((text.TextString, text.Position));
  74.           }
  75.         }
  76.       }
  77.  
  78.       foreach (var regionId in psr.Value.GetObjectIds())
  79.       {
  80.         if (regionId.ObjectClass != RXObject.GetClass(typeof(Region))) continue;
  81.  
  82.         using (var region = regionId.Open(OpenMode.ForRead) as Region)
  83.         {
  84.           Point3d origin1 = Point3d.Origin;
  85.           Vector3d xAxis = Vector3d.XAxis;
  86.           Vector3d yAxis = Vector3d.YAxis;
  87.  
  88.           var centroid = region.AreaProperties(ref origin1, ref xAxis, ref yAxis).Centroid;
  89.           var center = new Point3d(centroid.X, centroid.Y, 0);
  90.  
  91.           var smallReg = region.GetTransformedCopy(Matrix3d.Scaling(1 / 10d, center)) as Region;
  92.  
  93.           smallReg.TransformBy(Matrix3d.Displacement(center.GetVectorTo(Point3d.Origin)));
  94.  
  95.           RegionAreaProperties regProp = smallReg.AreaProperties(ref origin1, ref xAxis, ref yAxis);
  96.  
  97.           var x1 = Math.Abs(smallReg.GeometricExtents.MinPoint.X);
  98.           var x2 = Math.Abs(smallReg.GeometricExtents.MaxPoint.X);
  99.           var y1 = Math.Abs(smallReg.GeometricExtents.MinPoint.Y);
  100.           var y2 = Math.Abs(smallReg.GeometricExtents.MaxPoint.Y);
  101.  
  102.           var data = new Data
  103.           {
  104.             Name = "noname",
  105.             Center = center,
  106.             A = regProp.Area,
  107.             Jx = regProp.MomentsOfInertia.X,
  108.             Jy = regProp.MomentsOfInertia.Y,
  109.             Wx = Math.Round(regProp.MomentsOfInertia.X / Math.Max(y1, y2), 4, MidpointRounding.AwayFromZero),
  110.             Wy = Math.Round(regProp.MomentsOfInertia.Y / Math.Max(x1, x2), 4, MidpointRounding.AwayFromZero)
  111.           };
  112.  
  113.           foreach (var text in textList)
  114.           {
  115.             if (text.Item2.X < region.GeometricExtents.MinPoint.X) continue;
  116.             if (text.Item2.X > region.GeometricExtents.MaxPoint.X) continue;
  117.             if (text.Item2.Y < region.GeometricExtents.MinPoint.Y) continue;
  118.             if (text.Item2.Y > region.GeometricExtents.MaxPoint.Y) continue;
  119.             data.Name = text.Item1;
  120.             break;
  121.           }
  122.  
  123.           Datas.Add(data);
  124.         }
  125.       }
  126.  
  127.       ed.WriteMessage("Наименование\tA\tJx\tWx\tJy\tWy\n");
  128.       foreach (var data in Datas)
  129.       {
  130.         ed.WriteMessage($"{data.Name}\t{data.A}\t{data.Jx}\t{data.Wx}\t{data.Jy}\t{data.Wy}\n");
  131.       }
  132.  
  133.     }
  134.   }
  135. }
  136.  

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

  • ADN OPEN
  • Сообщений: 15
  • Карма: 1
Владимир Шу, Да, после Вашего ответа тоже стал копать в сторону Region.AreaProperties, но не смог найти примера какие параметры передавать этому методу. вот это было неочевидно совсем:
Код - C# [Выбрать]
  1. var origin = Point3d.Origin;
  2. var xAxis = Vector3d.XAxis;
  3. var yAxis = Vector3d.YAxis;
и в справке как всегда мало полезного, по крайней мере для меня:
ref Point3d origin   Input point (in WCS coords) for origin of the coordinate system to usefor evaluation.
ref Vector3d xAxis   Input X axis (in WCS coords) of the coordinate system to use forevaluation.
ref Vector3d yAxis   Input Y axis (in WCS coords) of the coordinate system to use forevaluation.
а танцы с бубнами вроде таких ничего не дали:
Код - C# [Выбрать]
  1. var o = new Point3d(0, 0, 0);
  2. var xAxis = new Vector3d(o.X + 1, o.Y, o.Z);
  3. var yAxis = new Vector3d(o.X, o.Y + 1, o.Z);
  4.  
  5. //var o = new Point3d(center.X, center.Y, center.Z);
  6. ////var xAxis = new Vector3d(center.X + 1, center.Y, center.Z);
  7. ////var yAxis = new Vector3d(center.X, center.Y + 1, center.Z);
  8. //var xAxis = new Vector3d(1, 0, 0);
  9. //var yAxis = new Vector3d(0, 1, 0);
  10.  
  11. var props = region.AreaProperties(ref o, ref xAxis, ref yAxis);
Спасибо Вам огромное, я уже готов был на Lisp перейти для этого случая, что для меня подобно изучению китайского.
Очень выручили