context: #region License
  
  // Copyright (c) 2013, ClearCanvas Inc.
  // All rights reserved.
  // http://www.clearcanvas.ca
  //
  // This file is part of the ClearCanvas RIS/PACS open source project.
  //
  // The ClearCanvas RIS/PACS open source project is free software: you can
  // redistribute it and/or modify it under the terms of the GNU General Public
  // License as published by the Free Software Foundation, either version 3 of the
  // License, or (at your option) any later version.
  //
  // The ClearCanvas RIS/PACS open source project 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
  // the ClearCanvas RIS/PACS open source project.  If not, see
  // <http://www.gnu.org/licenses/>.
  
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using ClearCanvas.Common;
  using ClearCanvas.Dicom.Iod;
  using ClearCanvas.ImageViewer.Mathematics;
  
  namespace ClearCanvas.ImageViewer.StudyManagement
  {
  	/// <summary>
  	/// Represents the spatial plane of an image and its relationship with the patient coordinate system as defined by the frame's DICOM Image Plane Module.
  	/// </summary>
  	public sealed class ImagePlaneHelper
  	{
  		private const float _defaultToleranceRadians = 100*float.Epsilon;
  
  		private readonly ImagePositionPatient _imagePositionPatient;
  		private readonly ImageOrientationPatient _imageOrientationPatient;
  		private readonly PixelSpacing _pixelSpacing;
  		private readonly int _rows;
  		private readonly int _columns;
  
  		private Matrix3D _rotationMatrix;
  		private Matrix _pixelToPatientTransform;
  		private Vector3D _imageRowOrientationPatient;
  		private Vector3D _imageColumnOrientationPatient;
  		private Vector3D _imageNormalPatient;
  		private Vector3D _imageTopLeftPatient;
  		private Vector3D _imageTopRightPatient;
  		private Vector3D _imageBottomLeftPatient;
  		private Vector3D _imageBottomRightPatient;
  		private Vector3D _imageCenterPatient;
  		private Vector3D _topLeftImagePlane;
  
  		/// <summary>
  		/// Constructor (for use by <see cref="Frame"/> class only).
  		/// </summary>
  		internal ImagePlaneHelper(Frame frame)
  			: this(frame.ImagePositionPatient, frame.ImageOrientationPatient, frame.PixelSpacing, frame.Rows, frame.Columns)
  		{
  			// this constructor is internal because it keeps references to the source frame's position, orientation and spacing properties
  			// if the frame is changed or disposed, the calculations made by this instance would be affected
  			// thus, only the Frame class should use this constructor, since it will thus have the same lifetime as the referenced objects
  			// if you need to create an instance separated from a frame, use the other constructor with appropriate clones of the position, orientation and spacing
  		}
  
  		/// <summary>
  		/// Initializes a new instance of <see cref="ImagePlaneHelper"/> from the specified image plane details.
  		/// </summary>
  		public ImagePlaneHelper(ImagePositionPatient imagePositionPatient, ImageOrientationPatient imageOrientationPatient, PixelSpacing pixelSpacing, int rows, int columns)
  		{
  			Platform.CheckForNullReference(imagePositionPatient, "imagePositionPatient");
  			Platform.CheckForNullReference(imageOrientationPatient, "imageOrientationPatient");
  			Platform.CheckForNullReference(pixelSpacing, "pixelSpacing");
              try
              {
                  Platform.CheckPositive(rows, "rows");
                  Platform.CheckPositive(columns, "columns");
              }
              catch (Exception ex)
              {
                  Platform.Log(LogLevel.Error, "the row is " + rows.ToString());
              }
  
  			_rows = rows;
  			_columns = columns;
  			_pixelSpacing = pixelSpacing;
  			_imageOrientationPatient = imageOrientationPatient;
  			_imagePositionPatient = imagePositionPatient;
  		}
  
  		/// <summary>
  		/// Gets the number of rows in the frame.
  		/// </summary>
  		public int ImageRows
  		{
  			get { return _rows; }
  		}
  
  		/// <summary>
  		/// Gets the number of columns in the frame.
  		/// </summary>
  		public int ImageColumns
  		{
  			get { return _columns; }
  		}
  
  		/// <summary>
  		/// Gets the <see cref="ImagePositionPatient"/> for the frame (the values of the Image Position (Patient) tag in the headers).
  		/// </summary>
  		public ImagePositionPatient ImagePositionPatient
  		{
  			get { return _imagePositionPatient; }
  		}
  
  		/// <summary>
  		/// Gets the <see cref="ImageOrientationPatient"/> for the frame (the values of the Image Orientation (Patient) tag in the headers).
  		/// </summary>
  		public ImageOrientationPatient ImageOrientationPatient
  		{
  			get { return _imageOrientationPatient; }
  		}
  
  		/// <summary>
  		/// Gets the <see cref="PixelSpacing"/> for the frame (the values of the Pixel Spacing tag in the headers).
  		/// </summary>
  		public PixelSpacing PixelSpacing
  		{
  			get { return _pixelSpacing; }
  		}
  
  		/// <summary>
  		/// Gets the origin of the image plane in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid,
  		/// and yields the same value as <see cref="ImageTopLeftPatient"/>.
  		/// </remarks>
  		public Vector3D ImagePositionPatientVector
  		{
  			get { return _imageTopLeftPatient ?? (_imageTopLeftPatient = !_imagePositionPatient.IsNull ? new Vector3D((float) _imagePositionPatient.X, (float) _imagePositionPatient.Y, (float) _imagePositionPatient.Z) : Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the orientation vector of the first row in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageRowOrientationPatientVector
  		{
  			get { return _imageRowOrientationPatient ?? (_imageRowOrientationPatient = !_imageOrientationPatient.IsNull ? new Vector3D((float) _imageOrientationPatient.RowX, (float) _imageOrientationPatient.RowY, (float) _imageOrientationPatient.RowZ).Normalize() : Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the orientation vector of the first column in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageColumnOrientationPatientVector
  		{
  			get { return _imageColumnOrientationPatient ?? (_imageColumnOrientationPatient = !_imageOrientationPatient.IsNull ? new Vector3D((float) _imageOrientationPatient.ColumnX, (float) _imageOrientationPatient.ColumnY, (float) _imageOrientationPatient.ColumnZ).Normalize() : Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the unit normal to the image plane in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageNormalPatient
  		{
  			get { return _imageNormalPatient ?? (_imageNormalPatient = GetNormalVector(_imageOrientationPatient) ?? Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the top-left corner of the image in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid,
  		/// and yields the same value as <see cref="ImagePositionPatientVector"/>.
  		/// </remarks>
  		public Vector3D ImageTopLeftPatient
  		{
  			get { return ImagePositionPatientVector; }
  		}
  
  		/// <summary>
  		/// Gets the top-right corner of the image in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageTopRightPatient
  		{
  			get { return _imageTopRightPatient ?? (_imageTopRightPatient = ConvertToPatient(new PointF(_columns, 0)) ?? Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the bottom-left corner of the image in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageBottomLeftPatient
  		{
  			get { return _imageBottomLeftPatient ?? (_imageBottomLeftPatient = ConvertToPatient(new PointF(0, _rows)) ?? Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the bottom-right corner of the image in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageBottomRightPatient
  		{
  			get { return _imageBottomRightPatient ?? (_imageBottomRightPatient = ConvertToPatient(new PointF(_columns, _rows)) ?? Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the centre of the image in the patient coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D ImageCenterPatient
  		{
  			get { return _imageCenterPatient ?? (_imageCenterPatient = ConvertToPatient(new PointF(_columns/2f, _rows/2f)) ?? Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Gets the top-left corner of the image plane relative to the patient origin in the image coordinate system.
  		/// </summary>
  		/// <remarks>
  		/// This property returns a zero vector if the image plane information is invalid.
  		/// </remarks>
  		public Vector3D TopLeftImagePlane
  		{
  			get { return _topLeftImagePlane ?? (_topLeftImagePlane = ConvertToImagePlane(ImageTopLeftPatient, Vector3D.Null) ?? Vector3D.Null); }
  		}
  
  		/// <summary>
  		/// Indicates whether or not the image plane information is valid and can be used to transform between patient and image coordinate systems.
  		/// </summary>
  		public bool IsValid
  		{
  			get
  			{
  				if (ImageOrientationPatient.IsNull)
  					return false;
  
  				if (PixelSpacing.IsNull)
  					return false;
  
  				var normal = GetNormalVector();
  				return normal != null && !normal.IsNull;
  			}
  		}
  
  		/// <summary>
  		/// Converts the input image position (expressed in pixels) to the patient coordinate system.
  		/// </summary>
  		/// <returns>A position vector, or null if the <see cref="Frame"/>'s position information is invalid.</returns>
  		public Vector3D ConvertToPatient(PointF positionPixels)
  		{
  			if (ImageOrientationPatient.IsNull || PixelSpacing.IsNull)
  				return null;
  
  			// A shortcut for when the pixel position is exactly (0, 0).
  			if (positionPixels.IsEmpty)
  				return ImagePositionPatientVector;
  
  			// Calculation of position in patient coordinates using 
  			// the matrix method described in Dicom PS 3.3 C.7.6.2.1.1.
  			var imageToPatientTransform = GetImageToPatientTransform();
  
  			Matrix columnMatrix = new Matrix(4, 1);
  			columnMatrix.SetColumn(0, positionPixels.X, positionPixels.Y, 0F, 1F);
  			Matrix result = imageToPatientTransform*columnMatrix;
  
  			return new Vector3D(result[0, 0], result[1, 0], result[2, 0]);
  		}
  
  		/// <summary>
  		/// Converts the input <paramref name="positionPatient">position vector</paramref> to the coordinate
  		/// system of the image plane, moving the origin to <paramref name="originPatient"/>.
  		/// </summary>
  		/// <remarks>
  		/// Note that the resultant position vector remains in units of mm and the z-coordinate is valid.
  		/// </remarks>
  		/// <param name="positionPatient">The position vector, in patient coordinates,
  		/// to be converted to the coordinate system of the image plane.</param>
  		/// <param name="originPatient">The new origin, in patient coordinates.</param>
  		/// <returns>A position vector, or null if the <see cref="Frame"/>'s position information is invalid.</returns>
  		public Vector3D ConvertToImagePlane(Vector3D positionPatient, Vector3D originPatient)
  		{
  			Platform.CheckForNullReference(positionPatient, "positionPatient");
  
  			var translated = positionPatient;
  			if (originPatient != null)
  				translated -= originPatient;
  
  			var rotationMatrix = GetRotationMatrix();
  			if (rotationMatrix == null)
  				return null;
  
  			// Rotate coordinate system to match that of the image plane.
  			return rotationMatrix*translated;
  		}
  
  		/// <summary>
  		/// Converts the input <paramref name="positionPatient">position vector</paramref> to the coordinate
  		/// system of the image plane, moving the origin to the top left corner of the image.
  		/// </summary>
  		/// <remarks>
  		/// Note that the resultant position vector remains in units of mm and the z-coordinate is valid.
  		/// </remarks>
  		/// <param name="positionPatient">The position vector, in patient coordinates,
  		/// to be converted to the coordinate system of the image plane.</param>
  		/// <returns>A position vector, or null if the <see cref="Frame"/>'s position information is invalid.</returns>
  		public Vector3D ConvertToImagePlane(Vector3D positionPatient)
  		{
  			Platform.CheckForNullReference(positionPatient, "positionPatient");
  			return ConvertToImagePlane(positionPatient, ImagePositionPatientVector);
  		}
  
  		/// <summary>
  		/// Converts a point in the image plane expressed in millimetres (mm) into a point expressed in pixels.
  		/// </summary>
  		/// <returns>The corresponding pixel coordinate, or null if the <see cref="Frame"/>'s position information is invalid.</returns>
  		public PointF? ConvertToImage(PointF positionMillimetres)
  		{
  			if (PixelSpacing.IsNull)
  				return null;
  
  			return new PointF(positionMillimetres.X/(float) PixelSpacing.Column, positionMillimetres.Y/(float) PixelSpacing.Row);
  		}
  
  		/// <summary>
  		/// Converts a point in the image expressed in pixels to a point expressed in mm.
  		/// </summary>
  		public PointF? ConvertFromImage(PointF positionPixels)
  		{
  			if (PixelSpacing.IsNull)
  				return null;
  
  			return new PointF(positionPixels.X*(float) PixelSpacing.Column, positionPixels.Y*(float) PixelSpacing.Row);
  		}
  
  		/// <summary>
  		/// Converts a point in the image plane expressed in millimetres (mm) into a point expressed in pixels.
  		/// </summary>
  		/// <returns>The corresponding pixel coordinate.</returns>
  		/// <exception cref="InvalidOperationException">Thrown if the pixel spacing information was invalid.</exception>
  		public PointF ConvertToImage2(PointF positionMillimetres)
  		{
  
gt: 			var result = ConvertToImage(positionMillimetres);