Initial commit

This commit is contained in:
ti_mo
2025-10-20 19:55:15 +02:00
commit 0d108223f9
14 changed files with 444 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
+16
View File
@@ -0,0 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelauneyTriangulation", "DelauneyTriangulation\DelauneyTriangulation.csproj", "{2A9A87E7-EE9E-4D2C-840D-C5BD1E979CDB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2A9A87E7-EE9E-4D2C-840D-C5BD1E979CDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A9A87E7-EE9E-4D2C-840D-C5BD1E979CDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A9A87E7-EE9E-4D2C-840D-C5BD1E979CDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A9A87E7-EE9E-4D2C-840D-C5BD1E979CDB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+15
View File
@@ -0,0 +1,15 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="DelauneyTriangulation.App"
xmlns:local="using:DelauneyTriangulation"
RequestedThemeVariant="Default">
<!-- "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>
+33
View File
@@ -0,0 +1,33 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using DelauneyTriangulation.ViewModels;
using DelauneyTriangulation.Views;
namespace DelauneyTriangulation;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT
BindingPlugins.DataValidators.RemoveAt(0);
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.6"/>
<PackageReference Include="Avalonia.Desktop" Version="11.3.6"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.6"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.6"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.6"/>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/>
</ItemGroup>
</Project>
@@ -0,0 +1,39 @@
using Avalonia;
namespace DelauneyTriangulation.Models;
public class Geometries
{
public sealed class GeomPoint(double x, double y, double z = 3)
{
public double X { get; } = x;
public double Y { get; } = y;
public double R { get; } = z;
public double Left => X - R;
public double Top => Y - R;
public double Size => 2 * R;
}
public sealed class GeomEdge(double x1, double y1, double x2, double y2)
{
public double X1 { get; } = x1;
public double X2 { get; } = x2;
public double Y1 { get; } = y1;
public double Y2 { get; } = y2;
public Point Start => new(X1, Y1);
public Point End => new(X2, Y2);
}
public sealed class GeomCircle(double x, double y, double r)
{
public double X { get; } = x;
public double Y { get; } = y;
public double R { get; } = r;
public double Left => X - R;
public double Top => Y - R;
public double Diameter => 2 * R;
}
}
+21
View File
@@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace DelauneyTriangulation;
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();
}
+32
View File
@@ -0,0 +1,32 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using DelauneyTriangulation.ViewModels;
namespace DelauneyTriangulation;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? data)
{
if (data is null)
return null;
var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
{
var control = (Control)Activator.CreateInstance(type)!;
control.DataContext = data;
return control;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using DelauneyTriangulation.Models;
namespace DelauneyTriangulation.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
[ObservableProperty] private int _pointCount = 20;
[ObservableProperty] private double _panX;
[ObservableProperty] private double _panY;
[ObservableProperty] private double _zoom = 1.0;
public double Width { get; set; } = 1280;
public double Height { get; set; } = 720;
public ObservableCollection<Geometries.GeomPoint> Points { get; } = new();
public ObservableCollection<Geometries.GeomEdge> Edges { get; } = new();
public ObservableCollection<Geometries.GeomCircle> Circles { get; } = new();
public MainWindowViewModel()
{
var rand = new Random();
for (int i = 0; i < PointCount; i++)
{
double x = rand.NextDouble() * Width;
double y = rand.NextDouble() * Height;
Points.Add(new Geometries.GeomPoint(x, y));
}
}
}
@@ -0,0 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace DelauneyTriangulation.ViewModels;
public class ViewModelBase : ObservableObject
{
}
@@ -0,0 +1,132 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:DelauneyTriangulation.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:DelauneyTriangulation.Models"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="DelauneyTriangulation.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="Delaunay Triangulation"
SystemDecorations="Full"
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="Default"
ExtendClientAreaTitleBarHeightHint="32"
TransparencyLevelHint="AcrylicBlur, Blur, Transparent"
Background="Transparent"
Width="{Binding Width}"
Height="{Binding Height}"
>
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<Window.Styles>
<Style Selector="Border.InnerCard">
<Setter Property="CornerRadius" Value="7.5"/>
<Setter Property="Background" Value="#30000000"/>
<Setter Property="BorderBrush" Value="#20FFFFFF"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Padding" Value="10"/>
<Setter Property="Margin" Value="5"/>
</Style>
</Window.Styles>
<Panel>
<Grid RowDefinitions="30,*">
<Border ZIndex="1000" Grid.Row="0" Background="#30000000" PointerPressed="OnTitleBarPointerPressed" DoubleTapped="OnTitleBarDoubleTapped">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Background="Transparent" Foreground="White" Text="Delaunay Triangulation - Timo Niemann"/>
</Border>
<Grid Grid.Row="1" ZIndex="1000" Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Classes="InnerCard">
<Border.Effect>
<DropShadowEffect BlurRadius="20" Color="#aa000000" OffsetX="5" OffsetY="5"/>
</Border.Effect>
<StackPanel Orientation="Vertical">
<Border Classes="InnerCard">
<TextBlock Text="Delaunay Triangulation" FontSize="18" Foreground="White"/>
</Border>
<Border Classes="InnerCard">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Value="{Binding PointCount, Mode=TwoWay}" Margin="5" Minimum="20" Maximum="500"/>
<TextBox Grid.Column="1" Text="{Binding PointCount, Mode=TwoWay}" FontSize="18" Foreground="White" Background="#30ffffff" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
</Grid>
</Border>
</StackPanel>
</Border>
</Grid>
<Canvas Grid.Row="0" Grid.RowSpan="2"
x:Name="Scene"
Background="#dd000000"
ClipToBounds="True"
PointerPressed="Scene_OnPointerPressed"
PointerMoved="Scene_OnPointerMoved"
PointerReleased="Scene_OnPointerReleased"
PointerWheelChanged="Scene_OnWheel"
>
<Canvas x:Name="SceneContent">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding Zoom}" ScaleY="{Binding Zoom}"/>
<TranslateTransform X="{Binding PanX}" Y="{Binding PanY}"/>
</TransformGroup>
</Canvas.RenderTransform>
<ItemsControl ItemsSource="{Binding Edges}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><Canvas/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:Geometries+GeomEdge">
<Line StartPoint="{Binding Start}"
EndPoint="{Binding End}"
Stroke="LightGray"
StrokeThickness="1"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Circles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><Canvas/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:Geometries+GeomCircle">
<Ellipse Stroke="Lime" StrokeThickness="1.5"
Width="{Binding Diameter}"
Height="{Binding Diameter}"
Canvas.Left="{Binding Left}"
Canvas.Top="{Binding Top}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Points}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><Canvas/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:Geometries+GeomPoint">
<Ellipse Fill="White" Stroke="Black" StrokeThickness="1"
Width="{Binding Size}"
Height="{Binding Size}"
Canvas.Left="{Binding Left}"
Canvas.Top="{Binding Top}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</Canvas>
</Grid>
</Panel>
</Window>
@@ -0,0 +1,69 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace DelauneyTriangulation.Views;
public partial class MainWindow : Window
{
private bool _isPanning;
private Point _panStart;
private Vector _panOffset = new();
private readonly TranslateTransform _panTransform = new();
public MainWindow()
{
InitializeComponent();
SceneContent.RenderTransform = _panTransform;
}
private void OnTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
BeginMoveDrag(e);
}
private void OnTitleBarDoubleTapped(object? sender, RoutedEventArgs e) => WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
private void Scene_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(Scene).Properties.IsLeftButtonPressed)
return;
_isPanning = true;
_panStart = e.GetPosition(this);
Scene.Cursor = new Cursor(StandardCursorType.SizeAll);
e.Handled = true;
}
private void Scene_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (!_isPanning) return;
var pos = e.GetPosition(this);
var delta = pos - _panStart;
var current = _panOffset + delta;
_panTransform.X = current.X;
_panTransform.Y = current.Y;
e.Handled = true;
}
private void Scene_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (!_isPanning) return;
var pos = e.GetPosition(this);
var delta = pos - _panStart;
_panOffset += delta;
_isPanning = false;
Scene.Cursor = new Cursor(StandardCursorType.Arrow);
e.Handled = true;
}
private void Scene_OnWheel(object? sender, PointerWheelEventArgs e)
{
}
}
+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="DelauneyTriangulation.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>