You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
12 KiB
C#
335 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using System.Drawing.Drawing2D;
|
|
using GeoSigma.SigmaDrawerUtil;
|
|
|
|
namespace GeoSigma.SigmaDrawerStyle
|
|
{
|
|
public delegate void ItemColorChangedEventHandler(GradientColorItem item);
|
|
public delegate void InsertNewColorItemEventHandler(double itemZ);
|
|
public delegate void ResetColorItemEventHandler(int itemIndex, GradientColorItem item);
|
|
public partial class GradientPanel : UserControl
|
|
{
|
|
public event ItemColorChangedEventHandler ItemColorChangedEvent;
|
|
public event InsertNewColorItemEventHandler InsertNewColorEvent;
|
|
public event ResetColorItemEventHandler ResetColorItemEvent;
|
|
public event ItemColorChangedEventHandler ItemColorEditFinishedEvent;
|
|
public List<GradientColorItem> Items { get; set; } = new List<GradientColorItem>();
|
|
private double zMin = 0;
|
|
private double zMax = 100;
|
|
private int handleHeight = 30;
|
|
private int paddingY = 2;
|
|
private int paddingX = 4;
|
|
|
|
private List<GradientHandler> handles { get; set; } = new List<GradientHandler>();
|
|
private int selectedHandleIndex = -1;
|
|
private SlowDownTimer _timer = new SlowDownTimer(20);
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GradientPanel"/> class.
|
|
/// </summary>
|
|
public GradientPanel()
|
|
{
|
|
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
|
|
InitializeComponent();
|
|
}
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
base.OnPaint(e);
|
|
|
|
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
|
|
|
|
Rectangle rectHandler = GetHandlerBound();
|
|
foreach (var handler in handles)
|
|
{
|
|
handler.Draw(e.Graphics, rectHandler);
|
|
}
|
|
}
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
|
{
|
|
base.OnPaintBackground(e);
|
|
// ControlPaint.DrawBorder(e.Graphics, Bounds, SystemColors.ControlText, ButtonBorderStyle.Solid);
|
|
PaintGradientValue(e.Graphics, GetGradientColorBounds(), Items);
|
|
}
|
|
|
|
public void DrawGradient(List<GradientColorItem> list)
|
|
{
|
|
Items = list;
|
|
if (Items.Count == 0) { return; }
|
|
zMin = Items.Min(r => r.Z);
|
|
zMax = Items.Max(r => r.Z);
|
|
handles = list.Select(item => new GradientHandler(item, zMin, zMax)).ToList();
|
|
|
|
this.Invalidate();
|
|
}
|
|
|
|
private Rectangle GetGradientColorBounds()
|
|
{
|
|
var bounds = this.ClientRectangle;
|
|
bounds.Inflate(-paddingX, (int)(-(paddingY + handleHeight) * 0.5));
|
|
bounds.Y = paddingY;
|
|
return bounds;
|
|
}
|
|
private Rectangle GetHandlerBound()
|
|
{
|
|
var bounds = this.ClientRectangle;
|
|
// bounds.Inflate(cx, (int)(-(padingY + handleHeight) * 0.5));
|
|
bounds.Inflate(-paddingX, 0);
|
|
bounds.Y = this.ClientRectangle.Height - handleHeight;
|
|
bounds.Height = handleHeight;
|
|
return bounds;
|
|
}
|
|
/// <summary>
|
|
/// 在指定画布的指定区域绘制渐变色
|
|
/// </summary>
|
|
/// <param name="g">画布</param>
|
|
/// <param name="bounds">区域</param>
|
|
/// <param name="list">颜色列表</param>
|
|
public static void PaintGradientValue(Graphics g, RectangleF bounds, List<GradientColorItem> list)
|
|
{
|
|
if (list == null || list.Count < 2)
|
|
{
|
|
return;
|
|
}
|
|
double zmin = list.Min(r => r.Z);
|
|
double zmax = list.Max(r => r.Z);
|
|
if (zmin == zmax)
|
|
{
|
|
return;
|
|
}
|
|
LinearGradientBrush br = new LinearGradientBrush(bounds, Color.Black, Color.Black, 0, false);
|
|
{
|
|
RectangleF rectFill = bounds;
|
|
rectFill.Inflate(-0.5f, 0);
|
|
ColorBlend cb = new ColorBlend();
|
|
cb.Colors = list.Select(model => Color.FromArgb(model.T, model.R, model.G, model.B)).ToArray();
|
|
List<float> pos = new List<float>();
|
|
cb.Positions = new float[cb.Colors.Length];
|
|
cb.Positions[0] = 0.0f;
|
|
cb.Positions[cb.Colors.Length - 1] = 1;
|
|
for (int i = 1; i < list.Count - 1; i++)
|
|
{
|
|
cb.Positions[i] = Convert.ToSingle((list[i].Z - zmin) / (zmax - zmin));
|
|
}
|
|
|
|
br.InterpolationColors = cb;
|
|
// br.RotateTransform(45);
|
|
g.FillRectangle(br, rectFill);
|
|
}
|
|
// 绘制突变
|
|
for (int i = 0; i < list.Count - 1; i++)
|
|
{
|
|
var item1 = list[i];
|
|
var item2 = list[i + 1];
|
|
|
|
var x1 = Convert.ToInt32(((item1.Z - zmin) / (zmax - zmin) * bounds.Width) + bounds.Left);
|
|
var x2 = Convert.ToInt32(((item2.Z - zmin) / (zmax - zmin) * bounds.Width) + bounds.Left);
|
|
|
|
var rect = RectangleF.FromLTRB(x1, bounds.Top, x2, bounds.Bottom);
|
|
|
|
if (rect.Width > 0)
|
|
{
|
|
if (item1.C == 0)
|
|
{
|
|
var brush = new SolidBrush(item1.ColorValue);
|
|
|
|
g.FillRectangle(brush, rect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 双击事件,插入控制点,或者修改颜色
|
|
/// </summary>
|
|
/// <param name="sender">当前控件</param>
|
|
/// <param name="e">事件参数</param>
|
|
private void GradientPanel_MouseDoubleClick(object sender, MouseEventArgs e)
|
|
{
|
|
var bounds = GetHandlerBound();
|
|
// 双击手柄时的处理
|
|
for (int i = 1; i < handles.Count - 1; i++)
|
|
{
|
|
GradientHandler handle = handles[i];
|
|
// 双击的是控制手柄时
|
|
if (handle.GetBounds(bounds).Contains(e.Location))
|
|
{
|
|
ColorDialog dlg = new ColorDialog();
|
|
dlg.Color = handle.Item.ColorValue;
|
|
if (dlg.ShowDialog() == DialogResult.OK)
|
|
{
|
|
handle.Item.ColorValue = dlg.Color;
|
|
ItemColorChangedEvent?.Invoke(handle.Item);
|
|
this.Invalidate();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
// 双击其它区域,插入新控制点
|
|
if (e.Location.X >= bounds.X && e.Location.X <= bounds.X + bounds.Width)
|
|
{
|
|
double dZInsert = (Convert.ToDouble(e.Location.X - bounds.Left) / bounds.Width * (zMax - zMin)) + zMin;
|
|
InsertNewColorEvent?.Invoke(dZInsert);
|
|
}
|
|
}
|
|
|
|
private void GradientPanel_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
var rectHandleArea = GetHandlerBound();
|
|
|
|
for (int i = 1; i < handles.Count - 1; i++)
|
|
{
|
|
GradientHandler handler = handles[i];
|
|
|
|
if (handler.GetBounds(rectHandleArea).Contains(e.Location))
|
|
{
|
|
selectedHandleIndex = i;
|
|
//selectedHandle = handler;
|
|
|
|
//_selected_z_begin = Items[i - 1].Z;
|
|
//_selected_z_end = Items[i + 1].Z;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GradientPanel_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
if (selectedHandleIndex > 0)
|
|
{ // 两端的控制点未处理
|
|
var bounds = GetHandlerBound();
|
|
GradientHandler handler = handles[selectedHandleIndex];
|
|
GradientHandler handlerLeft = handles[selectedHandleIndex - 1];
|
|
GradientHandler handlerRight = handles[selectedHandleIndex + 1];
|
|
|
|
double newZ = (Convert.ToDouble(e.X - bounds.Left) / bounds.Width * (zMax - zMin)) + zMin;
|
|
if (newZ > handlerLeft.Item.Z && newZ < handlerRight.Item.Z)
|
|
{
|
|
handler.Item.Z = newZ;
|
|
this.Invalidate();
|
|
int index = selectedHandleIndex;
|
|
_timer.Invoke(this, () => ResetColorItemEvent(index, handler.Item));
|
|
}
|
|
}
|
|
}
|
|
private void ResetColorItem(int itemIndex, GradientColorItem item)
|
|
{
|
|
ResetColorItemEvent?.Invoke(selectedHandleIndex, item);
|
|
}
|
|
|
|
private void GradientPanel_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
if (selectedHandleIndex > 0)
|
|
{
|
|
GradientHandler handle = handles[selectedHandleIndex];
|
|
ItemColorEditFinishedEvent?.Invoke(handle.Item);
|
|
}
|
|
selectedHandleIndex = -1;
|
|
}
|
|
}
|
|
|
|
#region GradientHandler
|
|
|
|
public class GradientHandler
|
|
{
|
|
#region Shape
|
|
|
|
private static GraphicsPath Shape;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GradientHandler"/> class.
|
|
/// </summary>
|
|
public GradientHandler()
|
|
{
|
|
if (Shape == null)
|
|
{
|
|
Shape = new GraphicsPath();
|
|
Shape.AddLines(new PointF[]
|
|
{
|
|
new PointF(0, 0),
|
|
new PointF(4f, 3f),
|
|
new PointF(4f, 13f),
|
|
new PointF(-4f, 13f),
|
|
new PointF(-4f, 3f),
|
|
new PointF(0, 0),
|
|
});
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public GradientColorItem Item { get; set; }
|
|
|
|
private SolidBrush _brush;
|
|
private Pen _pen = new Pen(Color.Black, 1f);
|
|
private double zMin;
|
|
private double zMax;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GradientHandler"/> class.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <param name="zMin"></param>
|
|
/// <param name="zMax"></param>
|
|
public GradientHandler(GradientColorItem item, double zMin, double zMax)
|
|
: this()
|
|
{
|
|
Item = item;
|
|
|
|
this.zMin = zMin;
|
|
this.zMax = zMax;
|
|
|
|
_brush = new SolidBrush(item.ColorValue);
|
|
}
|
|
|
|
internal RectangleF GetBounds(Rectangle r)
|
|
{
|
|
return GetPath(r).GetBounds();
|
|
}
|
|
|
|
private GraphicsPath GetPath(Rectangle r)
|
|
{
|
|
var x = ((Item.Z - zMin) / (zMax - zMin) * r.Width) + r.Left;
|
|
var y = r.Y;
|
|
|
|
var tmp = (GraphicsPath)Shape.Clone();
|
|
|
|
Matrix translateMatrix = new Matrix();
|
|
translateMatrix.Translate((float)x, y);
|
|
tmp.Transform(translateMatrix);
|
|
|
|
return tmp;
|
|
}
|
|
|
|
public void Draw(Graphics g, Rectangle r)
|
|
{
|
|
var path = GetPath(r);
|
|
_brush.Color = Item.ColorValue;
|
|
g.FillPath(_brush, path);
|
|
g.DrawPath(_pen, path);
|
|
}
|
|
|
|
public bool Update(Rectangle bounds, Point eLocation, double begin, double end)
|
|
{
|
|
double newZ = (Convert.ToDouble(eLocation.X - bounds.Left) / bounds.Width * (zMax - zMin)) + zMin;
|
|
|
|
if (newZ >= begin && newZ <= end)
|
|
{
|
|
Item.Z = newZ;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|