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

20/11/2014

Использование метода GetCameraInfo

Недавно мы обсуждали как можно получить информация о камере в 3D-виде. Единственный способ это сделать – использовать класс CustomExporter, который используется для реализации собственного модуля экспорта модели. Метод не совсем очевидный, но другого пути пока нет.

В конструктор класса CustomExporter передается класс, реализующий интерфейс IExportContext, в котором собственно и содержится набор методов, которые будут вызваны при экспорте модели.

Один из методов – OnViewBegin – принимает в качестве параметра объект класса ViewNode. А этот класс уже содержит метод GetCameraInfo(), который нам и нужен.

Собственно, задача состоит в том, чтобы создать класс, реализующий интерфейс IExportContext, при вызове метода OnViewBegin – получить CameraInfo и на этом прекратить экспорт, так как он нам не нужен.

Что ж, первое что необходимо сделать – создать класс, реализующий интерфейс.

Код - C#: [Выделить]
  1.         class DummyExportContext: IExportContext
  2.         {
  3.             public bool Start()
  4.             {
  5.                 throw new NotImplementedException();
  6.             }
  7.  
  8.             public void Finish()
  9.             {
  10.                 throw new NotImplementedException();
  11.             }
  12.  
  13.             public bool IsCanceled()
  14.             {
  15.                 throw new NotImplementedException();
  16.             }
  17.  
  18.             public RenderNodeAction OnViewBegin(ViewNode node)
  19.             {
  20.                 throw new NotImplementedException();
  21.             }
  22.  
  23.             public void OnViewEnd(ElementId elementId)
  24.             {
  25.                 throw new NotImplementedException();
  26.             }
  27.  
  28.             public RenderNodeAction OnElementBegin(ElementId elementId)
  29.             {
  30.                 throw new NotImplementedException();
  31.             }
  32.  
  33.             public void OnElementEnd(ElementId elementId)
  34.             {
  35.                 throw new NotImplementedException();
  36.             }
  37.  
  38.             public RenderNodeAction OnInstanceBegin(InstanceNode node)
  39.             {
  40.                 throw new NotImplementedException();
  41.             }
  42.  
  43.             public void OnInstanceEnd(InstanceNode node)
  44.             {
  45.                 throw new NotImplementedException();
  46.             }
  47.  
  48.             public RenderNodeAction OnLinkBegin(LinkNode node)
  49.             {
  50.                 throw new NotImplementedException();
  51.             }
  52.  
  53.             public void OnLinkEnd(LinkNode node)
  54.             {
  55.                 throw new NotImplementedException();
  56.             }
  57.  
  58.             public RenderNodeAction OnFaceBegin(FaceNode node)
  59.             {
  60.                 throw new NotImplementedException();
  61.             }
  62.  
  63.             public void OnFaceEnd(FaceNode node)
  64.             {
  65.                 throw new NotImplementedException();
  66.             }
  67.  
  68.             public void OnRPC(RPCNode node)
  69.             {
  70.                 throw new NotImplementedException();
  71.             }
  72.  
  73.             public void OnLight(LightNode node)
  74.             {
  75.                 throw new NotImplementedException();
  76.             }
  77.  
  78.             public void OnDaylightPortal(DaylightPortalNode node)
  79.             {
  80.                 throw new NotImplementedException();
  81.             }
  82.  
  83.             public void OnMaterial(MaterialNode node)
  84.             {
  85.                 throw new NotImplementedException();
  86.             }
  87.  
  88.             public void OnPolymesh(PolymeshTopology node)
  89.             {
  90.                 throw new NotImplementedException();
  91.             }
  92.         }

Нам нужен метод OnViewBegin. Получим в нем CameraInfo и прекратим экспорт. Метод вызывается только один раз. Естественно, полученный объект CameraInfo нам нужно сохранить, чтобы потом до него добраться. Для этого создадим поле и свойство

Код - C#: [Выделить]
  1.             private CameraInfo _cameraInfo;
  2.             public CameraInfo CameraInfo
  3.             {
  4.                 get { return _cameraInfo; }
  5.             }

Для того, чтобы прервать операцию экспорта, после того как мы получили информацию о камере, необходимо чтобы метод IsCanceled возвратил true. Для этого создадим поле _isCanseled и в методе IsCanceled будем возвращать значение этой переменной. Переменной _isCanceled зададим значение true сразу же после того, как мы получим CameraInfo в методе OnViewBegin.

Код - C#: [Выделить]
  1.             private bool _isCanceled = false;
  2.             public bool IsCanceled()
  3.             {
  4.                 Debug.Print(GetMethodName());
  5.                 return _isCanceled;
  6.             }
  7.             public RenderNodeAction OnViewBegin(ViewNode node)
  8.             {
  9.                 Debug.Print(GetMethodName());
  10.                 _cameraInfo = node.GetCameraInfo();
  11.  
  12.                 _isCanceled = true;
  13.                    
  14.                 return RenderNodeAction.Skip;
  15.             }

Так как мне было интересно, в каком порядке какие методы вызываются и когда, я в каждый метод добавил вывод имени класса в консоль Output.

Код - C#: [Выделить]
  1. Debug.Print(GetMethodName());

Метод GetMethodName выглядит следующим образом:

Код - C#: [Выделить]
  1.  
  2.             private string GetMethodName()
  3.             {
  4.                 StackFrame frame = new StackFrame(1);
  5.                 var method = frame.GetMethod();
  6.                 var type = method.DeclaringType;
  7.                 var name = method.Name;
  8.  
  9.                 return name;
  10.             }

Целиком код класса DummyExportContext приведен ниже:

Код - C#: [Выделить]
  1.         class DummyExportContext : IExportContext
  2.         {
  3.             private CameraInfo _cameraInfo;
  4.  
  5.             private bool _isCanceled = false;
  6.  
  7.             public CameraInfo CameraInfo
  8.             {
  9.                 get { return _cameraInfo; }
  10.             }
  11.  
  12.             public bool Start()
  13.             {
  14.                 Debug.Print(GetMethodName());
  15.                 return true;
  16.             }
  17.  
  18.             public void Finish()
  19.             {
  20.                 Debug.Print(GetMethodName());
  21.             }
  22.  
  23.             public bool IsCanceled()
  24.             {
  25.                 Debug.Print(GetMethodName());
  26.                 return _isCanceled;
  27.             }
  28.  
  29.             public RenderNodeAction OnViewBegin(ViewNode node)
  30.             {
  31.                 Debug.Print(GetMethodName());
  32.                 _cameraInfo = node.GetCameraInfo();
  33.  
  34.                 _isCanceled = true;
  35.                    
  36.                 return RenderNodeAction.Skip;
  37.             }
  38.  
  39.             public void OnViewEnd(ElementId elementId)
  40.             {
  41.                 Debug.Print(GetMethodName());
  42.             }
  43.  
  44.             public RenderNodeAction OnElementBegin(ElementId elementId)
  45.             {
  46.                 Debug.Print(GetMethodName());
  47.                 return RenderNodeAction.Skip;
  48.             }
  49.  
  50.             public void OnElementEnd(ElementId elementId)
  51.             {
  52.                 Debug.Print(GetMethodName());
  53.             }
  54.  
  55.             public RenderNodeAction OnInstanceBegin(InstanceNode node)
  56.             {
  57.                 Debug.Print(GetMethodName());
  58.                 return RenderNodeAction.Skip;
  59.             }
  60.  
  61.             public void OnInstanceEnd(InstanceNode node)
  62.             {
  63.                 Debug.Print(GetMethodName());
  64.             }
  65.  
  66.             public RenderNodeAction OnLinkBegin(LinkNode node)
  67.             {
  68.                 Debug.Print(GetMethodName());
  69.                 return RenderNodeAction.Skip;
  70.             }
  71.  
  72.             public void OnLinkEnd(LinkNode node)
  73.             {
  74.                 Debug.Print(GetMethodName());
  75.             }
  76.  
  77.             public RenderNodeAction OnFaceBegin(FaceNode node)
  78.             {
  79.                 Debug.Print(GetMethodName());
  80.                 return RenderNodeAction.Skip;
  81.             }
  82.  
  83.             public void OnFaceEnd(FaceNode node)
  84.             {
  85.                 Debug.Print(GetMethodName());
  86.             }
  87.  
  88.             public void OnRPC(RPCNode node)
  89.             {
  90.                 Debug.Print(GetMethodName());
  91.             }
  92.  
  93.             public void OnLight(LightNode node)
  94.             {
  95.                 Debug.Print(GetMethodName());
  96.             }
  97.  
  98.             public void OnDaylightPortal(DaylightPortalNode node)
  99.             {
  100.                 Debug.Print(GetMethodName());
  101.             }
  102.  
  103.             public void OnMaterial(MaterialNode node)
  104.             {
  105.                 Debug.Print(GetMethodName());
  106.             }
  107.  
  108.             public void OnPolymesh(PolymeshTopology node)
  109.             {
  110.                 Debug.Print(GetMethodName());
  111.             }
  112.  
  113.             private string GetMethodName()
  114.             {
  115.                 StackFrame frame = new StackFrame(1);
  116.                 var method = frame.GetMethod();
  117.                 var type = method.DeclaringType;
  118.                 var name = method.Name;
  119.  
  120.                 return name;
  121.             }
  122.         }

Чтобы в итоге воспользоваться нашим классом, необходимо создать объект этого класса, передать его в конструктор класса CustomExporter и вызвать метод Export(). После этого, получить из объекта класса DummyExportContext свойство CameraInfo.

Код - C#: [Выделить]
  1.             var context = new DummyExportContext();
  2.             CustomExporter customExporter = new CustomExporter(view3D.Document, context);
  3.  
  4.             customExporter.Export(view3D);
  5.  
  6.             var cameraInfo = context.CameraInfo;

Для удобства использования я создал метод-расширение GetCameraInfo для класса View3D.

Код - C#: [Выделить]
  1.     public static class View3DHelper
  2.     {
  3.         public static CameraInfo GetCameraInfo(this View3D view3D)
  4.         {
  5.             var context = new DummyExportContext();
  6.             CustomExporter customExporter = new CustomExporter(view3D.Document, context);
  7.  
  8.             customExporter.Export(view3D);
  9.  
  10.             var cameraInfo = context.CameraInfo;
  11.  
  12.             return cameraInfo;
  13.         }
  14. }

Теперь, для того чтобы получить информацию о камере в 3D-виде, достаточно просто вызвать метод View3D.GetCameraInfo().

Для демонстрации напишем простенькую команду:

Код - C#: [Выделить]
  1.     [Transaction(TransactionMode.Manual)]
  2.     public class Command : IExternalCommand
  3.     {
  4.         public Result Execute(
  5.           ExternalCommandData commandData,
  6.           ref string message,
  7.           ElementSet elements)
  8.         {
  9.             UIApplication uiapp = commandData.Application;
  10.             UIDocument uidoc = uiapp.ActiveUIDocument;
  11.  
  12.             var activeView = uidoc.ActiveView as View3D;
  13.  
  14.             if (activeView == null)
  15.             {
  16.                 message = "Перейдите в 3D-вид";
  17.                 return Result.Failed;
  18.             }          
  19.             var cameraInfo = activeView.GetCameraInfo();
  20.            
  21.             StringBuilder sb = new StringBuilder();
  22.             sb.AppendLine(string.Format("FarDistance: {0}", cameraInfo.TryGetValue(x=>x.FarDistance, double.NaN)));
  23.             //sb.AppendLine(string.Format("HorizontalExtent: {0}", cameraInfo.TryGetValue(x => x.HorizontalExtent, double.NaN)));
  24.             sb.AppendLine(string.Format("IsPespective: {0}", cameraInfo.TryGetValue(x=>x.IsPespective)));
  25.             sb.AppendLine(string.Format("IsValidObject: {0}", cameraInfo.TryGetValue(x=>x.IsValidObject)));
  26.             sb.AppendLine(string.Format("NearDistance: {0}", cameraInfo.TryGetValue(x => x.NearDistance, double.NaN)));
  27.             sb.AppendLine(string.Format("RightOffset: {0}", cameraInfo.TryGetValue(x => x.RightOffset, double.NaN)));
  28.             sb.AppendLine(string.Format("TargetDistance: {0}", cameraInfo.TryGetValue(x => x.TargetDistance, double.NaN)));
  29.             sb.AppendLine(string.Format("UpOffset: {0}", cameraInfo.TryGetValue(x => x.UpOffset, double.NaN)));
  30.             //sb.AppendLine(string.Format("VerticalExtent: {0}", cameraInfo.TryGetValue(x => x.VerticalExtent, double.NaN)));
  31.             TaskDialog.Show("CameraInfo", sb.ToString());
  32.  
  33.             return Result.Succeeded;
  34.         }
  35.     }

Результат выполнения команды:

 

В консоль Output будет выведено следующее:

Код - XML: [Выделить]
  1. Start
  2. IsCanceled
  3. OnViewBegin
  4. IsCanceled
  5. Finish

Немного поясню, что делает метод TryGetValue. Дело в том, что свойства HorizontalExtent и VerticalExtent  при попытке обратиться к ним падают с AccessViolationException. Для удобства, я реализовал метод TryGetValue, в котором я пытаюсь получить значение свойства, и если при обращении к свойству возникает исключение, то будет возвращено значение, переданное вторым параметром.

Код - C#: [Выделить]
  1.     public static class MayBeException
  2.     {
  3.         public static TResult TryGetValue<TSource, TResult>(this TSource source, Func<TSource, TResult> action, TResult valueIfExceptionIsThrown = default (TResult))
  4.         {
  5.             try
  6.             {
  7.                 var res = action(source);
  8.                 return res;
  9.             }
  10.             catch (Exception)
  11.             {
  12.                 return valueIfExceptionIsThrown;
  13.             }        
  14.         }
  15.     }

Но, дело в том, что AccessViolationException не удается корректно обработать, и не смотря на блок try, Revit просто молча закрывается. Строки пришлось закомментировать. А вызов метода остался.

Готовый проект для Visual Studio можно скачать по ссылке.

Автор: Виктор Чекалин
Автор перевода: Виктор Чекалин

Обсуждение: http://adn-cis.org/forum/index.php?topic=1094

Опубликовано 20.11.2014