Fly.cs
// Copyright 2012 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See the use restrictions. // using System; using System.Drawing; using ESRI.ArcGIS.Analyst3D; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.ADF.BaseClasses; using ESRI.ArcGIS.ADF.CATIDs; using System.Runtime.InteropServices; namespace sceneTools { [ClassInterface(ClassInterfaceType.None)] [Guid("485BE349-31DA-4cd5-B6A4-69E4758F2541")] public sealed class Fly : BaseTool { [DllImport("user32")] public static extern int SetCursor(int hCursor); [DllImport("user32")] public static extern int GetClientRect(int hwnd, ref Rectangle lpRect); #region COM Registration Function(s) [ComRegisterFunction()] [ComVisible(false)] static void RegisterFunction(Type registerType) { // Required for ArcGIS Component Category Registrar support ArcGISCategoryRegistration(registerType); // // TODO: Add any COM registration code here // } [ComUnregisterFunction()] [ComVisible(false)] static void UnregisterFunction(Type registerType) { // Required for ArcGIS Component Category Registrar support ArcGISCategoryUnregistration(registerType); // // TODO: Add any COM unregistration code here // } #region ArcGIS Component Category Registrar generated code /// <summary> /// Required method for ArcGIS Component Category registration - /// Do not modify the contents of this method with the code editor. /// </summary> private static void ArcGISCategoryRegistration(Type registerType) { string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID); ControlsCommands.Register(regKey); } /// <summary> /// Required method for ArcGIS Component Category unregistration - /// Do not modify the contents of this method with the code editor. /// </summary> private static void ArcGISCategoryUnregistration(Type registerType) { string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID); ControlsCommands.Unregister(regKey); } #endregion #endregion private ISceneHookHelper m_pSceneHookHelper; private bool m_bInUse; bool bCancel = false; private long m_lMouseX; private long m_lMouseY; private double m_dMotion; //speed of the scene fly through in scene units private IPoint m_pPointObs; //observer private IPoint m_pPointTgt; //target private double m_dDistance; //distance between target and observer private double m_dElevation; //normal fly angles in radians private double m_dAzimut; //normal fly angles in radians private int m_iSpeed; private System.Windows.Forms.Cursor m_flyCur; private System.Windows.Forms.Cursor m_moveFlyCur; public Fly() { base.m_category = "Sample_SceneControl(C#)"; base.m_caption = "Fly"; base.m_toolTip = "Fly"; base.m_name = "Sample_SceneControl(C#)/Fly"; base.m_message = "Flies through the scene"; //Load resources string[] res = GetType().Assembly.GetManifestResourceNames(); if(res.GetLength(0) > 0) { base.m_bitmap = new System.Drawing.Bitmap(GetType().Assembly.GetManifestResourceStream("sceneTools.fly.bmp")); } m_flyCur = new System.Windows.Forms.Cursor(GetType().Assembly.GetManifestResourceStream("sceneTools.fly.cur")); m_moveFlyCur = new System.Windows.Forms.Cursor(GetType().Assembly.GetManifestResourceStream("sceneTools.fly1.cur")); m_pSceneHookHelper = new SceneHookHelperClass (); m_iSpeed = 0; } public override void OnCreate(object hook) { m_pSceneHookHelper.Hook = hook; } public override bool Enabled { get { //Disable if orthographic (2D) view if (m_pSceneHookHelper.Hook == null || m_pSceneHookHelper.Scene == null) { return false; } else { ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera; if(pCamera.ProjectionType == esri3DProjectionType.esriOrthoProjection) return false; else return true; } } } public override int Cursor { get { if(m_bInUse) return m_moveFlyCur.Handle.ToInt32(); else return m_flyCur.Handle.ToInt32(); } } public override bool Deactivate() { return true; } public override void OnMouseUp(int Button, int Shift, int X, int Y) { if (! m_bInUse) { m_lMouseX = X; m_lMouseY = Y; if(m_iSpeed == 0) StartFlight(); } else { //Set the speed if (Button == 1) m_iSpeed = m_iSpeed + 1; else if (Button == 2) m_iSpeed = m_iSpeed - 1; //Start or end the flight if (m_iSpeed == 0) EndFlight(); else StartFlight(); } } public override void OnMouseMove(int Button, int Shift, int X, int Y) { if (! m_bInUse) return; m_lMouseX = X; m_lMouseY = Y; } public override void OnKeyUp(int keyCode, int Shift) { if(m_bInUse == true) { //Slow down the speed of the fly through if(keyCode == 40 || keyCode == 37) m_dMotion = m_dMotion / 2; //Speed up the speed of the fly through else if (keyCode == 38 || keyCode == 39) m_dMotion = m_dMotion * 2; else if (keyCode == 27) bCancel = true; } } public void StartFlight() { m_bInUse = true; //Get the extent of the scene graph IEnvelope pEnvelope; pEnvelope = m_pSceneHookHelper.SceneGraph.Extent; if (pEnvelope.IsEmpty) return; //Query the coordinates of the extent double dXmin, dXmax, dYmin, dYmax; pEnvelope.QueryCoords(out dXmin, out dYmin, out dXmax, out dYmax); //Set the speed of the scene if((dXmax - dXmin) > (dYmax - dYmin)) m_dMotion = (dXmax - dXmin)/100; else m_dMotion = (dYmax - dYmin) / 100; //Get camera's current observer and target ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera; m_pPointObs = pCamera.Observer; m_pPointTgt = pCamera.Target; //Get the differences between the observer and target double dx, dy, dz; dx = m_pPointTgt.X - m_pPointObs.X; dy = m_pPointTgt.Y - m_pPointObs.Y; dz = m_pPointTgt.Z - m_pPointObs.Z; //Determine the elevation and azimuth in radians and //the distance between the target and observer m_dElevation = Math.Atan(dz/ Math.Sqrt(dx*dx + dy*dy)); m_dAzimut = Math.Atan(dy / dx); m_dDistance = Math.Sqrt((dx*dx) + (dy*dy) + (dz*dz)); //Windows API call to set cursor SetCursor(m_moveFlyCur.Handle.ToInt32()); //Continue the flight Flight(); } public void Flight() { //Get IMessageDispatcher interface IMessageDispatcher pMessageDispatcher; pMessageDispatcher = new MessageDispatcherClass(); //Set the ESC key to be seen as a cancel action pMessageDispatcher.CancelOnClick = false; pMessageDispatcher.CancelOnEscPress = true; //Get the scene graph ISceneGraph pSceneGraph = (ISceneGraph) m_pSceneHookHelper.SceneGraph; //Get the scene viewer ISceneViewer pSceneViewer = (ISceneViewer) m_pSceneHookHelper.ActiveViewer; //Get the camera ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera; bCancel = false; do { //Get the elapsed time double dlastFrameDuration, dMeanFrameRate; pSceneGraph.GetDrawingTimeInfo(out dlastFrameDuration, out dMeanFrameRate); if(dlastFrameDuration < 0.01) dlastFrameDuration = 0.01; if(dlastFrameDuration > 1) dlastFrameDuration = 1; //Windows API call to get windows client coordinates Rectangle rect = new Rectangle(); if (GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd, ref rect) == 0) return; //Get normal vectors double dXMouseNormal, dYMouseNormal; dXMouseNormal = 2 * ((double)m_lMouseX / (double)rect.Right) - 1; dYMouseNormal = 2 * ((double)m_lMouseY / (double)rect.Bottom) - 1; //Set elevation and azimuth in radians for normal rotation m_dElevation = m_dElevation - (dlastFrameDuration * dYMouseNormal * Math.Abs(dYMouseNormal)); m_dAzimut = m_dAzimut - (dlastFrameDuration * dXMouseNormal * Math.Abs(dXMouseNormal)); if(m_dElevation > 0.45 * 3.141592) m_dElevation = 0.45 * 3.141592; if(m_dElevation < -0.45 * 3.141592) m_dElevation = -0.45 * 3.141592; if(m_dAzimut < 0) m_dAzimut = m_dAzimut + (2 * 3.141592); if(m_dAzimut > 2 * 3.141592) m_dAzimut = m_dAzimut - (2 * 3.141592); double dx, dy, dz; dx = Math.Cos(m_dElevation) * Math.Cos(m_dAzimut); dy = Math.Cos(m_dElevation) * Math.Sin(m_dAzimut); dz = Math.Sin(m_dElevation); //Change the viewing directions (target) m_pPointTgt.X = m_pPointObs.X + (m_dDistance * dx); m_pPointTgt.Y = m_pPointObs.Y + (m_dDistance * dy); m_pPointTgt.Z = m_pPointObs.Z + (m_dDistance * dz); //Move the camera in the viewing directions m_pPointObs.X = m_pPointObs.X + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dx); m_pPointObs.Y = m_pPointObs.Y + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dy); m_pPointTgt.X = m_pPointTgt.X + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dx); m_pPointTgt.Y = m_pPointTgt.Y + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dy); m_pPointObs.Z = m_pPointObs.Z + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dz); m_pPointTgt.Z = m_pPointTgt.Z + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dz); pCamera.Observer = m_pPointObs; pCamera.Target = m_pPointTgt; //Set the angle of the camera about the line of sight between the observer and target pCamera.RollAngle = 10 * dXMouseNormal * Math.Abs(dXMouseNormal); //Redraw the scene viewer pSceneViewer.Redraw(true); object objCancel; //Dispatch any waiting messages: OnMouseMove / OnMouseUp / OnKeyUp events //object objCancel = bCancel as object; pMessageDispatcher.Dispatch(m_pSceneHookHelper.ActiveViewer.hWnd, false, out objCancel); //End flight if ESC key pressed if (bCancel == true) EndFlight(); } while(m_bInUse == true && bCancel == false); bCancel = false; } public void EndFlight() { m_bInUse = false; //Get the scene graph ISceneGraph pSceneGraph = (ISceneGraph) m_pSceneHookHelper.SceneGraph; IPoint pPointTgt; pPointTgt = new PointClass(); object pOwner, pObject; Rectangle rect = new Rectangle(); //Windows API call to get windows client coordinates if(GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd, ref rect) != 0) { //Translate coordinates into a 3D point pSceneGraph.Locate(pSceneGraph.ActiveViewer, rect.Right / 2, rect.Bottom / 2, esriScenePickMode.esriScenePickAll, true, out pPointTgt, out pOwner, out pObject); } //Get the camera ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera; if(pPointTgt != null) { //Reposition target and observer pCamera.Target = pPointTgt; pCamera.Observer = m_pPointObs; } //Set the angle of the camera about the line //of sight between the observer and target pCamera.RollAngle = 0; pCamera.PropertiesChanged(); //Windows API call to set cursor SetCursor(m_moveFlyCur.Handle.ToInt32()); m_iSpeed = 0; } public override void OnKeyDown(int keyCode, int Shift) { if(keyCode == 27) //ESC is pressed { bCancel = true; } } } }