595 lines
20 KiB
C#
595 lines
20 KiB
C#
|
|
using MainShell.Common;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Input;
|
|||
|
|
using System.Windows.Media;
|
|||
|
|
using System.Windows.Threading;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
using System.Windows.Controls.Primitives;
|
|||
|
|
using System.ComponentModel;
|
|||
|
|
|
|||
|
|
namespace MainShell.Resources.CustomControl
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>оƬMapͼ<70>ؼ<EFBFBD><D8BC><EFBFBD>֧<EFBFBD><D6A7>5-10<31><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ķ<EFBFBD>̬״̬<D7B4><CCAC><EFBFBD>£<EFBFBD>֧<EFBFBD><D6A7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ק<EFBFBD><D7A7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
/// </summary>
|
|||
|
|
public class DieMapControl : FrameworkElement
|
|||
|
|
{
|
|||
|
|
private int _rows;
|
|||
|
|
private int _columns;
|
|||
|
|
private DieState[,] _dieStates;
|
|||
|
|
private Point[,] _dieCoordinates;
|
|||
|
|
private double _minX, _maxX, _minY, _maxY;
|
|||
|
|
private Dictionary<DieState, Brush> _stateBrushes;
|
|||
|
|
|
|||
|
|
// <20><>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>
|
|||
|
|
private DrawingVisual _visual;
|
|||
|
|
private VisualCollection _visuals;
|
|||
|
|
private DispatcherTimer _renderTimer;
|
|||
|
|
private bool _needsRedraw = false;
|
|||
|
|
|
|||
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
private MatrixTransform _transform = new MatrixTransform();
|
|||
|
|
private Point _lastMousePosition;
|
|||
|
|
private Point _mouseDownPosition;
|
|||
|
|
private bool _isDragging;
|
|||
|
|
private bool _isViewChangedByUser;
|
|||
|
|
private Popup _infoPopup;
|
|||
|
|
private TextBlock _infoText;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty BackgroundProperty =
|
|||
|
|
DependencyProperty.Register("Background", typeof(Brush), typeof(DieMapControl), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
|
|||
|
|
|
|||
|
|
public Brush Background
|
|||
|
|
{
|
|||
|
|
get { return (Brush)GetValue(BackgroundProperty); }
|
|||
|
|
set { SetValue(BackgroundProperty, value); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty MapModelProperty =
|
|||
|
|
DependencyProperty.Register("MapModel", typeof(DieMapModel), typeof(DieMapControl), new PropertyMetadata(null, OnMapModelChanged));
|
|||
|
|
|
|||
|
|
public DieMapModel MapModel
|
|||
|
|
{
|
|||
|
|
get { return (DieMapModel)GetValue(MapModelProperty); }
|
|||
|
|
set { SetValue(MapModelProperty, value); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty DieClickedCommandProperty =
|
|||
|
|
DependencyProperty.Register("DieClickedCommand", typeof(ICommand), typeof(DieMapControl), new PropertyMetadata(null));
|
|||
|
|
|
|||
|
|
public ICommand DieClickedCommand
|
|||
|
|
{
|
|||
|
|
get { return (ICommand)GetValue(DieClickedCommandProperty); }
|
|||
|
|
set { SetValue(DieClickedCommandProperty, value); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public event EventHandler<DieClickedEventArgs> DieClicked;
|
|||
|
|
|
|||
|
|
public class DieClickedEventArgs : EventArgs
|
|||
|
|
{
|
|||
|
|
public int Row { get; }
|
|||
|
|
public int Col { get; }
|
|||
|
|
public DieState State { get; }
|
|||
|
|
|
|||
|
|
public DieClickedEventArgs(int row, int col, DieState state)
|
|||
|
|
{
|
|||
|
|
Row = row;
|
|||
|
|
Col = col;
|
|||
|
|
State = state;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void OnMapModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|||
|
|
{
|
|||
|
|
var control = (DieMapControl)d;
|
|||
|
|
if (e.OldValue is DieMapModel oldModel)
|
|||
|
|
{
|
|||
|
|
oldModel.MapInitialized -= control.OnMapInitialized;
|
|||
|
|
oldModel.DieStateChanged -= control.OnDieStateChanged;
|
|||
|
|
oldModel.DieStatesChanged -= control.OnDieStatesChanged;
|
|||
|
|
}
|
|||
|
|
if (e.NewValue is DieMapModel newModel)
|
|||
|
|
{
|
|||
|
|
newModel.MapInitialized += control.OnMapInitialized;
|
|||
|
|
newModel.DieStateChanged += control.OnDieStateChanged;
|
|||
|
|
newModel.DieStatesChanged += control.OnDieStatesChanged;
|
|||
|
|
control.InitializeFromModel(newModel);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnMapInitialized(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
InitializeFromModel(MapModel);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnDieStateChanged(object sender, (int Row, int Col, DieState State) e)
|
|||
|
|
{
|
|||
|
|
_needsRedraw = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnDieStatesChanged(object sender, IEnumerable<(int Row, int Col, DieState State)> e)
|
|||
|
|
{
|
|||
|
|
_needsRedraw = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty DieSizeProperty =
|
|||
|
|
DependencyProperty.Register("DieSize", typeof(double), typeof(DieMapControl), new FrameworkPropertyMetadata(4.0, FrameworkPropertyMetadataOptions.AffectsRender, OnSizeChanged));
|
|||
|
|
|
|||
|
|
public double DieSize
|
|||
|
|
{
|
|||
|
|
get { return (double)GetValue(DieSizeProperty); }
|
|||
|
|
set { SetValue(DieSizeProperty, value); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty SpacingProperty =
|
|||
|
|
DependencyProperty.Register("Spacing", typeof(double), typeof(DieMapControl), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender, OnSizeChanged));
|
|||
|
|
|
|||
|
|
public double Spacing
|
|||
|
|
{
|
|||
|
|
get { return (double)GetValue(SpacingProperty); }
|
|||
|
|
set { SetValue(SpacingProperty, value); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void OnSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|||
|
|
{
|
|||
|
|
var control = (DieMapControl)d;
|
|||
|
|
control.RedrawAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public DieMapControl()
|
|||
|
|
{
|
|||
|
|
_visuals = new VisualCollection(this);
|
|||
|
|
_visual = new DrawingVisual();
|
|||
|
|
_visuals.Add(_visual);
|
|||
|
|
|
|||
|
|
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>ˢ<EFBFBD>ֵ䣬<D6B5><E4A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
_stateBrushes = new Dictionary<DieState, Brush>
|
|||
|
|
{
|
|||
|
|
{ DieState.Available, CreateFrozenBrush(Color.FromRgb(211, 211, 211)) }, // dz<><C7B3>ɫ
|
|||
|
|
{ DieState.Used, CreateFrozenBrush(Color.FromRgb(50, 205, 50)) }, // <20><>ɫ
|
|||
|
|
{ DieState.Error, CreateFrozenBrush(Color.FromRgb(255, 0, 0)) }, // <20><>ɫ
|
|||
|
|
{ DieState.NotExist, Brushes.Transparent }, // <><CDB8>
|
|||
|
|
{ DieState.Current, CreateFrozenBrush(Color.FromRgb(0, 191, 255)) }, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - <20><>ǰλ<C7B0><CEBB>
|
|||
|
|
{ DieState.Target, CreateFrozenBrush(Color.FromRgb(255, 165, 0)) } // <20><>ɫ - Ŀ<><C4BF>λ<EFBFBD><CEBB>
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// <20><>ʼ<EFBFBD><CABC>Popup
|
|||
|
|
_infoPopup = new Popup
|
|||
|
|
{
|
|||
|
|
Placement = PlacementMode.Mouse,
|
|||
|
|
StaysOpen = false,
|
|||
|
|
AllowsTransparency = true
|
|||
|
|
};
|
|||
|
|
Border popupBorder = new Border
|
|||
|
|
{
|
|||
|
|
Background = new SolidColorBrush(Color.FromArgb(220, 30, 30, 30)),
|
|||
|
|
BorderBrush = Brushes.Gray,
|
|||
|
|
BorderThickness = new Thickness(1),
|
|||
|
|
CornerRadius = new CornerRadius(4),
|
|||
|
|
Padding = new Thickness(8)
|
|||
|
|
};
|
|||
|
|
_infoText = new TextBlock
|
|||
|
|
{
|
|||
|
|
Foreground = Brushes.White,
|
|||
|
|
FontSize = 12
|
|||
|
|
};
|
|||
|
|
popupBorder.Child = _infoText;
|
|||
|
|
_infoPopup.Child = popupBorder;
|
|||
|
|
|
|||
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD>
|
|||
|
|
this.MouseWheel += OnMouseWheel;
|
|||
|
|
this.MouseDown += OnMouseDown;
|
|||
|
|
this.MouseUp += OnMouseUp;
|
|||
|
|
this.MouseMove += OnMouseMove;
|
|||
|
|
this.MouseLeave += OnMouseLeave;
|
|||
|
|
|
|||
|
|
// <20>ü<EFBFBD><C3BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߽<EFBFBD><DFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
this.ClipToBounds = true;
|
|||
|
|
|
|||
|
|
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>1<EFBFBD><31>3<EFBFBD><33> (Լ333ms)
|
|||
|
|
_renderTimer = new DispatcherTimer(DispatcherPriority.Render);
|
|||
|
|
_renderTimer.Interval = TimeSpan.FromMilliseconds(333);
|
|||
|
|
_renderTimer.Tick += RenderTimer_Tick;
|
|||
|
|
_renderTimer.Start();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void RenderTimer_Tick(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (_needsRedraw)
|
|||
|
|
{
|
|||
|
|
RedrawAll();
|
|||
|
|
_needsRedraw = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private SolidColorBrush CreateFrozenBrush(Color color)
|
|||
|
|
{
|
|||
|
|
var brush = new SolidColorBrush(color);
|
|||
|
|
brush.Freeze();
|
|||
|
|
return brush;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override int VisualChildrenCount => _visuals.Count;
|
|||
|
|
|
|||
|
|
protected override Visual GetVisualChild(int index)
|
|||
|
|
{
|
|||
|
|
if (index < 0 || index >= _visuals.Count)
|
|||
|
|
throw new ArgumentOutOfRangeException();
|
|||
|
|
return _visuals[index];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void OnRender(DrawingContext drawingContext)
|
|||
|
|
{
|
|||
|
|
base.OnRender(drawingContext);
|
|||
|
|
if (Background != null)
|
|||
|
|
{
|
|||
|
|
drawingContext.DrawRectangle(Background, null, new Rect(0, 0, ActualWidth, ActualHeight));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
|
|||
|
|
{
|
|||
|
|
base.OnRenderSizeChanged(sizeInfo);
|
|||
|
|
if (!_isViewChangedByUser && _rows > 0)
|
|||
|
|
{
|
|||
|
|
FitToScreen();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void InitializeFromModel(DieMapModel model)
|
|||
|
|
{
|
|||
|
|
if (model == null) return;
|
|||
|
|
|
|||
|
|
_rows = model.Rows;
|
|||
|
|
_columns = model.Columns;
|
|||
|
|
_dieStates = model.States;
|
|||
|
|
_dieCoordinates = model.Coordinates;
|
|||
|
|
|
|||
|
|
if (_dieCoordinates != null)
|
|||
|
|
{
|
|||
|
|
CalculateBounds();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// <20><><EFBFBD>ñ任
|
|||
|
|
_transform.Matrix = Matrix.Identity;
|
|||
|
|
|
|||
|
|
// <20>ӳ<EFBFBD>ִ<EFBFBD><D6B4>FitToScreen<65><6E>ȷ<EFBFBD><C8B7><EFBFBD>ؼ<EFBFBD><D8BC>Ѿ<EFBFBD><D1BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
Dispatcher.BeginInvoke(new Action(() =>
|
|||
|
|
{
|
|||
|
|
if (ActualWidth > 0 && ActualHeight > 0)
|
|||
|
|
{
|
|||
|
|
FitToScreen();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
RedrawAll();
|
|||
|
|
}
|
|||
|
|
}), DispatcherPriority.Loaded);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void CalculateBounds()
|
|||
|
|
{
|
|||
|
|
_minX = double.MaxValue;
|
|||
|
|
_maxX = double.MinValue;
|
|||
|
|
_minY = double.MaxValue;
|
|||
|
|
_maxY = double.MinValue;
|
|||
|
|
|
|||
|
|
for (int r = 0; r < _rows; r++)
|
|||
|
|
{
|
|||
|
|
for (int c = 0; c < _columns; c++)
|
|||
|
|
{
|
|||
|
|
var pt = _dieCoordinates[r, c];
|
|||
|
|
if (pt.X < _minX) _minX = pt.X;
|
|||
|
|
if (pt.X > _maxX) _maxX = pt.X;
|
|||
|
|
if (pt.Y < _minY) _minY = pt.Y;
|
|||
|
|
if (pt.Y > _maxY) _maxY = pt.Y;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void RedrawAll()
|
|||
|
|
{
|
|||
|
|
if (_rows == 0 || _columns == 0 || _dieStates == null) return;
|
|||
|
|
|
|||
|
|
using (DrawingContext dc = _visual.RenderOpen())
|
|||
|
|
{
|
|||
|
|
// Ӧ<>õ<EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD>ź<EFBFBD>ƽ<EFBFBD>Ʊ任
|
|||
|
|
dc.PushTransform(_transform);
|
|||
|
|
|
|||
|
|
double size = DieSize;
|
|||
|
|
double spacing = Spacing;
|
|||
|
|
|
|||
|
|
double physicalWidth = _maxX - _minX;
|
|||
|
|
double physicalHeight = _maxY - _minY;
|
|||
|
|
|
|||
|
|
if (physicalWidth == 0) physicalWidth = 1;
|
|||
|
|
if (physicalHeight == 0) physicalHeight = 1;
|
|||
|
|
|
|||
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><D0A1>δ<EFBFBD><CEB4><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>
|
|||
|
|
double baseWidth = _columns * (size + spacing);
|
|||
|
|
double baseHeight = _rows * (size + spacing);
|
|||
|
|
|
|||
|
|
for (int r = 0; r < _rows; r++)
|
|||
|
|
{
|
|||
|
|
for (int c = 0; c < _columns; c++)
|
|||
|
|
{
|
|||
|
|
DieState state = _dieStates[r, c];
|
|||
|
|
if (state == DieState.NotExist) continue;
|
|||
|
|
|
|||
|
|
Brush brush = _stateBrushes.ContainsKey(state) ? _stateBrushes[state] : Brushes.Transparent;
|
|||
|
|
if (brush == Brushes.Transparent) continue;
|
|||
|
|
|
|||
|
|
double x, y;
|
|||
|
|
|
|||
|
|
if (_dieCoordinates != null)
|
|||
|
|
{
|
|||
|
|
var pt = _dieCoordinates[r, c];
|
|||
|
|
x = ((pt.X - _minX) / physicalWidth) * baseWidth;
|
|||
|
|
y = (1.0 - (pt.Y - _minY) / physicalHeight) * baseHeight;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
x = c * (size + spacing);
|
|||
|
|
y = r * (size + spacing);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dc.DrawRectangle(brush, null, new Rect(x, y, size, size));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dc.Pop();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_needsRedraw = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region <EFBFBD><EFBFBD><EFBFBD>꽻<EFBFBD><EFBFBD> (<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD>)
|
|||
|
|
|
|||
|
|
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
|
|||
|
|
{
|
|||
|
|
Point mousePos = e.GetPosition(this);
|
|||
|
|
double scaleFactor = e.Delta > 0 ? 1.1 : 1 / 1.1;
|
|||
|
|
|
|||
|
|
Matrix matrix = _transform.Matrix;
|
|||
|
|
matrix.ScaleAt(scaleFactor, scaleFactor, mousePos.X, mousePos.Y);
|
|||
|
|
_transform.Matrix = matrix;
|
|||
|
|
_isViewChangedByUser = true;
|
|||
|
|
|
|||
|
|
RedrawAll();
|
|||
|
|
e.Handled = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnMouseDown(object sender, MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (e.ChangedButton == MouseButton.Middle)
|
|||
|
|
{
|
|||
|
|
FitToScreen();
|
|||
|
|
}
|
|||
|
|
else if (e.ChangedButton == MouseButton.Left)
|
|||
|
|
{
|
|||
|
|
_mouseDownPosition = e.GetPosition(this);
|
|||
|
|
_lastMousePosition = _mouseDownPosition;
|
|||
|
|
_isDragging = true;
|
|||
|
|
this.CaptureMouse();
|
|||
|
|
_infoPopup.IsOpen = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnMouseUp(object sender, MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (e.ChangedButton == MouseButton.Left)
|
|||
|
|
{
|
|||
|
|
_isDragging = false;
|
|||
|
|
this.ReleaseMouseCapture();
|
|||
|
|
|
|||
|
|
Point currentPosition = e.GetPosition(this);
|
|||
|
|
if ((currentPosition - _mouseDownPosition).Length < 5)
|
|||
|
|
{
|
|||
|
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
|||
|
|
{
|
|||
|
|
ShowDieInfo(currentPosition);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
HandleDieClick(currentPosition);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void HandleDieClick(Point mousePos)
|
|||
|
|
{
|
|||
|
|
if (_rows == 0 || _columns == 0 || _dieStates == null) return;
|
|||
|
|
|
|||
|
|
if (!_transform.Matrix.HasInverse) return;
|
|||
|
|
Matrix inverse = _transform.Matrix;
|
|||
|
|
inverse.Invert();
|
|||
|
|
Point canvasPos = inverse.Transform(mousePos);
|
|||
|
|
|
|||
|
|
double size = DieSize;
|
|||
|
|
double spacing = Spacing;
|
|||
|
|
double physicalWidth = _maxX - _minX;
|
|||
|
|
double physicalHeight = _maxY - _minY;
|
|||
|
|
if (physicalWidth == 0) physicalWidth = 1;
|
|||
|
|
if (physicalHeight == 0) physicalHeight = 1;
|
|||
|
|
|
|||
|
|
double baseWidth = _columns * (size + spacing);
|
|||
|
|
double baseHeight = _rows * (size + spacing);
|
|||
|
|
|
|||
|
|
int clickedRow = -1;
|
|||
|
|
int clickedCol = -1;
|
|||
|
|
|
|||
|
|
for (int r = 0; r < _rows; r++)
|
|||
|
|
{
|
|||
|
|
for (int c = 0; c < _columns; c++)
|
|||
|
|
{
|
|||
|
|
DieState state = _dieStates[r, c];
|
|||
|
|
if (state == DieState.NotExist) continue;
|
|||
|
|
|
|||
|
|
double x, y;
|
|||
|
|
if (_dieCoordinates != null)
|
|||
|
|
{
|
|||
|
|
var pt = _dieCoordinates[r, c];
|
|||
|
|
x = ((pt.X - _minX) / physicalWidth) * baseWidth;
|
|||
|
|
y = (1.0 - (pt.Y - _minY) / physicalHeight) * baseHeight;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
x = c * (size + spacing);
|
|||
|
|
y = r * (size + spacing);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (canvasPos.X >= x && canvasPos.X <= x + size &&
|
|||
|
|
canvasPos.Y >= y && canvasPos.Y <= y + size)
|
|||
|
|
{
|
|||
|
|
clickedRow = r;
|
|||
|
|
clickedCol = c;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (clickedRow != -1) break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (clickedRow != -1)
|
|||
|
|
{
|
|||
|
|
var dieInfo = (Row: clickedRow, Col: clickedCol, State: _dieStates[clickedRow, clickedCol]);
|
|||
|
|
|
|||
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD>
|
|||
|
|
DieClicked?.Invoke(this, new DieClickedEventArgs(clickedRow, clickedCol, _dieStates[clickedRow, clickedCol]));
|
|||
|
|
|
|||
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
if (DieClickedCommand != null && DieClickedCommand.CanExecute(dieInfo))
|
|||
|
|
{
|
|||
|
|
DieClickedCommand.Execute(dieInfo);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnMouseMove(object sender, MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (_isDragging)
|
|||
|
|
{
|
|||
|
|
Point currentPosition = e.GetPosition(this);
|
|||
|
|
Vector delta = currentPosition - _lastMousePosition;
|
|||
|
|
|
|||
|
|
Matrix matrix = _transform.Matrix;
|
|||
|
|
matrix.Translate(delta.X, delta.Y);
|
|||
|
|
_transform.Matrix = matrix;
|
|||
|
|
_isViewChangedByUser = true;
|
|||
|
|
|
|||
|
|
_lastMousePosition = currentPosition;
|
|||
|
|
RedrawAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnMouseLeave(object sender, MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (_isDragging)
|
|||
|
|
{
|
|||
|
|
_isDragging = false;
|
|||
|
|
this.ReleaseMouseCapture();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void FitToScreen()
|
|||
|
|
{
|
|||
|
|
if (_rows == 0 || _columns == 0) return;
|
|||
|
|
|
|||
|
|
double size = DieSize;
|
|||
|
|
double spacing = Spacing;
|
|||
|
|
double baseWidth = _columns * (size + spacing);
|
|||
|
|
double baseHeight = _rows * (size + spacing);
|
|||
|
|
|
|||
|
|
if (baseWidth == 0 || baseHeight == 0 || ActualWidth == 0 || ActualHeight == 0) return;
|
|||
|
|
|
|||
|
|
double scaleX = ActualWidth / baseWidth;
|
|||
|
|
double scaleY = ActualHeight / baseHeight;
|
|||
|
|
double scale = Math.Min(scaleX, scaleY) * 0.9; // 90% to leave some margin
|
|||
|
|
|
|||
|
|
if (scale <= 0) scale = 1;
|
|||
|
|
|
|||
|
|
_transform.Matrix = new Matrix(scale, 0, 0, scale,
|
|||
|
|
(ActualWidth - baseWidth * scale) / 2,
|
|||
|
|
(ActualHeight - baseHeight * scale) / 2);
|
|||
|
|
_isViewChangedByUser = false;
|
|||
|
|
|
|||
|
|
RedrawAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ShowDieInfo(Point mousePos)
|
|||
|
|
{
|
|||
|
|
if (_rows == 0 || _columns == 0 || _dieStates == null) return;
|
|||
|
|
|
|||
|
|
if (!_transform.Matrix.HasInverse) return;
|
|||
|
|
Matrix inverse = _transform.Matrix;
|
|||
|
|
inverse.Invert();
|
|||
|
|
Point canvasPos = inverse.Transform(mousePos);
|
|||
|
|
|
|||
|
|
double size = DieSize;
|
|||
|
|
double spacing = Spacing;
|
|||
|
|
double physicalWidth = _maxX - _minX;
|
|||
|
|
double physicalHeight = _maxY - _minY;
|
|||
|
|
if (physicalWidth == 0) physicalWidth = 1;
|
|||
|
|
if (physicalHeight == 0) physicalHeight = 1;
|
|||
|
|
|
|||
|
|
double baseWidth = _columns * (size + spacing);
|
|||
|
|
double baseHeight = _rows * (size + spacing);
|
|||
|
|
|
|||
|
|
int clickedRow = -1;
|
|||
|
|
int clickedCol = -1;
|
|||
|
|
|
|||
|
|
for (int r = 0; r < _rows; r++)
|
|||
|
|
{
|
|||
|
|
for (int c = 0; c < _columns; c++)
|
|||
|
|
{
|
|||
|
|
DieState state = _dieStates[r, c];
|
|||
|
|
if (state == DieState.NotExist) continue;
|
|||
|
|
|
|||
|
|
double x, y;
|
|||
|
|
if (_dieCoordinates != null)
|
|||
|
|
{
|
|||
|
|
var pt = _dieCoordinates[r, c];
|
|||
|
|
x = ((pt.X - _minX) / physicalWidth) * baseWidth;
|
|||
|
|
y = (1.0 - (pt.Y - _minY) / physicalHeight) * baseHeight;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
x = c * (size + spacing);
|
|||
|
|
y = r * (size + spacing);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (canvasPos.X >= x && canvasPos.X <= x + size &&
|
|||
|
|
canvasPos.Y >= y && canvasPos.Y <= y + size)
|
|||
|
|
{
|
|||
|
|
clickedRow = r;
|
|||
|
|
clickedCol = c;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (clickedRow != -1) break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (clickedRow != -1)
|
|||
|
|
{
|
|||
|
|
DieState state = _dieStates[clickedRow, clickedCol];
|
|||
|
|
string info = $"<22><> (Row): {clickedRow}\n<><6E> (Col): {clickedCol}\n״̬: {MainShell.Converter.EnumHelper.GetEnumDescription(state)}";
|
|||
|
|
if (_dieCoordinates != null)
|
|||
|
|
{
|
|||
|
|
var pt = _dieCoordinates[clickedRow, clickedCol];
|
|||
|
|
info += $"\nX: {pt.X:F3}\nY: {pt.Y:F3}";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_infoText.Text = info;
|
|||
|
|
_infoPopup.IsOpen = true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
_infoPopup.IsOpen = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
}
|