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

ADN Club => AutoCAD .NET API => Тема начата: Константин Кулябов от 08-08-2014, 14:18:25

Название: Выноска с привязкой к блоку
Отправлено: Константин Кулябов от 08-08-2014, 14:18:25
Помогите, кто может!
  Есть программа, состоящая из 2х основных блоков.
1 - запрос выбора блока (как простой, так и динамический) и извлечение его имени.

                   
Код - C# [Выбрать]
  1. PromptEntityOptions peo = new PromptEntityOptions("\nВыделите оборудование: ");
  2.                     peo.SetRejectMessage("\nЭто не блок.");
  3.                     peo.AddAllowedClass(typeof(BlockReference), false);
  4.                     PromptEntityResult per = ed.GetEntity(peo);
  5.  
2 - создание выноски с именем этого блока

сейчас выноска строится по запросу 2х точек:
                   
Код - C# [Выбрать]
  1. // Первая точка выноски
  2.  
  3.                 PromptPointResult result = ed.GetPoint("\nУкажите первую точку выноски: ");
  4.                       if (result.Status != PromptStatus.OK)
  5.                         return;
  6.  
  7.                    Point3d startPt = result.Value;
  8.  
  9.                     //конечная точка выноски
  10.  
  11.                     PromptPointOptions opts = new PromptPointOptions("\nУкажите вторую точку выноски: ");
  12.  
  13.  
  14.                     opts.BasePoint = startPt;
  15.                     opts.UseBasePoint = true;
  16.                     result = ed.GetPoint(opts);
  17.  
  18.                     if (result.Status != PromptStatus.OK)
  19.                         return;
  20.  
  21.                     Point3d endPt = result.Value;

Нужно, чтобы первая точка бралась автоматически, к примеру таже самая точка, куда щёлкнули выделяя блок (без дополнительного запроса), а вторая строится как обычно (или, не критично, ещё вариант когда выноска имеет определённую длину, т.е. вторая точка на конкретном расстоянии от первой)
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 08-08-2014, 14:29:56
Приветствую на форуме!
Так и в чем проблема передать во второй блок в качестве первой точки выноски per.PickedPoint (или точка вставки блока).
Соотвественно запрос первой точки выноски не нужен:
Код - C# [Выбрать]
  1. Point3d startPt = per.PickedPoint;
  2. PromptPointOptions opts =
  3.    new PromptPointOptions("\nУкажите вторую точку выноски: ");
  4. opts.BasePoint = startPt;
  5. opts.UseBasePoint = true;
  6. result = ed.GetPoint(opts);
  7. if (result.Status != PromptStatus.OK)
  8.     return;
  9. Point3d endPt = result.Value;
  10.  
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 08-08-2014, 14:30:51
Да. Забыл напомнить, что затем точки нужно будет преобразовать UCS -> WCS
Название: Re: Выноска с привязкой к блоку
Отправлено: Константин Кулябов от 08-08-2014, 16:13:01
Спасибо за идею. Всё получилось
Название: Re: Выноска с привязкой к блоку
Отправлено: Константин Кулябов от 25-11-2014, 14:41:12
Добрый день!

Всё-таки не смог уйти от проблемы с ПСК.
при прямом расположении блока всё хорошо, а вот при его повороте начало выноски ускакивает (в качестве начала выноски сделан параметр "Точечный" динамического блока)

а вот как провести преобразование координат блока, чтобы всё было хорошо -- не нашёл.
когда использовал ORIGIN - тоже всё работало, но пришлось немного поменять концепцию
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 25-11-2014, 14:52:03
Так приведи свой код - будет понятно что нужно исправить.
Название: Re: Выноска с привязкой к блоку
Отправлено: Константин Кулябов от 25-11-2014, 15:10:16
прошу прощения за стилистику

в качестве передаваемых свойств передаётся имя параметров, они же совпадают с именами слоёв
string[] LayerName = { "Электрика", "Вода", "Дренаж","Выноски" };



Код - C# [Выбрать]
  1.   private static void DisplayDynBlockProperties(Editor ed, BlockReference br, Transaction tr, string name, string LayerName)
  2.       {
  3.           Document doc = Application.DocumentManager.MdiActiveDocument;
  4.           Database db = doc.Database;
  5.  
  6.  
  7.              Boolean AddLead = false;  
  8.          
  9.           DynamicBlockReferencePropertyCollection pc = br.DynamicBlockReferencePropertyCollection;
  10.               AttributeCollection attCol = br.AttributeCollection;
  11.               Point3d startPt = br.Position;
  12.               double StX=startPt.X;
  13.               double StY=startPt.Y;
  14.  
  15.            LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);    
  16.           LayerTableRecord ltr = new LayerTableRecord();
  17.               ltr.Name = LayerName;
  18.  
  19.               lt.UpgradeOpen();
  20.               ObjectId idl = lt[LayerName];
  21.    
  22.               lt.DowngradeOpen();
  23.          
  24.           db.Clayer = idl;
  25.  
  26.               // Цикл получения свойств для каждого поля
  27.               foreach (DynamicBlockReferenceProperty prop in pc)
  28.               {
  29.  
  30.                   // Начать по свойству, типу и описанию
  31.  //                 ed.WriteMessage("\nProperty: \"{0}\" : {1}, значение {2}", prop.PropertyName, prop.UnitsType, prop.Value);
  32.  
  33.                   if (prop.PropertyName.Contains(LayerName) && prop.PropertyName.Contains("Y"))
  34.                   {
  35.                       double Y = Convert.ToDouble(prop.Value);
  36.                       StY = startPt.Y + Y;  
  37. //                      ed.WriteMessage("\n  Ура!! работает {0} \n  {1}",startPt.Y,Y);
  38.                       AddLead = true;
  39.                   }
  40.  
  41.                   if (prop.PropertyName.Contains(LayerName) && prop.PropertyName.Contains("X"))
  42.                   {
  43.                       double X = Convert.ToDouble(prop.Value);
  44.                       StX = startPt.X + X;
  45. //                      ed.WriteMessage("\n  Ура!! работает {0} \n  {1}", startPt.X, X);
  46.                       AddLead = true;
  47.                   }
  48.  
  49.  
  50.               }
  51.  
  52.             foreach (ObjectId attId in attCol)
  53.               {
  54.                  AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForRead);
  55.                  if ((attRef.Tag == LayerName) && (attRef.TextString != ""))
  56.      //                string str=("" + name +": "+attRef.TextString);
  57.                      name = (name + ":" +attRef.TextString);
  58.    //           string str = ("\n  Attribute Tag: " + attRef.Tag + "\n    Attribute String: " + attRef.TextString);
  59.    //               ed.WriteMessage(str);
  60.  
  61.               }
  62.  
  63.           if (AddLead)
  64.               DrawLeaderName(ed, br, name, StX, StY, startPt.Z);
  65.  
  66.           idl = lt["Выноски"];
  67.           db.Clayer = idl;
  68.       }
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 25-11-2014, 15:10:35
В принципе должно быть достаточно такого преобразования:
Код - C# [Выбрать]
  1. startPt = startPt.TransformBy(ed.ed.CurrentUserCoordinateSystem);
  2. endPt = endPt.TransformBy(ed.ed.CurrentUserCoordinateSystem);
Название: Re: Выноска с привязкой к блоку
Отправлено: Константин Кулябов от 25-11-2014, 15:12:56
ок. попробую
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 25-11-2014, 15:20:35
прошу прощения за стилистику
Это не стилистика. Это хуже:
1) Не отформатированный код без тэгов
2) Нет функции DrawLeaderName
3) Нет чертежа с блоком, чтобы можно было понять о чем идёт речь.
4) Не вижу какое отношение проблема имеет к ПСК, так что предыдущая моя подсказка точно не поможет.

 
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 25-11-2014, 23:05:44
Цитата: Константин Кулябов
Помогите, кто может!
Да уж... Начало не настраивает на позитив.

Пробежался по диагонали по исходникам... Александр Наумович предпочитает говорить в несколько сглаженной манере. Код автора данной темы, мягко говоря отвратителен. В суть клинописи не вникал, далее лишь самые поверхностные замечания.

1. Не забывай проверять результат, полученный из Application.DocumentManager.MdiActiveDocument на равенство null (актуально для AutoCAD 2015 и более новых версий, но заблаговременно можно добавить и в код более старых). Мало ли кто и когда дёрнет твой метод...

2. В первой строке кода метода ты создаёшь и инициализируешь переменную doc. Соответственно у тебя автоматом отпадает необходимость в первом параметре метода, поскольку Editor ты можешь спокойно получить от Document. Вообще в сигнатуре метода слишком много лишних, а так же непродуманных параметров:
 - от параметра Editor ed можно спокойно избавиться, т.к. он легко получается из doc.
 - Вместо BlockReference br лучше\надёжней\безопасней передавать его ObjectId.
 - Transaction tr лишний, его можно получить из db.

3. Начинать код тела метода всегда следует с проверки валидности переданных аргументов. У тебя какая-либо проверка напрочь отсутствует.

4. Что это за хитрая конструкция?

   
Код - C# [Выбрать]
  1. lt.UpgradeOpen();
  2.         ObjectId idl = lt[LayerName];  
  3.         lt.DowngradeOpen();
   
   Для того, чтобы получить идентификатор нужного слоя, нет никакой необходимости дёргать UpgradeOpen()\DowngradeOpen().
   
   Кроме того, прежде чем получать идентификатор слоя, неплохо сначала проверить, а есть ли он вообще (см. метод SymbolTable.Has(string)), а то может и дёргать-то не за что...
   
5. prop.PropertyName.Contains(LayerName)... А если рЕгИстрЫ символов вдруг не совпадут, то Титаник потонет? Вопрос риторический, положительный ответ очевиден.

6. prop.PropertyName.Contains(LayerName) && prop.PropertyName.Contains("X")...
   А ежели в имени слоя вдруг так же окажется буква "X", то можно кричать "Браво"? Аналогично и для "Y"...
   
7. double X = Convert.ToDouble(prop.Value); А если вдруг окажется не Double? Почитай о методе Double.TryParse.

8. if ((attRef.Tag == LayerName) && (attRef.TextString != ""))... Во первых, в случае несовпадения регистров символов, оператор == пошлёт тебе пламенный привет. Во вторых, вместо второй конструкции прочитай об String.IsNullOrEmpty.

9. применительно к DrawLeaderName(ed, br, name, StX, StY, startPt.Z); справедливо всё то, что изложено в п.2.
Название: Re: Выноска с привязкой к блоку
Отправлено: Дмитрий Загорулькин от 25-11-2014, 23:11:25
- Transaction tr лишний, его можно получить из db.
Ну, кстати, на мой взгляд, иногда передача ссылки на транзакцию имеет смысл - чтобы не плодить вложенных транзакций. Я нередко пользуюсь таким приемом.
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 25-11-2014, 23:16:23
Ну, кстати, на мой взгляд, иногда передача ссылки на транзакцию имеет смысл - чтобы не плодить вложенных транзакций. Я нередко пользуюсь таким приемом.
Зависит от контекста использования. Если ты в цикле перебираешь все объекты базы данных чертежа, то это будет целесообразным. Случай автора мне не напоминает данную ситуацию. чтобы не плодить вложенных транзакций он мог бы, как вариант, воспользоваться OpenCloseTransaction. Порой я тоже передаю ссылку на Transaction, например если использую лямбда-выражения в цикле, который может оказаться большим, но в случае обозначенном автором мне видится это излишним.
Название: Re: Выноска с привязкой к блоку
Отправлено: Дима_ от 25-11-2014, 23:20:18
- Вместо BlockReference br лучше\надёжней\безопасней передавать его ObjectId.
 - Transaction tr лишний, его можно получить из db.
Это спорные утверждения...
з.ы. по второму уже без меня разобрались
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 25-11-2014, 23:25:40
Ну, кстати, на мой взгляд, иногда передача ссылки на транзакцию имеет смысл - чтобы не плодить вложенных транзакций. Я нередко пользуюсь таким приемом.
А Database.TransactionManager.TopTransaction тебя не устроит для этой цели?
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 25-11-2014, 23:26:25
Вместо BlockReference br лучше\надёжней\безопасней передавать его ObjectId.
 - Transaction tr лишний, его можно получить из db.
Это спорные утверждения...
з.ы. по второму уже без меня разобрались
А по первому, я в прошлом не раз сталкивался с тем, что в переменную передавалась ссылка на объект, который к тому времени уже прекращал своё существование в виду того, что точка выполнения кода уже покинула блок using транзакции, в рамках которой этот объект был создан. Как следствие возникали всякие Fatal Error и прочие прелести.

Отсюда вывод: зачастую надёжней\безопасней оперировать ObjectId и получать его объект по необходимости.
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 25-11-2014, 23:27:49
Отсюда вывод: зачастую надёжней\безопасней оперировать ObjectId и получать его объект по необходимости.
+100. Для .NET это достаточно актуально.
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 25-11-2014, 23:28:02
А Database.TransactionManager.TopTransaction тебя не устроит для этой цели?
Не всегда. Порой нужно откатывать лишь некоторые изменения, в то же время сохраняя другие.
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 25-11-2014, 23:39:18
Если в коде метода Editor нужен лишь для того, чтобы отправлять сообщения в консоль, то я предпочитаю в качестве параметра передавать указатель на метод, имеющий тип

Код - C# [Выбрать]
  1. delegate void WriteMessage(String format, params String[] values);

Это позволяет использовать мой код без изменений даже в автономных exe приложениях. Например, в консольных приложениях, я параметром передаю указатель на Console.Write. А в плагинах различных CAD систем - указатель на ed.WriteMessage.

Чем выше уровень абстракции и чем меньше зависимостей в коде - тем лучше.
Название: Re: Выноска с привязкой к блоку
Отправлено: Дима_ от 25-11-2014, 23:48:43
А Database.TransactionManager.TopTransaction тебя не устроит для этой цели?
Если одновременно с двумя документами - то не всегда (иногда проще передать транзакцию чем ее DB).
Название: Re: Выноска с привязкой к блоку
Отправлено: Дима_ от 25-11-2014, 23:51:33
Чем выше уровень абстракции и чем меньше зависимостей в коде - тем лучше.
Андрей - тебе пора переходить на функциональные языки - там с абстракциями все в порядке.
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 25-11-2014, 23:53:00
Андрей - тебе пора переходить на функциональные языки - там с абстракциями все в порядке.
Почитываю\пробую Haskell.
Название: Re: Выноска с привязкой к блоку
Отправлено: Дима_ от 26-11-2014, 00:01:35
Одобряю - хороший выбор.   ;)

ИХМО чтобы более широко вникнуть в "тему" - лучше всего Scheme (он-же Racket)
для .Net - F# - это такой обыдленный упрощенный до платформы Haskell (или адаптированный под .Net OCaml).
Но Хаскель тоже весьма хорош.
Название: Re: Выноска с привязкой к блоку
Отправлено: Александр Ривилис от 26-11-2014, 00:21:06
Если одновременно с двумя документами - то не всегда (иногда проще передать транзакцию чем ее DB).
Не понял о чем это ты, если у каждого документа свой TransactionManager и соответственно своя TopTransaction.

Не всегда. Порой нужно откатывать лишь некоторые изменения, в то же время сохраняя другие.
Не смеши меня. Сколько раз во всех своих программах ты используешь откат транзакции? И помнишь ли ты, что откат транзакции значительно длительнее чем её Commit.

Андрей Бушман, Дима_, ну вы и наоффтопили...
Название: Re: Выноска с привязкой к блоку
Отправлено: Дима_ от 26-11-2014, 00:28:09
вот такие они - холодные зимние питерские вечера...
Название: Re: Выноска с привязкой к блоку
Отправлено: Дима_ от 26-11-2014, 00:42:57
А по первому, я в прошлом не раз сталкивался с тем, что в переменную передавалась ссылка на объект, который к тому времени уже прекращал своё существование в виду того, что точка выполнения кода уже покинула блок using транзакции
Согласен, я уж подзабыл - просто в F# надо специально сильно постараться чтоб так получилось - т.к. там переменных по умолчанию нет - и посему проблемы такой тоже (надо либо создавать переменную, либо вручную dispos'ировать транзакцию).
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 26-11-2014, 10:21:13
Сколько раз во всех своих программах ты используешь откат транзакции?
Честно говоря, так сходу и не вспомню где и когда. Соответственно, если и использовал, то очень редко. :)

И помнишь ли ты, что откат транзакции значительно длительнее чем её Commit.
Конечно помню. Потому и дёргаю Commit даже когда выполнял только лишь чтение.
Название: Re: Выноска с привязкой к блоку
Отправлено: Дмитрий Загорулькин от 26-11-2014, 10:24:25
А Database.TransactionManager.TopTransaction тебя не устроит для этой цели?
Мне почему-то хочется иметь ссылку именно на ту транзакцию, в которой получен передаваемый объект. Она же тоже может быть вложенной. Она также может быть OpenCloseTransaction, выдаст ли Database.TransactionManager.TopTransaction ссылку на нее? Я не знаю, надо экспериментировать...
Название: Re: Выноска с привязкой к блоку
Отправлено: Андрей Бушман от 26-11-2014, 10:58:11
Мне почему-то хочется иметь ссылку именно на ту транзакцию, в которой получен передаваемый объект. Она же тоже может быть вложенной.
Используя TopTransaction можно спокойно обойтись без вложенных транзакций. Но здесь есть подводный камень: а что, если TopTransaction не существует? Мало ли из какого кода вызван твой метод. Не исключено, что программист, вызывает твой метод не из контекста транзакции. В этом случае нужно либо документировать это требование, либо выполнять проверку существования транзакции. Т.е. может возникнуть потребность в том, чтобы проверять TopTransaction на null и если получаем "бинго!!!", то формировать соответствующий блоко using. На мой взгляд, в подобных случаях использование TopTransaction создаёт дополнительную нежелательную(???) зависимость (зависимость от контекста). В то же время, если твой метод внутренний и ты сам полностью контролируешь где и как он вызывается, то использовать TopTransaction, на мой взгляд, приемлемо.

Однако, если взять за правило работать с объектами исключительно либо через транзакцию, либо через её эмуляцию (что я считаю абсолютно надёжным и верным решением), то вероятность того, что TopTransaction будет null - крайне мала. Более того, если эта ошибка не будет обработана и выскочит, то это будет сигналом программеру, что в его коде ошибка (т.е. лишнее напоминание о том, чтобы он работал через транзакцию или эмуляцию).
Она также может быть OpenCloseTransaction, выдаст ли Database.TransactionManager.TopTransaction ссылку на нее? Я не знаю, надо экспериментировать...
OpenCloseTransaction наследуется от Transaction. Поскольку путём введения в API класса OpenCloseTransaction Autodesk тем самым постарался сделать единообразной работу как в транзакции, так и в её эмуляции, то вполне логично ожидать, что в обозначенной тобой ситуации будет возвращена ссылка на экземпляр OpenCloseTransaction.

UPD Если в своём коде ты не создавал транзакцию, то результат, полученный из TopTransaction можно проверять на null, как обозначено выше, либо не проверять, заставляя тем самым программера работать в контексте транзакции или эмуляции (этакий рычаг воздействия).