Разное > Работа для программистов

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

(1/1)

Serg34:
Скрипт уже написан на 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# [Выбрать] ---using System;using System.Collections.Generic;using System.Drawing;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.EditorInput;using Autodesk.AutoCAD.Geometry;using FSImark.Services;using Region = Autodesk.AutoCAD.DatabaseServices.Region; namespace FSImark.GetGeometryParameters{        public class GetGeometryParametersModel        {                public List<Data> Datas { get; set; } = new List<Data>();                public bool IsSearchName { get; set; } = true;                public void Do()                {                        var doc = Application.DocumentManager.MdiActiveDocument;                        var db = doc.Database;                        var ed = doc.Editor;                         var types = new List<TypedValue> { new TypedValue((int)DxfCode.Start, "REGION") };                        var filter = new SelectionFilter(types.ToArray());                        var pso = new PromptSelectionOptions                        {                                MessageForAdding = $"Выберите области{(IsSearchName ? " и наименования" : "")} для добавления их в набор",                                MessageForRemoval = $"Выберите области{(IsSearchName ? " и наименования" : "")}, подлежащие удалению из набора",                                SingleOnly = false,                                RejectObjectsFromNonCurrentSpace = true,                                AllowDuplicates = false                        };                        var psr = ed.GetSelection(pso, filter);                        if (psr.Status != PromptStatus.OK)                        {                                ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");                                return;                        }                         Datas.Clear();                         using (var tr = db.TransactionManager.StartTransaction())                        {                                var blockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;                                var blockTableRecord = tr.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;                                foreach (var regionId in psr.Value.GetObjectIds())                                {                                        if (!(tr.GetObject(regionId, OpenMode.ForWrite) is Region region)) continue;                                         using (region)                                        using (var origin = new Solid3d())                                        using (var geom = new Solid3d())                                        {                                                origin.Extrude(region, 1, 0); // экструдируем солид, чтобы не нужно было вертеть сечение                                                var center = origin.MassProperties.Centroid;                                                region.TransformBy(Matrix3d.Scaling(1 / 10D, center));                                                region.TransformBy(Matrix3d.Displacement(new Vector3d(-center.X, -center.Y, -center.Z)));                                                 geom.Extrude(region, 1, 0); // экструдируем солид, чтобы не нужно было вертеть сечение                                                 var x1 = geom.GeometricExtents.MinPoint.X.Abs();                                                var x2 = geom.GeometricExtents.MaxPoint.X.Abs();                                                var y1 = geom.GeometricExtents.MinPoint.Y.Abs();                                                var y2 = geom.GeometricExtents.MaxPoint.Y.Abs();                                                 var data = new Data                                                {                                                        Center = center,                                                        A = geom.MassProperties.Volume,                                                        Jx = geom.MassProperties.MomentsOfIntertia.X,                                                        Jy = geom.MassProperties.MomentsOfIntertia.Y,                                                        Wx = geom.MassProperties.MomentsOfIntertia.X / Math.Max(y1, y2),                                                        Wy = geom.MassProperties.MomentsOfIntertia.Y / Math.Max(x1, x2),                                                };                                                                                                foreach (var textId in blockTableRecord)                                                {                                                        if (!(tr.GetObject(textId, OpenMode.ForRead) is DBText text)) continue;                                                        if (text.Position.X < origin.GeometricExtents.MinPoint.X) continue;                                                        if (text.Position.X > origin.GeometricExtents.MaxPoint.X) continue;                                                        if (text.Position.Y < origin.GeometricExtents.MinPoint.Y) continue;                                                        if (text.Position.Y > origin.GeometricExtents.MaxPoint.Y) continue;                                                        data.Name = text.TextString;                                                        break;                                                }                                                                                                Datas.Add(data);                                        }                                }                        }                }                 public class Data                {                        public string Name { get; set; }                        public Point3d Center { get; set; }                        public double A { get; set; }                        public double Jx { get; set; }                        public double Wx { get; set; }                        public double Jy { get; set; }                        public double Wy { get; set; }                }        }}Скрипт на LISP


--- Код - Auto/Visual Lisp [Выбрать] ---(defun C:mihar (/ *error* pt1 pt2 ent1)              (defun *error* (msg) (princ))  (vl-load-com)        (setq pt1 (getpoint "\nУкажите рамкой (с права на лево) расчетный участок: "))         (setq pt2 (getcorner pt1 "\nУкажите вторую точку: "))        (command "_section" "_f" pt1 pt2 "" "" "_yz" "_m2p" pt1 pt2 )       (command  "_extrude" "Р" pt2 pt1 "" 0.0001)       (command "_union" "Р" pt2 pt1 "")  (setq ent1(entlast))               (defun centroid (entity /)    (vlax-safearray->list (vlax-variant-value (vla-get-centroid (vlax-ename->vla-object entity))))    ) ;_ end of defun  (defun mass (entity /)    (vla-get-volume (vlax-ename->vla-object entity))    ) ;_ end of defun  (if (setq ents (ssget "_L"))    (progn      (setq ents (vl-remove-if-not 'atom (mapcar 'cadr (ssnamex ents))))      (setq firstpoint (centroid (car ents)))      (setq firstpointmass (mass (car ents)))      (if (setq ents (cdr ents))  (progn    (foreach n ents      (setq secondpoint (centroid n))      (setq secondpointmass (mass n))      (setq length12 (sqrt (apply '+ (mapcar '(lambda (x) (* x x)) (mapcar '- firstpoint secondpoint)))))      (setq length1 (/ (* secondpointmass length12) (+ firstpointmass secondpointmass))) (if (and (= length1 length12)(= length1 0))(setq length12 1))       (setq delta (mapcar '(lambda (x) (/ (* length1 x) length12)) (mapcar '- firstpoint secondpoint)))      (setq firstpoint (mapcar '- firstpoint delta))      (setq firstpointmass (+ firstpointmass secondpointmass))      ) ;_ end of foreach    ) ;_ end of progn  ) ;_ end of if (setq osmode (getvar "osmode")      firstpoint (trans firstpoint 0 1)) (vla-startundomark (vla-get-activedocument (vlax-get-acad-object))) (setvar "osmode" 0) (COMMAND "_ucs" "_o" firstpoint       )(setvar "osmode" osmode))) (setvar "cmdecho" 1)(vla-endundomark (vla-get-activedocument (vlax-get-acad-object)))(command "_massprop" "_last" "" "Д" "" "_.undo" "5")               (princ)      ) ;_ end of progn    ) ;_ end of if  ) ;_ end of defun[/quote]


Владимир Шу:

--- Цитата: Serg34 от 23-01-2023, 13:07:15 ---Скрипт уже написан на C# (см. приложение), но проблема в том, что на C# он выдаёт неверные значения. Насколько мне известно это баг самого API для C#, который признали даже разработчики.
--- Конец цитаты ---
Слегка подправил код и для тестового файла получил точно такие же значения, что и на картинке. Следовательно, нормально работает API, это у Вас код кривой.

Serg34:

--- Цитата: Владимир Шу от 23-01-2023, 18:19:22 ---Слегка подправил код
--- Конец цитаты ---
Подскажите, пожалуйста, где именно в коде ошибка, сам я вряд ли найду :)

Владимир Шу:
не нужно регион преобразовывать в солид, для того, что бы получить центройд и моменты инерции, у региона есть эти свойства.

Я когда комент писал, то только проверил идею на Вашем коде, а когда картинку вставлял, уже основательно код почекрыжил, вот то, что получилось:

--- Код - C# [Выбрать] ---using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;  using Autodesk.AutoCAD.ApplicationServices;//using Autodesk.AutoCAD.ApplicationServices.Application;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.EditorInput;using Autodesk.AutoCAD.Geometry;using Autodesk.AutoCAD.Runtime; [assembly: CommandClass(typeof(TrackLineCreator.GetGeometryParametersModel))] namespace TrackLineCreator{  public class Data  {    public string Name { get; set; }    public Point3d Center { get; set; }    public double A { get; set; }    public double Jx { get; set; }    public double Wx { get; set; }    public double Jy { get; set; }    public double Wy { get; set; }  }    public static class GetGeometryParametersModel  {    public static List<Data> Datas { get; set; } = new List<Data>();    public static bool IsSearchName { get; set; } = true;        [CommandMethod("ppp")]    public static void Do()    {      var doc = Application.DocumentManager.MdiActiveDocument;      var db = doc.Database;      var ed = doc.Editor;       var types = new List<TypedValue> { new TypedValue((int)DxfCode.Start, "REGION") };      var filter = new SelectionFilter(types.ToArray());      var pso = new PromptSelectionOptions      {        MessageForAdding = $"Выберите области{(IsSearchName ? " и наименования" : "")} для добавления их в набор",        MessageForRemoval = $"Выберите области{(IsSearchName ? " и наименования" : "")}, подлежащие удалению из набора",        SingleOnly = false,        RejectObjectsFromNonCurrentSpace = true,        AllowDuplicates = false      };      var psr = ed.GetSelection(pso, filter);      if (psr.Status != PromptStatus.OK)      {        ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");        return;      }       Datas.Clear();       var textList = new List<(string, Point3d)>();      using (var ms = db.CurrentSpaceId.Open(OpenMode.ForRead) as BlockTableRecord)      {        foreach (var textId in ms)        {          if (textId.ObjectClass != RXObject.GetClass(typeof(DBText))) continue;          using (var text = textId.Open(OpenMode.ForRead) as DBText)          {            textList.Add((text.TextString, text.Position));          }        }      }       foreach (var regionId in psr.Value.GetObjectIds())      {        if (regionId.ObjectClass != RXObject.GetClass(typeof(Region))) continue;         using (var region = regionId.Open(OpenMode.ForRead) as Region)        {          Point3d origin1 = Point3d.Origin;          Vector3d xAxis = Vector3d.XAxis;          Vector3d yAxis = Vector3d.YAxis;           var centroid = region.AreaProperties(ref origin1, ref xAxis, ref yAxis).Centroid;          var center = new Point3d(centroid.X, centroid.Y, 0);           var smallReg = region.GetTransformedCopy(Matrix3d.Scaling(1 / 10d, center)) as Region;           smallReg.TransformBy(Matrix3d.Displacement(center.GetVectorTo(Point3d.Origin)));           RegionAreaProperties regProp = smallReg.AreaProperties(ref origin1, ref xAxis, ref yAxis);           var x1 = Math.Abs(smallReg.GeometricExtents.MinPoint.X);          var x2 = Math.Abs(smallReg.GeometricExtents.MaxPoint.X);          var y1 = Math.Abs(smallReg.GeometricExtents.MinPoint.Y);          var y2 = Math.Abs(smallReg.GeometricExtents.MaxPoint.Y);           var data = new Data          {            Name = "noname",            Center = center,            A = regProp.Area,            Jx = regProp.MomentsOfInertia.X,            Jy = regProp.MomentsOfInertia.Y,            Wx = Math.Round(regProp.MomentsOfInertia.X / Math.Max(y1, y2), 4, MidpointRounding.AwayFromZero),            Wy = Math.Round(regProp.MomentsOfInertia.Y / Math.Max(x1, x2), 4, MidpointRounding.AwayFromZero)          };           foreach (var text in textList)          {            if (text.Item2.X < region.GeometricExtents.MinPoint.X) continue;            if (text.Item2.X > region.GeometricExtents.MaxPoint.X) continue;            if (text.Item2.Y < region.GeometricExtents.MinPoint.Y) continue;            if (text.Item2.Y > region.GeometricExtents.MaxPoint.Y) continue;            data.Name = text.Item1;            break;          }           Datas.Add(data);        }      }       ed.WriteMessage("Наименование\tA\tJx\tWx\tJy\tWy\n");      foreach (var data in Datas)      {        ed.WriteMessage($"{data.Name}\t{data.A}\t{data.Jx}\t{data.Wx}\t{data.Jy}\t{data.Wy}\n");      }     }  }} 

Serg34:
Владимир Шу, Да, после Вашего ответа тоже стал копать в сторону Region.AreaProperties, но не смог найти примера какие параметры передавать этому методу. вот это было неочевидно совсем:

--- Код - C# [Выбрать] ---var origin = Point3d.Origin;var xAxis = Vector3d.XAxis;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# [Выбрать] ---var o = new Point3d(0, 0, 0);var xAxis = new Vector3d(o.X + 1, o.Y, o.Z);var yAxis = new Vector3d(o.X, o.Y + 1, o.Z); //var o = new Point3d(center.X, center.Y, center.Z);////var xAxis = new Vector3d(center.X + 1, center.Y, center.Z);////var yAxis = new Vector3d(center.X, center.Y + 1, center.Z);//var xAxis = new Vector3d(1, 0, 0);//var yAxis = new Vector3d(0, 1, 0); var props = region.AreaProperties(ref o, ref xAxis, ref yAxis);Спасибо Вам огромное, я уже готов был на Lisp перейти для этого случая, что для меня подобно изучению китайского.
Очень выручили

Навигация

[0] Главная страница сообщений

Перейти к полной версии