/*============================================================================= Project: SharpImage Module: siTransferFunctionPartTrapezoid.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.Drawing; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Diagnostics; using System.Drawing.Drawing2D; namespace SharpImage.Rendering { public class siTransferFunctionPartTrapezoid : siTransferFunctionPart { #region Enumerations //===================================================================== public enum siTrapezoidGradientModeEnum { Left, Right, Middle } //===================================================================== #endregion #region Constants //===================================================================== private const float CONTROL_POINT_WIDTH = 6.0F; private const float CONTROL_POINT_HEIGHT = 6.0F; //===================================================================== #endregion #region Instance Variables //===================================================================== //===================================================================== #endregion #region Construction and Disposal //===================================================================== /// /// Default constructor. This part will be drawn on all layers. /// public siTransferFunctionPartTrapezoid() : this(null, null) { } /// /// Constructor taking a layer key. /// /// Uses the parent.Image for creating a default sized part. /// The name of layer this part is to be drawn on. public siTransferFunctionPartTrapezoid(siTransferFunction parent, string layer) : base(layer) { // Create a default trapezoid if (parent != null) { this.m_BottomValue = (float)parent.Size0 * 0.5F; this.m_TopValue = (float)parent.Size0 * 0.5F; this.m_BottomWidth = (float)parent.Size0 * (1F / 4F); this.m_TopWidth = (float)parent.Size0 * (1F / 3F); this.m_BottomHeight = 0; this.m_TopHeight = parent.Size1; this.m_BottomPosition = 0.5F; this.m_TopPosition = 0.5F; } this.m_GradientMode = siTrapezoidGradientModeEnum.Middle; } /// /// Initialise the part Metadata. /// protected override void Initialise() { // Setup default Metadata base.Initialise(); this.Metadata["IsEditing"] = false; this.Metadata["IsEditingLine"] = false; this.Metadata["IsMouseOverControlLine"] = false; } //===================================================================== #endregion #region Properties //===================================================================== #region BackColor //===================================================================== /// /// Get/set the RGB components of the back color. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public override Color BackColorRGB { get { return Color.FromArgb(base.BackColor.R, base.BackColor.G, base.BackColor.B); } set { base.BackColor = Color.FromArgb(base.BackColor.A, value); this.RaiseModified(); } } /// /// Get/set the Alpha component of the back color. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public override int BackColorAlpha { get { return base.BackColor.A; } set { base.BackColor = Color.FromArgb(value, base.BackColor); this.RaiseModified(); } } /// /// This property is needed for serialization. /// It is not intended for general use. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public override string SerializableBackColor { get { return siColorHelper.SerializeColor(base.BackColor); } set { base.BackColor = siColorHelper.DeserializeColor(value); this.RaiseModified(); } } //===================================================================== #endregion #region BackColor1 //===================================================================== private Color m_BackColor1 = DEFAULT_BACK_COLOR; /// /// Get/set the back color of the part. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public virtual Color BackColor1 { get { return this.m_BackColor1; } set { this.m_BackColor1 = value; this.RaiseModified(); } } /// /// Get/set the RGB components of the back color. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public virtual Color BackColor1RGB { get { return Color.FromArgb(this.m_BackColor1.R, this.m_BackColor1.G, this.m_BackColor1.B); } set { this.m_BackColor1 = Color.FromArgb(this.m_BackColor1.A, value); this.RaiseModified(); } } /// /// Get/set the Alpha component of the back color. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] public virtual int BackColor1Alpha { get { return this.m_BackColor1.A; } set { this.m_BackColor1 = Color.FromArgb(value, this.m_BackColor1); this.RaiseModified(); } } /// /// This property is needed for serialization. /// It is not intended for general use. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] public virtual string SerializableBackColor1 { get { return siColorHelper.SerializeColor(this.m_BackColor1); } set { this.m_BackColor1 = siColorHelper.DeserializeColor(value); this.RaiseModified(); } } //===================================================================== #endregion #region BackColor2 //===================================================================== private Color m_BackColor2 = DEFAULT_BACK_COLOR; /// /// Get/set the back color of the part. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public virtual Color BackColor2 { get { return this.m_BackColor2; } set { this.m_BackColor2 = value; this.RaiseModified(); } } /// /// Get/set the RGB components of the back color. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] [System.Xml.Serialization.XmlIgnore] public virtual Color BackColor2RGB { get { return Color.FromArgb(this.m_BackColor2.R, this.m_BackColor2.G, this.m_BackColor2.B); } set { this.m_BackColor2 = Color.FromArgb(this.m_BackColor2.A, value); this.RaiseModified(); } } /// /// Get/set the Alpha component of the back color. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] public virtual int BackColor2Alpha { get { return this.m_BackColor2.A; } set { this.m_BackColor2 = Color.FromArgb(value, this.m_BackColor2); this.RaiseModified(); } } /// /// This property is needed for serialization. /// It is not intended for general use. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Color")] public virtual string SerializableBackColor2 { get { return siColorHelper.SerializeColor(this.m_BackColor2); } set { this.m_BackColor2 = siColorHelper.DeserializeColor(value); this.RaiseModified(); } } //===================================================================== #endregion #region BoundingBox //===================================================================== /// /// Get the rectangle bounding the part. /// [System.ComponentModel.Browsable(false)] [System.Xml.Serialization.XmlIgnore] public new RectangleF BoundingBox { get { return RectangleF.Empty; } set { } } /// /// Get the x coord of the top-left point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Bounding Box")] [System.Xml.Serialization.XmlIgnore] public new float BoundingBoxX { get { return 0.0F; } set { } } /// /// Get the y coord of the top-left point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Bounding Box")] [System.Xml.Serialization.XmlIgnore] public new float BoundingBoxY { get { return 0.0F; } set { } } /// /// Get the width point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Bounding Box")] [System.Xml.Serialization.XmlIgnore] public new float BoundingBoxWidth { get { return 0.0F; } set { } } /// /// Get the height point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Bounding Box")] [System.Xml.Serialization.XmlIgnore] public new float BoundingBoxHeight { get { return 0.0F; } set { } } //===================================================================== #endregion #region Parameters //===================================================================== private float m_BottomValue = 0.0F; private float m_BottomHeight = 0.0F; private float m_BottomWidth = 0.0F; private float m_BottomPosition = 0.0F; private float m_TopValue = 0.0F; private float m_TopHeight = 0.0F; private float m_TopWidth = 0.0F; private float m_TopPosition = 0.0F; /// /// Get/set the value of the bottom edge of the Trapezoid. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Parameters")] public float BottomValue { get { return this.m_BottomValue; } set { this.m_BottomValue = value; this.RaiseModified(); } } /// /// Get/set the height of the bottom edge of the Trapezoid. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Parameters")] public float BottomHeight { get { return this.m_BottomHeight; } set { this.m_BottomHeight = value; this.RaiseModified(); } } /// /// Get/set the width of the bottom edge of the Trapezoid. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Parameters")] public float BottomWidth { get { return this.m_BottomWidth; } set { this.m_BottomWidth = value; this.RaiseModified(); } } /// /// Get/set the bottom position of the line. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Parameters")] public float BottomPosition { get { return this.m_BottomPosition; } set { this.m_BottomPosition = value; this.RaiseModified(); } } /// /// Get/set the value of the top edge of the Trapezoid. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Parameters")] public float TopValue { get { return this.m_TopValue; } set { this.m_TopValue = value; this.RaiseModified(); } } /// /// Get/set the height of the top edge of the Trapezoid. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Parameters")] public float TopHeight { get { return this.m_TopHeight; } set { this.m_TopHeight = value; this.RaiseModified(); } } /// /// Get/set the width of the top edge of the Trapezoid. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Parameters")] public float TopWidth { get { return this.m_TopWidth; } set { this.m_TopWidth = value; this.RaiseModified(); } } /// /// Get/set the top position of the line. /// [System.ComponentModel.Browsable(false)] [System.ComponentModel.Category("Parameters")] public float TopPosition { get { return this.m_TopPosition; } set { this.m_TopPosition = value; this.RaiseModified(); } } //===================================================================== #endregion #region GradientTriangle //============================================================================ private bool m_GradientTriangle; /// /// Gets/sets the if the gradient is a triangle or a rectangle. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] public bool GradientTriangle { get { return this.m_GradientTriangle; } set { this.m_GradientTriangle = value; this.RaiseModified(); } } //============================================================================ #endregion #region GradientMode //============================================================================ private siTrapezoidGradientModeEnum m_GradientMode; /// /// Gets/sets the the gradient mode. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] public siTrapezoidGradientModeEnum GradientMode { get { return this.m_GradientMode; } set { this.m_GradientMode = value; this.RaiseModified(); } } //============================================================================ #endregion //===================================================================== #endregion #region Public Methods //===================================================================== public override string ToString() { return "Trapezoid: " + this.Layer + " " + this.BackColor.ToString() + " " + this.BottomValue.ToString() + " " + this.TopValue.ToString(); } //===================================================================== #endregion #region Paint Methods //===================================================================== protected override void OnPaintFillFunction(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Create the path PointF[] p = this.GetParameterPoints(parent.Size0, parent.Size1); p[0].Y = p[0].Y + 1; // NOTE: We add one to fix a bug with the path brush p[1].Y = p[1].Y + 1; // NOTE: We add one to fix a bug with the path brush PointF pTop = new PointF(p[2].X + this.m_TopPosition*this.TopWidth, parent.Size1 - this.TopHeight); PointF pBottom = new PointF(p[1].X + this.m_BottomPosition*this.BottomWidth, parent.Size1 - this.BottomHeight); GraphicsPath path = new GraphicsPath(); if (this.m_GradientMode == siTrapezoidGradientModeEnum.Middle) path.AddPolygon(new PointF[] { p[0], p[1], p[2], p[3] }); else // Left or right path.AddPolygon(new PointF[] { p[0], pBottom, p[1], p[2], pTop, p[3] }); path.CloseFigure(); // Fill the path float offset = parent.Size1 * 100.0F; PathGradientBrush brushFill = new PathGradientBrush(path); PointF pHori1 = new PointF(); PointF pHori2 = new PointF(); if (this.m_GradientTriangle && this.BottomWidth > this.TopWidth || !this.m_GradientTriangle && this.BottomWidth < this.TopWidth) { pHori1 = new PointF(0, -offset); pHori2 = new PointF(parent.Size0, -offset); } else { pHori1 = new PointF(0, offset); pHori2 = new PointF(parent.Size0, offset); } brushFill.CenterPoint = this.LineToLineIntersection(pTop, pBottom, pHori1, pHori2); brushFill.CenterColor = (this.BackColor1 == this.BackColor2) ? this.BackColor1: Color.Gray; if (this.m_GradientMode == siTrapezoidGradientModeEnum.Right) brushFill.SurroundColors = new Color[] { this.BackColor2, this.BackColor2, this.BackColor1, this.BackColor1, this.BackColor2, this.BackColor2 }; else if (this.m_GradientMode == siTrapezoidGradientModeEnum.Left) brushFill.SurroundColors = new Color[] { this.BackColor2, this.BackColor1, this.BackColor1, this.BackColor1, this.BackColor1, this.BackColor2 }; else if (this.m_GradientMode == siTrapezoidGradientModeEnum.Middle) brushFill.SurroundColors = new Color[] { this.BackColor2, this.BackColor1, this.BackColor1, this.BackColor2 }; e.Graphics.FillPath(brushFill, path); path.Dispose(); } protected override void OnPaintControlPoints(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Paint the gradient control if (this.BackColor1 != this.BackColor2) { Pen penGradient = new Pen(Color.FromArgb(255, Color.Gray), 1.0f); PointF[] p = this.GetParameterPoints(parent.Size0, parent.Size1); PointF pTop = new PointF(p[2].X + this.m_TopPosition * this.TopWidth, parent.Size1 - this.TopHeight); PointF pBottom = new PointF(p[1].X + this.m_BottomPosition * this.BottomWidth, parent.Size1 - this.BottomHeight); e.Graphics.DrawLine(penGradient, pTop, pBottom); } // Set smoothing mode to none SmoothingMode oldSmoothingMode = e.Graphics.SmoothingMode; e.Graphics.SmoothingMode = SmoothingMode.None; // Paint the control points SolidBrush brushFill = new SolidBrush(CONTROL_POINT_COLOR_FILL); SolidBrush brushOutline = new SolidBrush(CONTROL_POINT_COLOR_OUTLINE); Pen penOutline = new Pen(brushOutline, CONTROL_POINT_PEN_WIDTH); foreach (RectangleF cpt in this.GetControlPoints(parent.Size0, parent.Size1)) { e.Graphics.FillRectangle(brushFill, cpt); e.Graphics.DrawRectangle(penOutline, cpt.X, cpt.Y, cpt.Width, cpt.Height); } // Reset smoothing mode e.Graphics.SmoothingMode = oldSmoothingMode; } protected override void OnPaintBoundingBox(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Get the graphics handle Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; // Draw an outline around the whole part GraphicsPath path = this.GetGraphicsPath(parent.Size0, parent.Size1); Pen penOutline = new Pen(BOUNDING_BOX_COLOR_OUTLINE_ENABLED, PEN_WIDTH); g.DrawPath(penOutline, path); path.Dispose(); } protected override void OnPaintInformation(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Create the info string Brush brush = new SolidBrush(Color.Black); Font font = new Font("Arial", 8.0F, FontStyle.Bold); String info = this.ConstructInformationString(true, e); SizeF sizeInfo = e.Graphics.MeasureString(info, font); e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; // Create the point to print the string float x = this.TopValue - (sizeInfo.Width / 2.0F); float y = parent.Size1 - this.TopHeight; float width = this.TopWidth; float height = this.TopHeight - this.BottomHeight; PointF pointInfo = new PointF(x + INFO_OFFSET, y + INFO_OFFSET); // Only draw the info if the width will not cut off the info string if (sizeInfo.Height <= (height - 10 * INFO_OFFSET) && sizeInfo.Width <= (width - 2 * INFO_OFFSET)) { //e.Graphics.DrawString(info, font, brushShadow, pointInfo.X + 0.5F, pointInfo.Y + 0.5F); e.Graphics.DrawString(info, font, brush, pointInfo.X, pointInfo.Y); return; } // Adjust the info string info = this.ConstructInformationString(false, e); sizeInfo = e.Graphics.MeasureString(info, font); x = this.TopValue - (sizeInfo.Width / 2.0F); pointInfo = new PointF(x + INFO_OFFSET, y + INFO_OFFSET); // Only draw the info if the height will not cut off the info string if (sizeInfo.Height <= (height - 10 * INFO_OFFSET) && sizeInfo.Width <= (width - 2 * INFO_OFFSET)) { //e.Graphics.DrawString(info, font, brushShadow, pointInfo.X + 0.5F, pointInfo.Y + 0.5F); e.Graphics.DrawString(info, font, brush, pointInfo.X, pointInfo.Y); return; } } //===================================================================== #endregion #region Mouse Methods //===================================================================== protected override bool OnMouseDown(siTransferFunction parent, MouseEventArgs e) { // Do not consume events if invisible if (!this.Visible) return false; // Raise the mouse down event this.RaiseMouseDownEvent(e); // Record the last mouse down this.Metadata["LastMouseDown"] = e; this.Metadata["TopValueMouseDown"] = this.TopValue; this.Metadata["BottomValueMouseDown"] = this.BottomValue; this.Metadata["TopWidthMouseDown"] = this.TopWidth; this.Metadata["BottomWidthMouseDown"] = this.BottomWidth; this.Metadata["TopHeightMouseDown"] = this.TopHeight; this.Metadata["BottomHeightMouseDown"] = this.BottomHeight; this.Metadata["TopPositionMouseDown"] = this.m_TopPosition; this.Metadata["BottomPositionMouseDown"] = this.m_BottomPosition; // Check if the mouse is over a control point if ((bool)this.Metadata["IsMouseOverControlPoint"]) { this.Metadata["IsEditing"] = true; return true; } // Check if the mouse is over a control line if ((bool)this.Metadata["IsMouseOverControlLine"]) { this.Metadata["IsEditingLine"] = true; return true; } // Check if a double click if (e.Clicks == 2) { // Toggle the enabled property this.Enabled = this.Enabled ? false : true; return true; } // Check if the mouse is over the part GraphicsPath path = this.GetGraphicsPath(parent.Size0, parent.Size1); if (path.IsVisible(e.Location)) { this.Metadata["IsTranslating"] = true; path.Dispose(); return true; } path.Dispose(); // The event was not consumed this.Metadata["IsEditing"] = false; this.Metadata["IsEditingLine"] = false; this.Metadata["IsTranslating"] = false; return false; } protected override bool OnMouseUp(siTransferFunction parent, MouseEventArgs e) { // Do not consume events if invisible if (!this.Visible) return false; // Cancel an edit if ((bool)this.Metadata["IsEditing"]) { this.Metadata["IsEditing"] = false; return true; } // Cancel a line edit if ((bool)this.Metadata["IsEditingLine"]) { this.Metadata["IsEditingLine"] = false; return true; } // Cancel a translation if ((bool)this.Metadata["IsTranslating"]) { this.Metadata["IsTranslating"] = false; return true; } // Allow the base class to handle if (base.OnMouseUp(parent, e)) return true; // The event was not consumed this.Metadata["IsEditing"] = false; this.Metadata["IsEditingLine"] = false; this.Metadata["IsTranslating"] = false; return false; } protected override bool OnMouseMove(siTransferFunction parent, MouseEventArgs e) { // Do not consume events if invisible if (!this.Visible) return false; // Save the last mouse move MouseEventArgs lastMouseMove = this.Metadata["LastMouseMove"] as MouseEventArgs; this.Metadata["LastMouseMove"] = e; // Consume an editing event if ((bool)this.Metadata["IsEditing"]) { int cpindex = (int)this.Metadata["CurrentControlPointIndex"]; MouseEventArgs lastMouseDown = this.Metadata["LastMouseDown"] as MouseEventArgs; float valueTopMouseDown = (float)this.Metadata["TopValueMouseDown"]; float valueBottomMouseDown = (float)this.Metadata["BottomValueMouseDown"]; float widthTopMouseDown = (float)this.Metadata["TopWidthMouseDown"]; float widthBottomMouseDown = (float)this.Metadata["BottomWidthMouseDown"]; float heightTopMouseDown = (float)this.Metadata["TopHeightMouseDown"]; float heightBottomMouseDown = (float)this.Metadata["BottomHeightMouseDown"]; switch (cpindex) { case 0: this.m_BottomWidth = widthBottomMouseDown + (e.X - lastMouseDown.X) * 2F; if (this.m_BottomWidth < 0) this.m_BottomWidth = 0; if (this.m_BottomWidth > parent.Size0) this.m_BottomWidth = parent.Size0 - 1; this.m_BottomHeight = heightBottomMouseDown - (e.Y - lastMouseDown.Y); if (this.m_BottomHeight < 0) this.m_BottomHeight = 0; if (this.m_BottomHeight > parent.Size1) this.m_BottomHeight = parent.Size1; if (this.m_BottomHeight > this.m_TopHeight) this.m_BottomHeight = this.m_TopHeight; this.RaiseModified(); return true; case 1: this.m_BottomWidth = widthBottomMouseDown - (e.X - lastMouseDown.X) * 2F; if (this.m_BottomWidth < 0) this.m_BottomWidth = 0; if (this.m_BottomWidth > parent.Size0) this.m_BottomWidth = parent.Size0 - 1; this.m_BottomHeight = heightBottomMouseDown - (e.Y - lastMouseDown.Y); if (this.m_BottomHeight < 0) this.m_BottomHeight = 0; if (this.m_BottomHeight > parent.Size1) this.m_BottomHeight = parent.Size1; if (this.m_BottomHeight > this.m_TopHeight) this.m_BottomHeight = this.m_TopHeight; this.RaiseModified(); return true; case 2: this.m_TopWidth = widthTopMouseDown - (e.X - lastMouseDown.X) * 2F; if (this.m_TopWidth < 0) this.m_TopWidth = 0; if (this.m_TopWidth > parent.Size0) this.m_TopWidth = parent.Size0 - 1; this.m_TopHeight = heightTopMouseDown - (e.Y - lastMouseDown.Y); if (this.m_TopHeight < 0) this.m_TopHeight = 0; if (this.m_TopHeight > parent.Size1) this.m_TopHeight = parent.Size1; if (this.m_TopHeight < this.m_BottomHeight) this.m_TopHeight = this.m_BottomHeight; this.RaiseModified(); return true; case 3: this.m_TopWidth = widthTopMouseDown + (e.X - lastMouseDown.X) * 2F; if (this.m_TopWidth < 0) this.m_TopWidth = 0; if (this.m_TopWidth > parent.Size0) this.m_TopWidth = parent.Size0 - 1; this.m_TopHeight = heightTopMouseDown - (e.Y - lastMouseDown.Y); if (this.m_TopHeight < 0) this.m_TopHeight = 0; if (this.m_TopHeight > parent.Size1) this.m_TopHeight = parent.Size1; if (this.m_TopHeight < this.m_BottomHeight) this.m_TopHeight = this.m_BottomHeight; this.RaiseModified(); return true; case 4: this.m_BottomHeight = heightBottomMouseDown - (e.Y - lastMouseDown.Y); this.m_BottomValue = valueBottomMouseDown + (e.X - lastMouseDown.X); if (this.m_BottomHeight < 0) this.m_BottomHeight = 0; if (this.m_BottomHeight > parent.Size1) this.m_BottomHeight = parent.Size1; if (this.m_BottomHeight > this.m_TopHeight) this.m_BottomHeight = this.m_TopHeight; if (this.m_BottomValue < 0) this.m_BottomValue = 0; if (this.m_BottomValue > parent.Size0) this.m_BottomValue = parent.Size0; this.RaiseModified(); return true; case 5: this.m_TopHeight = heightTopMouseDown - (e.Y - lastMouseDown.Y); this.m_TopValue = valueTopMouseDown + (e.X - lastMouseDown.X); if (this.m_TopHeight < 0) this.m_TopHeight = 0; if (this.m_TopHeight > parent.Size1) this.m_TopHeight = parent.Size1; if (this.m_TopHeight < this.m_BottomHeight) this.m_TopHeight = this.m_BottomHeight; if (this.m_TopValue < 0) this.m_TopValue = 0; if (this.m_TopValue > parent.Size0) this.m_TopValue = parent.Size0; this.RaiseModified(); return true; } } // Consume a translation event if ((bool)this.Metadata["IsTranslating"]) { MouseEventArgs lastMouseDown = this.Metadata["LastMouseDown"] as MouseEventArgs; float valueTopMouseDown = (float)this.Metadata["TopValueMouseDown"]; float valueBottomMouseDown = (float)this.Metadata["BottomValueMouseDown"]; this.m_TopValue = valueTopMouseDown + (e.X - lastMouseDown.X); this.m_BottomValue = valueBottomMouseDown + (e.X - lastMouseDown.X); if (this.m_TopValue < 0) this.m_TopValue = 0; if (this.m_BottomValue < 0) this.m_BottomValue = 0; if (this.m_TopValue > parent.Size0) this.m_TopValue = parent.Size0 - 1; if (this.m_BottomValue > parent.Size0) this.m_BottomValue = parent.Size0 - 1; this.RaiseModified(); return true; } // Consume a line editing event if ((bool)this.Metadata["IsEditingLine"]) { MouseEventArgs lastMouseDown = this.Metadata["LastMouseDown"] as MouseEventArgs; float posTopMouseDown = (float)this.Metadata["TopPositionMouseDown"]; float posBottomMouseDown = (float)this.Metadata["BottomPositionMouseDown"]; this.m_TopPosition = posTopMouseDown + (e.X - lastMouseDown.X) / this.TopWidth; this.m_BottomPosition = posBottomMouseDown + (e.X - lastMouseDown.X) / this.TopWidth; if (this.m_TopPosition > 1) this.m_TopPosition = 1F; if (this.m_TopPosition < 0) this.m_TopPosition = 0F; if (this.m_BottomPosition > 1) this.m_BottomPosition = 1F; if (this.m_BottomPosition < 0) this.m_BottomPosition = 0F; this.RaiseModified(); return true; } // Check if the mouse is over the gradient position control line float offset = 3.5F; GraphicsPath pathLine = new GraphicsPath(); PointF[] p = this.GetParameterPoints(parent.Size0, parent.Size1); PointF pTop = new PointF(p[2].X + this.m_TopPosition * this.TopWidth, parent.Size1 - this.TopHeight); PointF pBottom = new PointF(p[1].X + this.m_BottomPosition * this.BottomWidth, parent.Size1 - this.BottomHeight); PointF pBottomLeft = new PointF(pBottom.X - offset, pBottom.Y); PointF pBottomRight = new PointF(pBottom.X + offset, pBottom.Y); PointF pTopLeft = new PointF(pTop.X - offset, pTop.Y); PointF pTopRight = new PointF(pTop.X + offset, pTop.Y); pathLine.AddPolygon(new PointF[] { pBottomLeft, pBottomRight, pTopRight, pTopLeft }); pathLine.CloseFigure(); if (pathLine.IsVisible(e.Location) && this.BackColor1 != this.BackColor2) { parent.Metadata["Cursor"] = Cursors.SizeWE; this.Metadata["IsMouseOverControlLine"] = true; this.Metadata["IsMouseOverPart"] = false; this.RaiseModified(); return true; } // Set the mouse control point over Metadata int cpIndex = 0; foreach (RectangleF cp in this.GetControlPoints(parent.Size0, parent.Size1)) { if (cp.Contains(e.Location)) { this.Metadata["IsMouseOverControlLine"] = false; this.Metadata["IsMouseOverControlPoint"] = true; this.Metadata["CurrentControlPointIndex"] = cpIndex; parent.Metadata["Cursor"] = this.GetControlPointCursor(cpIndex); this.RaiseModified(); return true; } cpIndex++; } // Check if the mouse is over the part GraphicsPath path = this.GetGraphicsPath(parent.Size0, parent.Size1); if (path.IsVisible(e.Location)) { parent.Metadata["Cursor"] = Cursors.SizeWE; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverControlLine"] = false; this.Metadata["IsMouseOverPart"] = true; this.Metadata["CurrentControlPointIndex"] = -1; this.RaiseModified(); return true; } path.Dispose(); // Allow the base class to handle if (base.OnMouseMove(parent, e)) return true; // The event was not consumed this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverPart"] = false; this.Metadata["CurrentControlPointIndex"] = -1; return false; } /// /// Get the cursor for the given control point index. /// /// /// protected override Cursor GetControlPointCursor(int cpIndex) { switch (cpIndex) { case 0: return Cursors.SizeAll; case 1: return Cursors.SizeAll; case 2: return Cursors.SizeAll; case 3: return Cursors.SizeAll; case 4: return Cursors.SizeAll; case 5: return Cursors.SizeAll; default: return Cursors.Default; } } //===================================================================== #endregion #region Private Methods //===================================================================== /// /// Get the control points. This property accessor builds the list /// from private member information each time it is called. /// public RectangleF[] GetControlPoints(float width, float height) { PointF[] points = this.GetParameterPoints(width, height); RectangleF[] result = new RectangleF[6]; result[0] = new RectangleF(points[0].X - CONTROL_POINT_WIDTH / 2.0F, points[0].Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT); result[1] = new RectangleF(points[1].X - CONTROL_POINT_WIDTH / 2.0F, points[1].Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT); result[2] = new RectangleF(points[2].X - CONTROL_POINT_WIDTH / 2.0F, points[2].Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT); result[3] = new RectangleF(points[3].X - CONTROL_POINT_WIDTH / 2.0F, points[3].Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT); PointF pointBottomHeight = new PointF(this.BottomValue, height - this.BottomHeight); result[4] = new RectangleF(pointBottomHeight.X - CONTROL_POINT_WIDTH / 2.0F, pointBottomHeight.Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT); PointF pointTopHeight = new PointF(this.TopValue, height - this.TopHeight); result[5] = new RectangleF(pointTopHeight.X - CONTROL_POINT_WIDTH / 2.0F, pointTopHeight.Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT); return result; } /// /// Get the points for controlling the parameters. /// The points are returned in GDI space. /// /// The width of the parent transfer function. /// The height of the parent transfer function. /// private PointF[] GetParameterPoints(float width, float height) { // Create the points PointF pointBottomLeft = new PointF(this.BottomValue - this.BottomWidth / 2, this.BottomHeight); PointF pointBottomRight = new PointF(this.BottomValue + this.BottomWidth / 2, this.BottomHeight); PointF pointTopLeft = new PointF(this.TopValue - this.TopWidth / 2, this.TopHeight); PointF pointTopRight = new PointF(this.TopValue + this.TopWidth / 2, this.TopHeight); // Adjust the points to GDI space pointBottomLeft.Y = height - pointBottomLeft.Y; pointBottomRight.Y = height - pointBottomRight.Y; pointTopLeft.Y = height - pointTopLeft.Y; pointTopRight.Y = height - pointTopRight.Y; // Return return new PointF[] { pointBottomRight, pointBottomLeft, pointTopLeft, pointTopRight }; } /// /// Get the path representing the function (in GDI space). /// /// /// /// private GraphicsPath GetGraphicsPath(float width, float height) { GraphicsPath path = new GraphicsPath(); path.AddPolygon(this.GetParameterPoints(width, height)); path.CloseFigure(); return path; } /// /// Computes the intersection point of two 2D lines (A and B). /// /// The start point of line A. /// The end point of line A. /// The start point of line B. /// The end point of line B. /// private PointF LineToLineIntersection(PointF p1, PointF p2, PointF p3, PointF p4) { float denom = ((p4.Y - p3.Y) * (p2.X - p1.X)) - ((p4.X - p3.X) * (p2.Y - p1.Y)); float num_a = ((p4.X - p3.X) * (p1.Y - p3.Y)) - ((p4.Y - p3.Y) * (p1.X - p3.X)); float num_b = ((p2.X - p1.X) * (p1.Y - p3.Y)) - ((p2.Y - p1.Y) * (p1.X - p3.X)); float ua = num_a / denom; float ub = num_b / denom; PointF result = new PointF(); result.X = p1.X + ua * (p2.X - p1.X); result.Y = p1.Y + ua * (p2.Y - p1.Y); return result; } //===================================================================== #endregion } }