/*============================================================================= Project: SharpImage Module: siPathSelection.cs Language: C# Author: Dan Mueller Date: $Date$ Revision: $Revision$ Copyright (c) Queensland University of Technology (QUT) 2007. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =============================================================================*/ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Windows.Forms; using System.Diagnostics; using SharpImage.Main; using SharpImage.Properties; namespace SharpImage.Rendering { public class siPathSelection : siSelection { #region Instance Variables //===================================================================== //========================================================================= #endregion #region Construction and Disposal //===================================================================== /// /// Public constuctor. /// public siPathSelection() : base() { } /// /// Parse a siPathSelection object from a string. /// This method will throw exceptions on badly formed strings. /// /// /// public static siPathSelection ParseFromString(String str) { siPathSelection result = new siPathSelection(); str = str.Replace("Path:", ""); str = str.Replace("[", ""); String[] parts = str.Split(']'); foreach (String part in parts) { String nicepart = part.Trim(' '); if (nicepart == null || nicepart.Length == 0) continue; int i = 0; String[] partsPoint = nicepart.Split(','); itk.itkPoint point = new itk.itkPoint((uint)partsPoint.Length); foreach (String partPoint in partsPoint) point[i++] = double.Parse(partPoint); result.AllPoints.Add(point); } return result; } //===================================================================== #endregion #region Properties //===================================================================== #region Points and Indicies //===================================================================== private List m_AllPoints = new List(); /// /// Gets the list of all points, including start and end. /// Start is at index 0, end is at index Count - 1. /// public List AllPoints { get { return this.m_AllPoints; } } /// /// Gets the list of way-points points. /// If no way-points, then an empty list is returned. /// public List WayPoints { get { if (this.m_AllPoints.Count > 2) return this.m_AllPoints.GetRange(1, this.m_AllPoints.Count - 2); else return new List(0); } } /// /// Get the start point of the path. /// Returns null if the list is empty. /// public itk.itkPoint Start { get { if (this.m_AllPoints.Count > 0) return this.m_AllPoints[0]; else return null; } } /// /// Get the end point of the path. /// Returns null if the list has less than 1 entry. /// public itk.itkPoint End { get { if (this.m_AllPoints.Count > 1) return this.m_AllPoints[this.m_AllPoints.Count - 1]; else return null; } } /// /// Get the list of indicies for all points, including start and end. /// /// public List GetIndicies(itk.itkImageBase input) { List result = new List(); foreach (itk.itkPoint point in this.AllPoints) { itk.itkIndex index; input.TransformPhysicalPointToIndex(point, out index); result.Add(index); } return result; } //===================================================================== #endregion #region TypeName //===================================================================== /// /// Returns a the type name of the selection. /// public override string TypeName { get { return "Path"; } } //===================================================================== #endregion #region Color //===================================================================== private Color m_Color = Color.Red; /// /// Gets/sets the color of the path. /// public Color Color { get { return this.m_Color; } set { this.m_Color = value; this.RaiseModified(); } } //===================================================================== #endregion //===================================================================== #endregion #region Events //===================================================================== public delegate void siPointsHandler(siPathSelection sender, itk.itkPoint point); private siPointsHandler m_EventStorage_PointAdded; private siPointsHandler m_EventStorage_PointSelected; /// /// An event raised when the user adds a point. /// public event siPointsHandler PointAdded { add { this.m_EventStorage_PointAdded += value; } remove { this.m_EventStorage_PointAdded -= value; } } /// /// An event raised when the user selects an existing point. /// public event siPointsHandler PointSelected { add { this.m_EventStorage_PointSelected += value; } remove { this.m_EventStorage_PointSelected -= value; } } /// /// Raises the PointAdded event. /// /// private void RaisePointAdded(itk.itkPoint point) { if (this.m_EventStorage_PointAdded != null) { this.m_EventStorage_PointAdded(this, point); this.RaiseModified(); } } /// /// Raises the PointSelected event. /// /// private void RaisePointSelected(itk.itkPoint point) { if (this.m_EventStorage_PointSelected != null) { this.m_EventStorage_PointSelected(this, point); this.RaiseModified(); } } //===================================================================== #endregion #region Public Methods //===================================================================== /// /// Changes the currently selected point. /// The point value can be null to unselect all point. /// If the point is not currently included as a landmark, no points are selected. /// /// public void SelectPoint(itk.itkPoint point) { this.Metadata["SelectedPoint"] = point; this.RaiseModified(); } /// /// Removes the given point from the list. /// /// public void RemovePoint(itk.itkPoint point) { this.AllPoints.Remove(point); this.RaiseModified(); } /// /// Removes all the points from the list /// public void ClearPoints() { this.AllPoints.Clear(); this.RaiseModified(); } /// /// Move the given point up in the list. /// /// public void MovePointUp(int index) { if (index <= 0) return; itk.itkPoint pointToMove = this.AllPoints[index]; this.AllPoints.RemoveAt(index); this.AllPoints.Insert(index-1, pointToMove); this.RaiseModified(); } /// /// Move the given point down in the list. /// /// public void MovePointDown(int index) { if (index >= this.AllPoints.Count - 1) return; itk.itkPoint pointToMove = this.AllPoints[index]; this.AllPoints.RemoveAt(index); this.AllPoints.Insert(index + 1, pointToMove); this.RaiseModified(); } /// /// Returns a string representation of the selection. /// public override string ToString() { String result = "Path: "; foreach (itk.itkPoint point in this.AllPoints) result += point.ToString() + " "; return result.Trim(' '); } /// /// Return a label for the given point. /// /// /// public String GetLabel(itk.itkPoint point) { int index = this.AllPoints.IndexOf(point); if (index == 0) return "Start"; else if (index == this.AllPoints.Count - 1) return "End"; else return index.ToString(); } //===================================================================== #endregion #region Actor Methods //===================================================================== /// /// Allows the actor to render itself. /// /// protected override void OnPaint(siRenderer renderer, PaintEventArgs e) { if (renderer is siGdiSliceRenderer) this.OnPaint(renderer as siGdiSliceRenderer, e); else this.ThrowNotSupported("The given renderer is not supported."); } /// /// Allows the actor to render itself to an siGdiSliceRenderer. /// /// /// protected void OnPaint(siGdiSliceRenderer renderer, PaintEventArgs e) { // Set to draw smooth Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; // Setup for rendering the seed location itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return; Font font = new Font("Arial", 8.0f); Brush brush = new SolidBrush(this.Color); Pen pen = new Pen(brush, 1.0F); const float width = 9.0F; const float widthHalf = width / 2.0F; float crossLength = (widthHalf / (float)Math.Cos(Math.PI / 4.0)) / 2.0F; // Enumerate the point list int label = 0; foreach (itk.itkPoint point in this.AllPoints) { // Pass the point through the inverse direction transform itk.itkPoint pointT = renderer.DirectionTransformInversePoint(point); // Convert the point to an index itk.itkIndex indexT; input.TransformPhysicalPointToIndex(pointT, out indexT); // Check the point is visible at the moment if (indexT.Dimension == 2 || (indexT.Dimension == 3 && renderer.Slice == indexT[2])) { // Get info for drawing index PointF pointScreen = renderer.TransformImageIndexToScreenPoint(indexT, true); // Check if the index is selected if (this.Metadata.ContainsKey("SelectedPoint") && (this.Metadata["SelectedPoint"] as itk.itkPoint) == point) { // Draw larger circle around selected point RectangleF rectSelected = new RectangleF(pointScreen.X - widthHalf - 2.0F, pointScreen.Y - widthHalf - 2.0F, width + 4.0F, width + 4.0F); g.DrawArc(pen, rectSelected, 0.0F, 360.0F); } // Draw RectangleF rect = new RectangleF(pointScreen.X - widthHalf, pointScreen.Y - widthHalf, width, width); g.DrawArc(pen, rect, 0.0F, 360.0F); g.DrawLine(pen, pointScreen.X - crossLength, pointScreen.Y - crossLength, pointScreen.X + crossLength, pointScreen.Y + crossLength); g.DrawLine(pen, pointScreen.X - crossLength, pointScreen.Y + crossLength, pointScreen.X + crossLength, pointScreen.Y - crossLength); // Draw the label beside the point String strLabel = label.ToString(); if (label == 0) strLabel = "Start"; else if (label == this.AllPoints.Count - 1) strLabel = "End"; SizeF sizeLabel = g.MeasureString(strLabel, font); float xLabel = pointScreen.X - sizeLabel.Width/2.0f + 1.0f; float yLabel = pointScreen.Y - sizeLabel.Height - widthHalf - 0.5f; g.DrawString(strLabel, font, brush, xLabel, yLabel); } // Increment the label label += 1; } // End enumerate points } /// /// Allows the actor to consume the MouseDown event. /// /// protected override bool OnMouseDown(siRenderer renderer, MouseEventArgs e) { if (renderer is siGdiSliceRenderer) return this.OnMouseDown(renderer as siGdiSliceRenderer, e); else throw new NotSupportedException("The given renderer is not supported."); } /// /// Allows the actor to consume the MouseDown event for an siGdiSliceRenderer. /// /// /// /// protected bool OnMouseDown(siGdiSliceRenderer renderer, MouseEventArgs e) { // Check the Renderer has all the required Metadata variables if (!renderer.ContainsMetadata("IsMouseInsideImageSpace", "LastImagePointMouseDown")) return false; // Only allow selections inside the image if (!(bool)renderer.Metadata["IsMouseInsideImageSpace"]) return false; // Check the mode if (e.Button == MouseButtons.Left) { // Add the point itk.itkImageBase input = this.GetInputAsImage(renderer); itk.itkPoint pointToAdd = (itk.itkPoint)renderer.Metadata["LastImagePointMouseDown"]; itk.itkPoint pointToAddT = renderer.DirectionTransformPoint(pointToAdd); // Check the point is not already added to the list foreach (itk.itkPoint pointInList in this.AllPoints) { if (pointToAddT == pointInList) { // Select this seed this.Metadata["SelectedPoint"] = pointToAddT; this.RaisePointSelected(pointToAddT); return true; } } // Add the point to the list, it is unique this.AllPoints.Add(pointToAddT); this.Metadata["SelectedPoint"] = pointToAddT; this.RaisePointAdded(pointToAddT); return true; } return false; } //===================================================================== #endregion } }