This commit is contained in:
2026-04-06 10:03:33 +03:00
commit 07680fc5ed
22 changed files with 1130 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.MCCAD.iml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AvaloniaProject">
<option name="projectPerEditor">
<map>
<entry key="MCCAD/App.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/MainWindow.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/PositiionControl.axaml" value="MCCAD/MCCAD.csproj" />
</map>
</option>
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+3
View File
@@ -0,0 +1,3 @@
<Solution>
<Project Path="MCCAD/MCCAD.csproj" />
</Solution>
+15
View File
@@ -0,0 +1,15 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MCCAD.App"
xmlns:local="using:MCCAD"
RequestedThemeVariant="Light">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
+28
View File
@@ -0,0 +1,28 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using MCCAD.ViewModels;
using MCCAD.Views;
namespace MCCAD;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

+26
View File
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.13" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.13" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.13" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.13" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.13">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="ReactiveUI.Avalonia" Version="11.4.12" />
</ItemGroup>
</Project>
+74
View File
@@ -0,0 +1,74 @@
using System;
using ReactiveUI;
namespace MCCAD.Models;
public struct HPoint
{
public static readonly HPoint Zero = new HPoint(0, 0);
public double X, Y;
public HPoint(double x, double y)
{
X = x;
Y = y;
}
public static HPoint operator +(HPoint p1, HPoint p2) => new HPoint(p1.X + p2.X, p1.Y + p2.Y);
public static HPoint operator -(HPoint p1, HPoint p2) => new HPoint(p1.X - p2.X, p1.Y - p2.Y);
public static HPoint operator *(HPoint p1, double k) => new HPoint(p1.X * k, p1.Y * k);
public void Round()
{
X = Math.Round(X);
Y = Math.Round(Y);
}
public static HPoint Vector(double angle)
{
return new HPoint(Math.Cos(angle), Math.Sin(angle));
}
public override string ToString()
{
return $"X:{Math.Round(X, 3)}; Y:{Math.Round(Y, 3)}";
}
public IntPoint ToIntPoint()
{
var ret = new IntPoint();
ret.X = (int)Math.Round(X);
ret.Y = (int)Math.Round(Y);
return ret;
}
public double Lenght => Math.Sqrt(X * X + Y * Y);
public HPoint Resuffled() => new HPoint(Y, X);
}
public struct IntPoint
{
public int X;
public int Y;
public int XPos { get => X; set => X = value; }
public IntPoint(int x, int y)
{
X = x;
Y = y;
}
public static IntPoint operator +(IntPoint x, IntPoint y)
{
return new IntPoint(x.X + y.X, x.Y + y.Y);
}
public static IntPoint operator -(IntPoint x, IntPoint y)
{
return new IntPoint(x.X - y.X, x.Y - y.Y);
}
public static bool operator ==(IntPoint x, IntPoint y) => x.X == y.X && x.Y == y.Y;
public static bool operator != (IntPoint x, IntPoint y) => x.X != y.X || x.Y != y.Y;
public override string ToString()
{
return $"X:{X}; Y:{Y}";
}
}
+68
View File
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace MCCAD.Models;
class Line
{
public HPoint from;
public HPoint to;
public Line(HPoint start, HPoint end)
{
from = start;
to = end;
}
public List<IntPoint> GetPixels()
{
Debug.WriteLine($"Line started");
var shape = new List<HPoint>();
HPoint vector = to - from;
if (Math.Abs(vector.X) >= Math.Abs(vector.Y))
{
for (int i = 0; i < Math.Abs(vector.X); i++)
{
var k = i / Math.Abs(vector.X);
var currentPoint = vector * k;
shape.Add(currentPoint);
}
}
else
{
for (int i = 0; i <= Math.Abs(vector.Y); i++)
{
var k = i / Math.Abs(vector.Y);
var currentPoint = vector * k;
shape.Add(currentPoint);
}
}
shape = shape.Select(i => i + from).ToList();
shape.ForEach(i => Debug.WriteLine($"{i} -> {i.ToIntPoint()}"));
var pts = shape.Select(i => i.ToIntPoint()).ToList();
return pts;
}
public static List<IntPoint> GetPixelsTrign(double angle, double len)
{
Debug.WriteLine($"Line {angle} rads started");
var shape = new List<HPoint>();
var s = Math.Sin(angle);
var c = Math.Cos(angle);
var tg = s / c;
var ctg = c / s;
var until = Math.Max(s, c) * len;
var k = Math.Min(tg, ctg);
var range = Enumerable.Range(0, (int)until);
foreach (var i in range)
{
shape.Add(new HPoint(i, i * Math.Min(s, c)));
}
if (s > c)
shape = shape.Select(i => i.Resuffled()).ToList();
shape.ForEach(i => Debug.WriteLine($"{i} -> {i.ToIntPoint()}"));
var pix = shape.Select(i => i.ToIntPoint()).ToList();
return pix;
}
}
+23
View File
@@ -0,0 +1,23 @@
using Avalonia;
using ReactiveUI.Avalonia;
using System;
namespace MCCAD;
sealed class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseReactiveUI(i => { });
}
+37
View File
@@ -0,0 +1,37 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using MCCAD.ViewModels;
namespace MCCAD;
/// <summary>
/// Given a view model, returns the corresponding view if possible.
/// </summary>
[RequiresUnreferencedCode(
"Default implementation of ViewLocator involves reflection which may be trimmed away.",
Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")]
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null)
return null;
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}
+308
View File
@@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using MCCAD.Models;
using ReactiveUI;
namespace MCCAD.ViewModels;
public delegate void ShemeUpdate(List<IntPoint> points, IFigureView view);
public delegate void ListUpdate();
public interface IFigureView
{
void Rebuild();
public event ShemeUpdate OnNewSheme;
PositionVM Position { get; set; }
public string Name { get; set; }
}
public class LineView : ReactiveObject, IFigureView
{
private static int _counter = 1;
public LineView () {
Position = new PositionVM
{
Chanded = Rebuild
};
Name = "Line " + _counter++;
}
public event ShemeUpdate OnNewSheme;
public PositionVM Position { get; set; } = new ();
public string Name { get; set; }
private double angle = 45;
private int lenght = 10;
public int Lenght
{
get => lenght;
set
{
this.RaiseAndSetIfChanged(ref lenght, value);
Rebuild();
}
}
public int Angle
{
get => (int)angle;
set
{
this.RaiseAndSetIfChanged(ref angle, value);
Rebuild();
}
}
public void Rebuild()
{
var endPoint = HPoint.Vector(angle == 90 ? Math.PI / 2 : angle / 360 * Math.Tau) * lenght;
Line line = new Line(HPoint.Zero, endPoint);
var shape = line.GetPixels();
shape = shape.Select(i => new IntPoint(i.X, -i.Y)).ToList();
var yMove = shape.Select(p => p.Y).Min();
shape = shape.Select(p => new IntPoint(p.X, p.Y - yMove)).ToList();
OnNewSheme.Invoke(shape, this);
}
}
public class PolygonView : ReactiveObject, IFigureView
{
public event ShemeUpdate OnNewSheme;
public PositionVM Position { get; set; }
public string Name { get; set; }
private int corners = 3;
private int radius = 5;
private int rotate;
private static int _counter = 1;
public PolygonView() {
Position = new PositionVM
{
Chanded = Rebuild
};
Name = "Polygon " + _counter++;
}
public int Rotate
{
get { return rotate; }
set
{
this.RaiseAndSetIfChanged(ref rotate, value);
Rebuild();
}
}
public int Radius
{
get { return radius; }
set
{
this.RaiseAndSetIfChanged(ref radius, value);
Rebuild();
}
}
public int Corners
{
get { return corners; }
set
{
this.RaiseAndSetIfChanged(ref corners, value);
Rebuild();
}
}
public void Rebuild()
{
List<IntPoint> shape = new List<IntPoint>();
void DrawFromTo(HPoint s, HPoint e)
{
var line = new Line(s, e);
var linePixs = line.GetPixels();
shape.AddRange(linePixs);
}
var len = radius - 1;
double alpha = Math.Tau / corners;
var start = new HPoint();
for (int i = 0; i < corners; i++)
{
double a = alpha * i;
var vec = HPoint.Vector(a) * len + start;
vec.Round();
DrawFromTo(start, vec);
start = vec;
}
shape = shape.Distinct().ToList();
var xMove = shape.Select(p => p.X).Min();
var yMove = shape.Select(p => p.Y).Min();
shape = shape.Select(p => new IntPoint(p.X - xMove, p.Y - yMove)).ToList();
//var line = new Line(pts[0], pts[1]);
//shape.AddRange(line.GetPixels());
//line = new Line(pts[1], pts[2]);
//shape.AddRange(line.GetPixels());
//line = new Line(pts[2], pts[0]);
//shape.AddRange(line.GetPixels());
//LinkedList<HPoint> vecs = new LinkedList<HPoint>(pts);
//var start = vecs.First;
//while (null != start.Next)
//{
// var end = start.Next.Value;
// DrawFromTo(start.Value, end);
// start = start.Next;
//}
//DrawFromTo(vecs.First.Value, vecs.Last.Value);
OnNewSheme.Invoke(shape, this);
}
}
public class OvalView : ReactiveObject, IFigureView
{
int width = 10;
int heigh = 10;
int top;
int bottom = 10;
int left = 0;
int right = 10;
public int Width
{
get => width;
set
{
this.RaiseAndSetIfChanged(ref width, value);
if (width < right)
right = width;
Rebuild();
}
}
public int Heigth
{
get => heigh;
set
{
this.RaiseAndSetIfChanged(ref heigh, value);
if (bottom > heigh)
bottom = heigh;
Rebuild();
}
}
public int TopLimit
{
get => top;
set
{
this.RaiseAndSetIfChanged(ref top, value);
Rebuild();
}
}
public int BottomLimit
{
get => bottom;
set
{
this.RaiseAndSetIfChanged(ref bottom, value);
Rebuild();
}
}
public int LeftLimit
{
get => left;
set
{
this.RaiseAndSetIfChanged(ref left, value);
Rebuild();
}
}
public int RightLimit
{
get => right;
set
{
this.RaiseAndSetIfChanged(ref right, value);
Rebuild();
}
}
public event ShemeUpdate OnNewSheme;
public PositionVM Position { get; set; }
public string Name { get; set; }
public double NormalizedLenth(double x, double y)
{
var hWidth = width / 2;
var hHeith = heigh / 2;
return new Vector2((float)(x / hWidth), (float)(y / hHeith)).Length();
}
private static int _counter = 1;
public OvalView()
{
Position = new PositionVM
{
Chanded = Rebuild
};
Name = "Oval " + _counter++;
}
public void Rebuild()
{
int RoundToInt(double value) => (int)Math.Round(value);
var shape = new List<IntPoint>();
var halfWidth = RoundToInt(width / 2);
var halfHeight = RoundToInt(heigh / 2);
List<int> yRange = Enumerable.Range(-halfHeight, heigh).ToList();
var xRange = Enumerable.Range(-halfWidth, width).ToList();
void addNormalized(int x, int y)
{
shape.Add(new IntPoint(x + RoundToInt(halfWidth), y + RoundToInt(halfHeight)));
}
bool evenWidth = width % 2 == 0;
bool evenHeight = heigh % 2 == 0;
int move = evenHeight ? 1 : 0;
int maxH = halfHeight - move;
foreach (var x in xRange)
{
double lX = x;
lX = x > 0 && evenWidth ? x + 1 : x;
int y = (int)Math.Ceiling(Math.Sqrt(1 - Math.Pow(lX / (double)halfWidth, 2)) * halfHeight);
shape.Add(new IntPoint(x, y - move));
shape.Add(new IntPoint(x, -y));
yRange.Remove(y - move);
yRange.Remove(-y);
}
move = width % 2 == 0 ? 1 : 0;
foreach (var y in yRange)
{
double lY = y;
lY = y > 0 && evenHeight ? y + 1 : y;
int x = (int)Math.Ceiling(Math.Sqrt(1 - Math.Pow(lY / (double)halfHeight, 2)) * halfWidth);
shape.Add(new IntPoint(x - move, y));
shape.Add(new IntPoint(-x, y));
}
int maxW = halfWidth - move;
shape = shape.Select(i => new IntPoint(i.X + halfWidth, i.Y + halfHeight)).ToList();
shape = shape.Where(i => i.Y >= top && i.Y <= bottom && i.X <= right && i.X >= left).ToList();
OnNewSheme?.Invoke(shape, this);
}
}
+35
View File
@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ReactiveUI;
namespace MCCAD.ViewModels;
public class MainWindowViewModel : ViewModelBase
{
private OvalView _ov;
private PolygonView _pv;
private LineView _lv;
public OvalView Ov
{
get => _ov;
set => this.RaiseAndSetIfChanged(ref _ov, value);
}
public PolygonView Pv
{
get => _pv;
set => this.RaiseAndSetIfChanged(ref _pv, value);
}
public LineView Lv
{
get => _lv;
set => this.RaiseAndSetIfChanged(ref _lv, value);
}
public int A { get; set; }
public string Greeting { get; set; }
public ObservableCollection<IFigureView> Figures { get; set; } = [];
}
+32
View File
@@ -0,0 +1,32 @@
using System;
using ReactiveUI;
namespace MCCAD.ViewModels;
public class PositionVM : ReactiveObject
{
private int _x;
private int _y;
public int X
{
get => _x;
set
{
this.RaiseAndSetIfChanged(ref _x, value);
Chanded?.Invoke();
}
}
public int Y
{
get => _y;
set
{
this.RaiseAndSetIfChanged(ref _y, value);
Chanded?.Invoke();
}
}
public Action Chanded;
}
+7
View File
@@ -0,0 +1,7 @@
using ReactiveUI;
namespace MCCAD.ViewModels;
public abstract class ViewModelBase : ReactiveObject
{
}
+155
View File
@@ -0,0 +1,155 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MCCAD.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:mcd="using:MCCAD.ViewModels"
xmlns:views="clr-namespace:MCCAD.Views"
x:Class="MCCAD.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="MCCAD">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition />
<ColumnDefinition Width="250"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="125" />
</Grid.RowDefinitions>
<StackPanel Margin="5,5,5,5">
<HyperlinkButton Click="CreateShape" Tag="Ov">Новый элипс</HyperlinkButton>
<HyperlinkButton Click="CreateShape" Tag="Pv">Новый многоугольник</HyperlinkButton>
<HyperlinkButton Click="CreateShape" Tag="Lv">Новая линия</HyperlinkButton>
<Panel Name="ToolsTab" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer DataContext="{Binding Path=Ov}" IsVisible="False">
<StackPanel>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Размеры">
<StackPanel>
<TextBlock Text="Ширина" />
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Slider Minimum="3" Maximum="50" VerticalAlignment="Center"
Value="{Binding Width}" />
<TextBox x:Name="WStr" Grid.Column="1" Margin="3" MaxLength="4"
Text="{Binding Width}" />
</Grid>
<TextBlock Text="Высота" />
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Slider Minimum="3" Maximum="50" VerticalAlignment="Center"
Value="{Binding Heigth}" />
<TextBox x:Name="HStr" Grid.Column="1" Margin="3" MaxLength="4"
Text="{Binding Heigth}" />
</Grid>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Ограничения">
<DockPanel LastChildFill="False" Name="LimitsDP">
<StackPanel DockPanel.Dock="Top">
<Slider Minimum="0" Maximum="{Binding Heigth}" Width="85" Margin="3"
Value="{Binding TopLimit}" />
<TextBlock HorizontalAlignment="Center" Name="TopTB"
Text="{Binding TopLimit, StringFormat={}от {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom">
<Slider Minimum="0" Maximum="{Binding Heigth}" Width="85" Margin="3"
Value="{Binding BottomLimit}" />
<TextBlock HorizontalAlignment="Center" Name="BotTB"
Text="{Binding BottomLimit, StringFormat={}до {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Left">
<Slider Minimum="0" Maximum="{Binding Width}" Width="85" Margin="3"
Value="{Binding LeftLimit}" />
<TextBlock HorizontalAlignment="Center" Name="LeftTB"
Text="{Binding LeftLimit, StringFormat={}от {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Right" Width="85">
<Slider Minimum="0" Maximum="{Binding Width}" Width="85" Margin="3"
Value="{Binding RightLimit}" />
<TextBlock HorizontalAlignment="Center" Name="RightTB"
Text="{Binding RightLimit, StringFormat={}до {0}}" />
</StackPanel>
</DockPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</ScrollViewer>
<ScrollViewer Margin="5" DataContext="{Binding Pv}" IsVisible="False">
<StackPanel>
<TextBlock Text="Сторона" />
<Slider Minimum="5" Maximum="100" VerticalAlignment="Center" Value="{Binding Radius}" />
<TextBlock Text="{Binding Radius}" />
<TextBlock Text="Углы" />
<Slider Minimum="3" Maximum="10" VerticalAlignment="Center" Value="{Binding Corners}" />
<TextBlock Text="{Binding Corners}" />
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</ScrollViewer>
<StackPanel Margin="5" DataContext="{Binding Lv}" IsVisible="False">
<StackPanel>
<TextBlock>Угол</TextBlock>
<Slider Value="{Binding Angle}" Minimum="0" Maximum="90" />
<TextBlock Text="{Binding Angle}" />
<TextBlock>Длина</TextBlock>
<Slider Value="{Binding Lenght}" Minimum="5" Maximum="90" />
<TextBlock Text="{Binding Lenght}" />
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</StackPanel>
</Panel>
</StackPanel>
<StackPanel Grid.Row="1" VerticalAlignment="Top">
<TextBlock x:Name="CurrentBlock" Margin="5" />
<Slider Minimum="50" Maximum="500" TickFrequency="5" ValueChanged="Slider_ValueChanged" Value="100" />
<TextBlock x:Name="ScaleTB" />
<TextBlock x:Name="TotalTB" Text="0" Margin="5" />
</StackPanel>
</Grid>
<ScrollViewer Grid.Column="1" Margin="5,5,5,5" HorizontalScrollBarVisibility="Visible">
<Grid>
<Ellipse x:Name="BaseElipse" Stroke="Black" StrokeThickness="10" Height="150" Margin="15" Width="150"
VerticalAlignment="Top" HorizontalAlignment="Left" IsVisible="False" />
<Canvas Name="ShemeChanvas" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="15" />
</Grid>
</ScrollViewer>
<StackPanel Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10" Spacing="10">
<TextBlock>Слои</TextBlock>
<ListBox SelectionChanged="SetupSelected" ItemsSource="{Binding Figures}" Name="Layers">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto *">
<TextBox Grid.Column="1" Text="{Binding Name}" HorizontalAlignment="Stretch"></TextBox>
<Border Margin="5" Background="OrangeRed" Width="30"></Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
+230
View File
@@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using MCCAD.Models;
using MCCAD.ViewModels;
namespace MCCAD.Views;
public partial class MainWindow : Window
{
public Dictionary<IFigureView, Canvas> _cache = new();
public bool mark = true;
int blockSize = 15;
private bool switching = false;
private MainWindowViewModel DC => DataContext as MainWindowViewModel;
public MainWindow()
{
InitializeComponent();
}
protected override void OnDataContextChanged(EventArgs e)
{
}
void DrawShape(List<IntPoint> points, IFigureView view)
{
if (!_cache.TryGetValue(view, out Canvas canvas))
{
canvas = new Canvas();
_cache.Add(view, canvas);
ShemeChanvas.Children.Add(canvas);
}
canvas.Children.Clear();
canvas.Width = points.Select(i => i.X).Max() * blockSize;
canvas.Height = points.Select(i => i.Y).Max() * blockSize;
Dictionary<IntPoint, string> markup = new Dictionary<IntPoint, string>();
if (mark)
{
var lined = points.OrderBy(i => i.Y).GroupBy(i => i.Y);
foreach (var l in lined)
{
if (l.Count() < 3)
continue;
var line = l.OrderBy(i => i.X).ToList();
var linePoint = new List<int> { 0 };
for (int i = 0; i < line.Count; i++)
{
if (i != line.Count - 1 && line[i].X != line[i + 1].X - 1)
{
linePoint.Add(i);
linePoint.Add(i + 1);
}
}
linePoint.Add(line.Count - 1);
var linesIds = linePoint.Chunk(2);
foreach (var microLineId in linesIds)
{
var micloLine = line.Skip(microLineId[0]).Take(microLineId[1] - microLineId[0] + 1).ToList();
if (micloLine.Count > 2)
{
markup.Add(micloLine.First(), "←");
markup.Add(micloLine.Last(), "→");
markup.Add(micloLine[micloLine.Count / 2], micloLine.Count.ToString());
}
}
}
lined = points.OrderBy(i => i.X).GroupBy(i => i.X);
foreach (var l in lined)
{
if (l.Count() < 3)
continue;
var line = l.OrderBy(i => i.Y).ToList();
var linePoint = new List<int>() { 0 };
for (int i = 0; i < line.Count; i++)
{
if (i != line.Count - 1 && line[i].Y != line[i + 1].Y - 1)
{
linePoint.Add(i);
linePoint.Add(i + 1);
}
}
linePoint.Add(line.Count - 1);
var linesIds = linePoint.Chunk(2);
foreach (var microLineId in linesIds)
{
var micloLine = line.Skip(microLineId[0]).Take(microLineId[1] - microLineId[0] + 1).ToList();
if (micloLine.Count > 2)
{
if (!markup.TryAdd(micloLine.First(), "↑"))
{
markup[micloLine.First()] = "+";
}
if (!markup.TryAdd(micloLine.Last(), "↓"))
{
markup[micloLine.Last()] = "+";
}
markup.Add(micloLine[micloLine.Count / 2], micloLine.Count.ToString());
}
}
}
}
foreach (var point in points)
{
Border block = new Border();
block.Height = blockSize;
block.Width = blockSize;
block.BorderThickness = new Thickness(2);
block.Background = new SolidColorBrush(Colors.LightGray);
block.DataContext = point;
block.PointerEntered += (sender, e) => { CurrentBlock.Text = (sender as Border).DataContext.ToString(); };
if (mark)
{
if (markup.TryGetValue(point, out var text))
block.Child = new TextBlock
{
Text = text, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, FontSize = 10
};
}
Canvas.SetLeft(block, (point.X + view.Position.X) * blockSize);
Canvas.SetTop(block, (point.Y + view.Position.Y) * blockSize);
canvas.Children.Add(block);
}
canvas.Width = blockSize * (points.Select(i => i.X).Max() + 5);
canvas.Height = blockSize * (points.Select(i => i.Y).Max() + 5);
TotalTB.Text = points.Count.ToString();
}
private void Slider_ValueChanged(object? sender, RangeBaseValueChangedEventArgs rangeBaseValueChangedEventArgs)
{
if (ShemeChanvas == null)
return;
var e = rangeBaseValueChangedEventArgs;
ShemeChanvas.RenderTransform = new ScaleTransform() { ScaleX = e.NewValue / 100, ScaleY = e.NewValue / 100 };
// ShemeChanvas.transf.LayoutTransform = new ScaleTransform() { ScaleX = e.NewValue / 95, ScaleY = e.NewValue / 95 };
ScaleTB.Text = Math.Round(rangeBaseValueChangedEventArgs.NewValue) + "%";
}
private void CreateShape(object? sender, RoutedEventArgs routedEventArgs)
{
try
{
var x = (sender as Control).Tag;
switch (x)
{
case "Ov":
DC.Ov = new OvalView();
DC.Ov.OnNewSheme += DrawShape;
DC.Ov.Rebuild();
DC.Figures.Add(DC.Ov);
SetupVisiblePanel(0);
break;
case "Pv":
DC.Pv = new PolygonView();
DC.Pv.OnNewSheme += DrawShape;
DC.Pv.Rebuild();
DC.Figures.Add(DC.Pv);
SetupVisiblePanel(1);
break;
case "Lv":
DC.Lv = new LineView();
DC.Lv.OnNewSheme += DrawShape;
DC.Lv.Rebuild();
DC.Figures.Add(DC.Lv);
SetupVisiblePanel(2);
break;
}
}
catch (Exception exception)
{
}
}
private void SetupSelected(object? sender, SelectionChangedEventArgs e)
{
if (Layers.SelectedItem == null)
{
return;
}
switching = true;
switch (Layers.SelectedItem)
{
case OvalView ov:
DC.Ov = ov;
SetupVisiblePanel(0);
break;
case PolygonView pv:
DC.Pv = pv;
SetupVisiblePanel(1);
break;
case LineView lv:
DC.Lv = lv;
SetupVisiblePanel(2);
break;
}
}
private void SetupVisiblePanel(int index)
{
for (var i = 0; i < ToolsTab.Children.Count; i++)
{
ToolsTab.Children[i].IsVisible = i == index;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:MCCAD.Models"
xmlns:viewModels="clr-namespace:MCCAD.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MCCAD.Views.PositionControl"
x:DataType="viewModels:PositionVM">
<Grid RowDefinitions="Auto *" ColumnDefinitions="Auto *">
<TextBlock VerticalAlignment="Center" Margin="5">X: </TextBlock>
<TextBlock VerticalAlignment="Center" Grid.Row="1" Margin="5">Y: </TextBlock>
<TextBox Grid.Column="1" Text="{Binding X}" Margin="5"></TextBox>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Y}" Margin="5"></TextBox>
</Grid>
</UserControl>
+13
View File
@@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace MCCAD.Views;
public partial class PositionControl : UserControl
{
public PositionControl()
{
InitializeComponent();
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="MCCAD.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>