/*=============================================================================
Project: SharpImage
Module: siDirectedSeed.cs
Language: C#
Author: Dan Mueller
Date: $Date: 2007-07-06 10:57:00 +1000 (Fri, 06 Jul 2007) $
Revision: $Revision: 2 $
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
{
#region siDirectedSeed Class
//=====================================================================
public class siDirectedSeed
{
private itk.itkIndex m_Seed;
itk.itkVector m_Direction;
public siDirectedSeed(itk.itkIndex seed, itk.itkVector direction)
{
this.m_Seed = seed;
this.m_Direction = direction;
}
public itk.itkIndex Seed
{
get { return this.m_Seed; }
set { this.m_Seed = value; }
}
public itk.itkVector Direction
{
get { return this.m_Direction; }
set { this.m_Direction = value; }
}
///
/// Returns a string representation of the DirectedSeed.
///
///
public override string ToString()
{
return "Index=" + this.Seed.ToString() + " Direction=" + this.Direction.ToString();
}
}
//=========================================================================
#endregion
public class siDirectedSeedSelector : siActor
{
#region Instance Variables
//=====================================================================
//=========================================================================
#endregion
#region Construction and Disposal
//=====================================================================
///
/// Public constuctor.
///
/// The Renderer to which this Actor is attached.
public siDirectedSeedSelector(siRenderer renderer) : base(renderer)
{
this.Metadata["IsSelectingDirection"] = false;
this.Metadata["SelectedDirectedSeed"] = null;
}
//=====================================================================
#endregion
#region Properties
//=====================================================================
#region DirectedSeeds
//=====================================================================
private List m_DirectedSeeds = new List();
///
/// Gets the list of directed seeds.
///
public List DirectedSeeds
{
get { return this.m_DirectedSeeds; }
}
//=====================================================================
#endregion
#region SelectedDirectedSeed
//=====================================================================
///
/// Gets the list of directed seed.
///
public siDirectedSeed SelectedDirectedSeed
{
get { return this.Metadata["SelectedDirectedSeed"] as siDirectedSeed; }
}
//=====================================================================
#endregion
//=====================================================================
#endregion
#region Events
//=====================================================================
public delegate void siDirectedSeedHandler(siDirectedSeedSelector sender, siDirectedSeed directedSeed);
private siDirectedSeedHandler m_EventStorage_DirectedSeedAdded;
private siDirectedSeedHandler m_EventStorage_DirectedSeedSelected;
///
/// An event raised when the user adds a seed.
///
public event siDirectedSeedHandler DirectedSeedAdded
{
add { this.m_EventStorage_DirectedSeedAdded += value; }
remove { this.m_EventStorage_DirectedSeedAdded -= value; }
}
///
/// An event raised when the user selects an existing seed.
///
public event siDirectedSeedHandler DirectedSeedSelected
{
add { this.m_EventStorage_DirectedSeedSelected += value; }
remove { this.m_EventStorage_DirectedSeedSelected -= value; }
}
///
/// Raises the SeedAdded event.
///
///
///
private void RaiseDirectedSeedAdded(itk.itkIndex seed, itk.itkVector direction)
{
this.RaiseDirectedSeedAdded(new siDirectedSeed(seed, direction));
}
///
/// Raises the SeededDirectionAdded event.
///
///
private void RaiseDirectedSeedAdded(siDirectedSeed directedSeed)
{
if (this.m_EventStorage_DirectedSeedAdded != null)
{
this.m_EventStorage_DirectedSeedAdded(this, directedSeed);
this.RaiseModified();
}
}
///
/// Raises the DirectedSeedSelected event.
///
///
private void RaiseDirectedSeedSelected(siDirectedSeed directedSeed)
{
if (this.m_EventStorage_DirectedSeedSelected != null)
{
this.m_EventStorage_DirectedSeedSelected(this, directedSeed);
this.RaiseModified();
}
}
//=====================================================================
#endregion
#region Public Methods
//=====================================================================
///
/// Changes the currently selected directed seed.
/// The value can be null to unselect all directed seeds.
///
///
public void SelectDirectedSeed(siDirectedSeed directedSeed)
{
this.Metadata["SelectedDirectedSeed"] = directedSeed;
this.RaiseModified();
}
///
/// Removes the given directed seed from the list.
///
///
public void RemoveDirectedSeed(siDirectedSeed directedSeed)
{
this.DirectedSeeds.Remove(directedSeed);
this.RaiseModified();
}
///
/// Removes all the directed seeds from the list
///
public void ClearDirectedSeeds()
{
this.DirectedSeeds.Clear();
this.RaiseModified();
}
//=====================================================================
#endregion
#region Actor Methods
//=====================================================================
///
/// Allows the actor to render itself.
///
///
protected override void OnPaint(PaintEventArgs e)
{
// Check the Renderer has all the required Metadata variables
if (!this.Parent.ContainsMetadata("CurrentSlice",
"ImageScreenRectangle"))
return;
// Set to draw smooth
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
// Setup for rendering the seed location
siGdiSliceRenderer rendererGDI = this.Parent as siGdiSliceRenderer;
const float width = 9.0F;
const float widthHalf = width / 2.0F;
const float directionLength = 30.0F;
float crossLength = (widthHalf / (float)Math.Cos(Math.PI / 4.0)) / 2.0F;
// Enumerate the seed list
foreach (siDirectedSeed directedSeed in this.DirectedSeeds)
{
// Check the seed is visible at the moment (assumes the GdiSliceRenderer)
if (directedSeed.Seed.Dimension == 2 ||
(directedSeed.Seed.Dimension == 3 &&
(int)this.Parent.Metadata["CurrentSlice"] == directedSeed.Seed[2]))
{
// Get info for drawing seed
Pen pen = new Pen(new SolidBrush(Color.Red), 1.0F);
RectangleF rectImageScreen = (RectangleF)this.Parent.Metadata["ImageScreenRectangle"];
PointF pointScreen = rendererGDI.TransformImageIndexToScreenPoint(directedSeed.Seed, rectImageScreen);
// Check if the seed is selected
if (this.Metadata.ContainsKey("SelectedDirectedSeed") &&
(this.Metadata["SelectedDirectedSeed"] as siDirectedSeed) == directedSeed)
{
// Draw larger circle around selected seed
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 seed
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);
// Get direction line points
float directionLengthX = (float)directedSeed.Direction[0] * directionLength;
float directionLengthY = (float)directedSeed.Direction[1] * directionLength;
float x1 = pointScreen.X - directionLengthX;
float y1 = pointScreen.Y - directionLengthY;
float x2 = pointScreen.X + directionLengthX;
float y2 = pointScreen.Y + directionLengthY;
// Create custom arrow cap
const float arrowlength = 6.5F;
const float arrowwidth = 2.5F;
GraphicsPath hPath = new GraphicsPath();
hPath.AddLine(new PointF(arrowwidth, -arrowlength), new PointF(0, 0));
hPath.AddLine(new PointF(0, 0), new PointF(-arrowwidth, -arrowlength));
CustomLineCap custCap = new CustomLineCap(null, hPath);
custCap.SetStrokeCaps(LineCap.Round, LineCap.Round);
pen.CustomEndCap = custCap;
pen.CustomStartCap = custCap;
// Draw direction
g.DrawLine(pen, x1, y1, x2, y2);
}
}//end enumerate seeds
}
///
/// Allows the actor to consume the MouseDown event.
///
///
protected override bool OnMouseDown(MouseEventArgs e)
{
// Let the base class consume
base.OnMouseDown(e);
// Only consume if a left button click
if (e.Button != MouseButtons.Left)
return false;
// Check the Renderer has all the required Metadata variables
if (!this.Parent.ContainsMetadata("IsMouseInsideImageSpace",
"LastImageIndexMouseDown"))
return false;
if (!(bool)this.Parent.Metadata["IsMouseInsideImageSpace"])
return false;
// Check if we are choosing the direction
if ((bool)this.Metadata["IsSelectingDirection"])
{
// Set the we are finished selecting the direction
this.Metadata["IsSelectingDirection"] = false;
}
else
{
// Add the seed
itk.itkIndex seed = (itk.itkIndex)this.Parent.Metadata["LastImageIndexMouseDown"];
// Check the seed is not already added to the list
foreach (siDirectedSeed itemInList in this.DirectedSeeds)
{
if (itemInList.Seed == seed)
{
// Select this directed seed
this.Metadata["SelectedDirectedSeed"] = itemInList;
this.RaiseDirectedSeedSelected(itemInList);
return true;
}
}
// Add the directed seed to the list, it is unique
siDirectedSeed newDirectedSeed = new siDirectedSeed(seed, new itk.itkVector(seed.Dimension));
this.DirectedSeeds.Add(newDirectedSeed);
this.Metadata["SelectedDirectedSeed"] = newDirectedSeed;
this.Metadata["IsSelectingDirection"] = true;
this.RaiseDirectedSeedAdded(newDirectedSeed);
return true;
}
// We did not consume the event
return false;
}
///
/// Allows the actor to consume the MouseMove event.
///
///
///
protected override bool OnMouseMove(MouseEventArgs e)
{
// Check the Renderer has all the required Metadata variables
if (!this.Parent.ContainsMetadata("ImageScreenRectangle",
"CurrentSlice"))
return false;
// Check if we are choosing the direction
if ((bool)this.Metadata["IsSelectingDirection"])
{
// Get the currently selected directed seed
siDirectedSeed current = this.Metadata["SelectedDirectedSeed"] as siDirectedSeed;
RectangleF rectImageScreen = (RectangleF)this.Parent.Metadata["ImageScreenRectangle"];
// Check we are on the same slice
if (current.Seed.Dimension == 3 &&
current.Seed[2] == (int)this.Parent.Metadata["CurrentSlice"])
{
// Transform the current seed into screen coords
PointF screenSeed = (this.Parent as siGdiSliceRenderer).TransformImageIndexToScreenPoint(current.Seed, rectImageScreen);
// Compute the vector and normalise
current.Direction[0] = (double)e.X - screenSeed.X;
current.Direction[1] = (double)e.Y - screenSeed.Y;
current.Direction[2] = 0.0;
}
else if (current.Seed.Dimension == 3)
{
// We are not on the same slice, compute the direction by
// subtracting the two indices
itk.itkIndex second = (this.Parent as siGdiSliceRenderer).TransformScreenPointToImageIndex(e.Location, rectImageScreen);
for (int i = 0; i < current.Seed.Dimension; i++)
current.Direction[i] = (double)(current.Seed[i] - second[i]);
}
// Normalise direction
double norm = current.Direction.GetNorm();
for (int i=0; i 0.0)
current.Direction[i] /= norm;
// Tell the renderer it needs to redraw
this.RaiseModified();
return true;
}
// We did not consume the event
return false;
}
//=====================================================================
#endregion
}
}