/*============================================================================= Project: SharpImage Module: siPolygonSelection.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. =============================================================================*/ #define DEBUG #define TRACE 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 IteratorType = itk.itkImageRegionIteratorWithIndex_IUC2; namespace SharpImage.Rendering { public class siPolygonSelection : siShapeSelection { #region Instance Members //===================================================================== itk.itkImageBase m_Template = null; //===================================================================== #endregion #region Construction and Disposal //===================================================================== /// /// Public constuctor. /// /// Uses the size and spacing of the image to compute the default polygon. public siPolygonSelection(itk.itkImageBase input, int extrusionDimension) : base() { // Save the template image this.m_Template = input; // Set the list of points this.m_Points = new List(); // Create some default points this.ExtrusionDimension = extrusionDimension; int[] shape = this.GetShapeDimensions(input.Dimension); itk.itkIndex index0 = new itk.itkIndex(input.Dimension); itk.itkIndex index1 = new itk.itkIndex(input.Dimension); itk.itkIndex index2 = new itk.itkIndex(input.Dimension); itk.itkIndex index3 = new itk.itkIndex(input.Dimension); index0[0] = (int)(input.Size[shape[0]] * 0.25); index0[1] = (int)(input.Size[shape[1]] * 0.25); index1[0] = (int)(input.Size[shape[0]] * 0.75); index1[1] = (int)(input.Size[shape[1]] * 0.25); index2[0] = (int)(input.Size[shape[0]] * 0.25); index2[1] = (int)(input.Size[shape[1]] * 0.75); index3[0] = (int)(input.Size[shape[0]] * 0.75); index3[1] = (int)(input.Size[shape[1]] * 0.75); itk.itkPoint point0; itk.itkPoint point1; itk.itkPoint point2; itk.itkPoint point3; input.TransformIndexToPhysicalPoint(index0, out point0); input.TransformIndexToPhysicalPoint(index1, out point1); input.TransformIndexToPhysicalPoint(index2, out point2); input.TransformIndexToPhysicalPoint(index3, out point3); this.Points.Add(point0); this.Points.Add(point1); this.Points.Add(point3); this.Points.Add(point2); // Set the extrusion type this.ShapeType = ShapeTypeEnum.Extrusion; this.ExtrusionStartPoint = 0.0; this.ExtrusionEndPoint = input.PhysicalSize[(int)input.Dimension - 1]; // Setup the initial Metadata this.Metadata["IsTranslating"] = false; this.Metadata["IsMovingControlPoint"] = false; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverSelection"] = false; this.Metadata["CurrentControlPointIndex"] = -1; } //===================================================================== #endregion #region Properties //===================================================================== private List m_Points; /// /// Gets the list of points comprising the polygon. /// public List Points { get { return this.m_Points; } } /// /// Gets a string describing the selection type. /// public override string TypeName { get{ return "Polygon"; } } /// /// Gets a string indicating the "Image" type for /// itkGenerateMaskImageFilter. /// public override string GenerateMaskImageFilterType { get { return "Image"; } } //===================================================================== #endregion #region Public Methods //===================================================================== /// /// Return a string representing the selection. /// /// public override string ToString() { String result = this.TypeName + ": "; foreach (itk.itkPoint point in this.Points) result += point.ToString() + " "; return result.Trim(' '); } /// /// Convert this selection to an itk.itkImage with UC pixel type. /// If size.Dimension == 2, the selection is simply rasterised. /// If size.Dimension == 3, the selection is tiled across the extrusion dimension. /// /// public itk.itkImageBase ToImage() { // Get the path in screen space GraphicsPath pathPixel = this.GetSelectionAsPixelPath(m_Template); // Get the output size, spacing, and origin itk.itkSize size = m_Template.Size; itk.itkSpacing spacing = m_Template.Spacing; itk.itkPoint origin = m_Template.Origin; // Create empty image itk.itkIndex indexPath = new itk.itkIndex(0, 0); itk.itkSize sizePath = new itk.itkSize(size[0], size[1]); itk.itkImageRegion regionPath = new itk.itkImageRegion(sizePath, indexPath); itk.itkImageBase imagePath = itk.itkImage_UC2.New(); imagePath.SetRegions(regionPath); imagePath.Allocate(); imagePath.FillBuffer(0U); imagePath.Spacing = new itk.itkSpacing(spacing[0], spacing[1]); imagePath.Origin = new itk.itkPoint(origin[0], origin[1]); // Convert the path to a 2D itkImage IteratorType it = new IteratorType(imagePath, regionPath); foreach (itk.itkPixel p in it) { itk.itkIndex indexIt = it.Index; if (pathPixel.IsVisible(indexIt[0], indexIt[1])) it.Set(255U); } // Clean up the path pathPixel.Dispose(); // Check the dimension if (size.Dimension == 2) { return imagePath; } else { itk.itkImageBase imageOutput = itk.itkImage_UC3.New(); // Compute the start and end indexes itk.itkIndex indexStart; itk.itkIndex indexEnd; itk.itkPoint pointStart = new itk.itkPoint(0.0, 0.0, this.ExtrusionStartPoint); itk.itkPoint pointEnd = new itk.itkPoint(0.0, 0.0, this.ExtrusionEndPoint); m_Template.TransformPhysicalPointToIndex(pointStart, out indexStart); m_Template.TransformPhysicalPointToIndex(pointEnd, out indexEnd); // Create a blank image itk.itkImageBase imageBlank = itk.itkImage_UC2.New(); imageBlank.SetRegions(regionPath); imageBlank.Allocate(); imageBlank.FillBuffer(0U); imageBlank.Spacing = imagePath.Spacing; imageBlank.Origin = imagePath.Origin; // Tile the inputs itk.itkTileImageFilter filterTile = itk.itkTileImageFilter.New(imagePath, imageOutput); filterTile.Layout = new uint[]{ 1U,1U,0U }; uint input = 0; for (int i=0; i /// 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. /// /// protected void OnPaint(siGdiSliceRenderer renderer, PaintEventArgs e) { // Set to draw smooth e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Always painting the extrusion this.OnPaintExtrusion(renderer, e); } /// /// Render the shape of the extruded object (a circle in this case). /// /// protected override void OnPaintShape(siGdiSliceRenderer renderer, PaintEventArgs e) { // Get the graphics handle Graphics g = e.Graphics; // Get the input itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return; // Get the selection as a path in screen space GraphicsPath pathScreen = this.GetSelectionAsScreenPath(renderer); // Convert the slice index to a point itk.itkPoint pointSlice; itk.itkIndex indexSlice = new itk.itkIndex(input.Dimension); indexSlice[this.ExtrusionDimension] = renderer.Slice; input.TransformIndexToPhysicalPoint(indexSlice, out pointSlice); // Determine if the current slice is within the start and end range if (pointSlice[this.ExtrusionDimension] < this.ExtrusionStartPoint || pointSlice[this.ExtrusionDimension] > this.ExtrusionEndPoint) // The slice does not intersect with the shape, there is nothing to paint return; // Draw the path SolidBrush brushFill = new SolidBrush(COLOR_FILL); SolidBrush brushOutline = new SolidBrush(COLOR_OUTLINE); Pen penOutline = new Pen(brushOutline, PEN_OUTLINE_WIDTH); g.DrawPath(penOutline, pathScreen); g.FillPath(brushFill, pathScreen); // Draw control points SolidBrush brushControlFill = new SolidBrush(COLOR_CONTROL_FILL); SolidBrush brushControlOutline = new SolidBrush(COLOR_CONTROL_OUTLINE); Pen penControlOutline = new Pen(brushControlOutline, PEN_CONTROL_OUTLINE_WIDTH); foreach (RectangleF controlPoint in this.GetControlPoints(renderer)) { g.FillRectangle(brushControlFill, controlPoint); g.DrawRectangle(penControlOutline, controlPoint.X, controlPoint.Y, controlPoint.Width, controlPoint.Height); } } /// /// Render the extruded object (ie. a rectangle starting at StartExtrusionIndex /// and ending at EndExtrusionIndex, of height 2.0*this.Radius). /// /// protected override void OnPaintExtrudedShape(siGdiSliceRenderer renderer, PaintEventArgs e) { // Get the graphics handle Graphics g = e.Graphics; // Get the rectangle RectangleF r = this.GetExtrusionAsScreenRectangle(renderer); if (r == RectangleF.Empty) return; // Get the brushes and pens SolidBrush brushFill = new SolidBrush(COLOR_FILL); SolidBrush brushOutline = new SolidBrush(COLOR_OUTLINE); Pen penOutline = new Pen(brushOutline, PEN_OUTLINE_WIDTH); // Draw outline and fill g.DrawRectangle(penOutline, r.X, r.Y, r.Width, r.Height); g.FillRectangle(brushFill, r.X, r.Y, r.Width, r.Height); // Draw control points SolidBrush brushControlFill = new SolidBrush(COLOR_CONTROL_FILL); SolidBrush brushControlOutline = new SolidBrush(COLOR_CONTROL_OUTLINE); Pen penControlOutline = new Pen(brushControlOutline, PEN_CONTROL_OUTLINE_WIDTH); foreach (RectangleF controlPoint in this.GetControlPoints(renderer)) { g.FillRectangle(brushControlFill, controlPoint); g.DrawRectangle(penControlOutline, controlPoint.X, controlPoint.Y, controlPoint.Width, controlPoint.Height); } } /// /// Allows the actor to consume the MouseMove event. /// /// /// protected override bool OnMouseMove(siRenderer renderer, MouseEventArgs e) { base.OnMouseMove(renderer, e); // Check the Renderer has all the required Metadata variables if (!renderer.ContainsMetadata("LastImagePointMouseMove")) return false; // Get the location as an itkPoint itk.itkImageBase input = this.GetInputAsImage(renderer); itk.itkPoint pointMouseMove = (itk.itkPoint)renderer.Metadata["LastImagePointMouseMove"]; itk.itkPoint pointMouseMoveT = renderer.DirectionTransformPoint(pointMouseMove); // Check if we are translating if ((bool)this.Metadata["IsTranslating"]) { if (this.IsRenderingShape(renderer, input.Direction)) { itk.itkPoint[] pointsBeforeTranslation = (itk.itkPoint[])this.Metadata["PointsBeforeTranslation"]; itk.itkPoint pointMouseDown = (itk.itkPoint)renderer.Metadata["LastImagePointMouseDown"]; itk.itkPoint pointMouseDownT = renderer.DirectionTransformPoint(pointMouseDown); itk.itkVector offset = pointMouseMoveT - pointMouseDownT; for (int i = 0; i < this.Points.Count; i++) this.Points[i] = pointsBeforeTranslation[i] + offset; this.RaiseModified(); return true; } else { // TODO: return false; } } // Check if we are editing the path if ((bool)this.Metadata["IsMovingControlPoint"]) { if (this.IsRenderingShape(renderer, input.Direction)) { int indexControlPoint = (int)this.Metadata["CurrentControlPointIndex"]; this.Points[indexControlPoint] = new itk.itkPoint(pointMouseMoveT.Data); this.RaiseModified(); return true; } else { int indexControlPoint = (int)this.Metadata["CurrentControlPointIndex"]; if (indexControlPoint == 0) this.ExtrusionEndPoint = pointMouseMoveT[2]; else this.ExtrusionStartPoint = pointMouseMoveT[2]; this.RaiseModified(); return true; } } // Check if the mouse is over a control point int controlPointIndex = this.IsMouseOverControlPoint(renderer as siGdiSliceRenderer, e); if (controlPointIndex >= 0) { // Show the correct cursor renderer.Metadata["Cursor"] = this.GetControlPointCursor(controlPointIndex); this.Metadata["CurrentControlPointIndex"] = controlPointIndex; this.Metadata["IsMouseOverControlPoint"] = true; this.Metadata["IsMouseOverSelection"] = false; return true; } // Check if the mouse is over the selection if (this.IsMouseOverSelection(renderer as siGdiSliceRenderer, e.Location)) { renderer.Metadata["Cursor"] = Cursors.Hand; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverSelection"] = true; this.Metadata["CurrentControlPointIndex"] = -1; return true; } // We did not consume this event renderer.Metadata["Cursor"] = null; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverSelection"] = false; this.Metadata["CurrentControlPointIndex"] = -1; return false; } /// /// Allows the actor to consume the MouseDown event. /// /// /// protected override bool OnMouseDown(siRenderer renderer, MouseEventArgs e) { base.OnMouseDown(renderer, e); // Get the input image itk.itkImageBase input = this.GetInputAsImage(renderer); // Check if we are over the selection if ((bool)this.Metadata["IsMouseOverSelection"]) { // Check the control key if (renderer.ContainsMetadata("KeyDown") && this.IsRenderingShape(renderer as siGdiSliceRenderer, input.Direction)) { KeyEventArgs eKey = (KeyEventArgs)renderer.Metadata["KeyDown"]; if (eKey != null && eKey.Control) { // Check if we added a control point if (this.AddControlPoint(renderer as siGdiSliceRenderer, e.Location)) return true; } } // Override image translation renderer.Metadata["IsTranslatingImage"] = false; // Start selection translation this.Metadata["IsTranslating"] = true; // Save the point list itk.itkPoint[] pointsBeforeTranslation = new itk.itkPoint[this.Points.Count]; for (int i = 0; i < this.Points.Count; i++) pointsBeforeTranslation[i] = new itk.itkPoint(this.Points[i].Data); this.Metadata["PointsBeforeTranslation"] = pointsBeforeTranslation; return true; } // Check if we are over a control point if ((bool)this.Metadata["IsMouseOverControlPoint"]) { // Check the control key if (renderer.ContainsMetadata("CurrentKey") && this.IsRenderingShape(renderer as siGdiSliceRenderer, input.Direction)) { KeyEventArgs eKey = (KeyEventArgs)renderer.Metadata["CurrentKey"]; if (eKey.Control) { // Check if we deleted a control point if (this.DeleteControlPoint((int)this.Metadata["CurrentControlPointIndex"])) return true; } } // Override other translations renderer.Metadata["IsTranslatingImage"] = false; this.Metadata["IsTranslating"] = false; // Start selection resize this.Metadata["IsMovingControlPoint"] = true; return true; } // We did not consume this event return false; } /// /// Allows the actor to consume the MouseUp event. /// /// /// protected override bool OnMouseUp(siRenderer renderer, MouseEventArgs e) { base.OnMouseUp(renderer, e); if ((bool)this.Metadata["IsTranslating"]) { this.Metadata["IsTranslating"] = false; return true; } else if ((bool)this.Metadata["IsMovingControlPoint"]) { this.Metadata["IsMovingControlPoint"] = false; return true; } // We did not consume this event return false; } //===================================================================== #endregion #region Private Helper Methods //===================================================================== /// /// Computes the control point rectangles for the current center and radius. /// /// private RectangleF[] GetControlPoints(siGdiSliceRenderer renderer) { // Get the input image itk.itkImageBase input = this.GetInputAsImage(renderer); // Compute some useful values float widthControl = CONTROL_WIDTH * (renderer.ZoomFactor < 0.25 ? (float)renderer.ZoomFactor : 1.0F); if (this.IsRenderingShape(renderer, input.Direction)) { // Get the path GraphicsPath pathScreen = this.GetSelectionAsScreenPath(renderer); // Create the control points RectangleF[] result = new RectangleF[pathScreen.PointCount]; for (int i = 0; i < pathScreen.PointCount; i++) { PointF point = pathScreen.PathPoints[i]; result[i] = new RectangleF(point.X - widthControl / 2.0F, point.Y - widthControl / 2.0F, widthControl, widthControl); } return result; } else { // Get the extrusion rectangle RectangleF r = this.GetExtrusionAsScreenRectangle(renderer); // Transform the test point itk.itkPoint pointTest = new itk.itkPoint(1.0, 1.0, 0.0); itk.itkPoint pointTestT = renderer.DirectionTransformInversePoint(pointTest); PointF point0 = new PointF(); PointF point1 = new PointF(); if (pointTestT[1] > 0.0 && pointTestT[2] > 0.0) { point0 = new PointF(r.X, r.Y + r.Height / 2.0F); point1 = new PointF(r.X + r.Width, r.Y + r.Height / 2.0F); } else if (pointTestT[1] > 0.0 && pointTestT[2] < 0.0) { point0 = new PointF(r.X, r.Y + r.Height / 2.0F); point1 = new PointF(r.X + r.Width, r.Y + r.Height / 2.0F); } else if (pointTestT[0] > 0.0 && pointTestT[2] < 0.0) { point0 = new PointF(r.X + r.Width / 2.0F, r.Y); point1 = new PointF(r.X + r.Width / 2.0F, r.Y - r.Height); } RectangleF[] result = new RectangleF[2]; result[0] = new RectangleF(point0.X - widthControl / 2.0F, point0.Y - widthControl / 2.0F, widthControl, widthControl); result[1] = new RectangleF(point1.X - widthControl / 2.0F, point1.Y - widthControl / 2.0F, widthControl, widthControl); return result; } } /// /// Returns the index of the control point which the mouse is over. /// Returns -1 if not over a control point. /// /// /// The index of the control OR -1 if not over a control point. private int IsMouseOverControlPoint(siGdiSliceRenderer renderer, MouseEventArgs e) { RectangleF[] controlPoints = this.GetControlPoints(renderer); int index = 0; foreach (RectangleF controlPoint in controlPoints) { if (controlPoint.Contains(e.Location)) return index; index++; } return -1; } /// /// Returns if the mouse is inside the selection. /// /// /// private bool IsMouseOverSelection(siGdiSliceRenderer renderer, PointF pointMouse) { // Get the input image itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return false; // Convert the slice index to a point itk.itkPoint pointSlice; itk.itkIndex indexSlice = new itk.itkIndex(input.Dimension); indexSlice[this.ExtrusionDimension] = renderer.Slice; input.TransformIndexToPhysicalPoint(indexSlice, out pointSlice); // Determine if the current slice is within the start and end range if (pointSlice[this.ExtrusionDimension] < this.ExtrusionStartPoint || pointSlice[this.ExtrusionDimension] > this.ExtrusionEndPoint) // The slice does not intersect with the shape, there is nothing to paint return false; // Test if the mouse is inside the path GraphicsPath pathScreen = this.GetSelectionAsScreenPath(renderer); bool result = pathScreen.IsVisible(pointMouse.X, pointMouse.Y); // Clean up and return pathScreen.Dispose(); return result; } /// /// Get the cursor for the given control point index. /// /// /// private Cursor GetControlPointCursor(int controlPoint) { return Cursors.Hand; } /// /// Create a path from the list of points comprising the polygon. /// /// private GraphicsPath GetSelectionAsScreenPath(siGdiSliceRenderer renderer) { // Get the input image itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return null; // Create a path in image pixel space GraphicsPath path = new GraphicsPath(); for (int i = 0; i < this.Points.Count; i++) { if (i == 0) { path.StartFigure(); } else { PointF pointScreen0 = renderer.TransformImagePointToScreenPoint(input, this.Points[i-1]); PointF pointScreen1 = renderer.TransformImagePointToScreenPoint(input, this.Points[i]); path.AddLine(pointScreen0, pointScreen1); } } path.CloseFigure(); return path; } /// /// Create a path from the list of points comprising the polygon. /// /// private GraphicsPath GetSelectionAsPixelPath(itk.itkImageBase input) { // Create a path in image pixel space GraphicsPath path = new GraphicsPath(); for (int i = 0; i < this.Points.Count; i++) { if (i == 0) { path.StartFigure(); } else { itk.itkContinuousIndex cindex0; itk.itkContinuousIndex cindex1; input.TransformPhysicalPointToContinuousIndex(this.Points[i - 1], out cindex0); input.TransformPhysicalPointToContinuousIndex(this.Points[i], out cindex1); PointF pointScreen0 = new PointF((float)cindex0[0], (float)cindex0[1]); PointF pointScreen1 = new PointF((float)cindex1[0], (float)cindex1[1]); path.AddLine(pointScreen0, pointScreen1); } } path.CloseFigure(); return path; } /// /// Gets the rectangle in screen coordinates which represents the slice of the /// cylinder. /// /// /// private RectangleF GetExtrusionAsScreenRectangle(siGdiSliceRenderer renderer) { RectangleF rectResult = new RectangleF(); // Get the input image itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return RectangleF.Empty; // Get the bounds of the polygon GraphicsPath pathScreen = this.GetSelectionAsScreenPath(renderer); RectangleF rectPathScreenBounds = pathScreen.GetBounds(); // Get the center of the polygon in physical space Point pointShapeScreen = new Point(); pointShapeScreen.X = (int)(rectPathScreenBounds.X); pointShapeScreen.Y = (int)(rectPathScreenBounds.Y); itk.itkPoint pointShape = renderer.TransformScreenPointToImagePoint(pointShapeScreen); // Determine which path rectangle axis to show int[] shape = this.GetShapeDimensions(input.Dimension); itk.itkPoint pointTest = new itk.itkPoint(1.0, 1.0, 0.0); itk.itkPoint pointStart = new itk.itkPoint(0.0, 0.0, 0.0); itk.itkPoint pointEnd = new itk.itkPoint(0.0, 0.0, 0.0); pointStart[shape[0]] = pointShape[shape[0]]; pointStart[shape[1]] = pointShape[shape[1]]; pointStart[this.ExtrusionDimension] = this.ExtrusionStartPoint; pointEnd[shape[0]] = pointShape[shape[0]]; pointEnd[shape[1]] = pointShape[shape[1]]; pointEnd[this.ExtrusionDimension] = this.ExtrusionEndPoint; itk.itkPoint pointTestT = renderer.DirectionTransformInversePoint(pointTest); itk.itkPoint pointStartT = renderer.DirectionTransformInversePoint(pointStart); itk.itkPoint pointEndT = renderer.DirectionTransformInversePoint(pointEnd); PointF pointScreenStart = renderer.TransformImagePointToScreenPoint(input, pointStartT); PointF pointScreenEnd = renderer.TransformImagePointToScreenPoint(input, pointEndT); // This is really dodgy, but I don't know how else to work // out which axis of the shape (width or height) to use... if (pointTestT[1] > 0.0 && pointTestT[2] > 0.0) { rectResult.X = pointScreenEnd.X; rectResult.Y = pointScreenEnd.Y; rectResult.Height = rectPathScreenBounds.Height; rectResult.Width = pointScreenStart.X - pointScreenEnd.X; } else if (pointTestT[1] > 0.0 && pointTestT[2] < 0.0) { rectResult.X = pointScreenEnd.X; rectResult.Y = pointScreenEnd.Y; rectResult.Height = rectPathScreenBounds.Width; rectResult.Width = pointScreenStart.X - pointScreenEnd.X; } else if (pointTestT[0] > 0.0 && pointTestT[2] < 0.0) { rectResult.X = pointScreenEnd.X; rectResult.Y = pointScreenEnd.Y; rectResult.Height = pointScreenStart.Y - pointScreenEnd.Y; rectResult.Width = rectPathScreenBounds.Height; } return rectResult; } /// /// Add a control point at the given screen coordinates. /// /// /// private bool AddControlPoint(siGdiSliceRenderer renderer, PointF pointScreen) { // Convert the pointScreen into an itkPoint itk.itkPoint pointScreenItk = new itk.itkPoint((double)pointScreen.X, (double)pointScreen.Y); // Find the indexes of the lines in the path to add the point between int index = 0; double distance = 8000000.0; GraphicsPath pathScreen = this.GetSelectionAsScreenPath(renderer); for (int i=0; i /// Delete the control point at the given index. This method does nothing /// if the number of control points is three. /// /// /// True if a point was deleted, false otherwise. private bool DeleteControlPoint(int indexControlPoint) { int count = this.Points.Count; // Check if there are 3 or less points if (count <= 3) return false; // Remove a point this.Points.RemoveAt(indexControlPoint); // Get if we actually deleted a point if (count != this.Points.Count) { this.RaiseModified(); return true; } else return false; } //===================================================================== #endregion } }