Проблемы с созданием дуговой трубы с центральным углом дуги больше 180 градусов.

Автор Тема: Проблемы с созданием дуговой трубы с центральным углом дуги больше 180 градусов.  (Прочитано 521 раз)

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

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2163
  • Карма: 583
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Заглянул в COM. Там то же самое:
Код - C# [Выбрать]
  1. namespace Autodesk.AECC.Interop.Pipe
  2. {
  3.     [DefaultMember("Item")]
  4.     [Guid("E936001C-0084-4186-8DF9-3D5372B7DC57")]
  5.     [TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)]
  6.     public interface IAeccPipes : IEnumerable
  7.     {
  8.         [DispId(1700)]
  9.         AeccPipe Add(string bstrPartFamilyGuid, AeccPartSizeFilter piPartSizeFilter, object varStartPoint, object varEndPoint);
  10.         [DispId(1703)]
  11.         AeccPipe AddCurvedPipe(string bstrPartFamilyGuid, AeccPartSizeFilter piPartSizeFilter, object varStartPoint, object varEndPoint, double dCurveRadius, bool bClockwise);
  12.         [DispId(1701)]
  13.         void Remove(object varIndexOrName);
  14.         [DispId(0)]
  15.         AeccPipe Item(object Index);
  16.         [DispId(-4)]
  17.         [TypeLibFunc(TypeLibFuncFlags.FRestricted | TypeLibFuncFlags.FHidden)]
  18.         IEnumerator GetEnumerator();
  19.  
  20.         [DispId(1702)]
  21.         int Count { get; }
  22.     }
  23. }

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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
А Вы какие файлы исследовали? У Civil функции сильно раскиданы по файлам.
Все arx/dbx/dll/crx.
Кстати, интересный факт. Для PressurePipeNetwork одноимённый метод работает через три точки, что обеспечивает ему однозначность:
Код - C# [Выбрать]
  1. // Autodesk.Civil.DatabaseServices.PressurePipeNetwork
  2. public unsafe ObjectId AddCurvePipe(Point3d startPoint, Point3d secondPoint, Point3d endPoint, PressurePartSize partSize)
  3. {
  4.         string paramName = "partSize";
  5.         if (null == partSize)
  6.         {
  7.                 throw new ArgumentNullException(paramName);
  8.         }
  9.         AcGePoint3d acGePoint3d;
  10.         <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d, startPoint);
  11.         AcGePoint3d acGePoint3d2;
  12.         <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d2, secondPoint);
  13.         AcGePoint3d acGePoint3d3;
  14.         <Module>.Autodesk.Civil.ToAcGePoint3d(&acGePoint3d3, endPoint);
  15.         AcDbObjectId ?kNull@AcDbObjectId@@2V1@B = <Module>.?kNull@AcDbObjectId@@2V1@B;
  16.         AeccPressurePart* impObj = partSize.GetImpObj();
  17.         <Module>.Autodesk.Civil.Checker.Check((Acad.ErrorStatus)<Module>.AeccDbPressurePipeNetwork.addCurvePipe(this.GetImpObj(), ref acGePoint3d, ref acGePoint3d2, ref acGePoint3d3, impObj, ref ?kNull@AcDbObjectId@@2V1@B), "Fail to add a curve pipe with the specified start point, second point and end point.");
  18.         return <Module>.ToObjectId(ref ?kNull@AcDbObjectId@@2V1@B);
  19. }
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
Заглянул в COM. Там то же самое:
Мелькнула мысль, что возможно играет роль знак радиуса. Попробуй поиграться с ним. Но это можно только через COM.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
Уговорили, сделал. Case ID: 15976503
Отлично! Будет наверно задержка в связи с Autodesk University, но очень интересно услышать результат.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение


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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
Исключение с последующим фаталом.
При положительных значениях не вылетает, но строит не то, что надо?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение


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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
При положительных - ведёт себя точно так же как и .NET метод.
Значит отбросили очередной вариант.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2163
  • Карма: 583
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Ееееееееееееехоу!! Кажисть, поборол! Ещё погоняю на разных вариантах, но пока, вроде как, работает как надо.  8)
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using Autodesk.Civil.DatabaseServices;
  7. using Autodesk.Civil.DatabaseServices.Styles;
  8. using System;
  9. using System.Collections.Generic;
  10.  
  11. namespace C3dTest
  12. {
  13.     public class CreateCurvePipeTest
  14.     {
  15.         [CommandMethod("CreatePipeFromArc")]
  16.         public void CreatePipeFromArc()
  17.         {
  18.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  19.             Editor ed = adoc.Editor;
  20.             Database db = adoc.Database;
  21.  
  22.             PromptEntityOptions partOps
  23.                 = new PromptEntityOptions("\nSelect network part: ");
  24.             partOps.SetRejectMessage("It's not part!");
  25.             partOps.AddAllowedClass(typeof(Part), false);
  26.             PromptEntityResult partRes = ed.GetEntity(partOps);
  27.             if (partRes.Status != PromptStatus.OK) return;
  28.  
  29.             PromptEntityOptions arcOpts
  30.                 = new PromptEntityOptions("\nSelect arc: ");
  31.             arcOpts.SetRejectMessage("It's not arc!");
  32.             arcOpts.AddAllowedClass(typeof(Arc), true);
  33.             PromptEntityResult arcRes = ed.GetEntity(arcOpts);
  34.             if (arcRes.Status != PromptStatus.OK) return;
  35.  
  36.             ObjectId
  37.                 netId,
  38.                 famId = default,
  39.                 sizeId = default;
  40.  
  41.             using (Transaction tr = db.TransactionManager.StartTransaction())
  42.             {
  43.                 Part part = tr.GetObject
  44.                     (partRes.ObjectId, OpenMode.ForRead) as Part;
  45.  
  46.                 netId = part.NetworkId;
  47.  
  48.                 Network net = tr.GetObject
  49.                     (netId, OpenMode.ForRead) as Network;
  50.  
  51.                 ObjectId partsListId = net.PartsListId;
  52.  
  53.                 PartsList partsList
  54.                     = tr.GetObject(partsListId, OpenMode.ForRead) as PartsList;
  55.  
  56.                 using (ObjectIdCollection pipeFamsIds
  57.                     = partsList.GetPartFamilyIdsByDomain(DomainType.Pipe))
  58.                 {
  59.                     if (pipeFamsIds.Count > 0)
  60.                     {
  61.                         famId = pipeFamsIds[0];
  62.                     }
  63.                 }
  64.  
  65.                 if (famId.IsValid)
  66.                 {
  67.                     PartFamily partFamily
  68.                         = tr.GetObject(famId, OpenMode.ForRead) as PartFamily;
  69.                     if (partFamily.PartSizeCount > 0)
  70.                     {
  71.                         sizeId = partFamily[0];
  72.                     }
  73.                 }
  74.  
  75.                 tr.Commit();
  76.             }
  77.  
  78.             if (!famId.IsValid || !sizeId.IsValid)
  79.             {
  80.                 return;
  81.             }
  82.  
  83.             ObjectId newPipeId = default;
  84.             Point3d? endPoint = null;
  85.             List<Point3d> tmp = new List<Point3d>();          
  86.  
  87.             using (Transaction tr = db.TransactionManager.StartTransaction())
  88.             {
  89.                 Arc arc = tr.GetObject(arcRes.ObjectId, OpenMode.ForRead) as Arc;              
  90.  
  91.                 CircularArc3d arc3dForCreate;
  92.  
  93.                 bool isClockWise;
  94.  
  95.                 using (CircularArc3d arc3dForSupport = arc.GetGeCurve() as CircularArc3d)
  96.                 {
  97.                     // Если дуга больше полукруга
  98.                     if ((arc3dForSupport.EndAngle - arc3dForSupport.StartAngle) > Math.PI)
  99.                     {
  100.                         // Идея такая. Сперва создаём трубу полукругом, а затем конечную точку
  101.                         // тащим к тому месту, где она должна быть. При этом, капризное API не
  102.                         // даёт сразу перетащить эту точку. Поэтому, перетаскивание делается
  103.                         // через череду промежуточных точек. Причём, чем больше получается итоговый
  104.                         // центральный угол дуги, тем меньше нужно делать шаг между точками.
  105.  
  106.                         // Сохраняем конечную точку. Одновременно это будет флагом того, что
  107.                         // полученную изначально трубу надо будет дотягивать до этой точки
  108.                         endPoint = arc3dForSupport.EndPoint;
  109.                        
  110.                         double
  111.                             // Почему-то иногда метод отказывается строить полный полукруг.
  112.                             // Поэтому чуть-чуть его уменьшаем
  113.                             tmpTotalAngle = 0.99 * Math.PI,
  114.                             tmpEndAngle = arc3dForSupport.StartAngle + tmpTotalAngle;
  115.                        
  116.                         arc3dForCreate = new CircularArc3d
  117.                             (arc3dForSupport.Center,
  118.                             arc3dForSupport.Normal,
  119.                             arc3dForSupport.ReferenceVector,
  120.                             arc3dForSupport.Radius,
  121.                             arc3dForSupport.StartAngle,
  122.                             tmpEndAngle);
  123.  
  124.                         // Начальный угловой шаг для вычисления точек на дуге
  125.                         double
  126.                             step = Math.PI / 6.0,
  127.                             // Пороговое значение центрального угла дуги
  128.                             // для первого уменьшения шага
  129.                             stage1 = Math.PI * 1.5,
  130.                             // Пороговое значение центрального угла дуги
  131.                             // для второго уменьшения шага
  132.                             stage2 = Math.PI * 11.0 / 6.0;
  133.                        
  134.                         while ((arc3dForSupport.EndAngle - tmpEndAngle) > step)
  135.                         {
  136.                             tmpTotalAngle += step;
  137.                             tmpEndAngle += step;
  138.                             tmp.Add(arc3dForSupport.EvaluatePoint(tmpEndAngle));
  139.  
  140.                             // Чем больше итоговая дуга - тем меньше шаг
  141.                             if (tmpTotalAngle > stage2)
  142.                             {
  143.                                 step = Math.PI / 36.0;
  144.                             }
  145.                             else if (tmpTotalAngle > stage1)
  146.                             {
  147.                                 step = Math.PI / 9;
  148.                             }                            
  149.                         }                        
  150.                     }
  151.                     else
  152.                     {
  153.                         arc3dForCreate = arc3dForSupport.Clone() as CircularArc3d;
  154.                     }
  155.  
  156.                     Plane xy = new Plane(Point3d.Origin, Vector3d.ZAxis);
  157.                     Point3d mid = arc3dForSupport.EvaluatePoint
  158.                         ((arc3dForSupport.EndAngle + arc3dForSupport.StartAngle) / 2.0);
  159.                     using (CircularArc2d arc2d = new CircularArc2d
  160.                         (arc3dForSupport.StartPoint.Convert2d(xy),
  161.                         mid.Convert2d(xy),
  162.                         arc3dForSupport.EndPoint.Convert2d(xy)))
  163.                     {
  164.                         isClockWise = arc2d.IsClockWise;
  165.                     }
  166.                 }
  167.  
  168.                 Network net = tr.GetObject(netId, OpenMode.ForWrite) as Network;
  169.  
  170.                 using (arc3dForCreate)
  171.                 {
  172.                     net.AddCurvePipe
  173.                     (famId,
  174.                     sizeId,
  175.                     arc3dForCreate,
  176.                     isClockWise,
  177.                     ref newPipeId,
  178.                     false);
  179.                 }
  180.  
  181.                 tr.Commit();
  182.             }
  183.  
  184.             if (newPipeId.IsValid && endPoint.HasValue)
  185.             {
  186.                 foreach (Point3d tmpPt in tmp)
  187.                 {
  188.                     using (Transaction tr = db.TransactionManager.StartTransaction())
  189.                     {
  190.                         Pipe pipe = tr.GetObject(newPipeId, OpenMode.ForWrite) as Pipe;
  191.                         pipe.EndPoint = tmpPt;
  192.                         tr.Commit();
  193.                     }
  194.                 }
  195.  
  196.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  197.                 {
  198.                     Pipe pipe = tr.GetObject(newPipeId, OpenMode.ForWrite) as Pipe;
  199.                     pipe.EndPoint = endPoint.Value;
  200.                     tr.Commit();
  201.                 }                
  202.             }
  203.         }
  204.     }
  205. }
  206.  

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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
Дмитрий Загорулькин,
Красиво!
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2163
  • Карма: 583
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Красиво!
Да это костыль на костыле: Не даёте построить большую дугу? - Построим поменьше и дотянем! Не даёте сразу дотянуть? - Дотянем кусками! Не даёте большими кусками тянуть под конец? - Дотянем маленькими! Голь на выдумки хитра. :D
Но, в целом, вроде, костыли эти сработали. Спасибо Вам за помощь!

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

  • Administrator
  • *****
  • Сообщений: 10373
  • Карма: 1314
  • Рыцарь ObjectARX
  • Skype: rivilis
Да это костыль на костыле:
Тем не менее тебе удалось обойти баг, который казался неисправимым.
Спасибо Вам за помощь!
Тут полностью твоя заслуга. :)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Дмитрий Загорулькин 22-11-2019, 18:39:09

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2163
  • Карма: 583
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Итоговый на данный момент вид метода для добавления криволинейной трубы в сеть:
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Создание дуговой трубы в сети
  3. /// </summary>
  4. /// <param name="network">Сеть, открытая для записи транзакцией</param>
  5. /// <param name="pipeFamilyId">Id семейства трубы</param>
  6. /// <param name="pipeSizeId">Id размера трубы</param>
  7. /// <param name="arc2d">Дуга-прототип</param>
  8. /// <param name="startPointZ">Координата Z начальной точки трубы</param>
  9. /// <param name="endPointZ">Координата Z конечной точки трубы</param>
  10. /// <param name="applyRules">Применить правила</param>
  11. /// <returns>Id новой трубы, при неудаче ObjectId.Null</returns>        
  12. public static ObjectId AddCurvePipe
  13.     (this Network network,
  14.     ObjectId pipeFamilyId,
  15.     ObjectId pipeSizeId,
  16.     CircularArc2d arc2d,
  17.     double startPointZ = 0.0,
  18.     double endPointZ = 0.0,
  19.     bool applyRules = false)
  20. {
  21.     ObjectId newPipeId = default;
  22.     bool needExtend = false;
  23.     List<Point2d> tmpPoints = new List<Point2d>();
  24.  
  25.     Point3d GetPoint3d(Point2d point2d, double z = 0.0)
  26.         => new Point3d(point2d.X, point2d.Y, z);
  27.  
  28.     // Вычисляем точку на дуге, чтобы она оказалась в
  29.     // промежутке до 180 градусов от начальной точки          
  30.     Point2d pointOnArc = arc2d.EvaluatePoint
  31.         ((arc2d.EndAngle - arc2d.StartAngle) * 0.3 + arc2d.StartAngle);
  32.  
  33.     CircularArc3d tmpArc3d;
  34.  
  35.     bool isClockWise = arc2d.IsClockWise;
  36.  
  37.     // Если дуга - полукруг или больше
  38.     if ((arc2d.EndAngle - arc2d.StartAngle) >= Math.PI && !arc2d.IsClosed())
  39.     {
  40.         // Идея такая. Сперва создаём трубу полукругом, а затем конечную точку
  41.         // тащим к тому месту, где она должна быть. При этом, капризное API не
  42.         // даёт сразу перетащить эту точку. Поэтому, перетаскивание делается
  43.         // через череду промежуточных точек. Причём, чем больше получается итоговый
  44.         // центральный угол дуги, тем меньше нужно делать шаг между точками.
  45.  
  46.         // Флаг того, что полученную изначально трубу
  47.         // надо будет дотягивать до конечной точки
  48.         needExtend = true;
  49.  
  50.         double
  51.             // Почему-то иногда метод отказывается строить полный полукруг.
  52.             // Поэтому чуть-чуть его уменьшаем
  53.             tmpTotalAngle = 0.99 * Math.PI,
  54.             tmpEndAngle = arc2d.StartAngle + tmpTotalAngle;
  55.  
  56.         Point2d tmpEndPoint = arc2d.EvaluatePoint(tmpEndAngle);
  57.  
  58.         tmpArc3d = new CircularArc3d
  59.             (GetPoint3d(arc2d.StartPoint),
  60.             GetPoint3d(pointOnArc),
  61.             GetPoint3d(tmpEndPoint));
  62.  
  63.         // Начальный угловой шаг для вычисления точек на дуге
  64.         double
  65.             step = Math.PI / 6.0,
  66.             // Пороговое значение центрального угла дуги
  67.             // для первого уменьшения шага
  68.             stage1 = Math.PI * 1.5,
  69.             // Пороговое значение центрального угла дуги
  70.             // для второго уменьшения шага
  71.             stage2 = Math.PI * 11.0 / 6.0;
  72.  
  73.         while ((arc2d.EndAngle - tmpEndAngle) > step)
  74.         {
  75.             tmpEndAngle += step;
  76.             tmpPoints.Add(arc2d.EvaluatePoint(tmpEndAngle));
  77.  
  78.             // Чем больше итоговая дуга - тем меньше шаг
  79.             tmpTotalAngle = tmpEndAngle - arc2d.StartAngle;
  80.  
  81.             if (tmpTotalAngle > stage2)
  82.             {
  83.                 step = Math.PI / 36.0;
  84.             }
  85.             else if (tmpTotalAngle > stage1)
  86.             {
  87.                 step = Math.PI / 9;
  88.             }
  89.         }
  90.     }
  91.     else
  92.     {
  93.         tmpArc3d = new CircularArc3d
  94.             (GetPoint3d(arc2d.StartPoint),
  95.             GetPoint3d(pointOnArc),
  96.             GetPoint3d(arc2d.EndPoint));
  97.     }
  98.  
  99.     using (tmpArc3d)
  100.     {
  101.         try
  102.         {
  103.             network.AddCurvePipe
  104.                 (pipeFamilyId,
  105.                 pipeSizeId,
  106.                 tmpArc3d,
  107.                 isClockWise,
  108.                 ref newPipeId,
  109.                 applyRules);
  110.         }
  111.         catch (System.Exception ex)
  112.         {
  113.             Debug.WriteLine(ex.Message);
  114.             Debug.WriteLine(ex.StackTrace);
  115.         }
  116.     }
  117.  
  118.     if (newPipeId.IsValid)
  119.     {
  120.         // https://adn-cis.org/forum/index.php?topic=8589.0
  121.         // Раз добавление трубы в сеть не работает если
  122.         // не запущена транзакция - воспользуемся этим
  123.         Pipe pipe = newPipeId.GetObject(OpenMode.ForWrite) as Pipe;
  124.  
  125.         if (needExtend)
  126.         {
  127.             // Задаём промежуточные точки
  128.             foreach (Point2d tmpPt in tmpPoints)
  129.             {
  130.                 pipe.EndPoint = GetPoint3d(tmpPt);
  131.             }
  132.         }
  133.  
  134.         // Задаём конечные точки с отметками
  135.         pipe.StartPoint = GetPoint3d
  136.             (arc2d.StartPoint, startPointZ);
  137.  
  138.         pipe.EndPoint = GetPoint3d
  139.             (arc2d.EndPoint, endPointZ);
  140.     }
  141.  
  142.     return newPipeId;
  143. }
  144.  
Тут полностью твоя заслуга.
Я благодарю за то что помогали в поисках! Так гораздо легче, чем когда один на один с проблемой разбираешься! Так что, не скромничайте  ;) Спасибо!

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2163
  • Карма: 583
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
При этом, капризное API не
        // даёт сразу перетащить эту точку. Поэтому, перетаскивание делается
        // через череду промежуточных точек. Причём, чем больше получается итоговый
        // центральный угол дуги, тем меньше нужно делать шаг между точками.
Похоже, придётся ещё один кейс отправлять в DevHelp и ещё пару "обёрток" корректирующих делать. В моём приложении много где используется изменение начальной-конечной точки трубы (около 15 вызовов). И если встречается такая "сильноизогнутая" труба, то изменение свойства Pipe.StartPoint и/или Pipe.EndPoint не срабатывает.  :-\


Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2163
  • Карма: 583
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Пришёл ответ: да, метод может строить только дугу до 180 градусов. Направили запрос команде разработчиков на улучшение этого API. Опять просят опросник заполнить... Признаться, что уже нашёлся костыль? :)