/// /// 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 ILNumerics.Drawing.Misc; namespace ILNumerics.Drawing { /// /// Class holding and managing limits for a 3 dimensional cube /// public sealed class ILClippingData { #region eventing /// /// fires if the data range have changed /// public event ILClippingDataChangedEvent Changed; /// /// called if the limits have changed /// private void OnChange() { if (m_eventingActive && Changed != null) { Changed ( this, new ClippingChangedEventArgs(this)); } m_isDirty = false; } #endregion #region attributes private bool m_eventingActive = true; private float m_xMin = float.MaxValue; private float m_yMin = float.MaxValue; private float m_zMin = float.MaxValue; private float m_xMax = float.MinValue; private float m_yMax = float.MinValue; private float m_zMax = float.MinValue; private float m_sphereRadius; private bool m_isDirty = false; private bool m_allowZeroVolume = true; #endregion #region properties /// /// the radius of a sphere tightly enclosing the box determined by this clipping data limits (readonly) /// public float SphereRadius { get { return m_sphereRadius; } } /// /// minimum value for x axis /// public float XMin { get { return m_xMin; } set { if (m_xMin != value) { m_isDirty = true; m_xMin = value; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } } } /// /// minimum value for y axis /// public float YMin { get { return m_yMin; } set { if (m_yMin != value) { m_isDirty = true; m_yMin = value; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } } } /// /// minimum value for z axis /// public float ZMin { get { return m_zMin; } set { if (m_zMin != value) { m_isDirty = true; m_zMin = value; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } } } /// /// maximum value for x axis /// public float XMax { get { return m_xMax; } set { if (m_xMax != value) { m_isDirty = true; m_xMax = value; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } } } /// /// maximum value for y axis /// public float YMax { get { return m_yMax; } set { if (m_yMax != value) { m_isDirty = true; m_yMax = value; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } } } /// /// maximum value for z axis /// public float ZMax { get { return m_zMax; } set { if (m_zMax != value) { m_isDirty = true; m_zMax = value; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } } } /// /// minimum (coordinate) /// public ILPoint3Df Min { get { return new ILPoint3Df(XMin,YMin,ZMin); } } /// /// maximum (coordinate) /// public ILPoint3Df Max { get { return new ILPoint3Df(XMax,YMax,ZMax); } } /// /// get center of this clipping range /// public ILPoint3Df CenterF { get { ILPoint3Df ret; ret.X = (XMax + XMin) / 2.0f; ret.Y = (YMax + YMin) / 2.0f; ret.Z = (ZMax + ZMin) / 2.0f; return ret; } } /// /// get width (x-direction) of this clipping range /// public float WidthF { get { return (XMax - XMin); } } /// /// get height (y-direction) of this clipping range /// public float HeightF { get { return (YMax - YMin); } } /// /// get depth (z-direction) of this clipping range /// public float DepthF { get { return (ZMax - ZMin); } } /// /// marks the limits as altered, without having fired a changed event yet /// public bool IsDirty { get { return m_isDirty; } } /// /// true: this clipping data always ensures a non-zero volume /// /// 'NonZeroVolumne' means, non of Depth,Width nor Heigth are allowed to be zero. If some edge of the cube is set to zero, the class expands this edge by 1 in each direction. public bool AllowZeroVolume { get { return m_allowZeroVolume; } set { m_allowZeroVolume = value; } } #endregion #region public interface /// /// suspend the firing of events until EventingResume has been called /// public void EventingSuspend() { m_eventingActive = false; m_isDirty = false; } /// /// Resume previously suspended eventing. Start sending events again. /// public void EventingResume() { m_eventingActive = true; if (m_isDirty && Changed != null) { OnChange(); } } /// /// enable eventing without sending pending events /// public void EventingStart() { m_eventingActive = true; m_isDirty = false; } /// /// update ranges for this object with union of both ranges. /// /// clipping ranges to create union with public void Update (ILClippingData clipData) { if (clipData.XMin < XMin) { m_isDirty = true; m_xMin = clipData.XMin; } if (clipData.YMin < YMin) { m_isDirty = true; m_yMin = clipData.YMin; } if (clipData.ZMin < ZMin) { m_isDirty = true; m_zMin = clipData.ZMin; } if (clipData.XMax > XMax) { m_isDirty = true; m_xMax = clipData.XMax; } if (clipData.YMax > YMax) { m_isDirty = true; m_yMax = clipData.YMax; } if (clipData.ZMax > ZMax) { m_isDirty = true; m_zMax = clipData.ZMax; } if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_isDirty && m_eventingActive && Changed != null) OnChange(); } /// /// update ranges for this object with point coords for specific axes /// /// point with coords to update ranges with /// bitflag combination to specify axis to be recognized: 1,2,4 -> x,y,z public void Update (ILPoint3Df point, int updateBitFlags) { if ((updateBitFlags & 1) != 0) { if (point.X < XMin) { m_isDirty = true; m_xMin = point.X; } if (point.X > XMax) { m_isDirty = true; m_xMax = point.X; } } if ((updateBitFlags & 2) != 0) { if (point.Y < YMin) { m_isDirty = true; m_yMin = point.Y; } if (point.Y > YMax) { m_isDirty = true; m_yMax = point.Y; } } if ((updateBitFlags & 4) != 0) { if (point.Z < ZMin) { m_isDirty = true; m_zMin = point.Z; } if (point.Z > ZMax) { m_isDirty = true; m_zMax = point.Z; } } if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_isDirty && m_eventingActive && Changed != null) OnChange(); } /// /// update clipping data for this object with union of this and rectangle specified /// /// left upper corner /// right lower corner public void Update (ILPoint3Df luCorner, ILPoint3Df rbCorner) { bool oldeventState = m_eventingActive; m_eventingActive = false; Update(luCorner,7); m_eventingActive = oldeventState; Update(rbCorner,7); } public void Update (ILPoint3Df center, float zoomFactor) { if (zoomFactor == 1.0f && center == CenterF) return; m_isDirty = true; float s = WidthF * zoomFactor / 2; m_xMin = center.X - s; m_xMax = center.X + s; s = HeightF * zoomFactor / 2; m_yMin = center.Y - s; m_yMax = center.Y + s; s = DepthF * zoomFactor / 2; m_zMin = center.Z - s; m_zMax = center.Z + s; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } /// /// Set clipping limits to volume inside the box specified /// /// left-upper-near corner of the volume box /// right-bottom-far corner of the volume box public void Set(ILPoint3Df lunCorner, ILPoint3Df rbfCorner) { m_isDirty = true; m_xMin = Math.Min(lunCorner.X,rbfCorner.X); m_xMax = Math.Max(lunCorner.X,rbfCorner.X); m_yMin = Math.Min(lunCorner.Y,rbfCorner.Y); m_yMax = Math.Max(lunCorner.Y,rbfCorner.Y); m_zMin = Math.Min(lunCorner.Z,rbfCorner.Z); m_zMax = Math.Max(lunCorner.Z,rbfCorner.Z); if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); if (m_eventingActive && Changed != null) OnChange(); } /// /// reset this clipping range to initial (all empty) /// public void Reset() { m_xMin = float.MaxValue; m_yMin = float.MaxValue; m_zMin = float.MaxValue; m_xMax = float.MinValue; m_yMax = float.MinValue; m_zMax = float.MinValue; m_isDirty = true; if (m_eventingActive && Changed != null) OnChange(); } /// /// copy this from other clipping data /// /// internal void CopyFrom(ILClippingData m_clippingData) { m_xMin = m_clippingData.XMin; m_yMin = m_clippingData.YMin; m_zMin = m_clippingData.ZMin; m_xMax = m_clippingData.XMax; m_yMax = m_clippingData.YMax; m_zMax = m_clippingData.ZMax; if (!m_allowZeroVolume) ensureVolumeNotZero(); m_sphereRadius = getSphereRadius(); m_isDirty = true; if (m_eventingActive && Changed != null) OnChange(); } /// /// stretch clipping region to unit cube [0 1][0 1][0 1] /// /// public ILPoint3Df ScaleToUnitCube () { ILPoint3Df ret = new ILPoint3Df(); float a = WidthF; if (a != 0.0f) ret.X = 1 / a; else ret.X = 0.0f; a = HeightF; if (a != 0.0f) ret.Y = 1 / a; else ret.Y = 0.0f; a = DepthF; if (a != 0.0f) ret.Z = 1 / a; else ret.Z = 0.0f; return ret; } /// /// offset centering scaled unit cube to zero: [-0.5 0.5][-0.5 0.5][-0.5 0.5] /// /// public ILPoint3Df CenterToUnitCube() { ILPoint3Df a = ScaleToUnitCube(); ILPoint3Df b = new ILPoint3Df(); b.X = -0.5f - a.X * XMin; b.Y = -0.5f - a.Y * YMin; if (ZMin < ZMax) b.Z = (-0.5f - a.Z * ZMin); else b.Z = 0.0f; return b; } /// /// creates clone of this clipping data /// /// clone public ILClippingData Clone() { ILClippingData ret = new ILClippingData(); ret.Update(this); return ret; } /// /// Map coordinat from unit cube space [-0.5..0.5] into the space limited by this clipping data /// /// x coordinate (unit cube space: -0.5 ... 0.5) /// y coordinate (unit cube space: -0.5 ... 0.5) /// z coordinate (unit cube space: -0.5 ... 0.5) /// public ILPoint3Df Map(float x, float y, float z) { ILPoint3Df ret; ret.X = (x + 0.5f) * (m_xMax-m_xMin) + m_xMin; ret.Y = (y + 0.5f) * (m_yMax-m_yMin) + m_yMin; ret.Z = (z + 0.5f) * (m_zMax-m_zMin) + m_zMin; //if (!m_allowZeroVolume) ensureVolumeNotZero(); return ret; } public override string ToString() { return String.Format("Min:{0} Max:{1}",Min,Max); } /// /// Expand/shrink all those edges, not touched by the given line /// /// near line point /// far line point /// multiplicator, shrink-/expand value public void GetZoomParameter(ILPoint3Df nearLineEnd, ILPoint3Df farLineEnd, float offset, out ILPoint3Df minCorner, out ILPoint3Df maxCorner) { // we determine, if the side is touched by the line. // if 'yes': the side is skipped (its limit/position are kept) // if 'no': the side is moved towards/away from center minCorner = this.Min; maxCorner = this.Max; float sX, sY, sZ; float lamb; float aX = nearLineEnd.X - farLineEnd.X; float aY = nearLineEnd.Y - farLineEnd.Y; float aZ = nearLineEnd.Z - farLineEnd.Z; float offX = WidthF * (offset/2.0f); float offY = HeightF * (offset/2.0f); float offZ = DepthF * (offset/2.0f); #region front side lamb = (m_yMin - nearLineEnd.Y) / aY; sX = nearLineEnd.X + lamb * aX; sZ = nearLineEnd.Z + lamb * aZ; if (sX > m_xMax || sX < m_xMin || sZ > m_zMax || sZ < m_zMin) { minCorner.Y += offY; } #endregion #region right side lamb = (m_xMax - nearLineEnd.X) / aX; sY = nearLineEnd.Y + lamb * aY; sZ = nearLineEnd.Z + lamb * aZ; if (sY > m_yMax || sY < m_yMin || sZ > m_zMax || sZ < m_zMin) { maxCorner.X -= offX; } #endregion #region back side lamb = (m_yMax - nearLineEnd.Y) / aY; sX = nearLineEnd.X + lamb * aX; sZ = nearLineEnd.Z + lamb * aZ; if (sX > m_xMax || sX < m_xMin || sZ > m_zMax || sZ < m_zMin) { maxCorner.Y -= offY; } #endregion #region left side lamb = (m_xMin - nearLineEnd.X) / aX; sY = nearLineEnd.Y + lamb * aY; sZ = nearLineEnd.Z + lamb * aZ; if (sY > m_yMax || sY < m_yMin || sZ > m_zMax || sZ < m_zMin) { minCorner.X += offX; } #endregion #region top side lamb = (m_zMax - nearLineEnd.Z) / aZ; sX = nearLineEnd.X + lamb * aX; sY = nearLineEnd.Y + lamb * aY; if (sY > m_yMax || sY < m_yMin || sX > m_xMax || sX < m_xMin) { maxCorner.Z -= offZ; } #endregion #region bottom side lamb = (m_zMin - nearLineEnd.Z) / aZ; sX = nearLineEnd.X + lamb * aX; sY = nearLineEnd.Y + lamb * aY; if (sY > m_yMax || sY < m_yMin || sX > m_xMax || sX < m_xMin) { minCorner.Z += offZ; } #endregion #region translate: move line to cross middle of view cube ILPoint3Df rQ = CenterF; sX = rQ.X - nearLineEnd.X; sY = rQ.Y - nearLineEnd.Y; sZ = rQ.Z - nearLineEnd.Z; lamb = (aX * sX + aY * sY + aZ * sZ) / (aX * aX + aY * aY + aZ * aZ); offX = rQ.X - (nearLineEnd.X + lamb * aX); offY = rQ.Y - (nearLineEnd.Y + lamb * aY); offZ = rQ.Z - (nearLineEnd.Z + lamb * aZ); minCorner.X -= offX; minCorner.Y -= offY; minCorner.Z -= offZ; maxCorner.X -= offX; maxCorner.Y -= offY; maxCorner.Z -= offZ; #endregion } #endregion #region private helper private float getSphereRadius() { return ((Max - Min) / 2f).GetLength(); } /// /// Ensure that this clipping data has valid length for all dimensions [deprecated] /// /// If the length of the clipping cube is infinity for any dimension, /// that dimension is set to a range of -0.5...0.5. /// This function is to be called by custom graphs which create their /// size relative to the size of the clipping container. Those /// graphs will need a valid container size and may call this function in the /// constructor. /// true if the length in any dimension had to be corrected (set to 1.0), false otherwise. private bool EnsureValidSize() { bool change = false; if (float.IsInfinity(WidthF)) { change = true; m_xMin = -0.5f; m_xMax = 0.5f; } if (float.IsInfinity(HeightF)) { change = true; m_yMin = -0.5f; m_yMax = 0.5f; } if (float.IsInfinity(DepthF)) { change = true; m_zMin = -0.5f; m_zMax = 0.5f; } if (change && m_eventingActive && Changed != null) { OnChange(); } return change; } private void ensureVolumeNotZero() { if (HeightF == 0) { m_isDirty = true; m_yMin = m_yMin - 1f; m_yMax = m_yMin + 2f; } if (WidthF == 0) { m_isDirty = true; m_xMin = m_xMin - 1f; m_xMax = m_xMin + 2f; } if (DepthF == 0) { m_isDirty = true; m_zMin = m_zMin - 1f; m_zMax = m_zMin + 2f; } } #endregion #region operator overloads /// /// Equalty operator overload, true if both cubes span the same region in 3D space /// /// cube 1 /// cube 2 /// true if both cubes span the same 3D space, false otherwise public static bool operator == (ILClippingData limit1, ILClippingData limit2) { return (limit1.m_xMax == limit2.m_xMax && limit1.m_yMax == limit2.m_yMax && limit1.m_zMax == limit2.m_zMax && limit1.m_xMin == limit2.m_xMin && limit1.m_yMin == limit2.m_yMin && limit1.m_zMin == limit2.m_zMin); } /// /// unequalty operator /// /// cube 1 /// cube 2 /// false if both cubes span the same 3D space, true otherwise public static bool operator !=(ILClippingData limit1, ILClippingData limit2) { return (limit1.m_xMax != limit2.m_xMax || limit1.m_yMax != limit2.m_yMax || limit1.m_zMax != limit2.m_zMax || limit1.m_xMin != limit2.m_xMin || limit1.m_yMin != limit2.m_yMin || limit1.m_zMin != limit2.m_zMin); } /// /// Returns hash code for this ILClippingData /// /// hash code public override int GetHashCode() { return base.GetHashCode(); } /// /// Compares to cube objects /// /// /// true if obj references this class instance, false otherwise public override bool Equals(object obj) { return base.Equals(obj); } #endregion } }