/*============================================================================= Project: SharpImage Module: siTransferFunctionPart.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.Drawing; using System.Collections.Generic; using System.Text; using System.Windows.Forms; namespace SharpImage.Rendering { public abstract class siTransferFunctionPart : siActor { #region Constants //===================================================================== protected const float PEN_WIDTH = 1.0F; protected const float INFO_OFFSET = 2.0F; protected const float CONTROL_POINT_PEN_WIDTH = 1.0F; protected static readonly Color CONTROL_POINT_COLOR_FILL = Color.White; protected static readonly Color CONTROL_POINT_COLOR_OUTLINE = Color.Black; protected static readonly Color BOUNDING_BOX_COLOR_OUTLINE_ENABLED = Color.Black; protected static readonly Color BOUNDING_BOX_COLOR_OUTLINE_DISABLED = Color.Gray; protected static readonly Color DEFAULT_BACK_COLOR = Color.FromArgb(30, 255, 192, 128); //===================================================================== #endregion #region Instance Variables //===================================================================== //===================================================================== #endregion #region Construction and Disposal //===================================================================== /// /// Default constructor. This part will be drawn on all layers. /// /// public siTransferFunctionPart() : this(null) { } /// /// Constructor taking a layer key. /// /// /// The name of layer this part is to be drawn on. public siTransferFunctionPart(string layer) : base() { // Init Metadata this.Initialise(); // Setup member variables this.m_Layer = layer; } /// /// Initialise the part Metadata. /// protected virtual void Initialise() { // Setup default Metadata this.Metadata["IsResizing"] = false; this.Metadata["IsTranslating"] = false; this.Metadata["IsMouseOverPart"] = false; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["CurrentControlPointIndex"] = -1; this.Metadata["LastMouseMove"] = new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0); } //===================================================================== #endregion #region Properties //===================================================================== #region Layer //===================================================================== private String m_Layer = null; /// /// Get the name of layer this part is drawn. /// If null or empty, then this part is drawn on ALL layers. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.ParenthesizePropertyName(true)] [System.ComponentModel.Category("(Information)")] public String Layer { get { return this.m_Layer; } set { this.m_Layer = value; this.RaiseModified(); } } //===================================================================== #endregion #region BackColor //===================================================================== private Color m_BackColor = 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 BackColor { get { return this.m_BackColor; } set { this.m_BackColor = 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 BackColorRGB { get { return Color.FromArgb(this.m_BackColor.R, this.m_BackColor.G, this.m_BackColor.B); } set { this.m_BackColor = Color.FromArgb(this.m_BackColor.A, value); this.RaiseModified(); } } /// /// Get/set the Alpha component of the back color. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Color")] public virtual int BackColorAlpha { get { return this.m_BackColor.A; } set { this.m_BackColor = Color.FromArgb(value, this.m_BackColor); 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 SerializableBackColor { get { return siColorHelper.SerializeColor(this.m_BackColor); } set { this.m_BackColor = siColorHelper.DeserializeColor(value); this.RaiseModified(); } } //===================================================================== #endregion #region Enabled //===================================================================== private bool m_Enabled = true; /// /// Get/set if the part is enabled, which is different from visible. /// If a part is disabled, it is still drawn to the screen, but not the function. /// If as part is invisible, it is not drawn to the screen, but to the function. /// To make a be totally ignored it must be disabled AND invisible. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("General")] public bool Enabled { get { return this.m_Enabled; } set { this.m_Enabled = value; this.RaiseModified(); } } //===================================================================== #endregion #region BoundingBox //===================================================================== protected RectangleF m_BoundingBox = new Rectangle(); /// /// Get the rectangle bounding the part. /// [System.ComponentModel.Browsable(false)] [System.Xml.Serialization.XmlIgnore] public RectangleF BoundingBox { get { return this.m_BoundingBox; } set { this.m_BoundingBox = value; this.RaiseModified(); } } /// /// Get the x coord of the top-left point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Bounding Box")] public float BoundingBoxX { get { return this.m_BoundingBox.X; } set { this.m_BoundingBox.X = value; this.RaiseModified(); } } /// /// Get the y coord of the top-left point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Bounding Box")] public float BoundingBoxY { get { return this.m_BoundingBox.Y; } set { this.m_BoundingBox.Y = value; this.RaiseModified(); } } /// /// Get the width point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Bounding Box")] public float BoundingBoxWidth { get { return this.m_BoundingBox.Width; } set { this.m_BoundingBox.Width = value; this.RaiseModified(); } } /// /// Get the height point of the rectangle bounding the part. /// [System.ComponentModel.Browsable(true)] [System.ComponentModel.Category("Bounding Box")] public float BoundingBoxHeight { get { return this.m_BoundingBox.Height; } set { this.m_BoundingBox.Height = value; this.RaiseModified(); } } //===================================================================== #endregion #region ControlPoints //===================================================================== private RectangleF[] m_ControlPoints = new RectangleF[0]; /// /// Get the control points. This property accessor builds the list /// from private member information each time it is called. /// [System.ComponentModel.Browsable(false)] [System.Xml.Serialization.XmlIgnore] public virtual RectangleF[] ControlPoints { get { return new RectangleF[0]; } } //===================================================================== #endregion //===================================================================== #endregion #region Public Methods //===================================================================== public override string ToString() { return "Transfer function part"; } //===================================================================== #endregion #region Paint Methods //===================================================================== /// /// Paint the transfer function part to the given graphics handle, /// using the given mode (Screen or Function). /// /// protected override void OnPaint(siTransferFunction parent, PaintEventArgs e) { const bool IGNORE_CASE = true; // Cast the paint event args siTransferFunctionPaintEventArgs etf = e as siTransferFunctionPaintEventArgs; if (e == null) return; // Paint everything for screen mode switch (etf.PaintMode) { case siTransferFunctionPaintMode.Screen: // Fall through case siTransferFunctionPaintMode.Publish: // Always paint this.OnPaintThisLayer(parent, etf); break; case siTransferFunctionPaintMode.Function: // Only paint the given layer if (this.m_Layer == null || this.m_Layer.Length == 0 || String.Compare(this.m_Layer, etf.Layer, IGNORE_CASE) == 0) this.OnPaintThisLayer(parent, etf); break; } } /// /// Paint the transfer function part to the given graphics handle, /// using the given mode (Screen or Function). The Layer should /// have already been checked by the OnPaint(PaintEventArgs) method. /// /// protected virtual void OnPaintThisLayer(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Don't draw disabled parts to the function if (e.PaintMode == siTransferFunctionPaintMode.Function && this.Enabled == false) return; // Don't draw invisible parts to the screen if (e.PaintMode == siTransferFunctionPaintMode.Screen && this.Visible == false) return; // Don't draw disable parts for the publication if (e.PaintMode == siTransferFunctionPaintMode.Publish && this.Enabled == false) return; // Paint the fill function this.OnPaintFillFunction(parent, e); // Paint information and other stuff switch (e.PaintMode) { case siTransferFunctionPaintMode.Function: // Don't paint any information break; case siTransferFunctionPaintMode.Screen: this.OnPaintBoundingBox(parent, e); this.OnPaintControlPoints(parent, e); this.OnPaintInformation(parent, e); break; case siTransferFunctionPaintMode.Publish: this.OnPaintBoundingBox(parent, e); this.OnPaintInformation(parent, e); break; } } protected virtual void OnPaintFillFunction(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Subclasses must override } protected virtual void OnPaintControlPoints(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Subclasses must override } protected virtual void OnPaintBoundingBox(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { // Subclasses must override } /// /// Get the information to draw. /// /// True and the full information is return, else a short string is returned. /// /// protected String ConstructInformationString(bool full, siTransferFunctionPaintEventArgs e) { String layer = (this.Layer == null || this.Layer.Length == 0) ? "No Layer" : this.Layer; String result = layer; if (e.PaintMode == siTransferFunctionPaintMode.Screen && full) result += (this.Enabled ? " [Enabled]" : " [Disabled]"); return result; } protected virtual void OnPaintInformation(siTransferFunction parent, siTransferFunctionPaintEventArgs e) { PointF pointInfo = new PointF(this.BoundingBox.X + INFO_OFFSET, this.BoundingBox.Y + INFO_OFFSET); Brush brush = new SolidBrush(Color.Black); //Brush brushShadow = new SolidBrush(Color.White); Font font = new Font("Arial", 8.0F, FontStyle.Bold); String info = this.ConstructInformationString(true, e); SizeF sizeInfo = e.Graphics.MeasureString(info, font); // Only draw the info if the height will not cut off the info string if (sizeInfo.Height <= (this.BoundingBox.Height - 2 * INFO_OFFSET) && sizeInfo.Width <= (this.BoundingBox.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); // Only draw the info if the height will not cut off the info string if (sizeInfo.Height <= (this.BoundingBox.Height - 2 * INFO_OFFSET) && sizeInfo.Width <= (this.BoundingBox.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; // Allow the base class to handle if (base.OnMouseDown(parent, e)) return true; // Check if the mouse is over a control point if ((bool)this.Metadata["IsMouseOverControlPoint"]) { this.Metadata["IsResizing"] = true; this.Metadata["IsTranslating"] = false; return true; } // Check if the event occured within the bounds of this part if (!this.BoundingBox.Contains(e.Location)) return false; // 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 if ((bool)this.Metadata["IsMouseOverPart"]) { this.Metadata["IsResizing"] = false; this.Metadata["IsTranslating"] = true; return true; } // The event was not consumed this.Metadata["IsResizing"] = 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; // Allow the base class to handle if (base.OnMouseUp(parent, e)) return true; // Cancel a translate if ((bool)this.Metadata["IsTranslating"]) { this.Metadata["IsTranslating"] = false; return true; } // Cancel a resize if ((bool)this.Metadata["IsResizing"]) { this.Metadata["IsResizing"] = false; return true; } // The event was not consumed this.Metadata["IsResizing"] = 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; // Allow the base class to handle //if (base.OnMouseMove(parent, e)) // return true; // Save the last mouse move MouseEventArgs lastMouseMove = this.Metadata["LastMouseMove"] as MouseEventArgs; this.Metadata["LastMouseMove"] = e; // Consume a translate event if ((bool)this.Metadata["IsTranslating"]) { PointF translate = new PointF(); translate.X = (float)(e.X - lastMouseMove.X); translate.Y = (float)(e.Y - lastMouseMove.Y); this.Translate(parent, translate); return true; } // Consume a resize event if ((bool)this.Metadata["IsResizing"]) { PointF resize = new PointF(); resize.X = (float)(e.X - lastMouseMove.X); resize.Y = (float)(e.Y - lastMouseMove.Y); this.Resize(parent, resize); return true; } // Set the mouse control point over Metadata int cpIndex = 0; foreach (RectangleF cp in this.ControlPoints) { if (cp.Contains(e.Location)) { this.Metadata["IsMouseOverControlPoint"] = true; this.Metadata["IsMouseOverPart"] = false; this.Metadata["CurrentControlPointIndex"] = cpIndex; parent.Metadata["Cursor"] = this.GetControlPointCursor(cpIndex); this.RaiseModified(); return true; } cpIndex++; } // Set the mouse over Metadata if (this.BoundingBox.Contains(e.Location)) { parent.Metadata["Cursor"] = Cursors.Hand; this.Metadata["IsMouseOverControlPoint"] = false; this.Metadata["IsMouseOverPart"] = true; this.Metadata["CurrentControlPointIndex"] = -1; this.RaiseModified(); 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 virtual Cursor GetControlPointCursor(int cpIndex) { return Cursors.Default; } //===================================================================== #endregion #region Translate and Resize Methods //===================================================================== /// /// Translates the part by the given amount. /// /// public virtual void Translate(siTransferFunction parent, PointF translate) { // Do nothing } /// /// Resize the part by the given amount. /// /// public virtual void Resize(siTransferFunction parent, PointF change) { // Do nothing } //===================================================================== #endregion } }