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