Разное > Работа для программистов
Написать скрипт на 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 перейти для этого случая, что для меня подобно изучению китайского.
Очень выручили
Навигация
Перейти к полной версии