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.

250 lines
8.9 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
namespace KepGridEditor
{
public partial class FormTrend : XtraForm
{
public event EventHandler<TrendChangedEventArgs> TrendDataChanged;
private BindingList<TrendSource> _trends = new BindingList<TrendSource>();
private bool _isInternalUpdating = false;
public List<TrendSource> TrendParams => _trends.ToList();
public int FusionMode => cbMode.SelectedIndex;
private int _nameCounter = 1; // 初始为1
public FormTrend()
{
InitializeComponent();
SetupUIConfig();
InitData();
BindEvents();
gaugeKnob.Paint += GaugeKnob_Paint;
gaugeKnob.MouseDown += GaugeKnob_MouseDown;
gaugeKnob.MouseMove += GaugeKnob_MouseMove;
// 鼠标松开时,通知主界面最终结果
gaugeKnob.MouseUp += (s, e) => NotifyDataChanged(TrendOperateType.Update);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
// 窗体完全显示后,手动触发一次 Update 通知
if (_trends.Count > 0)
{
NotifyDataChanged(TrendOperateType.Update);
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
NotifyDataChanged(TrendOperateType.Close);
}
private void SetupUIConfig()
{
cbAlgo.Properties.TextEditStyle = TextEditStyles.DisableTextEditor;
cbMode.Properties.TextEditStyle = TextEditStyles.DisableTextEditor;
cbAlgo.Properties.Items.Add("最小曲率法 (Thin Plate Spline)");
cbAlgo.SelectedIndex = 0;
cbMode.Properties.Items.AddRange(new[] { "加权融合", "极值融合" });
cbMode.SelectedIndex = 0;
arcScale.MinValue = 0;
arcScale.MaxValue = 360;
arcScale.StartAngle = -90;
arcScale.EndAngle = 270;
arcScale.MajorTickCount = 13;
arcScale.AppearanceTickmarkText.TextBrush = new DevExpress.XtraGauges.Core.Drawing.SolidBrushObject(Color.Black);
}
private void InitData()
{
listTrends.DataSource = _trends;
listTrends.DisplayMember = "DisplayText";
_trends.Add(new TrendSource { Name = $"趋势 {_nameCounter++}", Angle = 45, Ratio = 2.0 });
listTrends.SelectedIndex = 0;
}
private void BindEvents()
{
btnAdd.Click += (s, e) => {
var selected = listTrends.SelectedItem as TrendSource;
// 使用 _nameCounter++ 确保每次生成的数字都是唯一的,即使中间有删除
var newItem = new TrendSource
{
Name = $"趋势 {_nameCounter++}",
Angle = 0,
Ratio = 1.0,
Cx = selected?.Cx ?? 0,
Cy = selected?.Cy ?? 0
};
_trends.Add(newItem);
listTrends.SelectedIndex = _trends.Count - 1;
NotifyDataChanged(TrendOperateType.Update);
};
btnDel.Click += (s, e) =>
{
if (_trends.Count > 1)
{
var cur = listTrends.SelectedItem as TrendSource;
string nameToDelete = cur.Name; // 记录要删除的名字
_trends.RemoveAt(listTrends.SelectedIndex);
gaugeKnob.Invalidate();
NotifyDataChanged(TrendOperateType.Delete, nameToDelete); // 通知项删除
}
};
cbMode.SelectedIndexChanged += (s, e) => NotifyDataChanged(TrendOperateType.Update);
listTrends.SelectedIndexChanged += (s, e) => SyncUIFromModel();
trackRatio.ValueChanged += (s, e) =>
{
if (_isInternalUpdating || listTrends.SelectedItem == null) return;
var cur = listTrends.SelectedItem as TrendSource;
cur.Ratio = trackRatio.Value / 10.0;
lblRatioNum.Text = cur.Ratio.ToString("F1");
listTrends.Refresh();
};
// 滑块松开时通知
trackRatio.MouseUp += (s, e) => NotifyDataChanged(TrendOperateType.Update);
}
private void GaugeKnob_Paint(object sender, PaintEventArgs e)
{
if (listTrends.SelectedItem is TrendSource cur)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Point center = new Point(gaugeKnob.Width / 2, gaugeKnob.Height / 2);
double angleRad = (cur.Angle - 90) * Math.PI / 180.0;
int radius = (Math.Min(gaugeKnob.Width, gaugeKnob.Height) / 2) - 20;
Point endPoint = new Point((int)(center.X + radius * Math.Cos(angleRad)), (int)(center.Y + radius * Math.Sin(angleRad)));
using (Pen p = new Pen(Color.Blue, 6)) { p.EndCap = LineCap.ArrowAnchor; e.Graphics.DrawLine(p, center, endPoint); }
e.Graphics.FillEllipse(Brushes.DarkGray, center.X - 8, center.Y - 8, 16, 16);
}
}
private void GaugeKnob_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) UpdateAngle(e.Location); }
private void GaugeKnob_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) UpdateAngle(e.Location); }
private void UpdateAngle(Point pos)
{
if (listTrends.SelectedItem == null) return;
Point center = new Point(gaugeKnob.Width / 2, gaugeKnob.Height / 2);
double angleRad = Math.Atan2(pos.Y - center.Y, pos.X - center.X);
double angleDeg = angleRad * (180 / Math.PI) + 90;
if (angleDeg < 0) angleDeg += 360;
var cur = listTrends.SelectedItem as TrendSource;
cur.Angle = Math.Round(angleDeg);
lblAngleValue.Text = $"{cur.Angle}°";
listTrends.Refresh();
gaugeKnob.Invalidate();
}
private void SyncUIFromModel()
{
if (listTrends.SelectedItem is TrendSource cur)
{
_isInternalUpdating = true;
lblAngleValue.Text = $"{cur.Angle}°";
trackRatio.Value = (int)(cur.Ratio * 10);
lblRatioNum.Text = cur.Ratio.ToString("F1");
_isInternalUpdating = false;
gaugeKnob.Invalidate();
}
}
private void NotifyDataChanged(TrendOperateType type, string deletedName = "")
{
if (TrendDataChanged != null)
{
TrendDataChanged.Invoke(this, new TrendChangedEventArgs
{
ActiveTrend = listTrends.SelectedItem as TrendSource,
AllTrends = _trends.ToList(),
Mode = cbMode.SelectedIndex,
OperateType = type, // 告知主界面是更新、删除还是关闭
DeletedName = deletedName
});
}
}
private void updatePositionBtn_CheckedChanged(object sender, EventArgs e)
{
if (updatePositionBtn.Checked)
{
NotifyDataChanged(TrendOperateType.PickLocation);
}
}
public void ReleasePickButton()
{
if (updatePositionBtn.InvokeRequired)
{
updatePositionBtn.Invoke(new Action(() => updatePositionBtn.Checked = false));
}
else
{
updatePositionBtn.Checked = false;
}
NotifyDataChanged(TrendOperateType.Update);
}
}
public enum TrendOperateType
{
Update, // 更新(旋钮、滑块、添加)
Delete, // 删除
Close, // 界面关闭
PickLocation // 主界面进入拾取坐标模式
}
public class TrendChangedEventArgs : EventArgs
{
public TrendSource ActiveTrend { get; set; } // 当前被修改的项
public List<TrendSource> AllTrends { get; set; } // 全量列表
public int Mode { get; set; } // 融合模式
public TrendOperateType OperateType { get; set; } // 操作类型
public string DeletedName { get; set; } // 被删除项的名称方便OSG清理
}
public class TrendSource
{
public string Name { get; set; }
public double Angle { get; set; }
public double Ratio { get; set; }
public double Weight { get; set; } = 1.0;
public int Cx { get; set; } = 0;
public int Cy { get; set; } = 0;
public string DisplayText => $"{Name} : {Angle,3}° | 强度: {Ratio:F1} | w: {Weight:F2}";
}
}