/*============================================================================= Project: SharpImage Module: siCircleSelection.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; namespace SharpImage.Rendering { public class siCircleSelection : siShapeSelection { #region Construction and Disposal //===================================================================== /// /// Protected default constructor. /// protected siCircleSelection() : base() { // Set the extrusion type this.ShapeType = ShapeTypeEnum.Object; // Setup the initial Metadata this.Metadata["IsTranslating"] = false; this.Metadata["IsResizing"] = false; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverSelection"] = false; this.Metadata["CurrentControlPointIndex"] = -1; this.Metadata["MouseDownCenterOffset"] = new itk.itkVector(3U); } /// /// Public constuctor, which adds a circular selction to the given image. /// /// The initial center of the selection. /// The initial radius of the selection. public siCircleSelection(itk.itkPoint center, float radius) : this() { // Setup the initial member variables this.m_Center = center; this.m_Radius = radius; // Compute the default start and end points (for an extrusion object) this.ExtrusionDimension = 2; this.ExtrusionStartPoint = this.Center[2] - this.Radius / 2.0F; this.ExtrusionEndPoint = this.Center[2] + this.Radius / 2.0F; } /// /// Public constuctor, which adds a circular selction to the center of the given image. /// /// Uses the size and spacing of the image to compute the center. public siCircleSelection(itk.itkImageBase input) : this() { // Compute the default radius double radius = input.PhysicalSize[0] / 5.0; this.m_Radius = (float)Math.Ceiling(radius); // Compute the default center of the image this.m_Center = new itk.itkPoint(input.Dimension); for (int i = 0; i < input.Dimension; i++) this.m_Center[i] = input.PhysicalSize[i] / 2.0 + input.Origin[i]; // Compute the default start and end points (for an extrusion object) if (input.Dimension == 3) { this.ExtrusionDimension = 2; this.ExtrusionStartPoint = this.Center[2] - input.PhysicalSize[2] / 5.0; this.ExtrusionEndPoint = this.Center[2] + input.PhysicalSize[2] / 5.0; } } //===================================================================== #endregion #region Properties //===================================================================== private itk.itkPoint m_Center = null; private float m_Radius = 0; /// /// Get/set the center of the circle selection (in physical space). /// On set, the Modified event will be raised. /// public itk.itkPoint Center { get { return this.m_Center; } set { this.m_Center = value; this.RaiseModified(); } } /// /// Get/set the radius of the circular selection (in physical space). /// We assume that the spacing in the X and Y directions are /// equal or close. /// public float Radius { get { return this.m_Radius; } set { this.m_Radius = value; this.RaiseModified(); } } /// /// Gets a string describing the selection type. /// "Circle" for 2D images, "Sphere" for 3D objects, /// and "Cylinder" for 3D extrusions. /// public override string TypeName { get { String result = "Circle"; if (this.ShapeType == ShapeTypeEnum.Object && this.Center.Dimension == 3) result = "Sphere"; else if (this.ShapeType == ShapeTypeEnum.Extrusion && this.Center.Dimension == 3) result = "Cylinder"; return result; } } /// /// Returns the GenerateMaskImageFilter "Ellipse" type. /// public override string GenerateMaskImageFilterType { get { return "Ellipse"; } } #region Start/End Extrusion Point //===================================================================== private double m_ExtrusionDistanceFromCenter = 0.0; /// /// Gets/sets the starting point along the secondary plane for the extrusion. /// public override double ExtrusionStartPoint { get { return this.Center[2] - this.m_ExtrusionDistanceFromCenter; } set { this.m_ExtrusionDistanceFromCenter = value + this.Center[2]; this.RaiseModified(); } } /// /// Gets/sets the ending point along the secondary plane for the extrusion. /// public override double ExtrusionEndPoint { get { return this.Center[2] + this.m_ExtrusionDistanceFromCenter; } set { this.m_ExtrusionDistanceFromCenter = value - this.Center[2]; this.RaiseModified(); } } //===================================================================== #endregion //===================================================================== #endregion #region Public Methods //===================================================================== /// /// Return a string representing the selection. /// /// public override string ToString() { String result = this.TypeName + ": "; if (this.ShapeType == ShapeTypeEnum.Object) { result += "Center=" + this.Center.ToString() + " "; result += "Radius=" + this.Radius.ToString("0.0"); } else if (this.ShapeType == ShapeTypeEnum.Extrusion) { itk.itkPoint center = new itk.itkPoint(this.Center[0], this.Center[1]); itk.itkPoint size = new itk.itkPoint(this.ExtrusionStartPoint, this.ExtrusionEndPoint); result += "Center=" + center.ToString() + " "; result += "Size=" + size.ToString() + " "; result += "Radius=" + this.Radius.ToString("0.0"); } return result; } //===================================================================== #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. /// /// protected void OnPaint(siGdiSliceRenderer renderer, PaintEventArgs e) { // Set to draw smooth e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; switch (this.ShapeType) { case ShapeTypeEnum.Object: this.OnPaintObject(renderer, e); break; case ShapeTypeEnum.Extrusion: this.OnPaintExtrusion(renderer, e); break; } } /// /// Renderer the circular selection as an object (a circle in 2D or a /// sphere in 3D). /// /// protected void OnPaintObject(siGdiSliceRenderer renderer, PaintEventArgs e) { // Get the graphics handle Graphics g = e.Graphics; // Pass the center through the direction transform itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return; itk.itkPoint pointCenterT = renderer.DirectionTransformInversePoint(this.Center); itk.itkIndex indexCenterT; input.TransformPhysicalPointToIndex(pointCenterT, out indexCenterT); // Convert center to screen index PointF screenCenter = renderer.TransformImagePointToScreenPoint(input, pointCenterT); // Convert radius to this slice and then screen coordinates float radiusSlice = this.ComputeRadiusAtSlicePlane(renderer, input, indexCenterT); float diameterSlice = 2.0F * radiusSlice; // Only render if the radius is valid if (radiusSlice <= 0.0F) return; // Draw fill SolidBrush brushFill = new SolidBrush(COLOR_FILL); float x = screenCenter.X - radiusSlice; float y = screenCenter.Y - radiusSlice; g.FillEllipse(brushFill, x, y, diameterSlice, diameterSlice); // Draw outline SolidBrush brushOutline = new SolidBrush(COLOR_OUTLINE); Pen penOutline = new Pen(brushOutline, PEN_OUTLINE_WIDTH); g.DrawEllipse(penOutline, x, y, diameterSlice, diameterSlice); // 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 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; // Pass the center through the direction transform itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return; itk.itkPoint pointCenterT = renderer.DirectionTransformInversePoint(this.Center); itk.itkIndex indexCenterT; input.TransformPhysicalPointToIndex(pointCenterT, out indexCenterT); // Convert center to screen index PointF screenCenter = renderer.TransformImagePointToScreenPoint(input, pointCenterT); // Convert the slice index to a point itk.itkPoint pointSlice; itk.itkIndex indexSlice = new itk.itkIndex(0, 0, renderer.Slice); input.TransformIndexToPhysicalPoint(indexSlice, out pointSlice); // Determine if the current slice is within the start and end range if (pointSlice[2] < this.ExtrusionStartPoint || pointSlice[2] > this.ExtrusionEndPoint) // The slice does not intersect with the shape, there is nothing to paint return; // Draw fill float radius = this.ComputeRadiusAtSlicePlane(renderer, input, indexCenterT); float diameter = 2.0F * radius; SolidBrush brushFill = new SolidBrush(COLOR_FILL); float x = screenCenter.X - radius; float y = screenCenter.Y - radius; g.FillEllipse(brushFill, x, y, diameter, diameter); // Draw outline SolidBrush brushOutline = new SolidBrush(COLOR_OUTLINE); Pen penOutline = new Pen(brushOutline, PEN_OUTLINE_WIDTH); g.DrawEllipse(penOutline, x, y, diameter, diameter); // 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.GetExtrudedShapeScreenRectangle(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"] ) { // Translate the center of the selection itk.itkVector vectorOffset = (itk.itkVector)this.Metadata["MouseDownCenterOffset"]; itk.itkPoint center = pointMouseMoveT + vectorOffset; this.Center = center; return true; } // Check if we are resizing if ( (bool)this.Metadata["IsResizing"] ) { double radius = 0.0; if (this.ShapeType == ShapeTypeEnum.Object) { radius = pointMouseMoveT.EuclideanDistanceTo(this.Center); } else if (this.ShapeType == ShapeTypeEnum.Extrusion && this.IsRenderingShape(renderer, this.GetInputAsImage(renderer).Direction)) { itk.itkPoint pointMouse2D = new itk.itkPoint(pointMouseMoveT[0], pointMouseMoveT[1]); itk.itkPoint pointCenter2D = new itk.itkPoint(this.Center[0], this.Center[1]); radius = pointMouse2D.EuclideanDistanceTo(pointCenter2D); } else if (this.ShapeType == ShapeTypeEnum.Extrusion && !this.IsRenderingShape(renderer, this.GetInputAsImage(renderer).Direction)) { itk.itkPoint pointMouse2D = new itk.itkPoint(pointMouseMoveT[0], pointMouseMoveT[1]); itk.itkPoint pointCenter2D = new itk.itkPoint(this.Center[0], this.Center[1]); radius = pointMouse2D.EuclideanDistanceTo(pointCenter2D); this.m_ExtrusionDistanceFromCenter = Math.Abs(this.Center[2] - pointMouseMoveT[2]); } this.Radius = (float)radius; return true; } // Check if the mouse is over a control point int controlPointIndex = this.IsMouseOverControlPoint(renderer, 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, pointMouseMoveT)) { 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); // Check if we are over the selection if ( (bool)this.Metadata["IsMouseOverSelection"] ) { // Override image translation renderer.Metadata["IsTranslatingImage"] = false; // Start selection translation this.Metadata["IsTranslating"] = true; // Compute the center offset itk.itkImageBase input = this.GetInputAsImage(renderer); itk.itkPoint pointMouseDown = (itk.itkPoint)renderer.Metadata["LastImagePointMouseDown"]; itk.itkPoint pointMouseDownT = renderer.DirectionTransformPoint(pointMouseDown); itk.itkVector vectorOffset = (this.Center - pointMouseDownT); this.Metadata["MouseDownCenterOffset"] = vectorOffset; return true; } // Check if we are over a control point if ( (bool)this.Metadata["IsMouseOverControlPoint"] ) { // Override other translations renderer.Metadata["IsTranslatingImage"] = false; this.Metadata["IsTranslating"] = false; // Start selection resize this.Metadata["IsResizing"] = 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["IsResizing"] ) { this.Metadata["IsResizing"] = false; return true; } // We did not consume this event return false; } //===================================================================== #endregion #region Private Helper Methods //===================================================================== /// /// Compute the radius at the given slice. /// /// private float ComputeRadiusAtSlicePlane(siGdiSliceRenderer renderer, itk.itkImageBase input, itk.itkIndex indexCenterT) { // Setup for calculation double radiusSlice = this.Radius; // Find the difference in the current slice and the center slice if (this.Center.Dimension == 3 && this.ShapeType == ShapeTypeEnum.Object) { double centerDiff = input.Spacing[2] * (indexCenterT[2] - renderer.Slice); // Check if the selection is intersected by the current slice plane if (centerDiff >= this.Radius || -centerDiff >= this.Radius) // The selection is not intersected by the slice, return radiusSlice = 0.0F; else // Else, compute the radius at the current slice plane radiusSlice = Math.Sqrt(this.Radius*this.Radius - centerDiff*centerDiff); } // Adjust for zoom and return return (float)((radiusSlice / input.Spacing[0]) * renderer.ZoomFactor); } /// /// Computes the control point rectangles for the current center and radius. /// /// private RectangleF[] GetControlPoints(siGdiSliceRenderer renderer) { // Get the input itk.itkImageBase input = this.GetInputAsImage(renderer); // Get the control width float widthControl = CONTROL_WIDTH * (renderer.ZoomFactor < 0.25 ? (float)renderer.ZoomFactor : 1.0F); // The control points depend on the shape type if (this.ShapeType == ShapeTypeEnum.Object || this.ShapeType == ShapeTypeEnum.Extrusion && this.IsRenderingShape(renderer, input.Direction)) { // Pass the center through the direction transform itk.itkPoint pointCenterT = renderer.DirectionTransformInversePoint(this.Center); itk.itkIndex indexCenterT; input.TransformPhysicalPointToIndex(pointCenterT, out indexCenterT); // Convert center to screen index PointF screenCenter = renderer.TransformImagePointToScreenPoint(input, pointCenterT); // Convert radius to this slice float radiusSlice = this.ComputeRadiusAtSlicePlane(renderer, input, indexCenterT); float diameterSlice = 2.0F * radiusSlice; // Setup for calculation float offsetControl = (radiusSlice / (float)Math.Cos(Math.PI / 4.0)) / 2.0F; // Create the control points RectangleF[] result = new RectangleF[4]; result[0] = new RectangleF(screenCenter.X - offsetControl - widthControl / 2.0F, screenCenter.Y - offsetControl - widthControl / 2.0F, widthControl, widthControl); result[1] = new RectangleF(screenCenter.X - offsetControl - widthControl / 2.0F, screenCenter.Y + offsetControl - widthControl / 2.0F, widthControl, widthControl); result[2] = new RectangleF(screenCenter.X + offsetControl - widthControl / 2.0F, screenCenter.Y - offsetControl - widthControl / 2.0F, widthControl, widthControl); result[3] = new RectangleF(screenCenter.X + offsetControl - widthControl / 2.0F, screenCenter.Y + offsetControl - widthControl / 2.0F, widthControl, widthControl); return result; } else if (this.ShapeType == ShapeTypeEnum.Extrusion && !this.IsRenderingShape(renderer, input.Direction)) { RectangleF r = this.GetExtrudedShapeScreenRectangle(renderer); // Create the control points RectangleF[] result = new RectangleF[4]; result[0] = new RectangleF(r.X - widthControl / 2.0F, r.Y - widthControl / 2.0F, widthControl, widthControl); result[1] = new RectangleF(r.X + r.Width - widthControl / 2.0F, r.Y - widthControl / 2.0F, widthControl, widthControl); result[2] = new RectangleF(r.X - widthControl / 2.0F, r.Y + r.Height - widthControl / 2.0F, widthControl, widthControl); result[3] = new RectangleF(r.X + r.Width - widthControl / 2.0F, r.Y + r.Height - widthControl / 2.0F, widthControl, widthControl); return result; } return new RectangleF[0]; } /// /// 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(siRenderer renderer, MouseEventArgs e) { RectangleF[] controlPoints = this.GetControlPoints(renderer as siGdiSliceRenderer); 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. /// /// The position of the mouse in phyiscal space, transformed by the direction matrix. /// private bool IsMouseOverSelection(siRenderer renderer, itk.itkPoint pointMouseT) { if (this.ShapeType == ShapeTypeEnum.Object) { return (this.Center.EuclideanDistanceTo(pointMouseT) <= this.Radius); } else if (this.ShapeType == ShapeTypeEnum.Extrusion && this.IsRenderingShape(renderer, this.GetInputAsImage(renderer).Direction)) { // Create 2D center and mouse points (we ignore the secondary axis) itk.itkPoint pointMouse2D = new itk.itkPoint(pointMouseT[0], pointMouseT[1]); itk.itkPoint pointCenter2D = new itk.itkPoint(this.Center[0], this.Center[1]); // Compute return (pointCenter2D.EuclideanDistanceTo(pointMouse2D) <= this.Radius); } else if (this.ShapeType == ShapeTypeEnum.Extrusion && !this.IsRenderingShape(renderer, this.GetInputAsImage(renderer).Direction)) { // Get the screen rectangle RectangleF r = this.GetExtrudedShapeScreenRectangle(renderer as siGdiSliceRenderer); if (r == RectangleF.Empty) return false; // Use the mouse coords to determine if the mouse is over the selection MouseEventArgs last = renderer.Metadata["LastScreenMouseMove"] as MouseEventArgs; return r.Contains(last.Location); } return false; } /// /// Get the cursor for the given control point index. /// /// /// private Cursor GetControlPointCursor(int controlPoint) { switch (controlPoint) { case 0: return Cursors.SizeNWSE; case 1: return Cursors.SizeNESW; case 2: return Cursors.SizeNESW; case 3: return Cursors.SizeNWSE; default: return Cursors.Default; } } /// /// Gets the rectangle in screen coordinates which represents the slice of the /// cylinder. /// /// /// private RectangleF GetExtrudedShapeScreenRectangle(siGdiSliceRenderer renderer) { // Get the input image itk.itkImageBase input = this.GetInputAsImage(renderer); if (input == null) return RectangleF.Empty; // Convert the slice index to physical space itk.itkPoint pointSlice; itk.itkIndex indexSlice = new itk.itkIndex(0, 0, renderer.Slice); input.TransformIndexToPhysicalPoint(indexSlice, out pointSlice); // Pass the z-axis through the direction transform itk.itkPoint zaxis = new itk.itkPoint(0.0, 0.0, 1.0); itk.itkPoint zaxisT = renderer.DirectionTransformInversePoint(zaxis); // Pass the center through the direction transform itk.itkPoint pointCenterT = renderer.DirectionTransformInversePoint(this.Center); // Create the rectangle in physical space (from the view of the primary axis) itk.itkPoint pointCenterTop = new itk.itkPoint(this.Center[0], this.Center[1], this.ExtrusionStartPoint); itk.itkPoint pointCenterBottom = new itk.itkPoint(this.Center[0], this.Center[1], this.ExtrusionEndPoint); itk.itkPoint pointCenterTopT = renderer.DirectionTransformInversePoint(pointCenterTop); itk.itkPoint pointCenterBottomT = renderer.DirectionTransformInversePoint(pointCenterBottom); // Compute the radius, and check this slice intersects with the shape double radius = this.Radius - Math.Abs(pointCenterTopT[2] - pointSlice[2]); if (radius <= 0.0) return RectangleF.Empty; // Draw the extruded rectangle if (zaxisT[0] != 0.0) { // Adjust for the radius pointCenterTopT[1] += radius; pointCenterBottomT[1] -= radius; } else if (zaxisT[1] != 0.0) { // Adjust for the radius pointCenterTopT[0] += radius; pointCenterBottomT[0] -= radius; } // Convert to screen coords PointF pointCenterTopScreen = renderer.TransformImagePointToScreenPoint(input, pointCenterTopT); PointF pointCenterBottomScreen = renderer.TransformImagePointToScreenPoint(input, pointCenterBottomT); // Construct and return the rectangle float x = pointCenterBottomScreen.X; float y = pointCenterBottomScreen.Y; float w = Math.Abs(pointCenterBottomScreen.X - pointCenterTopScreen.X); float h = Math.Abs(pointCenterBottomScreen.Y - pointCenterTopScreen.Y); return new RectangleF(x, y, w, h); } //===================================================================== #endregion } }