/*=============================================================================
Project: SharpImage
Module: siTransferFunctionPartOval.cs
Language: C#
Author: Dan Mueller
Date: $Date: 2008-01-11 14:55:13 +1000 (Fri, 11 Jan 2008) $
Revision: $Revision: 30 $
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 siTransferFunctionPartOval : siTransferFunctionPart
{
#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 siTransferFunctionPartOval() : 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 siTransferFunctionPartOval(siTransferFunction parent, string layer) : base(layer)
{
// Create a default rectangle
if (parent != null)
{
float x = (float)parent.Image.Size[0] / 4F;
float y = (float)parent.Image.Size[1] / 4F;
float w = (float)parent.Image.Size[0] / 2F;
float h = (float)parent.Image.Size[1] / 2F;
this.BoundingBox = new RectangleF(x, y, w, h);
}
}
//=====================================================================
#endregion
#region Properties
//=====================================================================
#region ControlPoints
//=====================================================================
///
/// 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 override RectangleF[] ControlPoints
{
get
{
RectangleF[] result = new RectangleF[8];
// Create corner control points
result[0] = new RectangleF(this.BoundingBox.X + this.BoundingBox.Width / 2.0F - CONTROL_POINT_WIDTH / 2.0F, this.BoundingBox.Y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
result[2] = new RectangleF(this.BoundingBox.X + this.BoundingBox.Width - CONTROL_POINT_WIDTH / 2.0F, this.BoundingBox.Y + this.BoundingBox.Height / 2.0F - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
result[4] = new RectangleF(this.BoundingBox.X + this.BoundingBox.Width / 2.0F - CONTROL_POINT_WIDTH / 2.0F, this.BoundingBox.Y + this.BoundingBox.Height - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
result[6] = new RectangleF(this.BoundingBox.X - CONTROL_POINT_WIDTH / 2.0F, this.BoundingBox.Y + this.BoundingBox.Height / 2.0F - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
// Create control points on ellipse
float x = (this.BoundingBox.Width / 2.0F) * (float)Math.Cos(Math.PI / 4.0);
float y = (this.BoundingBox.Height / 2.0F) * (float)Math.Sin(Math.PI / 4.0);
result[1] = new RectangleF((this.BoundingBox.X + this.BoundingBox.Width / 2.0F) + x - CONTROL_POINT_WIDTH / 2.0F, (this.BoundingBox.Y + this.BoundingBox.Height / 2.0F) - y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
result[3] = new RectangleF((this.BoundingBox.X + this.BoundingBox.Width / 2.0F) - x - CONTROL_POINT_WIDTH / 2.0F, (this.BoundingBox.Y + this.BoundingBox.Height / 2.0F) + y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
result[5] = new RectangleF((this.BoundingBox.X + this.BoundingBox.Width / 2.0F) + x - CONTROL_POINT_WIDTH / 2.0F, (this.BoundingBox.Y + this.BoundingBox.Height / 2.0F) + y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
result[7] = new RectangleF((this.BoundingBox.X + this.BoundingBox.Width / 2.0F) - x - CONTROL_POINT_WIDTH / 2.0F, (this.BoundingBox.Y + this.BoundingBox.Height / 2.0F) - y - CONTROL_POINT_WIDTH / 2.0F, CONTROL_POINT_WIDTH, CONTROL_POINT_HEIGHT);
// Return
return result;
}
}
//=====================================================================
#endregion
#region Falloff
//============================================================================
private Single m_Falloff = 0.2F;
///
/// Gets/sets the falloff location (clamped between 0.0 and 1.0).
/// 0.0 is at the edge, 1.0 is the center.
///
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Color")]
public Single Falloff
{
get { return this.m_Falloff; }
set
{
this.m_Falloff = value;
if (this.m_Falloff < 0.0F) this.m_Falloff = 0.0F;
if (this.m_Falloff > 1.0F) this.m_Falloff = 1.0F;
this.RaiseModified();
}
}
//============================================================================
#endregion
//=====================================================================
#endregion
#region Public Methods
//=====================================================================
public override string ToString()
{
return "Oval: " + this.Layer + " " + this.BackColor.ToString() + " " + this.BoundingBox.ToString();
}
//=====================================================================
#endregion
#region Paint Methods
//=====================================================================
protected override void OnPaintFillFunction(siTransferFunction parent, siTransferFunctionPaintEventArgs e)
{
//Brush brushFill = new SolidBrush(this.BackColor);
GraphicsPath path = new GraphicsPath();
path.AddArc(this.BoundingBox, 0.0F, 360.0F);
PathGradientBrush brushFill = new PathGradientBrush(path);
//brushFill.CenterPoint = new PointF(this.BoundingBoxX + this.BoundingBoxWidth / 2.0F, this.BoundingBoxY + this.BoundingBoxHeight / 2.0F);
brushFill.CenterColor = this.BackColor;
brushFill.SurroundColors = new Color[] { Color.Transparent };
Blend blend = new Blend(3);
blend.Positions = new float[] { 0.0F, this.Falloff, 1.0F };
blend.Factors = new float[] { 0.0F, 1.0F, 1.0F };
brushFill.Blend = blend;
e.Graphics.FillEllipse(brushFill, this.BoundingBox);
path.Dispose();
}
protected override void OnPaintControlPoints(siTransferFunction parent, siTransferFunctionPaintEventArgs e)
{
// 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.ControlPoints)
{
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)
{
Pen penOutline = null;
if (this.Enabled) penOutline = new Pen(BOUNDING_BOX_COLOR_OUTLINE_ENABLED, PEN_WIDTH);
else penOutline = new Pen(BOUNDING_BOX_COLOR_OUTLINE_DISABLED, PEN_WIDTH);
e.Graphics.DrawEllipse(penOutline, this.BoundingBox);
}
protected override void OnPaintInformation(siTransferFunction parent, siTransferFunctionPaintEventArgs e)
{
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);
PointF pointInfo = new PointF(this.BoundingBox.X + this.BoundingBox.Width / 2.0F - sizeInfo.Width / 2.0F, this.BoundingBox.Y + this.BoundingBox.Height / 2.0F - sizeInfo.Height / 2.0F);
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
// 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);
pointInfo = new PointF(this.BoundingBox.X + this.BoundingBox.Width / 2.0F - sizeInfo.Width / 2.0F, this.BoundingBox.Y + this.BoundingBox.Height / 2.0F - sizeInfo.Height / 2.0F);
// 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
//=====================================================================
///
/// Get the cursor for the given control point index.
///
///
///
protected override Cursor GetControlPointCursor(int cpIndex)
{
switch (cpIndex)
{
case 0: return Cursors.SizeNS;
case 1: return Cursors.SizeNESW;
case 2: return Cursors.SizeWE;
case 3: return Cursors.SizeNESW;
case 4: return Cursors.SizeNS;
case 5: return Cursors.SizeNWSE;
case 6: return Cursors.SizeWE;
case 7: return Cursors.SizeNWSE;
default: return Cursors.Default;
}
}
//=====================================================================
#endregion
#region Translate and Resize Methods
//=====================================================================
///
/// Translates the part by the given amount.
///
///
public override void Translate(siTransferFunction parent, PointF translate)
{
// Translate the rectangle
m_BoundingBox.X += translate.X;
m_BoundingBox.Y += translate.Y;
// Check the translation will keep rectangle inside the parent transfer function
if (m_BoundingBox.X + m_BoundingBox.Width >= parent.Image.Size[0])
m_BoundingBox.X = parent.Image.Size[0] - m_BoundingBox.Width - 1f;
if (m_BoundingBox.Y + m_BoundingBox.Height >= parent.Image.Size[1])
m_BoundingBox.Y = parent.Image.Size[1] - m_BoundingBox.Height - 1f;
// Check the index is inside the image
if (m_BoundingBox.X < 0F)
m_BoundingBox.X = 0F;
if (m_BoundingBox.Y < 0F)
m_BoundingBox.Y = 0F;
if (m_BoundingBox.X > parent.Image.Size[0])
m_BoundingBox.X = parent.Image.Size[0] - 1;
if (m_BoundingBox.Y > parent.Image.Size[1])
m_BoundingBox.Y = parent.Image.Size[1] - 1;
this.RaiseModified();
}
///
/// Resize the part by the given amount.
///
///
public override void Resize(siTransferFunction parent, PointF change)
{
switch ((int)this.Metadata["CurrentControlPointIndex"])
{
case 0:
m_BoundingBox.Y += change.Y;
m_BoundingBox.Height -= change.Y;
break;
case 1:
m_BoundingBox.Width += change.X;
m_BoundingBox.Y += change.Y;
m_BoundingBox.Height -= change.Y;
break;
case 2:
m_BoundingBox.Width += change.X;
break;
case 3:
m_BoundingBox.Height += change.Y;
m_BoundingBox.X += change.X;
m_BoundingBox.Width -= change.X;
break;
case 4:
m_BoundingBox.Height += change.Y;
break;
case 5:
m_BoundingBox.Width += change.X;
m_BoundingBox.Height += change.Y;
break;
case 6:
m_BoundingBox.X += change.X;
m_BoundingBox.Width -= change.X;
break;
case 7:
m_BoundingBox.X += change.X;
m_BoundingBox.Y += change.Y;
m_BoundingBox.Width -= change.X;
m_BoundingBox.Height -= change.Y;
break;
}
// Ensure the resize did not put rectangle outside of the image
if (m_BoundingBox.X + m_BoundingBox.Width >= parent.Image.Size[0])
m_BoundingBox.Width = parent.Image.Size[0] - m_BoundingBox.X;
if (m_BoundingBox.Y + m_BoundingBox.Height >= parent.Image.Size[1])
m_BoundingBox.Height = parent.Image.Size[1] - m_BoundingBox.Y;
if (m_BoundingBox.X < 0)
m_BoundingBox.X = 0;
if (m_BoundingBox.Y < 0)
m_BoundingBox.Y = 0;
if (m_BoundingBox.X > parent.Image.Size[0])
m_BoundingBox.X = parent.Image.Size[0] - 1;
if (m_BoundingBox.Y > parent.Image.Size[1])
m_BoundingBox.Y = parent.Image.Size[1] - 1;
if (m_BoundingBox.Width < 1)
m_BoundingBox.Width = 1;
if (m_BoundingBox.Height < 1)
m_BoundingBox.Height = 1;
if (m_BoundingBox.Width >= parent.Image.Size[0])
m_BoundingBox.Width = parent.Image.Size[0] - 1;
if (m_BoundingBox.Height >= parent.Image.Size[1])
m_BoundingBox.Height = parent.Image.Size[1] - 1;
this.RaiseModified();
}
//=====================================================================
#endregion
}
}