/// /// This file is part of ILNumerics Community Edition. /// /// ILNumerics Community Edition - high performance computing for applications. /// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net /// /// ILNumerics Community Edition is free software: you can redistribute it and/or modify /// it under the terms of the GNU General Public License version 3 as published by /// the Free Software Foundation. /// /// ILNumerics Community Edition is distributed in the hope that it will be useful, /// but WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details. /// /// You should have received a copy of the GNU General Public License /// along with ILNumerics Community Edition. See the file License.txt in the root /// of your distribution package. If not, see . /// /// In addition this software uses the following components and/or licenses: /// /// ================================================================================= /// The Open Toolkit Library License /// /// Copyright (c) 2006 - 2009 the Open Toolkit library. /// /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights to /// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of /// the Software, and to permit persons to whom the Software is furnished to do /// so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in all /// copies or substantial portions of the Software. /// /// ================================================================================= /// using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; namespace ILNumerics.Drawing { /// /// This class represents the camera's positioning and aiming direction. /// [DebuggerDisplay("r:{m_distance} φ:{m_phiDebugDisp}° ρ:{m_rhoDebugDisp}° - P:{Position} - L:{LookAt}")] public class ILCamera { #region event handling /// /// fires, if the position of the camera has changed /// public event EventHandler Changed; /// /// Fires a Changed event /// public virtual void OnChange() { if (Changed != null && !m_suspended) Changed(this,new EventArgs()); } #endregion #region attributes // input attributes (writable) private bool m_suspended = false; protected float m_posX; protected float m_posY; protected float m_posZ; protected float m_topX; protected float m_topY; protected float m_topZ; protected float m_lookAtX; protected float m_lookAtY; protected float m_lookAtZ; // output/cached attributes (readonly) // polar coordinates private float m_distance = 1.0f; private float m_phi = 0; private float m_rho = 0.0f; /// /// Offset angle for 2nd cached triangular phi value (needed for surface plots) /// internal float Offset = (float)(Math.PI / 4); /// /// cachced triangular phi value with offset (needed for surface plots) /// internal float CosPhiShift; /// /// cachced triangular phi value with offset (needed for surface plots) /// internal float SinPhiShift; /// /// cached value for cosine of phi - this is readonly and for performance only. /// internal float CosPhi; /// /// cached value for sine of phi - this is readonly and for performance only. /// internal float SinPhi; /// /// cached value for cosine of rho - this is readonly and for performance only. /// internal float CosRho; /// /// cached value for sine of rho - this is readonly and for performance only. /// internal float SinRho; private CameraQuadrant m_quadrant; #endregion #region properties /// /// point, the camera is aiming at (world coords) /// public ILPoint3Df LookAt { get { return new ILPoint3Df(m_lookAtX,m_lookAtY,m_lookAtZ); } set { //m_posX += (value.X - m_lookAtX); //m_posY += (value.Y - m_lookAtY); //m_posZ += (value.Z - m_lookAtZ); m_lookAtX = value.X; m_lookAtY = value.Y; m_lookAtZ = value.Z; updatePosition(); OnChange(); } } /// /// distance from the scene (readonly) /// public float Distance { get { return m_distance; } set { m_distance = Math.Abs(value); updateCachedVars(); updatePosition(); OnChange(); } } /// /// debugger helper: display phi in degrees (readonly) /// private int m_phiDebugDisp { get { return (int)Math.Round(m_phi * 180 / Math.PI); } } /// /// debugger helper: display rho in degrees /// private int m_rhoDebugDisp { get { return (int)Math.Round(m_rho * 180 / Math.PI); } } /// /// rotation of the scene (seen from above) [radians, readlony, rel. to lookat] /// public float Phi { get { return m_phi; } set { m_phi = (float)((value + (Math.PI * 2)) % (Math.PI * 2)); updateCachedVars(); updatePosition(); OnChange(); } } /// /// pitch of the scene [radians], setting moves camera around lookat point /// public float Rho { get { return m_rho; } set { if (value < 0.0f) m_rho = 0.0f; else if (value > Math.PI) m_rho = (float)Math.PI; else m_rho = value; updateCachedVars(); updatePosition(); OnChange(); } } /// /// Quadrant the camera is currently watching the scene from /// public CameraQuadrant Quadrant { get{ return m_quadrant; } } /// /// Determine, if camera is placed in an upper quadrant of the scene /// public bool LooksFromTop { get { return m_rho < Math.PI/2; } } /// /// Determine, if camera is located in an left quadrant of the scene /// public bool LooksFromLeft { get { return Math.Sin(m_phi) < 0; } } /// /// Determine, if camera is located in an front quadrant of the scene /// public bool LooksFromFront { get { return Math.Cos(m_phi) >= 0; } } /// /// true, when looking from top on the un-rotated scene (common for 2D plots) /// public bool Is2DView { get { return Math.Abs(SinPhi) < 1e-5 && Math.Abs(SinRho) < 1e-5; } } /// /// get/set camera position, absolute cartesian coordinates /// /// Keep in mind, the angle for phi points towards negative Y axis! The cartesian property /// handles the camera position in absolute world coordinates, while the /// polar coordinates (Rho, Phi, Distance) supress the camera position by means of coordinates /// relative to the LookAt point (i.e. usually the center of the viewing cube)! public ILPoint3Df Position { get { ILPoint3Df ret = new ILPoint3Df(m_posX, m_posY, m_posZ); return ret; } set { m_posX = value.X; m_posY = value.Y; m_posZ = value.Z; cart2Pol(value - new ILPoint3Df(m_lookAtX, m_lookAtY, m_lookAtZ)); updateCachedVars(); OnChange(); } } /// /// orientation of the camera, normalized, readonly /// /// This vector is readonly always points 'upwards'. public ILPoint3Df Top { get { ILPoint3Df ret = new ILPoint3Df(m_topX, m_topY, m_topZ); return ret; } } #endregion #region constructors public ILCamera (ILCamera vport) { m_rho = vport.m_rho; m_phi = vport.m_phi; m_distance = vport.m_distance; m_lookAtX = vport.m_lookAtX; m_lookAtY = vport.m_lookAtY; m_lookAtZ = vport.m_lookAtZ; m_posX = vport.m_posX; m_posY = vport.m_posY; m_posZ = vport.m_posZ; updateCachedVars(); computeQuadrant(); } /// /// Create a viewport: view at scene from top, no rotation /// public ILCamera () { m_posX = 0; m_posY = 0; m_posZ = 10; m_quadrant = CameraQuadrant.TopLeftFront; updateCachedVars(); } public ILCamera (float Phi, float Rho, float Distance) { m_rho = Rho; m_phi = Phi; m_distance = Distance; updateCachedVars(); updatePosition(); computeQuadrant(); } #endregion #region public interface /// /// suspend the firing of events until EventingResume() was called /// public void EventingSuspend() { m_suspended = true; } /// /// Resume firing 'Change' events after it has been suspended /// public void EventingResume() { EventingResume(true); } /// /// Resume firing 'Change' events, optionally skip pending events /// internal void EventingResume(bool fireEvents) { m_suspended = false; if (fireEvents) OnChange(); } /// /// Set both angles and distance at once /// /// Rotation, radians /// Pitch, radians /// Distance from scene public void Set(float phi, float rho, float distance) { if (distance < 0) throw new Exceptions.ILArgumentException("Camera distance must be positive!"); m_phi = (float)(phi % (Math.PI * 2)); m_rho = (float)(rho % (Math.PI)); m_distance = distance; updateCachedVars(); OnChange(); } /// /// Set complete camera position (angles and distance) at once /// /// Rotation (degrees) /// Pitch (degrees) /// Distance from scene public void SetDeg(float phi,float rho, float distance) { Set ((float)(phi/180.0 * Math.PI),(float)(rho / 180.0f * Math.PI),distance); } /// /// Convert camera position to string /// /// string display with distance,roatation and pitch public override string ToString() { return String.Format("r:{0} φ:{1}° ρ:{2}° - P:{3} - L:{4}", m_distance,m_phiDebugDisp,m_rhoDebugDisp,Position,LookAt); } #endregion #region private helper /// /// update internal cartesian (absolut) coordinates of position relative /// to lookAt point. To be called after any polar coordinates were changed. /// private void updatePosition() { m_posX = m_lookAtX + (m_distance * SinRho * SinPhi); m_posY = m_lookAtY + (m_distance * SinRho * -CosPhi); m_posZ = m_lookAtZ + (m_distance * CosRho); } private void cart2Pol(ILPoint3Df cartVec) { cartVec.ToPolar(out m_distance, out m_phi, out m_rho); } ///// ///// update position, cons. polar coord and lookat as new ///// //private void pol2Cart() { //} private void computeQuadrant() { //if (m_phi == 0.0 && m_rho == 0.0) { // m_quadrant = CameraQuadrant.TopLeftFront; // return; //} if (m_rho < System.Math.PI / 2) { // top if (m_phi < Math.PI) { // right if (m_phi < Math.PI / 2) { // front m_quadrant = CameraQuadrant.TopRightFront; } else { // back m_quadrant = CameraQuadrant.TopRightBack; } } else { // left if (m_phi > Math.PI / 2 * 3) { // front m_quadrant = CameraQuadrant.TopLeftFront; } else { // back m_quadrant = CameraQuadrant.TopLeftBack; } } } else { // bottom if (m_phi < Math.PI) { // right if (m_phi < Math.PI / 2) { // front m_quadrant = CameraQuadrant.BottomRightFront; } else { // back m_quadrant = CameraQuadrant.BottomRightBack; } } else { // left if (m_phi > Math.PI / 2 * 3) { // front m_quadrant = CameraQuadrant.BottomLeftFront; } else { // back m_quadrant = CameraQuadrant.BottomLeftBack; } } } } private void updateCachedVars () { CosPhi = (float)Math.Cos(m_phi); SinPhi = (float)Math.Sin(m_phi); SinPhiShift = (float)Math.Sin(m_phi + Offset); CosPhiShift = (float)Math.Cos(m_phi + Offset); CosRho = (float)Math.Cos(m_rho); SinRho = (float)Math.Sin(m_rho); // update top ILPoint3Df top = ILPoint3Df.normalize(-SinPhi * CosRho, CosPhi * CosRho, SinRho); m_topX = top.X; m_topY = top.Y; m_topZ = top.Z; computeQuadrant(); } #endregion } }