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#

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
}