/// /// 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. /// /// ================================================================================= /// #pragma warning disable 1591 using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Reflection; using System.Windows.Forms; using System.IO; using ILNumerics.Drawing; using ILNumerics.Drawing.Interfaces; using ILNumerics.Drawing.Collections; using ILNumerics.Drawing.Graphs; using ILNumerics.Drawing.Labeling; using ILNumerics.Drawing.Misc; using System.Diagnostics; namespace ILNumerics.Drawing { /// /// Basic abstract base class for GL dependent display. /// /// This control is the main plot control of ILNumerics. public abstract class ILPanel : Control { #region attributes protected ILRendererManager m_textRendererManager; protected ILClippingData m_clippingView; protected ILAxisCollection m_axes; protected ILGraphCollection m_graphs; protected ILTextureManager m_textureManager; protected Point m_mouseStart; protected bool m_isDragging; protected bool m_active = false; protected bool m_drawHidden = true; protected bool m_ready = false; private bool m_isStartingUp = true; protected ILCamera m_camera; protected ILCamera m_defaultView; protected bool m_autoDefaultView = true; protected InteractiveModes m_selectingMode = InteractiveModes.Rotating; protected InteractiveModes m_oldSelectingMode; protected bool m_isCtrlKeyDown; protected ILPoint3Df m_scaling; protected Projection m_projection = Projection.Orthographic; protected Color m_backColor = Color.FromKnownColor(KnownColor.Control); protected Color m_cubeBGColor = Color.FromArgb(250, 250, 250); protected ILColormap m_colormap; protected ILLegend m_legend; protected ZoomModes m_zoomMode = ZoomModes.RollHard; protected ILZoomAction m_zoomAction; protected float m_zoomOffset = 50f; protected GraphicDeviceType m_graphicsDevice; protected ILLineProperties m_selectionRectangle; protected bool m_fillBackground = true; private AutoZoomOptions m_autoZoomOptions = AutoZoomOptions.OnDataChanges; protected ILLayoutData m_layoutData = new ILLayoutData(); protected ILLightCollection m_lights = new ILLightCollection(); protected ILRenderProperties m_renderProperties; protected ILAction m_action; protected List m_sortingCacheList = new List(); /// /// pixel size of the current PlotCubeScreenRectangle /// protected RectangleF m_plotBoxScreenRectF; protected PlotBoxScreenSizeMode m_plotBoxScreenSizeMode; protected AspectRatioMode m_aspectRatio; protected double[] m_clipplanes = new double[24]; private const int MAXRENDERPASSES = 2; private const float LABELS_VERTICAL_MIN_RHO = 0.8f; protected const float pi05 = (float) Math.PI / 2; protected const float pi2 = (float) Math.PI * 2; protected const float pi32 = (float) Math.PI / 2 * 3; protected const float pi4 = (float) Math.PI / 4; protected const float pi8 = (float) Math.PI / 8; #endregion #region properties /// /// Gets the mode for automatic view angle updates or sets it /// public bool AutoDefaultView { get { return m_autoDefaultView; } set { m_autoDefaultView = value; } } /// /// Determines how the projected data plots are mapped to PlotCubeScreenRectF /// public AspectRatioMode AspectRatio { get { return m_aspectRatio; } set { m_aspectRatio = value; } } /// /// The normalizes projected size (range 0..1) of plot cube on 2D client area, on set: also sets PlotCubeScreenMode -> Manual /// public RectangleF PlotBoxScreenRect { get { return m_plotBoxScreenRectF; } set { m_plotBoxScreenRectF = value; m_plotBoxScreenSizeMode = PlotBoxScreenSizeMode.Manual; //Invalidate(); } } /// /// Options for determining the size of the plot cube on the 2D screen client area, default: optimal /// public PlotBoxScreenSizeMode PlotBoxScreenSizeMode { get { return m_plotBoxScreenSizeMode; } set { m_plotBoxScreenSizeMode = value; if (value == PlotBoxScreenSizeMode.Maximum) { m_plotBoxScreenRectF = new RectangleF(0, 0, 1f, 1f); } } } /// /// Access collection of lights for configuration /// public ILLightCollection Lights { get { return m_lights; } } /// /// Legend for panel's graphs /// public ILLegend Legend { get { return m_legend; } } /// /// Get texture manager instance, storing all textures used in the scene /// internal ILTextureManager TextureManager { get { return m_textureManager; } } /// /// Colormap used to translate color indices into true colors /// public ILColormap Colormap { get { return m_colormap; } set { m_colormap = value; OnColormapChanged(); } } /// /// Determines if background of the rendering cube will be filled with the CubeBackgroundColor property value /// public bool BackgroundFilled { get { return m_fillBackground; } set { m_fillBackground = value; //Invalidate(); } } /// /// True: the control will always be drawn, even if it does not own the focus /// public bool DrawInactive { get { return m_drawHidden; } set { m_drawHidden = value; //Invalidate(); } } /// /// Get/set the background color for the inner cube drawing /// public Color BackColorCube { get { return m_cubeBGColor; } set { m_cubeBGColor = value; //Invalidate(); } } /// /// View settings, get access to the clipping limits for all axises /// public ILClippingData Limits { get { return m_clippingView; } } /// /// Get the type of device currently used for rendering /// public GraphicDeviceType GraphicDeviceType { get { return m_graphicsDevice; } } /// /// Get/set properties for selection rectangle, drawn when zooming with the mouse /// public ILLineProperties SelectionRectangle { get { return m_selectionRectangle; } } /// /// color of the figure background /// public override Color BackColor { get { return m_backColor; } set { m_backColor = value; //Invalidate(); } } /// /// type of projection: orthographic (default) or perspective /// public Projection Projection { get { return m_projection; } set { m_projection = value; //Invalidate(); } } /// /// Get or set viewport properties (distance & angles) /// /// Changing public ILCamera Camera { get { return m_camera; } } /// /// Get the current mode for mouse interaction or set's it /// public InteractiveModes InteractiveMode { get { return m_selectingMode; } set { m_selectingMode = value; m_oldSelectingMode = value; //if (value == InteractiveModes.Rotating) // m_axes[AxisNames.ZAxis].Visible = true; //else if (value == InteractiveModes.ZoomRectangle) // m_axes[AxisNames.ZAxis].Visible = false; } } /// /// Gives all graphs as value collection (readonly) /// public ILGraphCollection Graphs { get { if (m_graphs == null) CreateControl(); return m_graphs; } } /// /// Get axes collection - holds all 3 axes /// public ILAxisCollection Axes { get { if (m_axes == null) CreateControl(); return m_axes; } } /// /// (experimental) content for graphs will be clipped outside the unit cube /// /// For 2D plots, not clipping the vertex data may lead to hiding /// the labels drawn next to axes. For 3D plots Clipping may cause unexpected behavior. /// Therefore Clipping will be activated for 2D plots by default and /// deactivated for 3D plots by default. public bool ClipViewData { get { return m_renderProperties.Clipping; } set { m_renderProperties.Clipping = value; } } /// /// Manager providing collection of available IILTextRenderer types /// /// IILTextRenderer are used to draw labels for axis of this panel (device specific). /// Text renderer objects must be instantiated through the ILTextRendererManager instance's /// CreateInstance() method. public ILRendererManager TextRendererManager { get { return m_textRendererManager; } } /// /// Get or set default camera position for reset of the scene /// /// The default position is used when the scene is reset. That /// reset is usually triggered by double clicking the panel with the mouse. /// setting this value to null will make the panel ignore any double /// clicks, which enables the user to manually react to double click via the /// common DoubleClick event of the panel and reset the camera position from /// outside the component. public ILCamera DefaultView { get { return m_defaultView; } protected set { if (value != null) { m_defaultView = value; m_autoDefaultView = false; } else { m_autoDefaultView = true; } } } /// /// Options for the view cube adapting data limit changes /// public AutoZoomOptions AutoZoomContent { get { return m_autoZoomOptions; } set { m_autoZoomOptions = value; } } /// /// choose the ramp for zooming /// public ZoomModes ZoomMode { get { return m_zoomMode; } set { m_zoomMode = value; } } /// /// How much the view cube will be shrinked/expanded on zooming operations (percent) /// public float ZoomOffset { set { m_zoomOffset = value; } get { return m_zoomOffset; } } protected ILZoomAction ZoomAction { get { return m_zoomAction; } } #endregion #region constructors protected ILPanel(GraphicDeviceType graphicsDevice) : base () { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.ctor()",DateTime.Now, Environment.TickCount); #endif this.DoubleBuffered = false; //BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; m_plotBoxScreenSizeMode = PlotBoxScreenSizeMode.Optimal; m_graphicsDevice = graphicsDevice; m_textureManager = new ILTextureManager(m_graphicsDevice); m_textureManager.DefaultHeight = 500; m_textureManager.DefaultWidth = 500; m_selectionRectangle = new ILLineProperties(); m_selectionRectangle.Antialiasing = false; m_selectionRectangle.Color = Color.Blue; m_selectionRectangle.Width = 1; m_selectionRectangle.Style = LineStyle.Solid; m_selectionRectangle.Changed += new EventHandler(m_selectionRectangle_Changed); m_camera = new ILCamera((float)0.0f,0.0f,10.0f); m_defaultView = new ILCamera(m_camera); m_defaultView.Changed += new EventHandler(m_defaultView_Changed); m_renderProperties = new ILRenderProperties (); m_renderProperties.Camera = Camera; m_textRendererManager = new ILRendererManager(this); m_clippingView = new ILClippingData(); m_clippingView.AllowZeroVolume = false; m_layoutData = new ILLayoutData(m_camera); m_clippingView.Changed += new ILClippingDataChangedEvent(m_viewLimits_Changed); m_colormap = new ILColormap(); m_colormap.Changed += new EventHandler(m_colormap_Changed); Padding = new Padding(5); BackColor = Color.White; Dock = DockStyle.Fill; m_legend = ILLegend.Create(this); m_legend.Changed += new EventHandler(m_legend_Changed); m_active = false; m_ready = false; } #endregion #region public interface function /// /// Dispose this panel, frees all devices, graph- and axis collection /// public new void Dispose () { Dispose(false); } /// /// dispose this panel /// /// manual disposing /// derived classed (ILDXPanel,ILOGLPanel) free their resources here protected override void Dispose(bool disposing) { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.Dispose(bool) start",DateTime.Now, Environment.TickCount); Trace.Indent(); #endif m_ready = false; if (m_graphs != null) m_graphs.Dispose(); if (m_axes != null) m_axes.Dispose(); if (m_textureManager != null) m_textureManager.Dispose(); base.Dispose(disposing); #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.Dispose(bool) end",DateTime.Now, Environment.TickCount); #endif } /// /// causes the panel to redraw /// public override void Refresh() { if (InvokeRequired) { Invoke((MethodInvoker) delegate () { Refresh(); }); } else { base.Refresh(); } } #endregion #region events /// /// fired, if the data limits of any graphs changed /// public event ILClippingDataChangedEvent DataLimitsChanged; /// /// fired, if the clipping rectangle for viewing graphs changed /// public event ILClippingDataChangedEvent ViewLimitsChanged; /// /// fires, if the current colormap has changed /// public event EventHandler ColormapChanged; /// /// fired, if the internal graphics device reset (Direct3D devices only) /// public event ILGraphicsDeviceResetEvent GraphicsDeviceReset; /// /// fired, if the internal graphics device has been (re)created /// public event ILGraphicsDeviceCreatedEvent GraphicsDeviceCreated; #endregion #region event handlers protected void m_defaultView_Changed(object sender, EventArgs e) { m_autoDefaultView = false; } protected override void OnHandleCreated(EventArgs e) { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.OnHandleCreated() start",DateTime.Now, Environment.TickCount); Trace.Indent(); #endif base.OnHandleCreated(e); this.SetStyle(ControlStyles.AllPaintingInWmPaint,true); this.SetStyle(ControlStyles.UserPaint,true); #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.OnHandleCreated() end",DateTime.Now, Environment.TickCount); #endif } protected override void OnHandleDestroyed(EventArgs e) { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.OnHandleDestroyed() start",DateTime.Now, Environment.TickCount); Trace.Indent(); #endif if (m_zoomAction != null) m_zoomAction.Cancel(); Dispose(); base.OnHandleDestroyed(e); #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.OnHandleDestroyed() end",DateTime.Now, Environment.TickCount); #endif } protected virtual void OnViewLimitsChanged(ClippingChangedEventArgs e) { if (ViewLimitsChanged != null) ViewLimitsChanged(this,e); } protected virtual void OnDataLimitsChanged(ClippingChangedEventArgs e) { if (DataLimitsChanged != null) DataLimitsChanged(this,e); } protected virtual void OnGraphicsDeviceReset() { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.OnGraphicsDeviceReset() start",DateTime.Now, Environment.TickCount); Trace.Indent(); #endif Configure(); if (GraphicsDeviceReset != null) { GraphicsDeviceReset(this,null); } #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.OnGraphicsDeviceReset() end",DateTime.Now, Environment.TickCount); #endif } protected virtual void OnGraphicsDeviceCreated() { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.OnGraphicsDeviceCreated() start",DateTime.Now, Environment.TickCount); Trace.Indent(); #endif IILCreationFactory graphFact = GetCreationFactory(); m_graphs = new ILGraphCollection(graphFact); m_graphs.CollectionChanged += new ILGraphCollectionChangedEvent(m_graphs_OnCollectionChanged); m_graphs.Limits.Changed += new ILClippingDataChangedEvent(m_dataLimits_Changed); m_graphs.GraphChanged += new ILGraphChangedEvent(m_graphs_GraphChanged); m_axes = new ILAxisCollection(m_clippingView,graphFact); if (GraphicsDeviceCreated!= null) { GraphicsDeviceCreated(this,null); } //Invalidate(); #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.OnGraphicsDeviceCreated() end",DateTime.Now, Environment.TickCount); #endif } protected virtual void OnColormapChanged() { if (ColormapChanged != null) ColormapChanged(this,null); m_graphs.Invalidate(); } protected override void OnLostFocus(EventArgs e) { base.OnLostFocus(e); m_active = false; } protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); m_active = true; } protected override void OnVisibleChanged(EventArgs e) { if (IsHandleCreated && !IsDisposed) { base.OnVisibleChanged(e); if (Visible) m_active = true; else m_active = false; } } protected override void OnSizeChanged(EventArgs e) { base.OnSizeChanged(e); base.Invalidate(); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (m_selectingMode == InteractiveModes.Rotating) { #region rotation if (m_isDragging || (e.Button == System.Windows.Forms.MouseButtons.Left && Math.Sqrt(Math.Pow(Math.Abs(e.X - m_mouseStart.X),2) + Math.Pow(Math.Abs(e.Y - m_mouseStart.Y),2)) > 3.0)) { m_isDragging = true; m_camera.EventingSuspend(); int distX = e.Location.X - m_mouseStart.X; int distY = e.Location.Y - m_mouseStart.Y; float tmp = m_camera.Phi - distX / 200.0f; // if alt is pressed, lock on even angles if (((Control.ModifierKeys & Keys.Alt) != 0) && (Math.Abs(Math.Round(tmp/pi4) - (tmp / pi4)) < 0.05)) { tmp = (float)(Math.Round(tmp / pi4) * pi4); } m_camera.Phi = tmp; tmp = m_camera.Rho - distY / 200.0f; // if alt is pressed, lock on even angles if (((Control.ModifierKeys & Keys.Alt) != 0) && (Math.Abs(Math.Round(tmp/pi8) - (tmp / pi8)) < 0.05)) { tmp = (float)(Math.Round(tmp / pi8) * pi8); } m_camera.Rho = tmp; if (m_camera.Rho > Math.PI) m_camera.Rho = (float)Math.PI; if (m_camera.Rho < 0) m_camera.Rho = 0.0f; m_camera.EventingResume(); m_mouseStart = e.Location; Refresh(); } #endregion } else if (m_selectingMode == InteractiveModes.ZoomRectangle) { #region selection rectangle if (m_isDragging || (e.Button == System.Windows.Forms.MouseButtons.Left && Math.Sqrt(Math.Pow(Math.Abs(e.X - m_mouseStart.X),2) + Math.Pow(Math.Abs(e.Y - m_mouseStart.Y),2)) > 3.0)) { m_isDragging = true; Cursor = Cursors.Cross; Refresh(); } #endregion } else if (m_selectingMode == InteractiveModes.Translating) { if (m_isDragging || (e.Button == System.Windows.Forms.MouseButtons.Left && Math.Sqrt(Math.Pow(Math.Abs(e.X - m_mouseStart.X),2) + Math.Pow(Math.Abs(e.Y - m_mouseStart.Y),2)) > 1.0)) { ILPoint3Df p1,p2,dummy; Screen2World(m_mouseStart.X,Height - m_mouseStart.Y, out p1, out dummy); Screen2World(e.X, Height - e.Y, out p2, out dummy); if (m_projection == Projection.Perspective) { p1 = ILPoint3Df.normalize(p1 - m_camera.Position) * m_camera.Distance; p2 = ILPoint3Df.normalize(p2 - m_camera.Position) * m_camera.Distance; } p1 = p2 - p1; m_camera.LookAt -= p1; m_mouseStart = e.Location; Refresh(); } } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); //System.Diagnostic.Debug.WriteLine("MouseUp"); if (m_selectingMode == InteractiveModes.ZoomRectangle && m_isDragging) { Zoom(Screen2World2D(m_mouseStart.X,Height - m_mouseStart.Y),Screen2World2D(e.X, Height - e.Y)); } m_isDragging = false; Cursor = Cursors.Default; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); m_mouseStart = e.Location; } protected override void OnMouseClick(MouseEventArgs e) { base.OnMouseClick(e); //System.Diagnostic.Debug.WriteLine("Click"); if (m_isDragging == false && (m_selectingMode == InteractiveModes.ZoomRectangle || m_selectingMode == InteractiveModes.Rotating)) { // determine new center coords ILPoint3Df near, far; Screen2World(e.X,Height - e.Y, out near, out far); //Limits.CenterF.Z); if (e.Button == System.Windows.Forms.MouseButtons.Left) { Zoom(near,far,m_zoomOffset / 100); } else if (e.Button == System.Windows.Forms.MouseButtons.Right) Zoom(near,far,- m_zoomOffset / 100); } } protected override void OnMouseDoubleClick(MouseEventArgs e) { base.OnMouseDoubleClick(e); ResetView(true); Refresh(); } protected override void OnKeyDown(KeyEventArgs e) { if (e.Control && !m_isCtrlKeyDown) { m_oldSelectingMode = m_selectingMode; m_selectingMode = InteractiveModes.Translating; m_isCtrlKeyDown = true; Cursor = Cursors.NoMove2D; //m_isDragging = false; } base.OnKeyDown(e); } protected override void OnKeyUp(KeyEventArgs e) { if (!e.Control) { if (m_isCtrlKeyDown) { m_isCtrlKeyDown = false; m_selectingMode = m_oldSelectingMode; } Cursor = Cursors.Default; //m_isDragging = false; } base.OnKeyUp(e); } protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { if (DesignMode) { e.Graphics.Clear(Color.LightBlue); return; } if (e.ClipRectangle.Width == 0 || e.ClipRectangle.Height == 0) { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.OnPaint(): ClipRectangle.Size = empty -> skipping",DateTime.Now, Environment.TickCount); #endif return; } /* THIS IS PRELIMINARY IMPLEMENTATION! REASON: While using GDI renderer, * it is importatnt, to have the whole (visible) surface area invalidated * and available for clipping of the graphics objects in 'e'. Otherwise * rendered objects will not show, after the buffers have swapped. The * following code helps pick those situations, where the clipping region * is only a subregion of the controls surface, but it introduces new * problems, if the whole control is (partially) obscured by other controls. * Since the GDI Renderer currently is not the recommended method of rendering, * this code is commented until a better solution was found. */ // if (e.ClipRectangle.X > 0 || e.ClipRectangle.Y > 0) { //#if TRACE // Trace.TraceInformation("{0},{1} ILPanel.OnPaint(): partial clipping detected -> refreshing whole controls area",DateTime.Now, Environment.TickCount); //#endif // Refresh(); // return; // } try { #if TRACE Trace.TraceInformation("{0},{1} ILPanel.OnPaint(): started (re-)configure",DateTime.Now, Environment.TickCount); Trace.Indent(); #endif Configure(); if (m_isStartingUp && (m_autoZoomOptions != AutoZoomOptions.Never)) { ResetView(true); } #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.OnPaint(): rendering started",DateTime.Now, Environment.TickCount); Trace.Indent(); Trace.TraceInformation(String.Format("this: {1}, ClipRectangle: {0}, Graphics.ClipBounds: L:{2} T:{3} W:{4} H:{5}" ,e.ClipRectangle.ToString() ,this.ClientRectangle.ToString() ,e.Graphics.VisibleClipBounds.Left ,e.Graphics.VisibleClipBounds.Top ,e.Graphics.VisibleClipBounds.Width ,e.Graphics.VisibleClipBounds.Height)); #endif m_renderProperties.Graphics = e.Graphics; m_renderProperties.Reason = RenderReason.PaintEvent; m_renderProperties.MinX = int.MaxValue; m_renderProperties.MinY = int.MaxValue; m_renderProperties.MaxX = int.MinValue; m_renderProperties.MaxY = int.MinValue; RenderScene(m_renderProperties.Reset()); #if TRACE Trace.Unindent(); Trace.TraceInformation("{0},{1} ILPanel.OnPaint(): rendering ended",DateTime.Now, Environment.TickCount); #endif if (m_isStartingUp) { m_isStartingUp = false; Refresh(); } System.Diagnostics.Debug.WriteLine("Current Camera Setting: " + m_camera.ToString()); } catch (Exceptions.ILArgumentException exc) { #if TRACE Trace.TraceWarning("{0},{1} ILPanel.OnPaint(): rendering failed. reason: {2}",DateTime.Now, Environment.TickCount,exc.ToString()); Trace.Indent(); #endif System.Diagnostics.Debug.WriteLine("ILPanel.OnPaint failed: " + exc.ToString()); throw new Exceptions.ILArgumentException("rendering failed. Details attached.",exc); } } void m_legend_Changed(object sender, EventArgs e) { Invalidate(); } void m_graphs_GraphChanged(object sender, ILGraphChangedEventArgs args) { ResetView(false); } protected void m_graphs_OnCollectionChanged(object sender, ILGraphCollectionChangedEventArgs args) { if (args.Reason == GraphCollectionChangeReason.Added) { if (AutoDefaultView && (args.Configurator != null)) { args.Configurator.ConfigurePanel(this); } } } protected void m_viewLimits_Changed(object sender, ClippingChangedEventArgs e) { if (this.InvokeRequired && IsHandleCreated) { try { Invoke(new ILClippingDataChangedEvent(m_viewLimits_Changed), sender, e); } catch (Exception) { } } else { m_camera.LookAt = m_clippingView.CenterF; OnViewLimitsChanged(e); } } protected void m_dataLimits_Changed(object sender, ClippingChangedEventArgs e) { if (m_autoDefaultView) { m_defaultView.EventingSuspend(); bool oldDefaultViewSetting = m_autoDefaultView; m_defaultView.LookAt = m_graphs.Limits.CenterF; m_defaultView.Distance = m_graphs.Limits.SphereRadius * 2; m_defaultView.EventingResume(false); } if (m_autoZoomOptions == AutoZoomOptions.OnDataChanges) ResetView(false); OnDataLimitsChanged(e); } protected void m_colormap_Changed(object sender, EventArgs e) { OnColormapChanged(); } void m_selectionRectangle_Changed(object sender, EventArgs e) { // nothing to do here } #endregion event handler #region virtual abstract interface /// /// Get current rendering device (implementation dependent) /// /// public abstract object GetDeviceContext(); /// /// Causes a reconfiguration of all axes and graphs on the next paint event /// /// Call this method after any changes to vertex relevant data. It causes all drawable objects to clear their caches and recalculate all vertex data. protected new void Invalidate() { if (!m_ready) return; m_ready = false; m_graphs.Invalidate(); m_axes.Invalidate(); //base.Invalidate(this.ClientRectangle, true); <- this would cause an immediate redraw also. Use Refresh() for this! } /// /// update viewing limits to show all data, rotate the scene to default (-> DefaultView) /// public virtual void ResetView() { ResetView(true); } /// /// update viewing limits to show all data, optionally reset the scene rotation /// /// true: rotate the scene to the default (-> DefaultView) public virtual void ResetView(bool resetRotation) { if (m_zoomAction != null) m_zoomAction.Cancel(); m_clippingView.EventingSuspend(); m_clippingView.CopyFrom(m_graphs.Limits); m_clippingView.Update(m_clippingView.CenterF,1.1f); m_clippingView.EventingResume(); if (m_defaultView != null && resetRotation) { m_camera.LookAt = m_defaultView.LookAt; m_camera.Phi = m_defaultView.Phi; m_camera.Rho = m_defaultView.Rho; m_camera.Distance = m_defaultView.Distance; } } /// /// Move & shrink/expand current view cube along a given line /// /// /// /// protected virtual void Zoom(ILPoint3Df nearLineEnd, ILPoint3Df farLineEnd, float offset) { ILPoint3Df minCorner, maxCorner; m_clippingView.GetZoomParameter(nearLineEnd,farLineEnd,offset, out minCorner, out maxCorner); Zoom(minCorner,maxCorner); //m_camera.LookAt = m_clippingView.CenterF; } /// /// move the center of the viewing cube and expand / shrink the volume by offset /// /// new center /// offset multiplicator, 1f means: no change protected virtual void Zoom(ILPoint3Df center, float offset) { m_clippingView.Update(center,offset); Refresh(); } /// /// Zoome the scene to new limits /// /// 'upper left' (first) corner of the new viewing cube /// 'bottom right' (opposed) corner of the viewing cube protected virtual void Zoom(ILPoint3Df luCorner, ILPoint3Df rbCorner) { if (m_zoomAction != null) m_zoomAction.Cancel(); ILActionRamp ramp; switch (m_zoomMode) { case ZoomModes.Jump: ramp = ILActionRamp.NoRamp; break; case ZoomModes.RollSoft: ramp = ILActionRamp.Soft; break; case ZoomModes.RollHard: ramp = ILActionRamp.Hard; break; case ZoomModes.RollOverride: ramp = ILActionRamp.Override; break; default: ramp = ILActionRamp.Linear; break; } m_zoomAction = new ILZoomAction(m_clippingView.Min, luCorner, m_clippingView.Max, rbCorner, ramp, this); m_zoomAction.Run(); } /// /// Transform two coordinates for a line from world to screen coordinates /// /// 1st coordinate (world) /// 2nd coordinate (world) /// [output] 1st coordinate (screen pixels) /// [output] 2nd coordinate (screen pixels) /// This function is provided by the concrete derived class, using the /// current rendering framework. public abstract void World2Screen(ILPoint3Df start, ILPoint3Df end, out Point start2D, out Point end2D); /// /// Transform from a point on screen into world coordinates [depricated] /// /// screen x /// screen y: GL viewport coord! -> (0,0) is lower left corner! /// world coordinate point public abstract ILPoint3Df Screen2World2D(int x, int y); /// /// gives the line in world coords, a specific point on screen lays on /// /// screen x /// screen y: GL viewport coord! -> (0,0) is lower left corner /// far end point of the resulting line in world coords /// near end point of the resulting line in world coords public abstract void Screen2World(int x, int y, out ILPoint3Df nearClip, out ILPoint3Df farClip); /// /// Transform world coordinate to screen coordinate under current transformation /// /// world coordinate /// screen location /// the actual transform is carried out in the derived specialized class, /// where the current transformation matrices are known public abstract Point World2Screen(ILPoint3Df worldPoint); /// /// Transform world coordinate to screen coordinate, provide (custom) modelview matrix /// /// /// (custom) model view matrix. The parameter must match the format required by the deriving concrete ILPanel class. ILOGLPanel: double[16] /// screen location /// the actual transform is carried out in the derived specialized class, /// where the current transformation matrices are known public abstract Point World2Screen(ILPoint3Df worldPoint, double[] modelview); /// /// Draws content of this subfigure into predefined bitmap /// /// predefined bitmap to draw content into. The size must have been initialized according to 'bounds'. /// Rectangle specifying the region to be copied. public new abstract void DrawToBitmap(Bitmap bitmap, Rectangle bounds); #endregion #region helper functions /// /// draws the selection rectangle with GDI functions /// /// end point /// The start point is stored in the member m_mouseStart. protected virtual void drawSelectionRect(Point endPoint) { Graphics g = Graphics.FromHwnd(this.Handle); Pen pen = new Pen(m_selectionRectangle.Color, 1.0f); Point[] points = new Point[3]; // upper left corner points[0] = new Point(m_mouseStart.X, m_mouseStart.Y + (endPoint.Y - m_mouseStart.Y) / 4); points[1] = new Point(m_mouseStart.X, m_mouseStart.Y); points[2] = new Point(m_mouseStart.X + (endPoint.X - m_mouseStart.X) / 4, m_mouseStart.Y); g.DrawLines(pen, points); // lower left corner points[0] = new Point(m_mouseStart.X, endPoint.Y - (endPoint.Y - m_mouseStart.Y) / 4); points[1] = new Point(m_mouseStart.X, endPoint.Y); points[2] = new Point(m_mouseStart.X + (endPoint.X - m_mouseStart.X) / 4, endPoint.Y); g.DrawLines(pen, points); // lower right corner points[0] = new Point(endPoint.X, endPoint.Y - (endPoint.Y - m_mouseStart.Y) / 4); points[1] = new Point(endPoint.X, endPoint.Y); points[2] = new Point(endPoint.X - (endPoint.X - m_mouseStart.X) / 4, endPoint.Y); g.DrawLines(pen, points); // upper right corner points[0] = new Point(endPoint.X, m_mouseStart.Y + (endPoint.Y - m_mouseStart.Y) / 4); points[1] = new Point(endPoint.X, m_mouseStart.Y); points[2] = new Point(endPoint.X - (endPoint.X - m_mouseStart.X) / 4, m_mouseStart.Y); g.DrawLines(pen, points); } public static short StippleFromLineStyle(LineStyle style, ref int stipFactr) { short ret = 1; switch (style) { case LineStyle.Dashed: ret = (short)255; stipFactr = 2; break; case LineStyle.PointDash: ret = (short)255 + 4096; stipFactr = 1; break; case LineStyle.Dotted: ret = (short)13107; // 3 + 48 + 768 + 8192 + 4096; stipFactr = 2; break; case LineStyle.UserPattern: break; // ret = 0; case LineStyle.None: break; default: // solid ret = (short)-1; break; } return ret; } private void calculateDefaultView(bool setPositions, bool setDirection) { m_defaultView.EventingSuspend(); if (setPositions) { m_defaultView.LookAt = m_graphs.Limits.CenterF; m_defaultView.Distance = m_clippingView.SphereRadius * 2f; } if (setDirection) { m_defaultView.Phi = 0; // -(float)Math.PI / 2; m_defaultView.Rho = 0; } m_defaultView.EventingResume(false); } #endregion #region private rendering setup helper protected void helperUpdateMatrices(float width2D, float height2D, out float worldSceneWidth, out float worldSceneHeight, out ILPoint3Df top, out ILPoint3Df moveOffset) { float localPlotCubeScreenRectLeft; float localPlotCubeScreenRectWidth; float localPlotCubeScreenRectTop; float localPlotCubeScreenRectHeight; float offsetX; float offsetY; localPlotCubeScreenRectHeight = m_plotBoxScreenRectF.Height; localPlotCubeScreenRectWidth = m_plotBoxScreenRectF.Width; localPlotCubeScreenRectTop = m_plotBoxScreenRectF.Top; localPlotCubeScreenRectLeft = m_plotBoxScreenRectF.Left; if (AspectRatio == AspectRatioMode.StretchToFill) { worldSceneWidth = width2D / localPlotCubeScreenRectWidth; worldSceneHeight = height2D / localPlotCubeScreenRectHeight; } else { //if (RenderAspectRatioMode == RenderAspectRatioMode.MaintainRatios) { float plotCubeScreenRectAspectRatio = (m_plotBoxScreenRectF.Width * ClientSize.Width) / (m_plotBoxScreenRectF.Height * ClientSize.Height); float dataAspectRatio = width2D / height2D; if (plotCubeScreenRectAspectRatio > dataAspectRatio) { // width > height worldSceneHeight = height2D; worldSceneWidth = worldSceneHeight * dataAspectRatio; // enlarge the scene, so we have rendering margin outside the data cube worldSceneHeight /= localPlotCubeScreenRectHeight; worldSceneWidth /= (localPlotCubeScreenRectWidth * (dataAspectRatio / plotCubeScreenRectAspectRatio)); } else { // height >= width worldSceneWidth = width2D; worldSceneHeight = worldSceneWidth / dataAspectRatio; // enlarge the scene, so we have rendering margin outside the data cube worldSceneWidth /= localPlotCubeScreenRectWidth; worldSceneHeight /= (localPlotCubeScreenRectHeight / (dataAspectRatio / plotCubeScreenRectAspectRatio)); } } // one more pixel please... worldSceneHeight = (worldSceneHeight / ClientSize.Height * (ClientSize.Height + 1)); worldSceneWidth = (worldSceneWidth / ClientSize.Width * (ClientSize.Width + 1)); // if PlotCubeScreenRect is not centered, we move the scene out of center accordingly (..further down) top = new ILPoint3Df(-m_camera.SinPhi * m_camera.CosRho, m_camera.CosPhi * m_camera.CosRho, m_camera.SinRho); ILPoint3Df moveX = ILPoint3Df.crossN(m_camera.Position - m_camera.LookAt, top); offsetX = -(localPlotCubeScreenRectLeft + (localPlotCubeScreenRectWidth / 2f) - 0.5f) * worldSceneWidth; offsetY = -(localPlotCubeScreenRectTop + (localPlotCubeScreenRectHeight / 2f) - 0.5f) * worldSceneHeight; moveOffset = top * offsetY + moveX * offsetX; } protected virtual void RenderScene(ILRenderProperties p) { //System.Diagnostics.Debug.Print("m_camera:{0}", m_camera); if (!DesignMode) { lock (m_sortingCacheList) { iRenderingState1(p); //make current // configure axes (determine tick labels in mode 'auto') m_axes.XAxis.Configure(p); m_axes.YAxis.Configure(p); m_axes.ZAxis.Configure(p); //computeLayoutData(p); float rotatedViewLimitX, rotatedViewLimitY, rotatedViewLimitZ; GetTransformedSize(out rotatedViewLimitX, out rotatedViewLimitY, out rotatedViewLimitZ); // determine needed margins according to current tic label collections (which are left over from last rendering run or empty) Size ticLabelsMargins = GetMaxTicLabelSize(p.Graphics); updatePlotCubeScreenRect(rotatedViewLimitX, rotatedViewLimitY, ticLabelsMargins); p.PassCount = 0; for (; p.PassCount < MAXRENDERPASSES; p.PassCount++) { UpdateMatrices(rotatedViewLimitX, rotatedViewLimitY, rotatedViewLimitZ); updateTickLabelLines(); updateLabelPositions(p); if (p.PassCount == 0) { } iRenderingState2(p); // do the actual drawing //iRenderingState3(p); #region check if we can fit the plot cube more precisely if (m_plotBoxScreenSizeMode == PlotBoxScreenSizeMode.StrictOptimal) { float t = m_plotBoxScreenRectF.Top; float l = m_plotBoxScreenRectF.Left; float r = m_plotBoxScreenRectF.Right; float b = m_plotBoxScreenRectF.Bottom, tmp; bool resize = false; tmp = (float)p.MinX / ClientSize.Width; if (tmp < l) { if (p.MinX != 0) { l -= tmp; resize = true; } } else if (l > 0) { l = 0; resize = true; } tmp = (float)p.MaxX / ClientSize.Width; if (tmp > r) { if (p.MaxX != (ClientSize.Width - 1)) { r += (1 - tmp); resize = true; } } else if (r < 1) { r = (float)(ClientSize.Width - 1) / ClientSize.Width; resize = true; } tmp = (float)p.MinY / ClientSize.Height; if (tmp < t) { if (p.MinY != 0) { t -= tmp; resize = true; } } else if (t > 0) { t = 0; resize = true; } tmp = (float)p.MaxY / ClientSize.Height; if (tmp > b) { if (p.MaxY != (ClientSize.Height - 1)) { b += (1 - tmp); resize = true; } } else if (b < 1) { b = (float)(ClientSize.Height - 1) / ClientSize.Height; resize = true; } if (resize) { // ... there is still room left we can use for enlarging the render cube m_plotBoxScreenRectF = new RectangleF(l, t, r - l, b - t); p.Reason = RenderReason.RecalcLabels; p.MaxY = int.MinValue; p.MaxX = int.MinValue; p.MinY = int.MaxValue; p.MinX = int.MaxValue; } continue; } #endregion break; } iRenderingState3(p); } } } protected abstract void iRenderingState1(ILRenderProperties p); /// /// [internal] Configure this panel, to make it ready for output, set "m_ready = true" at end! /// protected virtual void Configure() { foreach (ILGraph graph in m_graphs) { graph.Configure(); } // update clipping view m_clipplanes[0] = -1.0; m_clipplanes[3] = m_clippingView.Max.X; m_clipplanes[4] = 1.0; m_clipplanes[7] = -m_clippingView.Min.X; m_clipplanes[9] = -1.0; m_clipplanes[11] = m_clippingView.Max.Y; m_clipplanes[13] = 1.0; m_clipplanes[15] = -m_clippingView.Min.Y; m_clipplanes[18] = -1.0; m_clipplanes[19] = m_clippingView.Max.Z; m_clipplanes[22] = 1.0; m_clipplanes[23] = -m_clippingView.Min.Z; m_ready = true; } /// /// draw the scene: all axes, graphs, background etc. (device dependant) /// /// protected abstract void iRenderingState2(ILRenderProperties p); protected abstract void iRenderingState3(ILRenderProperties p); protected abstract void UpdateMatrices(float width2D, float height2D, float depth2D); /// /// initialize all device specific classes, first called after the panel has been created /// /// derived types should init all devices here protected virtual void Initialize() { } protected PointF GetXTickLabelLine(out Point start2D, out Point end2D) { PointF anchor = new PointF(0,0); // TickLabelAlign.left; ILPoint3Df start = new ILPoint3Df(), end = new ILPoint3Df(); ILTickCollection ticks = m_axes[0].LabeledTicks; float tickLen = (ticks.Direction == TickDirection.Outside) ? ticks.TickFraction : 0f; float padX = ticks.Padding, padY = padX; switch (m_camera.Quadrant) { case CameraQuadrant.TopLeftFront: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Min.Y - tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(0,0); //TickLabelAlign.left | TickLabelAlign.top; padX = -m_camera.SinPhi * padX; padY = (m_camera.CosPhi - m_camera.SinRho * m_camera.SinPhi) * (padY) + m_camera.SinPhi * ticks.Font.Height / 2; break; case CameraQuadrant.TopLeftBack: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Max.Y + tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(1,0); //TickLabelAlign.right | TickLabelAlign.top; padX *= m_camera.SinPhi; padY = (-m_camera.CosPhi - m_camera.SinRho * m_camera.SinPhi) * padY + m_camera.SinPhi * ticks.Font.Height / 2; break; case CameraQuadrant.TopRightBack: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Max.Y + tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(0,0); //TickLabelAlign.left | TickLabelAlign.top; padX *= m_camera.SinPhi; padY = (-m_camera.CosPhi + m_camera.SinRho * m_camera.SinPhi) * padY - m_camera.SinPhi * ticks.Font.Height / 2; break; case CameraQuadrant.TopRightFront: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Min.Y - tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; if (m_camera.Is2DView) { anchor = new PointF(.5f, 0); //TickLabelAlign.center | TickLabelAlign.top; } else { anchor = new PointF(1,0); //TickLabelAlign.right | TickLabelAlign.top; } padX *= -m_camera.SinPhi; padY = (m_camera.CosPhi + m_camera.SinRho * m_camera.SinPhi) * padY - m_camera.SinPhi * ticks.Font.Height / 2; break; case CameraQuadrant.BottomLeftFront: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Max.Y + tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(1,0); //TickLabelAlign.right | TickLabelAlign.top; padX *= m_camera.SinPhi; padY = (m_camera.CosPhi - m_camera.SinRho * m_camera.SinPhi) * padY + m_camera.SinPhi * ticks.Font.Height / 2; break; case CameraQuadrant.BottomLeftBack: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Min.Y - tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(0,0); //TickLabelAlign.left | TickLabelAlign.top; padX *= -m_camera.SinPhi; padY = (-m_camera.CosPhi - m_camera.SinRho * m_camera.SinPhi) * padY + m_camera.SinPhi * ticks.Font.Height / 2; break; case CameraQuadrant.BottomRightBack: start.X = m_clippingView.Min.X; start.Y = m_clippingView.Min.Y - tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(1,0); //TickLabelAlign.right | TickLabelAlign.top; padX *= -m_camera.SinPhi; padY = (-m_camera.CosPhi + m_camera.SinRho * m_camera.SinPhi) * padY - m_camera.SinPhi * ticks.Font.Height / 2; break; default: // BottomRightFront start.X = m_clippingView.Min.X; start.Y = m_clippingView.Max.Y + tickLen; start.Z = m_clippingView.Min.Z; end.X = m_clippingView.Max.X; end.Y = start.Y; end.Z = start.Z; anchor = new PointF(0,0); //TickLabelAlign.left | TickLabelAlign.top; padX *= m_camera.SinPhi; padY = (m_camera.CosPhi + m_camera.SinRho * m_camera.SinPhi) * padY - m_camera.SinPhi * ticks.Font.Height / 2; break; } World2Screen(start, end, out start2D, out end2D); // align in screen coords // int offY = (int)((m_camera.SinRho - (m_camera.SinPhi % Math.PI)) // * m_axes[0].LabeledTicks.Font.Height / 2 // + padY); // add padding //System.Diagnostics.Debug.WriteLine(m_camera.Phi); start2D.Y += (int)padY + 1; end2D.Y += (int)padY + 1; start2D.X += (int)(padX + 1); end2D.X += (int)(padX + 1); return anchor; } protected PointF GetYTickLabelLine(out Point start2D, out Point end2D) { PointF anchor; // = new PointF(0,0); // TickLabelAlign.left; ILPoint3Df start = new ILPoint3Df(), end = new ILPoint3Df(); ILTickCollection ticks = m_axes[1].LabeledTicks; float tickLen = (ticks.Direction == TickDirection.Outside) ? ticks.TickFraction : 0f; float padX = ticks.Padding, padY = padX; if (m_camera.Is2DView) { start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(1, 0); // TickLabelAlign.right | TickLabelAlign.top; padX *= -m_camera.CosPhi; padY = (-m_camera.SinPhi + m_camera.SinRho * m_camera.CosPhi) * padY - m_camera.CosPhi * ticks.Font.Height / 2; } else { switch (m_camera.Quadrant) { case CameraQuadrant.TopLeftFront: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(1, 0); // TickLabelAlign.right | TickLabelAlign.top; padX *= -m_camera.CosPhi; padY = (-m_camera.SinPhi + m_camera.SinRho * m_camera.CosPhi) * padY - m_camera.CosPhi * ticks.Font.Height / 2; break; case CameraQuadrant.TopLeftBack: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(0, 0); // TickLabelAlign.left | TickLabelAlign.top; padX *= -m_camera.CosPhi; padY = (-m_camera.SinPhi - m_camera.SinRho * m_camera.CosPhi) * padY + m_camera.CosPhi * ticks.Font.Height / 2; break; case CameraQuadrant.TopRightBack: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(1,0); // TickLabelAlign.right | TickLabelAlign.top; padX *= m_camera.CosPhi; padY = (m_camera.SinPhi - m_camera.SinRho * m_camera.CosPhi) * padY + m_camera.CosPhi * ticks.Font.Height / 2; break; case CameraQuadrant.TopRightFront: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(0,0); // TickLabelAlign.left | TickLabelAlign.top; padX *= m_camera.CosPhi; padY = (m_camera.SinPhi + m_camera.SinRho * m_camera.CosPhi) * padY - m_camera.CosPhi * ticks.Font.Height / 2; break; case CameraQuadrant.BottomLeftFront: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(0,0); // TickLabelAlign.left | TickLabelAlign.top; padX *= m_camera.CosPhi; padY = (-m_camera.SinPhi + m_camera.SinRho * m_camera.CosPhi) * padY - m_camera.CosPhi * ticks.Font.Height / 2; break; case CameraQuadrant.BottomLeftBack: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(1,0); // TickLabelAlign.right | TickLabelAlign.top; padX *= m_camera.CosPhi; padY = (-m_camera.SinPhi - m_camera.SinRho * m_camera.CosPhi) * padY + m_camera.CosPhi * ticks.Font.Height / 2; break; case CameraQuadrant.BottomRightBack: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(0,0); // TickLabelAlign.left | TickLabelAlign.top; padX *= -m_camera.CosPhi; padY = (m_camera.SinPhi - m_camera.SinRho * m_camera.CosPhi) * padY + m_camera.CosPhi * ticks.Font.Height / 2; break; default: // BottomRightFront start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y; start.Z = m_clippingView.Min.Z; end.X = start.X; end.Y = m_clippingView.Max.Y; end.Z = start.Z; anchor = new PointF(1,0); // TickLabelAlign.right | TickLabelAlign.top; padX *= -m_camera.CosPhi; padY = (m_camera.SinPhi + m_camera.SinRho * m_camera.CosPhi) * padY - m_camera.CosPhi * ticks.Font.Height / 2; break; } } World2Screen(start, end, out start2D, out end2D); // align in screen coords //int offY = (int)(Math.Abs(m_camera.CosPhi) * ticks.Font.Height / 2); //offY -= (int)(Math.Sin(m_camera.Rho) * ticks.Font.Height / 2); start2D.Y += (int)padY; end2D.Y += (int)padY; start2D.X += (int)padX; end2D.X += (int)padX; return anchor; } protected PointF GetZTickLabelLine(out Point start2D, out Point end2D) { PointF anchor = new PointF(1, .5f); // TickLabelAlign.vertCenter | TickLabelAlign.right; ILPoint3Df start = new ILPoint3Df(), end = new ILPoint3Df(); ILTickCollection ticks = m_axes[2].LabeledTicks; float tickLen = 0; if (ticks.Direction == TickDirection.Outside) tickLen = ticks.TickFraction; start.Z = m_clippingView.Min.Z; switch (m_camera.Quadrant) { case CameraQuadrant.TopLeftFront: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Max.Y + tickLen; break; case CameraQuadrant.TopLeftBack: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Max.Y + tickLen; break; case CameraQuadrant.TopRightBack: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Min.Y - tickLen; break; case CameraQuadrant.TopRightFront: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y - tickLen; break; case CameraQuadrant.BottomLeftFront: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Max.Y + tickLen; break; case CameraQuadrant.BottomLeftBack: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Max.Y + tickLen; break; case CameraQuadrant.BottomRightBack: start.X = m_clippingView.Max.X + tickLen; start.Y = m_clippingView.Min.Y - tickLen; break; case CameraQuadrant.BottomRightFront: start.X = m_clippingView.Min.X - tickLen; start.Y = m_clippingView.Min.Y - tickLen; break; default: break; } end = start; end.Z = m_clippingView.Max.Z; World2Screen(start, end, out start2D, out end2D); start2D.X -= (ticks.Padding - 1); end2D.X -= (ticks.Padding - 1); return anchor; } /// /// Get size of projected view cube - after (!) rotation but before projection -> world space /// includes the bounding box, tightly enclosing the current view limits setting /// No labels, No ticks included!! Just the data cube with roatation! /// /// out, screen size for X /// out, screen size for Y /// out, screen size for Z protected virtual void GetTransformedSize(out float x, out float y, out float z) { float xSize = m_clippingView.WidthF; // the unrotated width - without labels size float ySize = m_clippingView.HeightF; // dito for height float zSize = m_clippingView.DepthF; if (m_camera.Rho != 0.0f || m_camera.Phi != 0.0f) { // standard 2D view? float cosPhi = Math.Abs(m_camera.CosPhi); float sinPhi = Math.Abs(m_camera.SinPhi); x = (cosPhi * xSize + sinPhi * ySize); //zSize * Math.Abs(m_camera.SinRho) + (Math.Abs(m_camera.CosRho) * y = zSize * Math.Abs(m_camera.SinRho) + (Math.Abs(m_camera.CosRho) * (sinPhi * xSize + cosPhi * ySize)); z = zSize * Math.Abs(m_camera.CosRho) + (Math.Abs(m_camera.SinRho) * (sinPhi * xSize + cosPhi * ySize)); } else { x = xSize; y = ySize; z = zSize; } } private void axisLabelVertical2D(ILAxis axis) { ILLabel label = axis.Label; ILTickCollection ticks = axis.LabeledTicks; Size tickSize = ticks.Size, labelSize = label.Size; label.Orientation = TextOrientation.Vertical; label.Anchor = new PointF(.5f, 0); label.m_position.X = ticks.m_lineEnd.X - tickSize.Width - ticks.Padding - label.Padding; switch (label.Alignment) { case LabelAlign.Center: label.m_position.Y = (ticks.m_lineEnd.Y + ticks.m_lineStart.Y) / 2; break; case LabelAlign.Upper: label.m_position.Y = ticks.m_lineEnd.Y + labelSize.Width / 2; break; default: // lower label.m_position.Y = ticks.m_lineStart.Y - labelSize.Width / 2; break; } if (label.m_position.Y < labelSize.Width / 2) { label.m_position.Y = labelSize.Width / 2; } if (label.m_position.Y > ClientSize.Height - 1 - labelSize.Width / 2) { label.m_position.Y = ClientSize.Height - 1 - labelSize.Width / 2; } } private void positionZAxisLabel(ILAxis axis) { ILLabel label = axis.Label; ILTickCollection ticks = axis.LabeledTicks; Size tickSize = ticks.Size, labelSize = label.Size; label.Orientation = TextOrientation.Vertical; label.Anchor = new PointF(.5f, 0); switch (label.Alignment) { case LabelAlign.Center: label.m_position.Y = (ticks.m_lineStart.Y + ticks.m_lineEnd.Y) / 2; break; case LabelAlign.Upper: label.m_position.Y = ticks.m_lineEnd.Y - labelSize.Width / 2; break; default: // lower label.m_position.Y = ticks.m_lineStart.Y + labelSize.Width / 2 ; break; } label.m_position.X = ticks.m_lineStart.X - tickSize.Width - ticks.Padding - label.Padding; if (label.m_position.Y < labelSize.Width / 2) { label.m_position.Y = labelSize.Width / 2; } if (label.m_position.Y > ClientSize.Height - 1 - labelSize.Width / 2) { label.m_position.Y = ClientSize.Height - 1 - labelSize.Width / 2; //label.Orientation = TextOrientation.Horizontal; //label.m_position.X = label.Padding; //label.m_position.Y = label.Padding; } } private void axisLabel_BottomRightTopLeft(ILRenderProperties p, ILAxis axis) { ILLabel label = axis.Label; ILTickCollection ticks = axis.LabeledTicks; Size tickSize = ticks.Size, labelSize = label.Size; label.Anchor = new PointF(.5f, 0); bool horiz = decideLabelHorizontalOrientation(p, m_camera, axis); if (horiz) { #region Horizontal label.Orientation = TextOrientation.Horizontal; switch (label.Alignment) { case LabelAlign.Center: label.m_position.X = (ticks.m_lineStart.X + ticks.m_lineEnd.X) / 2; break; case LabelAlign.Upper: label.m_position.X = ticks.m_lineEnd.X + labelSize.Width / 2; break; default: // lower label.m_position.X = ticks.m_lineStart.X - labelSize.Width / 2; break; } label.m_position.Y = ticks.m_lineStart.Y + tickSize.Height + ticks.Padding + label.Padding; if (label.m_position.X < labelSize.Width / 2) { label.m_position.X = labelSize.Width / 2; } #endregion } else { #region Vertical label.Orientation = TextOrientation.Vertical; switch (label.Alignment) { case LabelAlign.Center: label.m_position.Y = (ticks.m_lineStart.Y + ticks.m_lineEnd.Y) / 2; break; case LabelAlign.Upper: label.m_position.Y = ticks.m_lineEnd.Y + labelSize.Width / 2; break; default: // lower label.m_position.Y = ticks.m_lineStart.Y - labelSize.Width / 2; break; } label.m_position.X = ticks.m_lineEnd.X - tickSize.Width - label.Padding - ticks.Padding; if (label.m_position.Y < labelSize.Width / 2) label.m_position.Y = labelSize.Width / 2; if (label.m_position.Y > ClientSize.Height - 1 - labelSize.Width / 2) { label.m_position.Y = ClientSize.Height - 1 - labelSize.Width / 2; } #endregion } } private void axisLabel_BottomLeftTopRight(ILRenderProperties p, ILAxis axis) { ILLabel label = axis.Label; ILTickCollection ticks = axis.LabeledTicks; Size tickSize = ticks.Size, labelSize = label.Size; bool horiz = decideLabelHorizontalOrientation(p, m_camera, axis); if (horiz) { #region Horizontal label.Orientation = TextOrientation.Horizontal; label.Anchor = new PointF(0.5f,0); switch (label.Alignment) { case LabelAlign.Center: label.m_position.X = (ticks.m_lineEnd.X + ticks.m_lineStart.X) / 2; break; case LabelAlign.Upper: label.m_position.X = ticks.m_lineEnd.X - label.Size.Width / 2; break; default: // lower label.m_position.X = ticks.m_lineStart.X + label.Size.Width / 2; break; } label.m_position.Y = ticks.m_lineStart.Y + tickSize.Height + ticks.Padding + label.Padding; if (label.m_position.X > ClientSize.Width - labelSize.Width / 2) { label.m_position.X = ClientSize.Width - labelSize.Width / 2; } #endregion } else { #region Vertical label.Orientation = TextOrientation.Vertical; label.Anchor = new PointF(0.5f, 1); switch (label.Alignment) { case LabelAlign.Center: label.m_position.Y = (ticks.m_lineEnd.Y + ticks.m_lineStart.Y) / 2; break; case LabelAlign.Upper: label.m_position.Y = ticks.m_lineEnd.Y + labelSize.Width / 2; break; default: // lower label.m_position.Y = ticks.m_lineStart.Y - labelSize.Width / 2; break; } label.m_position.X = ticks.m_lineEnd.X + tickSize.Width + ticks.Padding + label.Padding; if (label.m_position.Y > ClientSize.Height - 1 - labelSize.Width / 2) { label.m_position.Y = ClientSize.Height - 1 - labelSize.Width / 2; } #endregion } } private void axisLabel_TopRightBottomLeft(ILRenderProperties p, ILAxis axis) { ILLabel label = axis.Label; ILTickCollection ticks = axis.LabeledTicks; Size tickSize = ticks.Size, labelSize = label.Size; bool horiz = decideLabelHorizontalOrientation(p, m_camera, axis); label.Anchor = new PointF(.5f, 0); if (horiz) { #region Horizontal label.Orientation = TextOrientation.Horizontal; switch (label.Alignment) { case LabelAlign.Center: label.m_position.X = (ticks.m_lineEnd.X + ticks.m_lineStart.X) / 2; break; case LabelAlign.Upper: label.m_position.X = ticks.m_lineEnd.X + labelSize.Width / 2; break; default: // lower label.m_position.X = ticks.m_lineStart.X - labelSize.Width / 2; break; } label.m_position.Y = ticks.m_lineEnd.Y + tickSize.Height + ticks.Padding + label.Padding; if (label.m_position.X < labelSize.Width / 2) label.m_position.X = labelSize.Width / 2; if (label.m_position.X > ClientSize.Width - labelSize.Width / 2 - 1) label.m_position.X = ClientSize.Width - labelSize.Width / 2 - 1; #endregion } else { #region Vertical label.Orientation = TextOrientation.Vertical; switch (label.Alignment) { case LabelAlign.Center: label.m_position.Y = (ticks.m_lineEnd.Y + ticks.m_lineStart.Y) / 2; break; case LabelAlign.Upper: label.m_position.Y = ticks.m_lineEnd.Y - labelSize.Width / 2; break; default: // lower label.m_position.Y = ticks.m_lineStart.Y + labelSize.Width / 2; break; } label.m_position.X = ticks.m_lineStart.X + tickSize.Width + ticks.Padding + label.Padding; if (label.m_position.Y < labelSize.Width / 2) { label.m_position.Y = labelSize.Width / 2; } if (label.m_position.Y > ClientSize.Height - 1 - labelSize.Width / 2) { label.m_position.Y = ClientSize.Height - 1 - labelSize.Width / 2; } #endregion } } private void axisLabel_TopLeftBottomRight(ILRenderProperties p, ILAxis axis) { ILLabel label = axis.Label; ILTickCollection ticks = axis.LabeledTicks; Size tickSize = ticks.Size, labelSize = label.Size; bool horiz = decideLabelHorizontalOrientation(p, m_camera, axis); label.Anchor = new PointF(.5f, 0); if (horiz) { #region Horizontal label.Orientation = TextOrientation.Horizontal; switch (label.Alignment) { case LabelAlign.Center: label.m_position.X = (ticks.m_lineEnd.X + ticks.m_lineStart.X) / 2; break; case LabelAlign.Upper: label.m_position.X = ticks.m_lineEnd.X - labelSize.Width / 2; break; default: // lower label.m_position.X = ticks.m_lineStart.X + labelSize.Width / 2; break; } label.m_position.Y = ticks.m_lineEnd.Y + tickSize.Height + ticks.Padding + label.Padding; if (label.m_position.X < labelSize.Width / 2) { label.m_position.X = labelSize.Width / 2; } #endregion } else { #region Vertical label.Orientation = TextOrientation.Vertical; switch (label.Alignment) { case LabelAlign.Center: label.m_position.Y = (ticks.m_lineEnd.Y + ticks.m_lineStart.Y) / 2; break; case LabelAlign.Upper: label.m_position.Y = ticks.m_lineEnd.Y - labelSize.Width / 2; break; default: // lower label.m_position.Y = ticks.m_lineStart.Y + labelSize.Width / 2; break; } label.m_position.X = ticks.m_lineStart.X - tickSize.Width - ticks.Padding - label.Padding; if (label.m_position.Y < labelSize.Width / 2) { label.m_position.Y = labelSize.Width / 2; } if (label.m_position.Y > ClientSize.Height - 1 - labelSize.Width / 2) { label.m_position.Y = ClientSize.Height - 1 - labelSize.Width / 2; } #endregion } } // return true if the label needs horizontal orientation private bool decideLabelHorizontalOrientation(ILRenderProperties p, ILCamera m_camera, ILAxis axis) { if (p.Reason == RenderReason.RecalcLabels) { return axis.Label.Orientation == TextOrientation.Horizontal; } float offX = axis.LabeledTicks.m_lineStart.X - axis.LabeledTicks.m_lineEnd.X; float offY = axis.LabeledTicks.m_lineStart.Y - axis.LabeledTicks.m_lineEnd.Y; return offY * offY < offX * offX; //if (axis.Index == 0) { // b = (float)((1.0 - m_camera.SinRho * 0.9) * Math.Sin(m_camera.Phi * 2.0f)); //} else if (axis.Index == 1) { //} //return (m_camera.SinRho >= LABELS_VERTICAL_MIN_RHO // || Math.Sign(Math.Cos(m_camera.Phi * 2.0)) != (axis.Index * 2 - 1)); } private int GetMargin4OptimalLabelX() { return 0; } private int GetMargin4OptimalLabelY() { return 0; } private Size GetMaxTicLabelSize(Graphics gr) { Size ret = m_axes.MeasureMaxTickLabelSize(gr); // add label sizes int maxHeight = 0; if (m_axes[0].Label.Visible && !String.IsNullOrEmpty(m_axes[0].Label.Text)) { maxHeight = m_axes[0].Label.Size.Height; } if (m_axes[1].Label.Visible && !String.IsNullOrEmpty(m_axes[1].Label.Text)) { if (maxHeight < m_axes[1].Label.Size.Height) maxHeight = m_axes[1].Label.Size.Height; } if (m_axes[2].Label.Visible && !String.IsNullOrEmpty(m_axes[2].Label.Text)) { if (maxHeight < m_axes[2].Label.Size.Height) maxHeight = m_axes[2].Label.Size.Height; } ret = new Size(ret.Width + maxHeight, ret.Height + maxHeight); return ret; } /// /// calculate the real pixels of the plot cube rectangle for drawing into /// /// /// private void updatePlotCubeScreenRect(float xSize, float ySize, Size tickLabelMargins) { switch (PlotBoxScreenSizeMode) { //case PlotCubeScreenSizeMode.Maximum: // <- will be done in PlotCubeScreenSizeMode.set() // m_plotCubeScreenRectF = new RectangleF(0.0f, 0.0f, 1.0f, 1.0f); // break; case PlotBoxScreenSizeMode.Optimal: case PlotBoxScreenSizeMode.StrictOptimal: float saveX, saveY; saveX = ((float)tickLabelMargins.Width / ClientSize.Width); if (saveX < 0) saveX = 0; if (saveX > 1) saveX = 1; saveY = ((float)tickLabelMargins.Height / ClientSize.Height); if (saveY < 0) saveY = 0; if (saveY > 1) saveY = 1; m_plotBoxScreenRectF = new RectangleF(saveX, saveY, 1f - (2 * saveX), 1f - (2 * saveY)); break; default: // manual: use predefined PlotCubeScreenRectF break; } } private void updateLabelPositions(ILRenderProperties p) { positionZAxisLabel(m_axes[2]); switch (m_camera.Quadrant) { case CameraQuadrant.TopRightFront: axisLabel_TopLeftBottomRight(p, m_axes[0]); // special case: 2D view? if (m_camera.SinPhi < 1e-5 && m_camera.SinRho < 1e-5) { axisLabelVertical2D(m_axes[1]); } else { axisLabel_BottomLeftTopRight(p, m_axes[1]); } break; case CameraQuadrant.TopRightBack: axisLabel_TopRightBottomLeft(p, m_axes[0]); axisLabel_TopLeftBottomRight(p, m_axes[1]); break; case CameraQuadrant.TopLeftFront: axisLabel_BottomLeftTopRight(p, m_axes[0]); axisLabel_BottomRightTopLeft(p, m_axes[1]); break; case CameraQuadrant.TopLeftBack: axisLabel_BottomRightTopLeft(p, m_axes[0]); axisLabel_TopRightBottomLeft(p, m_axes[1]); break; case CameraQuadrant.BottomLeftFront: axisLabel_TopLeftBottomRight(p, m_axes[0]); axisLabel_TopRightBottomLeft(p, m_axes[1]); break; case CameraQuadrant.BottomLeftBack: axisLabel_TopRightBottomLeft(p, m_axes[0]); axisLabel_BottomRightTopLeft(p, m_axes[1]); break; case CameraQuadrant.BottomRightBack: axisLabel_BottomRightTopLeft(p, m_axes[0]); axisLabel_BottomLeftTopRight(p, m_axes[1]); break; case CameraQuadrant.BottomRightFront: axisLabel_BottomLeftTopRight(p, m_axes[0]); axisLabel_TopLeftBottomRight(p, m_axes[1]); break; default: break; } } private void updateTickLabelLines() { // determine tick label lines ILTickCollection ticks = m_axes[2].LabeledTicks; ticks.m_anchor = GetZTickLabelLine(out ticks.m_lineStart, out ticks.m_lineEnd); ticks = m_axes[1].LabeledTicks; ticks.m_anchor = GetYTickLabelLine(out ticks.m_lineStart, out ticks.m_lineEnd); ticks = m_axes[0].LabeledTicks; ticks.m_anchor = GetXTickLabelLine(out ticks.m_lineStart, out ticks.m_lineEnd); } #endregion #region factory member /// /// create OpenGL panel /// /// OpenGL panel public static ILPanel Create () { return Create(GraphicDeviceType.OpenGL); } /// /// create graphic device specific panel /// /// specify GL type. Supported are: OpenGL (recommended) or Direct3D (alpha state) /// GL specific panel public static ILPanel Create (GraphicDeviceType type) { ILPanel ret; Type panelType; Assembly assembly; string myPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); switch (type) { case GraphicDeviceType.Direct3D: //must create DXPanel dynamically ? // actually not, because DX is split into individual assembly // but under mono it seems the runtime tries to load the d3d part // anyway, so we do without a compile time reference. assembly = Assembly.LoadFile(Path.Combine(myPath,"ILNumerics.DrawingD3D.dll")); panelType = assembly.GetType("ILNumerics.Drawing.Platform.OpenGL.ILDXPanel"); ret = (ILPanel)panelType.InvokeMember("ILDXPanel", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null); return ret; default: // the default is to use OpenGL, which is included into the main assembly (this one) assembly = Assembly.GetExecutingAssembly(); panelType = assembly.GetType("ILNumerics.Drawing.Platform.OpenGL.ILOGLPanel"); ret = (ILPanel)panelType.InvokeMember("ILOGLPanel", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null); return ret; } } /// /// Create GL dependend graph factory /// /// ILGraphFactory,will be used for creating all graphs /// derived types may return GL dependend factory internal abstract IILCreationFactory GetCreationFactory(); #endregion } }