|
|
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}";
|
|
|
|
|
|
}
|
|
|
} |