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