Использование метода GetCameraInfo
Недавно мы обсуждали как можно получить информация о камере в 3D-виде. Единственный способ это сделать – использовать класс CustomExporter, который используется для реализации собственного модуля экспорта модели. Метод не совсем очевидный, но другого пути пока нет.
В конструктор класса CustomExporter передается класс, реализующий интерфейс IExportContext, в котором собственно и содержится набор методов, которые будут вызваны при экспорте модели.
Один из методов – OnViewBegin – принимает в качестве параметра объект класса ViewNode. А этот класс уже содержит метод GetCameraInfo(), который нам и нужен.
Собственно, задача состоит в том, чтобы создать класс, реализующий интерфейс IExportContext, при вызове метода OnViewBegin – получить CameraInfo и на этом прекратить экспорт, так как он нам не нужен.
Что ж, первое что необходимо сделать – создать класс, реализующий интерфейс.
- class DummyExportContext: IExportContext
- {
- public bool Start()
- {
- throw new NotImplementedException();
- }
- public void Finish()
- {
- throw new NotImplementedException();
- }
- public bool IsCanceled()
- {
- throw new NotImplementedException();
- }
- public RenderNodeAction OnViewBegin(ViewNode node)
- {
- throw new NotImplementedException();
- }
- public void OnViewEnd(ElementId elementId)
- {
- throw new NotImplementedException();
- }
- public RenderNodeAction OnElementBegin(ElementId elementId)
- {
- throw new NotImplementedException();
- }
- public void OnElementEnd(ElementId elementId)
- {
- throw new NotImplementedException();
- }
- public RenderNodeAction OnInstanceBegin(InstanceNode node)
- {
- throw new NotImplementedException();
- }
- public void OnInstanceEnd(InstanceNode node)
- {
- throw new NotImplementedException();
- }
- public RenderNodeAction OnLinkBegin(LinkNode node)
- {
- throw new NotImplementedException();
- }
- public void OnLinkEnd(LinkNode node)
- {
- throw new NotImplementedException();
- }
- public RenderNodeAction OnFaceBegin(FaceNode node)
- {
- throw new NotImplementedException();
- }
- public void OnFaceEnd(FaceNode node)
- {
- throw new NotImplementedException();
- }
- public void OnRPC(RPCNode node)
- {
- throw new NotImplementedException();
- }
- public void OnLight(LightNode node)
- {
- throw new NotImplementedException();
- }
- public void OnDaylightPortal(DaylightPortalNode node)
- {
- throw new NotImplementedException();
- }
- public void OnMaterial(MaterialNode node)
- {
- throw new NotImplementedException();
- }
- public void OnPolymesh(PolymeshTopology node)
- {
- throw new NotImplementedException();
- }
- }
Нам нужен метод OnViewBegin. Получим в нем CameraInfo и прекратим экспорт. Метод вызывается только один раз. Естественно, полученный объект CameraInfo нам нужно сохранить, чтобы потом до него добраться. Для этого создадим поле и свойство
- private CameraInfo _cameraInfo;
- public CameraInfo CameraInfo
- {
- get { return _cameraInfo; }
- }
Для того, чтобы прервать операцию экспорта, после того как мы получили информацию о камере, необходимо чтобы метод IsCanceled возвратил true. Для этого создадим поле _isCanseled и в методе IsCanceled будем возвращать значение этой переменной. Переменной _isCanceled зададим значение true сразу же после того, как мы получим CameraInfo в методе OnViewBegin.
- private bool _isCanceled = false;
- public bool IsCanceled()
- {
- Debug.Print(GetMethodName());
- return _isCanceled;
- }
- public RenderNodeAction OnViewBegin(ViewNode node)
- {
- Debug.Print(GetMethodName());
- _cameraInfo = node.GetCameraInfo();
- _isCanceled = true;
- return RenderNodeAction.Skip;
- }
Так как мне было интересно, в каком порядке какие методы вызываются и когда, я в каждый метод добавил вывод имени класса в консоль Output.
- Debug.Print(GetMethodName());
Метод GetMethodName выглядит следующим образом:
- private string GetMethodName()
- {
- StackFrame frame = new StackFrame(1);
- var method = frame.GetMethod();
- var type = method.DeclaringType;
- var name = method.Name;
- return name;
- }
Целиком код класса DummyExportContext приведен ниже:
- class DummyExportContext : IExportContext
- {
- private CameraInfo _cameraInfo;
- private bool _isCanceled = false;
- public CameraInfo CameraInfo
- {
- get { return _cameraInfo; }
- }
- public bool Start()
- {
- Debug.Print(GetMethodName());
- return true;
- }
- public void Finish()
- {
- Debug.Print(GetMethodName());
- }
- public bool IsCanceled()
- {
- Debug.Print(GetMethodName());
- return _isCanceled;
- }
- public RenderNodeAction OnViewBegin(ViewNode node)
- {
- Debug.Print(GetMethodName());
- _cameraInfo = node.GetCameraInfo();
- _isCanceled = true;
- return RenderNodeAction.Skip;
- }
- public void OnViewEnd(ElementId elementId)
- {
- Debug.Print(GetMethodName());
- }
- public RenderNodeAction OnElementBegin(ElementId elementId)
- {
- Debug.Print(GetMethodName());
- return RenderNodeAction.Skip;
- }
- public void OnElementEnd(ElementId elementId)
- {
- Debug.Print(GetMethodName());
- }
- public RenderNodeAction OnInstanceBegin(InstanceNode node)
- {
- Debug.Print(GetMethodName());
- return RenderNodeAction.Skip;
- }
- public void OnInstanceEnd(InstanceNode node)
- {
- Debug.Print(GetMethodName());
- }
- public RenderNodeAction OnLinkBegin(LinkNode node)
- {
- Debug.Print(GetMethodName());
- return RenderNodeAction.Skip;
- }
- public void OnLinkEnd(LinkNode node)
- {
- Debug.Print(GetMethodName());
- }
- public RenderNodeAction OnFaceBegin(FaceNode node)
- {
- Debug.Print(GetMethodName());
- return RenderNodeAction.Skip;
- }
- public void OnFaceEnd(FaceNode node)
- {
- Debug.Print(GetMethodName());
- }
- public void OnRPC(RPCNode node)
- {
- Debug.Print(GetMethodName());
- }
- public void OnLight(LightNode node)
- {
- Debug.Print(GetMethodName());
- }
- public void OnDaylightPortal(DaylightPortalNode node)
- {
- Debug.Print(GetMethodName());
- }
- public void OnMaterial(MaterialNode node)
- {
- Debug.Print(GetMethodName());
- }
- public void OnPolymesh(PolymeshTopology node)
- {
- Debug.Print(GetMethodName());
- }
- private string GetMethodName()
- {
- StackFrame frame = new StackFrame(1);
- var method = frame.GetMethod();
- var type = method.DeclaringType;
- var name = method.Name;
- return name;
- }
- }
Чтобы в итоге воспользоваться нашим классом, необходимо создать объект этого класса, передать его в конструктор класса CustomExporter и вызвать метод Export(). После этого, получить из объекта класса DummyExportContext свойство CameraInfo.
- var context = new DummyExportContext();
- CustomExporter customExporter = new CustomExporter(view3D.Document, context);
- customExporter.Export(view3D);
- var cameraInfo = context.CameraInfo;
Для удобства использования я создал метод-расширение GetCameraInfo для класса View3D.
- public static class View3DHelper
- {
- public static CameraInfo GetCameraInfo(this View3D view3D)
- {
- var context = new DummyExportContext();
- CustomExporter customExporter = new CustomExporter(view3D.Document, context);
- customExporter.Export(view3D);
- var cameraInfo = context.CameraInfo;
- return cameraInfo;
- }
- }
Теперь, для того чтобы получить информацию о камере в 3D-виде, достаточно просто вызвать метод View3D.GetCameraInfo().
Для демонстрации напишем простенькую команду:
- [Transaction(TransactionMode.Manual)]
- public class Command : IExternalCommand
- {
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements)
- {
- UIApplication uiapp = commandData.Application;
- UIDocument uidoc = uiapp.ActiveUIDocument;
- var activeView = uidoc.ActiveView as View3D;
- if (activeView == null)
- {
- message = "Перейдите в 3D-вид";
- return Result.Failed;
- }
- var cameraInfo = activeView.GetCameraInfo();
- StringBuilder sb = new StringBuilder();
- sb.AppendLine(string.Format("FarDistance: {0}", cameraInfo.TryGetValue(x=>x.FarDistance, double.NaN)));
- //sb.AppendLine(string.Format("HorizontalExtent: {0}", cameraInfo.TryGetValue(x => x.HorizontalExtent, double.NaN)));
- sb.AppendLine(string.Format("IsPespective: {0}", cameraInfo.TryGetValue(x=>x.IsPespective)));
- sb.AppendLine(string.Format("IsValidObject: {0}", cameraInfo.TryGetValue(x=>x.IsValidObject)));
- sb.AppendLine(string.Format("NearDistance: {0}", cameraInfo.TryGetValue(x => x.NearDistance, double.NaN)));
- sb.AppendLine(string.Format("RightOffset: {0}", cameraInfo.TryGetValue(x => x.RightOffset, double.NaN)));
- sb.AppendLine(string.Format("TargetDistance: {0}", cameraInfo.TryGetValue(x => x.TargetDistance, double.NaN)));
- sb.AppendLine(string.Format("UpOffset: {0}", cameraInfo.TryGetValue(x => x.UpOffset, double.NaN)));
- //sb.AppendLine(string.Format("VerticalExtent: {0}", cameraInfo.TryGetValue(x => x.VerticalExtent, double.NaN)));
- TaskDialog.Show("CameraInfo", sb.ToString());
- return Result.Succeeded;
- }
- }
Результат выполнения команды:
В консоль Output будет выведено следующее:
- Start
- IsCanceled
- OnViewBegin
- IsCanceled
- Finish
Немного поясню, что делает метод TryGetValue. Дело в том, что свойства HorizontalExtent и VerticalExtent при попытке обратиться к ним падают с AccessViolationException. Для удобства, я реализовал метод TryGetValue, в котором я пытаюсь получить значение свойства, и если при обращении к свойству возникает исключение, то будет возвращено значение, переданное вторым параметром.
- public static class MayBeException
- {
- public static TResult TryGetValue<TSource, TResult>(this TSource source, Func<TSource, TResult> action, TResult valueIfExceptionIsThrown = default (TResult))
- {
- try
- {
- var res = action(source);
- return res;
- }
- catch (Exception)
- {
- return valueIfExceptionIsThrown;
- }
- }
- }
Но, дело в том, что AccessViolationException не удается корректно обработать, и не смотря на блок try, Revit просто молча закрывается. Строки пришлось закомментировать. А вызов метода остался.
Готовый проект для Visual Studio можно скачать по ссылке.
Автор перевода: Виктор Чекалин
Обсуждение: http://adn-cis.org/forum/index.php?topic=1094
Опубликовано 20.11.2014