/*=============================================================================
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
}
}