506 lines
16 KiB
C#
506 lines
16 KiB
C#
using DrawFigureLibrary;
|
||
using DrawFigureLibrary.Figures;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Imaging;
|
||
|
||
|
||
namespace Main
|
||
{
|
||
public partial class MainWindow : Window
|
||
{
|
||
private RenderTargetBitmap? _renderTarget ;
|
||
|
||
private int _polyExpected ;
|
||
private List<Point> _polyPoints = new List<Point>() ;
|
||
|
||
private DrawFigureLibrary.Figure? _mouseSelected ;
|
||
private bool _isDragging ;
|
||
private Point _lastMouse ;
|
||
private int CanvasW => _renderTarget?.PixelWidth ?? 800;
|
||
private int CanvasH => _renderTarget?.PixelHeight ?? 400;
|
||
|
||
private void ClickAddRectangle(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(RectName, out string name)) return;
|
||
|
||
if (!TryReadInt(RectX.Text, "X", 0, CanvasW - 1, out int x)) return;
|
||
if (!TryReadInt(RectY.Text, "Y", 0, CanvasH - 1, out int y)) return;
|
||
if (!TryReadInt(RectW.Text, "W", 1, CanvasW, out int w)) return;
|
||
if (!TryReadInt(RectH.Text, "H", 1, CanvasH, out int h)) return;
|
||
|
||
if (x + w > CanvasW || y + h > CanvasH)
|
||
{
|
||
MessageBox.Show("Фигура не помещается в область рисования.");
|
||
return;
|
||
}
|
||
|
||
ShapeContainer.AddFigure(new RectangleFigure(name, x, y, w, h));
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickAddSquare(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(SquareName, out string name)) return;
|
||
|
||
if (!TryReadInt(SquareX.Text, "X", 0, CanvasW - 1, out int x)) return;
|
||
if (!TryReadInt(SquareY.Text, "Y", 0, CanvasH - 1, out int y)) return;
|
||
|
||
int maxSide = Math.Min(CanvasW, CanvasH);
|
||
if (!TryReadInt(SquareSide.Text, "Side", 1, maxSide, out int side)) return;
|
||
|
||
if (x + side > CanvasW || y + side > CanvasH)
|
||
{
|
||
MessageBox.Show("Фигура не помещается в область рисования.");
|
||
return;
|
||
}
|
||
|
||
ShapeContainer.AddFigure(new SquareFigure(name, x, y, side));
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickAddEllipse(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(EllipseName, out string name)) return;
|
||
|
||
if (!TryReadInt(EllipseX.Text, "X", 0, CanvasW - 1, out int x)) return;
|
||
if (!TryReadInt(EllipseY.Text, "Y", 0, CanvasH - 1, out int y)) return;
|
||
if (!TryReadInt(EllipseW.Text, "W", 1, CanvasW, out int w)) return;
|
||
if (!TryReadInt(EllipseH.Text, "H", 1, CanvasH, out int h)) return;
|
||
|
||
if (x + w > CanvasW || y + h > CanvasH)
|
||
{
|
||
MessageBox.Show("Фигура не помещается в область рисования.");
|
||
return;
|
||
}
|
||
|
||
ShapeContainer.AddFigure(new EllipseFigure(name, x, y, w, h));
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickAddCircle(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(CircleName, out string name)) return;
|
||
|
||
if (!TryReadInt(CircleX.Text, "X", 0, CanvasW - 1, out int x)) return;
|
||
if (!TryReadInt(CircleY.Text, "Y", 0, CanvasH - 1, out int y)) return;
|
||
|
||
int maxD = Math.Min(CanvasW, CanvasH);
|
||
if (!TryReadInt(CircleD.Text, "Диаметр", 1, maxD, out int d)) return;
|
||
|
||
if (x + d > CanvasW || y + d > CanvasH)
|
||
{
|
||
MessageBox.Show("Фигура не помещается в область рисования.");
|
||
return;
|
||
}
|
||
|
||
ShapeContainer.AddFigure(new CircleFigure(name, x, y, d));
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickPolyStart(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(PolyName, out string name)) return;
|
||
|
||
if (!TryReadInt(PolyCount.Text, "Кол-во вершин", 3, 50, out _polyExpected)) return;
|
||
|
||
_polyPoints = new List<Point>();
|
||
|
||
PolyCount.IsEnabled = false;
|
||
PolyX.IsEnabled = true;
|
||
PolyY.IsEnabled = true;
|
||
PolyAddPointBtn.IsEnabled = true;
|
||
PolyFinishBtn.IsEnabled = false;
|
||
|
||
PolyStatus.Text = $"Точек: 0 / {_polyExpected}";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickPolyAddPoint(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadInt(PolyX.Text, "X точки", 0, CanvasW - 1, out int x)) return;
|
||
if (!TryReadInt(PolyY.Text, "Y точки", 0, CanvasH - 1, out int y)) return;
|
||
|
||
if (_polyPoints.Count >= _polyExpected)
|
||
{
|
||
MessageBox.Show("Все точки уже введены.");
|
||
return;
|
||
}
|
||
|
||
_polyPoints.Add(new Point(x, y));
|
||
PolyStatus.Text = $"Точек: {_polyPoints.Count} / {_polyExpected}";
|
||
|
||
if (_polyPoints.Count == _polyExpected)
|
||
{
|
||
PolyAddPointBtn.IsEnabled = false;
|
||
PolyFinishBtn.IsEnabled = true;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickPolyFinish(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(PolyName, out string name)) return;
|
||
|
||
if (_polyPoints.Count != _polyExpected)
|
||
{
|
||
MessageBox.Show("Не все точки введены.");
|
||
return;
|
||
}
|
||
|
||
if (_polyPoints.Count < 3)
|
||
{
|
||
MessageBox.Show("Минимум 3 точки.");
|
||
return;
|
||
}
|
||
|
||
ShapeContainer.AddFigure(new PolygonFigure(name, _polyPoints.ToArray()));
|
||
Upd();
|
||
|
||
PolyCount.IsEnabled = true;
|
||
PolyX.IsEnabled = false;
|
||
PolyY.IsEnabled = false;
|
||
PolyAddPointBtn.IsEnabled = false;
|
||
PolyFinishBtn.IsEnabled = false;
|
||
PolyStatus.Text = "";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickAddTriangle(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(TriName, out string name)) return;
|
||
|
||
if (!TryReadInt(Ax.Text, "Ax", 0, CanvasW - 1, out int ax)) return;
|
||
if (!TryReadInt(Ay.Text, "Ay", 0, CanvasH - 1, out int ay)) return;
|
||
|
||
if (!TryReadInt(Bx.Text, "Bx", 0, CanvasW - 1, out int bx)) return;
|
||
if (!TryReadInt(By.Text, "By", 0, CanvasH - 1, out int by)) return;
|
||
|
||
if (!TryReadInt(Cx.Text, "Cx", 0, CanvasW - 1, out int cx)) return;
|
||
if (!TryReadInt(Cy.Text, "Cy", 0, CanvasH - 1, out int cy)) return;
|
||
|
||
var a = new Point(ax, ay);
|
||
var b = new Point(bx, by);
|
||
var c = new Point(cx, cy);
|
||
|
||
ShapeContainer.AddFigure(new Triangle(name, a, b, c));
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickAddTerrainPointSunPointHumanPointText(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!TryReadName(SceneName, out string name)) return;
|
||
|
||
if (!TryReadInt(SceneX.Text, "X", 0, CanvasW - 1, out int x)) return;
|
||
if (!TryReadInt(SceneY.Text, "Y", 0, CanvasH - 1, out int y)) return;
|
||
|
||
if (!TryReadInt(SceneW.Text, "W", 1, CanvasW, out int sceneW)) return;
|
||
if (!TryReadInt(SceneH.Text, "H", 1, CanvasH, out int sceneH)) return;
|
||
|
||
if (x + sceneW > CanvasW || y + sceneH > CanvasH)
|
||
{
|
||
MessageBox.Show("Сцена не помещается в область рисования.");
|
||
return;
|
||
}
|
||
|
||
ShapeContainer.AddFigure(new TerrainPointSunPointHumanPointText(name, x, y, sceneW, sceneH));
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickMoveSelected(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
var fig = GetSelectedFigureOrThrow();
|
||
|
||
if (!TryReadInt(MoveDx.Text, "dX", -CanvasW, CanvasW, out int dx)) return;
|
||
if (!TryReadInt(MoveDy.Text, "dY", -CanvasH, CanvasH, out int dy)) return;
|
||
|
||
if (fig.MoveByChecked(dx, dy))
|
||
Redraw();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickDeleteSelected(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
var fig = GetSelectedFigureOrThrow();
|
||
|
||
bool ok = ShapeContainer.RemoveFigure(fig);
|
||
if (!ok) throw new Exception("Фигура для удаления не найдена.");
|
||
|
||
Upd();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
private void ClickResizeSelected(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
var fig = GetSelectedFigureOrThrow();
|
||
|
||
if (!TryReadInt(ResizeW.Text, "W", 1, CanvasW, out int newW)) return;
|
||
if (!TryReadInt(ResizeH.Text, "H", 1, CanvasH, out int newH)) return;
|
||
|
||
if (fig is SquareFigure || fig is CircleFigure)
|
||
{
|
||
if (newW != newH)
|
||
{
|
||
MessageBox.Show("Для квадрата/круга W должен быть равен H.");
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (!fig.ResizeToChecked(newW, newH))
|
||
{
|
||
MessageBox.Show("Фигура не помещается в область рисования.");
|
||
return;
|
||
}
|
||
|
||
Redraw();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
}
|
||
}
|
||
|
||
public MainWindow()
|
||
{
|
||
InitializeComponent();
|
||
}
|
||
|
||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
Init.DrawingImage = DrawingImage;
|
||
|
||
int width = (int)DrawingImage.Width;
|
||
int height = (int)DrawingImage.Height;
|
||
|
||
if (width <= 0) width = 800;
|
||
if (height <= 0) height = 400;
|
||
|
||
_renderTarget = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
|
||
DrawingImage.Source = _renderTarget;
|
||
|
||
ImgBorder.Width = width;
|
||
ImgBorder.Height = height;
|
||
|
||
Upd();
|
||
}
|
||
|
||
private void Redraw()
|
||
{
|
||
if (_renderTarget == null) return;
|
||
|
||
DrawingVisual drawVis = new DrawingVisual();
|
||
|
||
using (var ctx = drawVis.RenderOpen())
|
||
{
|
||
foreach (var fig in ShapeContainer.FigureList)
|
||
{
|
||
fig.Draw(ctx);
|
||
}
|
||
}
|
||
|
||
_renderTarget.Clear();
|
||
_renderTarget.Render(drawVis);
|
||
DrawingImage.Source = _renderTarget;
|
||
}
|
||
private void RefreshCombo()
|
||
{
|
||
FigureCombo.ItemsSource = null;
|
||
FigureCombo.ItemsSource = ShapeContainer.FigureList.Select(f => f.Name);
|
||
if (FigureCombo.Items.Count > 0 && FigureCombo.SelectedIndex < 0)
|
||
{
|
||
FigureCombo.SelectedIndex = 0;
|
||
}
|
||
}
|
||
|
||
private void Upd()
|
||
{
|
||
Redraw();
|
||
RefreshCombo();
|
||
}
|
||
|
||
private Figure GetSelectedFigureOrThrow()
|
||
{
|
||
if (FigureCombo.SelectedItem == null) throw new Exception("Фигура не выбрана.");
|
||
|
||
string name = FigureCombo.SelectedItem.ToString()!;
|
||
var fig = ShapeContainer.FindByName(name);
|
||
if (fig == null) throw new Exception("Фигура для операции не найдена.");
|
||
|
||
return fig;
|
||
}
|
||
|
||
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
var p = e.GetPosition(DrawingImage);
|
||
|
||
_mouseSelected =
|
||
ShapeContainer.FigureList
|
||
.AsEnumerable()
|
||
.Reverse()
|
||
.FirstOrDefault(f => f.HitTest(p));
|
||
|
||
if (_mouseSelected != null)
|
||
{
|
||
FigureCombo.SelectedItem = _mouseSelected.Name;
|
||
|
||
_isDragging = true;
|
||
_lastMouse = p;
|
||
|
||
DrawingImage.CaptureMouse();
|
||
e.Handled = true;
|
||
}
|
||
}
|
||
|
||
private void MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
if (!_isDragging || _mouseSelected == null) return;
|
||
if (e.LeftButton != MouseButtonState.Pressed) return;
|
||
|
||
var p = e.GetPosition(DrawingImage);
|
||
|
||
int dx = (int)Math.Round(p.X - _lastMouse.X);
|
||
int dy = (int)Math.Round(p.Y - _lastMouse.Y);
|
||
|
||
if (dx == 0 && dy == 0) return;
|
||
|
||
if (_mouseSelected.MoveByChecked(dx, dy))
|
||
{
|
||
_lastMouse = p;
|
||
Redraw();
|
||
}
|
||
|
||
e.Handled = true;
|
||
}
|
||
|
||
private void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||
{
|
||
if (!_isDragging) return;
|
||
|
||
_isDragging = false;
|
||
_mouseSelected = null;
|
||
|
||
DrawingImage.ReleaseMouseCapture();
|
||
e.Handled = true;
|
||
}
|
||
private bool TryReadInt(string raw, string field, int min, int max, out int value)
|
||
{
|
||
if (!int.TryParse(raw?.Trim(), out value))
|
||
{
|
||
MessageBox.Show($"Поле \"{field}\": нужно целое число.");
|
||
return false;
|
||
}
|
||
|
||
if (value < min || value > max)
|
||
{
|
||
MessageBox.Show($"Поле \"{field}\": допустимо [{min}..{max}].");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
private bool TryReadName(TextBox tb, out string name)
|
||
{
|
||
name = (tb.Text ?? "").Trim();
|
||
|
||
if (string.IsNullOrWhiteSpace(name))
|
||
{
|
||
MessageBox.Show("Имя фигуры не должно быть пустым.");
|
||
return false;
|
||
}
|
||
|
||
if (name.Length > 40)
|
||
{
|
||
MessageBox.Show("Имя фигуры слишком длинное (максимум 40 символов).");
|
||
return false;
|
||
}
|
||
|
||
string kostil = name;
|
||
|
||
if (ShapeContainer.FigureList.Any(f => string.Equals(f.Name, kostil, StringComparison.OrdinalIgnoreCase)))
|
||
{
|
||
MessageBox.Show("Фигура с таким именем уже существует.");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
} |