Merge branch 'master' into aleader/notif-api-consistency
This commit is contained in:
Коммит
8b9742062d
|
@ -0,0 +1,97 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.4;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
<Title>Windows Community Toolkit Diagnostics .NET Standard</Title>
|
||||
<Description>
|
||||
This package includes .NET Standard code only helpers such as:
|
||||
- Guard: Helper methods to verify conditions when running code.
|
||||
- ThrowHelper: Helper methods to efficiently throw exceptions.
|
||||
</Description>
|
||||
<PackageTags>UWP Toolkit Windows IncrementalLoadingCollection String Array extensions helpers</PackageTags>
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition=" '$(TargetFramework)' == 'netstandard1.4' ">
|
||||
<ItemGroup>
|
||||
|
||||
<!-- .NET Standard 1.4 doesn't have the Span<T> type, ValueTuple or the [Pure] attribute -->
|
||||
<PackageReference Include="System.Diagnostics.Contracts" Version="4.3.0" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<When Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<ItemGroup>
|
||||
|
||||
<!-- .NET Standard 2.0 doesn't have the Span<T> type -->
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<When Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
|
||||
<PropertyGroup>
|
||||
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
||||
<!-- .NET Standard 2.1 doesn't have the Unsafe type -->
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<When Condition=" '$(TargetFramework)' == 'net5.0' ">
|
||||
<PropertyGroup>
|
||||
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Generated\Guard.Comparable.Numeric.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>Guard.Comparable.Numeric.g.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Generated\Guard.Collection.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>Guard.Collection.g.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Generated\ThrowHelper.Collection.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>ThrowHelper.Collection.g.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Generated\TypeInfo.ttinclude">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>TypeInfo.g.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- T4 service used by the Guard APIs -->
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Generated\Guard.Comparable.Numeric.g.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Guard.Comparable.Numeric.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Generated\Guard.Collection.g.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Guard.Collection.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Generated\ThrowHelper.Collection.g.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ThrowHelper.Collection.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Generated\TypeInfo.g.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>TypeInfo.ttinclude</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -11,7 +11,6 @@ using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
|
|||
#endif
|
||||
using Microsoft.Toolkit.HighPerformance.Enumerables;
|
||||
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
|
|
|
@ -10,7 +10,6 @@ using System.Runtime.InteropServices;
|
|||
using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
|
||||
#endif
|
||||
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
|
|
|
@ -23,7 +23,14 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe byte ToByte(this bool flag)
|
||||
{
|
||||
return *(byte*)&flag;
|
||||
// Whenever we need to take the address of an argument, we make a local copy first.
|
||||
// This will be removed by the JIT anyway, but it can help produce better codegen and
|
||||
// remove unwanted stack spills if the caller is using constant arguments. This is
|
||||
// because taking the address of an argument can interfere with some of the flow
|
||||
// analysis executed by the JIT, which can in some cases block constant propagation.
|
||||
bool copy = flag;
|
||||
|
||||
return *(byte*)©
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -58,7 +65,8 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe int ToBitwiseMask32(this bool flag)
|
||||
{
|
||||
byte rangeFlag = *(byte*)&flag;
|
||||
bool copy = flag;
|
||||
byte rangeFlag = *(byte*)©
|
||||
int
|
||||
negativeFlag = rangeFlag - 1,
|
||||
mask = ~negativeFlag;
|
||||
|
@ -77,7 +85,8 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe long ToBitwiseMask64(this bool flag)
|
||||
{
|
||||
byte rangeFlag = *(byte*)&flag;
|
||||
bool copy = flag;
|
||||
byte rangeFlag = *(byte*)©
|
||||
long
|
||||
negativeFlag = (long)rangeFlag - 1,
|
||||
mask = ~negativeFlag;
|
||||
|
|
|
@ -7,9 +7,6 @@ using System.Diagnostics.Contracts;
|
|||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#if SPAN_RUNTIME_SUPPORT
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
#endif
|
||||
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
|
|
|
@ -10,9 +10,6 @@ using System.Runtime.CompilerServices;
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
|
||||
using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces;
|
||||
#if SPAN_RUNTIME_SUPPORT
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
#endif
|
||||
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
|
|
|
@ -8,9 +8,6 @@ using System.Runtime.CompilerServices;
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Toolkit.HighPerformance.Enumerables;
|
||||
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
|
||||
#if SPAN_RUNTIME_SUPPORT
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
{
|
||||
|
|
|
@ -8,9 +8,6 @@ using System.Runtime.CompilerServices;
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Toolkit.HighPerformance.Enumerables;
|
||||
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
|
||||
#if SPAN_RUNTIME_SUPPORT
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
{
|
||||
|
|
|
@ -209,8 +209,9 @@ namespace Microsoft.Toolkit.HighPerformance.Helpers
|
|||
// and perform an OR with the resulting value of the previous
|
||||
// operation. This will always guaranteed to work, thanks to the
|
||||
// initial code clearing that bit before setting it again.
|
||||
bool copy = flag;
|
||||
uint
|
||||
flag32 = *(byte*)&flag,
|
||||
flag32 = *(byte*)©,
|
||||
shift = flag32 << n,
|
||||
or = and | shift;
|
||||
|
||||
|
@ -378,8 +379,9 @@ namespace Microsoft.Toolkit.HighPerformance.Helpers
|
|||
ulong
|
||||
bit = 1ul << n,
|
||||
not = ~bit,
|
||||
and = value & not,
|
||||
flag64 = *(byte*)&flag,
|
||||
and = value & not;
|
||||
bool copy = flag;
|
||||
ulong flag64 = *(byte*)©,
|
||||
shift = flag64 << n,
|
||||
or = and | shift;
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Helpers
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Helpers
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ using static Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
|
|||
|
||||
#pragma warning disable CA2231
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
namespace Microsoft.Toolkit.HighPerformance
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Memory2D{T}"/> represents a 2D region of arbitrary memory. It is to <see cref="Span2D{T}"/>
|
||||
|
@ -894,7 +894,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
|
|||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Microsoft.Toolkit.HighPerformance.Memory.Memory2D<{typeof(T)}>[{this.height}, {this.width}]";
|
||||
return $"Microsoft.Toolkit.HighPerformance.Memory2D<{typeof(T)}>[{this.height}, {this.width}]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -19,7 +19,7 @@ using static Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
|
|||
|
||||
#pragma warning disable CA2231
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
namespace Microsoft.Toolkit.HighPerformance
|
||||
{
|
||||
/// <summary>
|
||||
/// A readonly version of <see cref="Memory2D{T}"/>.
|
||||
|
@ -907,7 +907,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
|
|||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Microsoft.Toolkit.HighPerformance.Memory.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]";
|
||||
return $"Microsoft.Toolkit.HighPerformance.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
|
|||
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
namespace Microsoft.Toolkit.HighPerformance
|
||||
{
|
||||
/// <inheritdoc cref="ReadOnlySpan2D{T}"/>
|
||||
public readonly ref partial struct ReadOnlySpan2D<T>
|
||||
|
|
|
@ -17,7 +17,7 @@ using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.Runti
|
|||
|
||||
#pragma warning disable CS0809, CA1065
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
namespace Microsoft.Toolkit.HighPerformance
|
||||
{
|
||||
/// <summary>
|
||||
/// A readonly version of <see cref="Span2D{T}"/>.
|
||||
|
@ -974,7 +974,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
|
|||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Microsoft.Toolkit.HighPerformance.Memory.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]";
|
||||
return $"Microsoft.Toolkit.HighPerformance.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
|
|||
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
namespace Microsoft.Toolkit.HighPerformance
|
||||
{
|
||||
/// <inheritdoc cref="Span2D{T}"/>
|
||||
public readonly ref partial struct Span2D<T>
|
||||
|
|
|
@ -17,7 +17,7 @@ using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.Runti
|
|||
|
||||
#pragma warning disable CS0809, CA1065
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
namespace Microsoft.Toolkit.HighPerformance
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Span2D{T}"/> represents a 2D region of arbitrary memory. Like the <see cref="Span{T}"/> type,
|
||||
|
@ -1130,7 +1130,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
|
|||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Microsoft.Toolkit.HighPerformance.Memory.Span2D<{typeof(T)}>[{Height}, {this.width}]";
|
||||
return $"Microsoft.Toolkit.HighPerformance.Span2D<{typeof(T)}>[{Height}, {this.width}]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="$(ExtrasUwpMetaPackageVersion)" PrivateAssets="all" IsImplicitlyDefined="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- In order to support .NET Native, we need to include an appropriate .rd.xml for UWP (remove from everything else) -->
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'uap10.0.19041' and '$(TargetFramework)' != 'native'">
|
||||
<EmbeddedResource Remove="Properties\Microsoft.Toolkit.Uwp.Notifications.rd.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Microsoft.Toolkit.Uwp.Notifications.targets" Pack="true" PackagePath="build\native" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!--
|
||||
This file enable reflections for Toolkit.Notifications and all of its public/private members,
|
||||
enabling the library to work in .NET Native even if developers modified their default rd.xml.
|
||||
See issue https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3093 for more details.
|
||||
-->
|
||||
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="Microsoft.Toolkit.Uwp.Notifications">
|
||||
<!-- TODO: We can potentially minimize the metadata needed, but further
|
||||
investigation is needed to fill this in and adequately test this -->
|
||||
<Assembly Name="Microsoft.Toolkit.Uwp.Notifications" Dynamic="Required All" />
|
||||
</Library>
|
||||
</Directives>
|
|
@ -273,6 +273,7 @@
|
|||
<Content Include="SamplePages\Eyedropper\Eyedropper.png" />
|
||||
<Content Include="SamplePages\OnDevice\OnDevice.png" />
|
||||
<Content Include="SamplePages\AcrylicBrush\AcrylicBrush.png" />
|
||||
<Content Include="SamplePages\CanvasPathGeometry\CanvasPathGeometry.png" />
|
||||
<Content Include="SamplePages\PipelineBrush\PipelineBrush.png" />
|
||||
<Content Include="SamplePages\RemoteDeviceHelper\RemoteDeviceHelper.png" />
|
||||
<Content Include="SamplePages\ImageCropper\ImageCropper.png" />
|
||||
|
@ -485,6 +486,7 @@
|
|||
<Compile Include="SamplePages\AutoFocusBehavior\AutoFocusBehaviorPage.xaml.cs">
|
||||
<DependentUpon>AutoFocusBehaviorPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\CanvasPathGeometry\GeometryStreamReader.cs" />
|
||||
<Compile Include="SamplePages\ColorPicker\ColorPickerButtonPage.xaml.cs">
|
||||
<DependentUpon>ColorPickerButtonPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -494,6 +496,9 @@
|
|||
<Compile Include="SamplePages\EnumValuesExtension\EnumValuesExtensionPage.xaml.cs">
|
||||
<DependentUpon>EnumValuesExtensionPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\CanvasPathGeometry\CanvasPathGeometryPage.xaml.cs">
|
||||
<DependentUpon>CanvasPathGeometryPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\FocusBehavior\FocusBehaviorPage.xaml.cs">
|
||||
<DependentUpon>FocusBehaviorPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -975,6 +980,10 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\CanvasPathGeometry\CanvasPathGeometryPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\TilesBrush\TilesBrushPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png
Normal file
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.5 KiB |
|
@ -0,0 +1,156 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.CanvasPathGeometryPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="0.1*" />
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="0.9*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Margin="12,3"
|
||||
VerticalAlignment="Center"
|
||||
Text="Samples: " />
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"
|
||||
MinWidth="145" />
|
||||
<ColumnDefinition Width="*"
|
||||
MinWidth="145" />
|
||||
<ColumnDefinition Width="*"
|
||||
MinWidth="145" />
|
||||
<ColumnDefinition Width="*"
|
||||
MinWidth="145" />
|
||||
<ColumnDefinition Width="*"
|
||||
MinWidth="145" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0"
|
||||
Width="120"
|
||||
Height="36"
|
||||
Margin="10,3"
|
||||
HorizontalAlignment="Center"
|
||||
Click="{x:Bind OnShowRoundedStarSample}"
|
||||
Content="Rounded Star" />
|
||||
<Button Grid.Column="1"
|
||||
Width="120"
|
||||
Height="36"
|
||||
Margin="10,3"
|
||||
HorizontalAlignment="Center"
|
||||
Click="{x:Bind OnShowStarSample}"
|
||||
Content="Star" />
|
||||
<Button Grid.Column="2"
|
||||
Width="120"
|
||||
Height="36"
|
||||
Margin="10,3"
|
||||
HorizontalAlignment="Center"
|
||||
Click="{x:Bind OnShowSpiralSample}"
|
||||
Content="Spiral" />
|
||||
<Button Grid.Column="3"
|
||||
Width="120"
|
||||
Height="36"
|
||||
Margin="10,3"
|
||||
HorizontalAlignment="Center"
|
||||
Click="{x:Bind OnShowFlowerSample}"
|
||||
Content="Flower" />
|
||||
<Button Grid.Column="4"
|
||||
Width="120"
|
||||
Height="36"
|
||||
Margin="10,3"
|
||||
HorizontalAlignment="Center"
|
||||
Click="{x:Bind OnShowGearSample}"
|
||||
Content="Gear" />
|
||||
|
||||
</Grid>
|
||||
<TextBox x:Name="InputData"
|
||||
Grid.Row="2"
|
||||
Margin="10,3"
|
||||
FontFamily="Courier New"
|
||||
FontSize="14"
|
||||
PlaceholderText="Enter SVG/XAML Path Data and press Parse. (Or click on one of the Samples from above)"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
SelectionHighlightColor="#007aff"
|
||||
Text="{Binding InputText, Mode=TwoWay}"
|
||||
TextChanged="{x:Bind OnInputTextChanged}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button Grid.Row="3"
|
||||
Width="120"
|
||||
Height="36"
|
||||
Margin="10,3"
|
||||
HorizontalAlignment="Left"
|
||||
Click="{x:Bind OnClearCanvas}"
|
||||
Content="Clear" />
|
||||
<Pivot x:Name="RootPivot"
|
||||
Grid.Row="4">
|
||||
<PivotItem Foreground="Black"
|
||||
Header="Canvas">
|
||||
<ScrollViewer x:Name="RenderScroll"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<win2d:CanvasControl x:Name="RenderCanvas"
|
||||
Width="{Binding ElementName=RenderScroll, Path=ActualWidth}"
|
||||
Height="{Binding ElementName=RenderScroll, Path=ActualHeight}"
|
||||
MinWidth="1200"
|
||||
MinHeight="800"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
ClearColor="#A7A7A7"
|
||||
Draw="{x:Bind OnCanvasDraw}" />
|
||||
</ScrollViewer>
|
||||
</PivotItem>
|
||||
<PivotItem Foreground="Black"
|
||||
Header="Commands">
|
||||
<ScrollViewer Background="#272727"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock x:Name="CommandsList"
|
||||
Padding="10"
|
||||
FontFamily="Courier New"
|
||||
FontSize="16"
|
||||
Foreground="White"
|
||||
IsTextSelectionEnabled="True"
|
||||
SelectionHighlightColor="#007aff"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
<StackPanel Grid.Row="4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Margin="10,10,5,-10"
|
||||
Text="StrokeThickness" />
|
||||
<Slider x:Name="StrokeThickness"
|
||||
Width="150"
|
||||
Height="30"
|
||||
Margin="0,4,10,4"
|
||||
Background="White"
|
||||
Foreground="White"
|
||||
Maximum="10"
|
||||
Minimum="0"
|
||||
StepFrequency="0.1"
|
||||
ValueChanged="{x:Bind OnStrokeThicknessChanged}" />
|
||||
<TextBlock Margin="10,10,5,-10"
|
||||
Text="Stroke Color: " />
|
||||
<ComboBox x:Name="StrokeList"
|
||||
Width="150"
|
||||
Height="30"
|
||||
Margin="0,4,10,4"
|
||||
SelectionChanged="{x:Bind OnStrokeColorChanged}" />
|
||||
<TextBlock Margin="10,10,5,-10"
|
||||
Text="Fill Color: " />
|
||||
<ComboBox x:Name="FillList"
|
||||
Width="150"
|
||||
Height="30"
|
||||
Margin="0,4,10,4"
|
||||
SelectionChanged="{x:Bind OnFillColorChanged}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,330 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class CanvasPathGeometryPage : Page
|
||||
{
|
||||
private const string Sample1 =
|
||||
"F0 M 656.500,400.500 C 656.500,350.637 598.572,307.493 514.292,286.708 C 493.507,202.428 450.363,144.500 400.500,144.500 C 350.637,144.500 307.493,202.428 286.708,286.708 C 202.428,307.493 144.500,350.637 144.500,400.500 C 144.500,450.363 202.428,493.507 286.708,514.292 C 307.493,598.572 350.637,656.500 400.500,656.500 C 450.363,656.500 493.507,598.572 514.292,514.292 C 598.572,493.507 656.500,450.363 656.500,400.500 ZM 581.519,219.481 C 546.261,184.222 474.793,194.676 400.500,239.574 C 326.207,194.676 254.739,184.222 219.481,219.481 C 184.222,254.739 194.676,326.207 239.574,400.500 C 194.676,474.792 184.222,546.261 219.481,581.519 C 254.739,616.778 326.207,606.324 400.500,561.426 C 474.793,606.324 546.261,616.778 581.519,581.519 C 616.778,546.261 606.324,474.792 561.426,400.500 C 606.324,326.207 616.778,254.739 581.519,219.481 ZU 112.5 112.5 570 570 36 36";
|
||||
|
||||
private const string Sample2 =
|
||||
"F1 M 331.341,81.975 L 398.766,218.593 L 549.533,240.500 L 440.437,346.842 L 466.191,497.000 L 331.341,426.105 L 196.491,497.000 L 222.245,346.842 L 113.150,240.500 L 263.916,218.593 L 331.341,81.975 Z";
|
||||
|
||||
private const string Sample3 =
|
||||
"F1 M 545.497,397.058 C 454.492,512.882 286.824,533.003 171.000,441.998 C 78.340,369.194 62.244,235.059 135.048,142.400 C 193.291,68.272 300.599,55.395 374.726,113.639 C 434.028,160.233 444.330,246.079 397.736,305.381 C 360.460,352.823 291.783,361.064 244.341,323.788 C 206.388,293.968 199.795,239.026 229.616,201.073 C 253.472,170.711 297.425,165.436 327.788,189.293 C 352.078,208.378 356.297,243.540 337.212,267.830 C 321.944,287.262 293.814,290.638 274.382,275.370 C 258.836,263.155 256.136,240.651 268.350,225.106 C 278.122,212.669 296.125,210.509 308.562,220.280 C 318.511,228.098 320.239,242.500 312.422,252.449";
|
||||
|
||||
private const string Sample4 =
|
||||
"F1 M 311.717,332.110 C 285.669,332.110 264.552,310.994 264.552,284.945 C 264.552,258.897 285.669,237.781 311.717,237.781 C 337.765,237.781 358.881,258.897 358.881,284.945 C 358.881,310.994 337.765,332.110 311.717,332.110 Z M 505.712,232.846 C 634.939,203.833 411.705,171.395 371.772,213.383 C 411.705,171.395 311.872,-30.889 311.872,92.013 C 311.872,-30.889 212.038,171.395 251.972,213.383 C 212.038,171.395 -11.196,203.833 118.031,232.846 C -11.196,203.833 150.338,361.289 214.951,327.320 C 150.338,361.289 112.205,583.622 192.072,460.719 C 112.205,583.622 311.872,478.651 311.872,397.737 C 311.872,478.651 511.538,583.622 431.672,460.719 C 511.538,583.622 473.405,361.289 408.792,327.320 C 473.405,361.289 634.939,203.833 505.712,232.846 Z";
|
||||
|
||||
private const string Sample5 =
|
||||
"F1 M 391.853,348.284 C 391.853,357.113 384.696,364.271 375.867,364.271 L 301.927,364.271 C 293.098,364.271 285.940,357.113 285.940,348.284 L 285.940,274.345 C 285.940,265.515 293.098,258.358 301.927,258.358 L 375.867,258.358 C 384.696,258.358 391.853,265.515 391.853,274.345 L 391.853,348.284 Z M 544.748,282.990 L 485.488,267.081 C 472.521,263.600 466.301,248.839 472.866,237.128 L 502.867,183.604 C 512.642,166.166 494.336,146.433 476.214,154.872 L 420.592,180.776 C 408.421,186.445 394.169,179.136 391.670,165.944 L 380.248,105.658 C 376.526,86.017 349.819,82.667 341.362,100.780 L 315.403,156.378 C 309.723,168.543 294.107,172.105 283.714,163.607 L 236.213,124.767 C 220.737,112.113 198.125,126.714 203.289,146.025 L 219.141,205.301 C 222.610,218.271 212.937,231.038 199.512,231.208 L 138.159,231.988 C 118.170,232.242 110.233,257.962 126.602,269.436 L 176.847,304.655 C 187.841,312.361 188.638,328.358 178.464,337.118 L 131.965,377.153 C 116.816,390.196 127.269,415.001 147.184,413.268 L 208.312,407.950 C 221.687,406.786 232.580,418.529 230.417,431.779 L 220.531,492.336 C 217.310,512.066 241.261,524.348 255.403,510.220 L 298.811,466.854 C 308.310,457.365 324.202,459.358 331.062,470.899 L 362.415,523.643 C 372.629,540.827 398.872,534.840 400.624,514.927 L 406.001,453.804 C 407.178,440.430 420.634,431.742 433.307,436.173 L 491.227,456.425 C 510.098,463.022 526.353,441.568 514.895,425.187 L 479.725,374.908 C 472.030,363.906 476.753,348.601 489.310,343.850 L 546.697,322.133 C 565.393,315.057 564.054,288.173 544.748,282.990 Z";
|
||||
|
||||
private const string ErrorString =
|
||||
"F1 M 19.648,24.605 L 19.648,30.220 L 29.404,30.220 L 29.404,28.149 C 29.404,27.229 29.581,26.573 29.936,26.181 C 30.290,25.790 30.753,25.594 31.325,25.594 C 31.885,25.594 " +
|
||||
"32.342,25.790 32.696,26.181 C 33.051,26.573 33.228,27.229 33.228,28.149 L 33.228,34.044 L 15.227,34.044 C 14.307,34.044 13.651,33.867 13.259,33.512 C 12.867,33.158 12.672,32.695 " +
|
||||
"12.672,32.122 C 12.672,31.563 12.870,31.106 13.268,30.751 C 13.666,30.397 14.319,30.220 15.227,30.220 L 15.824,30.220 L 15.824,15.260 L 15.227,15.260 C 14.307,15.260 13.651,15.082 " +
|
||||
"13.259,14.728 C 12.867,14.373 12.672,13.910 12.672,13.338 C 12.672,12.766 12.867,12.303 13.259,11.948 C 13.651,11.594 14.307,11.417 15.227,11.417 L 32.388,11.436 L 32.388,17.255 C " +
|
||||
"32.388,18.163 32.214,18.813 31.866,19.205 C 31.518,19.596 31.058,19.792 30.486,19.792 C 29.914,19.792 29.451,19.600 29.096,19.214 C 28.742,18.829 28.564,18.176 28.564,17.255 L " +
|
||||
"28.564,15.260 L 19.648,15.260 L 19.648,20.781 L 23.006,20.781 C 23.006,19.786 23.099,19.146 23.285,18.860 C 23.671,18.250 24.218,17.946 24.927,17.946 C 25.487,17.946 25.944,18.142 " +
|
||||
"26.298,18.533 C 26.652,18.925 26.830,19.581 26.830,20.501 L 26.830,24.903 C 26.830,25.737 26.730,26.297 26.531,26.582 C 26.133,27.167 25.599,27.459 24.927,27.459 C 24.218,27.459 " +
|
||||
"23.671,27.155 23.285,26.545 C 23.099,26.259 23.006,25.612 23.006,24.605 L 19.648,24.605 ZM 45.707,17.106 L 45.707,19.494 C 47.311,18.337 48.577,17.567 49.503,17.181 C 50.430,16.795 " +
|
||||
"51.297,16.603 52.105,16.603 C 53.349,16.603 54.555,17.063 55.724,17.983 C 56.520,18.605 56.918,19.239 56.918,19.886 C 56.918,20.433 56.728,20.896 56.349,21.275 C 55.970,21.655 " +
|
||||
"55.513,21.844 54.978,21.844 C 54.505,21.844 54.008,21.608 53.486,21.135 C 52.963,20.663 52.497,20.427 52.087,20.427 C 51.552,20.427 50.753,20.762 49.690,21.434 C 48.626,22.105 " +
|
||||
"47.299,23.113 45.707,24.456 L 45.707,30.220 L 51.154,30.220 C 52.074,30.220 52.730,30.397 53.122,30.751 C 53.514,31.106 53.710,31.569 53.710,32.141 C 53.710,32.701 53.514,33.158 " +
|
||||
"53.122,33.512 C 52.730,33.867 52.074,34.044 51.154,34.044 L 39.607,34.044 C 38.687,34.044 38.031,33.867 37.639,33.512 C 37.248,33.158 37.052,32.695 37.052,32.122 C 37.052,31.563 " +
|
||||
"37.248,31.106 37.639,30.751 C 38.031,30.397 38.687,30.220 39.607,30.220 L 41.883,30.220 L 41.883,20.930 L 40.503,20.930 C 39.582,20.930 38.927,20.753 38.535,20.399 C 38.143,20.044 " +
|
||||
"37.947,19.581 37.947,19.009 C 37.947,18.449 38.143,17.992 38.535,17.638 C 38.927,17.283 39.582,17.106 40.503,17.106 L 45.707,17.106 ZM 68.633,17.106 L 68.633,19.494 C 70.237,18.337 " +
|
||||
"71.502,17.567 72.429,17.181 C 73.355,16.795 74.222,16.603 75.031,16.603 C 76.274,16.603 77.480,17.063 78.650,17.983 C 79.445,18.605 79.843,19.239 79.843,19.886 C 79.843,20.433 " +
|
||||
"79.654,20.896 79.274,21.275 C 78.895,21.655 78.438,21.844 77.903,21.844 C 77.431,21.844 76.933,21.608 76.411,21.135 C 75.889,20.663 75.423,20.427 75.012,20.427 C 74.477,20.427 " +
|
||||
"73.678,20.762 72.615,21.434 C 71.552,22.105 70.224,23.113 68.633,24.456 L 68.633,30.220 L 74.079,30.220 C 74.999,30.220 75.656,30.397 76.047,30.751 C 76.439,31.106 76.635,31.569 " +
|
||||
"76.635,32.141 C 76.635,32.701 76.439,33.158 76.047,33.512 C 75.656,33.867 74.999,34.044 74.079,34.044 L 62.533,34.044 C 61.612,34.044 60.957,33.867 60.565,33.512 C 60.173,33.158 " +
|
||||
"59.977,32.695 59.977,32.122 C 59.977,31.563 60.173,31.106 60.565,30.751 C 60.957,30.397 61.612,30.220 62.533,30.220 L 64.809,30.220 L 64.809,20.930 L 63.428,20.930 C 62.508,20.930 " +
|
||||
"61.852,20.753 61.460,20.399 C 61.069,20.044 60.873,19.581 60.873,19.009 C 60.873,18.449 61.069,17.992 61.460,17.638 C 61.852,17.283 62.508,17.106 63.428,17.106 L 68.633,17.106 ZM " +
|
||||
"98.460,25.911 C 98.460,24.680 98.018,23.548 97.135,22.516 C 95.929,21.123 94.343,20.427 92.379,20.427 C 90.650,20.427 89.208,20.980 88.051,22.087 C 86.895,23.194 86.316,24.474 " +
|
||||
"86.316,25.929 C 86.316,27.123 86.901,28.239 88.070,29.278 C 89.239,30.316 90.675,30.835 92.379,30.835 C 94.095,30.835 95.537,30.316 96.706,29.278 C 97.875,28.239 98.460,27.117 " +
|
||||
"98.460,25.911 Z M 102.284,25.892 C 102.284,27.360 101.876,28.780 101.062,30.154 C 100.247,31.529 99.035,32.623 97.425,33.438 C 95.814,34.252 94.132,34.659 92.379,34.659 C " +
|
||||
"90.638,34.659 88.971,34.258 87.380,33.456 C 85.788,32.654 84.575,31.563 83.742,30.182 C 82.909,28.802 82.492,27.360 82.492,25.855 C 82.492,24.325 82.915,22.824 83.761,21.350 C " +
|
||||
"84.606,19.876 85.822,18.717 87.408,17.871 C 88.993,17.026 90.650,16.603 92.379,16.603 C 94.119,16.603 95.795,17.035 97.406,17.899 C 99.016,18.763 100.232,19.926 101.053,21.387 C " +
|
||||
"101.873,22.849 102.284,24.350 102.284,25.892 ZM 114.483,17.106 L 114.483,19.494 C 116.088,18.337 117.353,17.567 118.279,17.181 C 119.206,16.795 120.073,16.603 120.882,16.603 C " +
|
||||
"122.125,16.603 123.331,17.063 124.500,17.983 C 125.296,18.605 125.694,19.239 125.694,19.886 C 125.694,20.433 125.504,20.896 125.125,21.275 C 124.746,21.655 124.289,21.844 " +
|
||||
"123.754,21.844 C 123.282,21.844 122.784,21.608 122.262,21.135 C 121.740,20.663 121.273,20.427 120.863,20.427 C 120.328,20.427 119.529,20.762 118.466,21.434 C 117.403,22.105 " +
|
||||
"116.075,23.113 114.483,24.456 L 114.483,30.220 L 119.930,30.220 C 120.850,30.220 121.506,30.397 121.898,30.751 C 122.290,31.106 122.486,31.569 122.486,32.141 C 122.486,32.701 " +
|
||||
"122.290,33.158 121.898,33.512 C 121.506,33.867 120.850,34.044 119.930,34.044 L 108.384,34.044 C 107.463,34.044 106.807,33.867 106.416,33.512 C 106.024,33.158 105.828,32.695 " +
|
||||
"105.828,32.122 C 105.828,31.563 106.024,31.106 106.416,30.751 C 106.807,30.397 107.463,30.220 108.384,30.220 L 110.659,30.220 L 110.659,20.930 L 109.279,20.930 C 108.359,20.930 " +
|
||||
"107.703,20.753 107.311,20.399 C 106.919,20.044 106.723,19.581 106.723,19.009 C 106.723,18.449 106.919,17.992 107.311,17.638 C 107.703,17.283 108.359,17.106 109.279,17.106 L " +
|
||||
"114.483,17.106 ZM 140.431,32.645 C 140.431,33.192 140.225,33.655 139.815,34.034 C 139.405,34.414 138.838,34.603 138.118,34.603 C 137.396,34.603 136.830,34.414 136.420,34.034 C " +
|
||||
"136.010,33.655 135.804,33.192 135.804,32.645 C 135.804,32.110 136.006,31.653 136.411,31.274 C 136.815,30.895 137.384,30.705 138.118,30.705 C 138.851,30.705 139.420,30.891 " +
|
||||
"139.824,31.264 C 140.228,31.637 140.431,32.098 140.431,32.645 Z M 141.046,13.655 L 139.983,25.183 C 139.933,25.780 139.734,26.244 139.386,26.573 C 139.038,26.903 138.603,27.067 " +
|
||||
"138.080,27.067 C 137.558,27.067 137.123,26.903 136.774,26.573 C 136.426,26.244 136.227,25.780 136.178,25.183 L 135.096,13.655 C 135.046,13.071 135.021,12.685 135.021,12.499 C " +
|
||||
"135.021,11.529 135.313,10.749 135.898,10.158 C 136.482,9.567 137.210,9.272 138.080,9.272 C 138.938,9.272 139.662,9.570 140.253,10.167 C 140.844,10.764 141.139,11.516 141.139,12.424 " +
|
||||
"C 141.139,12.611 141.108,13.021 141.046,13.655 Z";
|
||||
|
||||
private const string ParseError1 = "Parameter \"(pathData matches.Count == 0)\" must be false, was true: ";
|
||||
private const string ParseError2 = "Parameter \"(pathData matches.Count > 1)\" must be false, was true: ";
|
||||
private const string ParseError3 = "PATH_ERR003";
|
||||
|
||||
private DispatcherQueueTimer _typeTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
|
||||
private List<Color> _colors;
|
||||
private List<string> _samples;
|
||||
|
||||
private string _data = string.Empty;
|
||||
private StringBuilder _logger;
|
||||
|
||||
private float _strokeThickness;
|
||||
private Color _strokeColor;
|
||||
private Color _fillColor;
|
||||
private bool _selectionChanged = false;
|
||||
private bool _isParsing = false;
|
||||
|
||||
private CanvasGeometry _errorGeometry;
|
||||
private GeometryStreamReader _reader;
|
||||
|
||||
private SolidColorBrush _commandBrush;
|
||||
private SolidColorBrush _commandErrorBrush;
|
||||
|
||||
public string InputText { get; set; }
|
||||
|
||||
public CanvasPathGeometryPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
_reader = new GeometryStreamReader();
|
||||
_logger = new StringBuilder();
|
||||
_colors = new List<Color>()
|
||||
{
|
||||
Colors.Transparent,
|
||||
Colors.Black,
|
||||
Colors.White,
|
||||
Colors.Crimson,
|
||||
CanvasPathGeometry.CreateColor("#bf5af2"),
|
||||
CanvasPathGeometry.CreateColor("#0a84ff"),
|
||||
CanvasPathGeometry.CreateColor("#32d74b"),
|
||||
CanvasPathGeometry.CreateColor("#ff9500"),
|
||||
CanvasPathGeometry.CreateColor("#ffd60a")
|
||||
};
|
||||
|
||||
_commandBrush = new SolidColorBrush(Colors.White);
|
||||
_commandErrorBrush = new SolidColorBrush(Colors.Red);
|
||||
|
||||
var colorList = new List<string>()
|
||||
{
|
||||
"Transparent",
|
||||
"Black",
|
||||
"White",
|
||||
"Crimson",
|
||||
"Purple",
|
||||
"LightBlue",
|
||||
"LightGreen",
|
||||
"Orange",
|
||||
"Yellow"
|
||||
};
|
||||
|
||||
this._samples = new List<string>()
|
||||
{
|
||||
string.Empty,
|
||||
Sample1,
|
||||
Sample2,
|
||||
Sample3,
|
||||
Sample4,
|
||||
Sample5
|
||||
};
|
||||
|
||||
var sampleList = new List<string>()
|
||||
{
|
||||
"None",
|
||||
"Sample 1",
|
||||
"Sample 2",
|
||||
"Sample 3",
|
||||
"Sample 4",
|
||||
"Sample 5",
|
||||
};
|
||||
|
||||
StrokeList.ItemsSource = colorList;
|
||||
FillList.ItemsSource = colorList;
|
||||
|
||||
StrokeThickness.Value = 1;
|
||||
StrokeList.SelectedIndex = 1;
|
||||
FillList.SelectedIndex = 0;
|
||||
|
||||
_selectionChanged = false;
|
||||
}
|
||||
|
||||
private void ParseData()
|
||||
{
|
||||
_data = InputData.Text;
|
||||
_isParsing = true;
|
||||
RenderCanvas.Invalidate();
|
||||
_isParsing = false;
|
||||
}
|
||||
|
||||
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_data))
|
||||
{
|
||||
CommandsList.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
this._errorGeometry ??= CanvasPathGeometry.CreateGeometry(sender, ErrorString);
|
||||
|
||||
_logger?.Clear();
|
||||
CommandsList.Text = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
_logger?.AppendLine("// The following commands represent the CanvasPathBuilder command(s) needed");
|
||||
_logger?.AppendLine("// to create the CanvasGeometry from the specified Win2d Path Mini Language.");
|
||||
var geometry = CanvasPathGeometry.CreateGeometry(sender, _data);
|
||||
_reader.StartLogging();
|
||||
geometry.SendPathTo(_reader);
|
||||
_logger?.AppendLine(_reader.EndLogging());
|
||||
CommandsList.Text = _logger?.ToString() ?? string.Empty;
|
||||
|
||||
args.DrawingSession.FillGeometry(geometry, _fillColor);
|
||||
args.DrawingSession.DrawGeometry(geometry, _strokeColor, _strokeThickness);
|
||||
RootPivot.SelectedIndex = 0;
|
||||
CommandsList.Foreground = _commandBrush;
|
||||
}
|
||||
catch (ArgumentException argEx)
|
||||
{
|
||||
var message = argEx.Message;
|
||||
var errorCode = message.Substring(0, 11);
|
||||
var parseError = string.Empty;
|
||||
if (message.StartsWith(ParseError1))
|
||||
{
|
||||
parseError = "Parse Error: No matching data!";
|
||||
}
|
||||
else if (message.StartsWith(ParseError2))
|
||||
{
|
||||
parseError = "Parse Error: Multiple FillRule elements present in Path Data!";
|
||||
}
|
||||
else if (message.StartsWith(ParseError3))
|
||||
{
|
||||
var tokens = message.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (tokens.Length == 3)
|
||||
{
|
||||
parseError = $"Parse Error at {tokens[1]}. Cannot parse '{tokens[2]}'.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parseError = "Parsing error! Invalid input data!";
|
||||
}
|
||||
|
||||
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
|
||||
CommandsList.Text = parseError;
|
||||
RootPivot.SelectedIndex = 1;
|
||||
CommandsList.Foreground = _commandErrorBrush;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
|
||||
CommandsList.Text = "Parsing error! Invalid input data!";
|
||||
RootPivot.SelectedIndex = 1;
|
||||
CommandsList.Foreground = _commandErrorBrush;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStrokeThicknessChanged(object sender, RangeBaseValueChangedEventArgs e)
|
||||
{
|
||||
_strokeThickness = (float)StrokeThickness.Value;
|
||||
_selectionChanged = true;
|
||||
RenderCanvas.Invalidate();
|
||||
}
|
||||
|
||||
private void OnStrokeColorChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (StrokeList.SelectedIndex > -1)
|
||||
{
|
||||
_strokeColor = _colors[StrokeList.SelectedIndex];
|
||||
_selectionChanged = true;
|
||||
}
|
||||
|
||||
RenderCanvas.Invalidate();
|
||||
}
|
||||
|
||||
private void OnFillColorChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (FillList.SelectedIndex > -1)
|
||||
{
|
||||
_fillColor = _colors[FillList.SelectedIndex];
|
||||
_selectionChanged = true;
|
||||
}
|
||||
|
||||
RenderCanvas.Invalidate();
|
||||
}
|
||||
|
||||
private void ShowSample(int index)
|
||||
{
|
||||
InputData.Text = _samples.ElementAt(index);
|
||||
|
||||
if (!_selectionChanged)
|
||||
{
|
||||
StrokeThickness.Value = 4;
|
||||
StrokeList.SelectedIndex = 1;
|
||||
FillList.SelectedIndex = 5;
|
||||
_selectionChanged = false;
|
||||
}
|
||||
|
||||
_data = InputData.Text;
|
||||
RenderCanvas.Invalidate();
|
||||
}
|
||||
|
||||
private void OnClearCanvas(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InputData.Text = string.Empty;
|
||||
ParseData();
|
||||
}
|
||||
|
||||
private void OnShowRoundedStarSample(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSample(1);
|
||||
}
|
||||
|
||||
private void OnShowStarSample(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSample(2);
|
||||
}
|
||||
|
||||
private void OnShowSpiralSample(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSample(3);
|
||||
}
|
||||
|
||||
private void OnShowFlowerSample(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSample(4);
|
||||
}
|
||||
|
||||
private void OnShowGearSample(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSample(5);
|
||||
}
|
||||
|
||||
public void OnInputTextChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Call the ParseData method only after 0.3 seconds have elapsed since last trigger.
|
||||
_typeTimer.Debounce(ParseData, TimeSpan.FromSeconds(0.3));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to read the <see cref="CanvasGeometry"/> path data.
|
||||
/// </summary>
|
||||
internal class GeometryStreamReader : ICanvasPathReceiver
|
||||
{
|
||||
private readonly StringBuilder _cmdBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GeometryStreamReader"/> class.
|
||||
/// </summary>
|
||||
public GeometryStreamReader()
|
||||
{
|
||||
_cmdBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts logging the data for the sample app
|
||||
/// </summary>
|
||||
public void StartLogging()
|
||||
{
|
||||
_cmdBuilder.Clear();
|
||||
_cmdBuilder.AppendLine($"using (var pathBuilder = new CanvasPathBuilder(null))");
|
||||
_cmdBuilder.AppendLine("{\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes reading the geometry path data and returns the data as formatted string.
|
||||
/// </summary>
|
||||
/// <returns><see cref="CanvasPathBuilder"/> commands to create the CanvasGeometry</returns>
|
||||
public string EndLogging()
|
||||
{
|
||||
_cmdBuilder.AppendLine("}");
|
||||
return _cmdBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new figure at the specified point, with the specified figure fill option.
|
||||
/// </summary>
|
||||
/// <param name="point">Start point</param>
|
||||
/// <param name="fill"><see cref="CanvasFigureFill"/></param>
|
||||
public void BeginFigure(Vector2 point, CanvasFigureFill fill)
|
||||
{
|
||||
_cmdBuilder.AppendLine($"\n pathBuilder.BeginFigure(new Vector2({point.X}, {point.Y}));");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a single arc to the path, specified by start and end points through which an ellipse will be fitted.
|
||||
/// </summary>
|
||||
/// <param name="point">Start Point</param>
|
||||
/// <param name="x">radiusX</param>
|
||||
/// <param name="y">radiusY</param>
|
||||
/// <param name="z">rotationAngle</param>
|
||||
/// <param name="sweepDirection"><see cref="CanvasSweepDirection"/></param>
|
||||
/// <param name="arcSize"><see cref="CanvasArcSize"/></param>
|
||||
public void AddArc(Vector2 point, float x, float y, float z, CanvasSweepDirection sweepDirection, CanvasArcSize arcSize)
|
||||
{
|
||||
_cmdBuilder.AppendLine($" pathBuilder.AddArc(new Vector2({point.X}, {point.Y}), {x}, {y}, {z}, {sweepDirection}, {arcSize});");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a cubic bezier to the path. The bezier starts where the path left off, and has the specified control points and end point.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint1">First ControlPoint</param>
|
||||
/// <param name="controlPoint2">Second Control Point</param>
|
||||
/// <param name="endPoint">EndPoint</param>
|
||||
public void AddCubicBezier(Vector2 controlPoint1, Vector2 controlPoint2, Vector2 endPoint)
|
||||
{
|
||||
_cmdBuilder.AppendLine($" pathBuilder.AddCubicBezier(new Vector2({controlPoint1.X}, {controlPoint1.Y}), new Vector2({controlPoint2.X}, {controlPoint2.Y}), new Vector2({endPoint.X}, {endPoint.Y}));");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a line segment to the path, with the specified end point.
|
||||
/// </summary>
|
||||
/// <param name="endPoint">EndPoint</param>
|
||||
public void AddLine(Vector2 endPoint)
|
||||
{
|
||||
_cmdBuilder.AppendLine($" pathBuilder.AddLine(new Vector2({endPoint.X}, {endPoint.Y}));");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a quadratic bezier to the path. The bezier starts where the path left off, and has the specified control point and end point.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint">Control Point</param>
|
||||
/// <param name="endPoint">EndPoint</param>
|
||||
public void AddQuadraticBezier(Vector2 controlPoint, Vector2 endPoint)
|
||||
{
|
||||
_cmdBuilder.AppendLine($" pathBuilder.AddQuadraticBezier(new Vector2({controlPoint.X}, {controlPoint.Y}), new Vector2({endPoint.X}, {endPoint.Y}));");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the method used to determine which points are inside the geometry described by this path builder, and which points are outside.
|
||||
/// </summary>
|
||||
/// <param name="filledRegionDetermination"><see cref="CanvasFilledRegionDetermination"/></param>
|
||||
public void SetFilledRegionDetermination(CanvasFilledRegionDetermination filledRegionDetermination)
|
||||
{
|
||||
_cmdBuilder.AppendLine($" pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.{filledRegionDetermination});");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies stroke and join options to be applied to new segments added to the path builder.
|
||||
/// </summary>
|
||||
/// <param name="figureSegmentOptions"><see cref="CanvasFigureSegmentOptions"/></param>
|
||||
public void SetSegmentOptions(CanvasFigureSegmentOptions figureSegmentOptions)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// >Ends the current figure; optionally, closes it.
|
||||
/// </summary>
|
||||
/// <param name="figureLoop"><see cref="CanvasFigureLoop"/></param>
|
||||
public void EndFigure(CanvasFigureLoop figureLoop)
|
||||
{
|
||||
_cmdBuilder.AppendLine($" pathBuilder.EndFigure({(figureLoop == CanvasFigureLoop.Closed ? "CanvasFigureLoop.Closed" : "CanvasFigureLoop.Open")});");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -900,6 +900,14 @@
|
|||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/MarkdownParser.md"
|
||||
},
|
||||
{
|
||||
"Name": "Win2d Path Mini Language Parser",
|
||||
"Type": "CanvasPathGeometryPage",
|
||||
"Subcategory": "Parser",
|
||||
"About": "CanvasPathGeometry class allows you to convert Win2d Path Mini Language string to CanvasGeometry, Brushes, CanvasStrokes or CanvasStrokeStyles.",
|
||||
"Icon": "/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/CanvasPathGeometry.md"
|
||||
},
|
||||
{
|
||||
"Name": "LiveTile",
|
||||
"Type": "LiveTilePage",
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
@ -605,7 +604,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
if (!Matrix4x4.Decompose(to, out Vector3 toScale, out Quaternion toRotation, out Vector3 toTranslation))
|
||||
{
|
||||
ThrowHelper.ThrowArgumentException("The destination matrix could not be decomposed");
|
||||
ThrowThrowArgumentExceptionForToDecompose();
|
||||
}
|
||||
|
||||
Vector3? fromScale = null;
|
||||
|
@ -616,7 +615,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
if (!Matrix4x4.Decompose(from.GetValueOrDefault(), out Vector3 scale3, out Quaternion rotation4, out Vector3 translation3))
|
||||
{
|
||||
ThrowHelper.ThrowArgumentException("The initial matrix could not be decomposed");
|
||||
ThrowThrowArgumentExceptionForFromDecompose();
|
||||
}
|
||||
|
||||
fromScale = scale3;
|
||||
|
@ -629,6 +628,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Translation(toTranslation, fromTranslation, delay, duration, easingType, easingMode);
|
||||
|
||||
return this;
|
||||
|
||||
static void ThrowThrowArgumentExceptionForToDecompose() => throw new ArgumentException("The destination matrix could not be decomposed");
|
||||
static void ThrowThrowArgumentExceptionForFromDecompose() => throw new ArgumentException("The initial matrix could not be decomposed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -6,7 +6,6 @@ using System;
|
|||
using System.Diagnostics.Contracts;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Composition;
|
||||
|
@ -161,7 +160,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
easingFunction);
|
||||
}
|
||||
|
||||
return ThrowHelper.ThrowInvalidOperationException<CompositionAnimation>("Invalid animation type");
|
||||
throw new InvalidOperationException("Invalid animation type");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -216,7 +215,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
easingFunction);
|
||||
}
|
||||
|
||||
return ThrowHelper.ThrowInvalidOperationException<Timeline>("Invalid animation type");
|
||||
throw new InvalidOperationException("Invalid animation type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -229,9 +228,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
private TValue GetToAs<TValue>()
|
||||
where TValue : unmanaged
|
||||
{
|
||||
T to = To;
|
||||
|
||||
return Unsafe.As<T, TValue>(ref to);
|
||||
// We employ this (T2)(object)t1 pattern multiple times in this library to alter generics.
|
||||
// This is an equivalent but safer alternative to using Unsafe.As<TFrom, TTo>(ref TFrom).
|
||||
// For instance, this method will result in the following IL being emitted:
|
||||
// =============================
|
||||
// IL_0000: ldarg.0
|
||||
// IL_0001: call instance !0 class AnimationFactory`1<!T>::get_To()
|
||||
// IL_0006: box !T
|
||||
// IL_000b: unbox.any !!TValue
|
||||
// IL_0010: ret
|
||||
// =============================
|
||||
// The key point is that the JIT (and AOT compilers such as .NET Native) can recognize this
|
||||
// pattern and optimize the boxing away in case the types match. This is the case whenever
|
||||
// the generic arguments are value types, which due to generic types in .NET being reified
|
||||
// results in a completely different generic instantiation of the same method, making the
|
||||
// type arguments effectively constant values known at compile time, ie. at JIT time.
|
||||
// As a result of this, the boxing is completely avoided and the value is returned directly.
|
||||
// Leveraging this pattern lets us keep the same optimal codegen while avoiding the extra
|
||||
// NuGet package dependency on UWP, and the more dangerous path using the Unsafe APIs.
|
||||
// As an example, assuming T is float, the JIT will produce the following codegen on x64:
|
||||
// =============================
|
||||
// L0000: vzeroupper
|
||||
// L0003: vmovss xmm0, [rcx+8]
|
||||
// L0008: ret
|
||||
// =============================
|
||||
// We can see how the property value is loaded directly from the underlying field and
|
||||
// then returned to the caller: no boxing or unwanted overhead is introduced at all.
|
||||
return (TValue)(object)To;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -251,7 +274,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
|
||||
T from = From.GetValueOrDefault();
|
||||
|
||||
return Unsafe.As<T, TValue>(ref from);
|
||||
return (TValue)(object)from;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
|
|||
namespace Microsoft.Toolkit.Uwp.UI.Animations.Builders.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A small generic builder type that allows to create <see cref="ReadOnlySpan{T}"/> instances.
|
||||
/// A small generic builder type that allows to create <see cref="ArraySegment{T}"/> instances.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of items to create a sequence of.</typeparam>
|
||||
internal struct ListBuilder<T>
|
||||
|
@ -56,14 +56,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Builders.Helpers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="ReadOnlySpan{T}"/> instance with the current items.
|
||||
/// Gets a <see cref="ArraySegment{T}"/> instance with the current items.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ReadOnlySpan{T}"/> instance with the current items.</returns>
|
||||
/// <returns>A <see cref="ArraySegment{T}"/> instance with the current items.</returns>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<T> AsSpan()
|
||||
public ArraySegment<T> GetArraySegment()
|
||||
{
|
||||
return this.array.AsSpan(0, this.index);
|
||||
return new(this.array, 0, this.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
@ -33,7 +32,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
TimeSpan? delay,
|
||||
TimeSpan duration,
|
||||
RepeatOption repeat,
|
||||
ReadOnlySpan<TKeyFrame> keyFrames)
|
||||
ArraySegment<TKeyFrame> keyFrames)
|
||||
where TKeyFrame : struct, IKeyFrameInfo
|
||||
{
|
||||
KeyFrameAnimation animation;
|
||||
|
@ -42,7 +41,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
BooleanKeyFrameAnimation boolAnimation = target.Compositor.CreateBooleanKeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(boolAnimation, duration))
|
||||
{
|
||||
|
@ -58,7 +57,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
|
||||
{
|
||||
|
@ -83,7 +82,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
|
||||
{
|
||||
|
@ -108,7 +107,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Vector2KeyFrameAnimation vector2Animation = target.Compositor.CreateVector2KeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(vector2Animation, duration))
|
||||
{
|
||||
|
@ -133,7 +132,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Vector3KeyFrameAnimation vector3Animation = target.Compositor.CreateVector3KeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(vector3Animation, duration))
|
||||
{
|
||||
|
@ -158,7 +157,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Vector4KeyFrameAnimation vector4Animation = target.Compositor.CreateVector4KeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(vector4Animation, duration))
|
||||
{
|
||||
|
@ -183,7 +182,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
ColorKeyFrameAnimation colorAnimation = target.Compositor.CreateColorKeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(colorAnimation, duration))
|
||||
{
|
||||
|
@ -208,7 +207,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
QuaternionKeyFrameAnimation quaternionAnimation = target.Compositor.CreateQuaternionKeyFrameAnimation();
|
||||
|
||||
foreach (ref readonly var keyFrame in keyFrames)
|
||||
foreach (var keyFrame in keyFrames)
|
||||
{
|
||||
if (keyFrame.TryInsertExpressionKeyFrame(quaternionAnimation, duration))
|
||||
{
|
||||
|
@ -231,7 +230,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
}
|
||||
else
|
||||
{
|
||||
return ThrowHelper.ThrowInvalidOperationException<CompositionAnimation>("Invalid animation type");
|
||||
throw new InvalidOperationException("Invalid animation type");
|
||||
}
|
||||
|
||||
animation.Duration = duration;
|
||||
|
@ -284,7 +283,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
this.delay,
|
||||
this.duration,
|
||||
this.repeat,
|
||||
this.keyFrames.AsSpan());
|
||||
this.keyFrames.GetArraySegment());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
this.delay,
|
||||
this.duration,
|
||||
this.repeat,
|
||||
this.keyFrames.AsSpan());
|
||||
this.keyFrames.GetArraySegment());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue GetValueAs<TValue>()
|
||||
{
|
||||
return Unsafe.As<T, TValue>(ref Unsafe.AsRef(in this.value));
|
||||
return (TValue)(object)this.value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -47,8 +47,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
|
||||
// We can retrieve the total duration from the last timed keyframe, and then set
|
||||
// this as the target duration and use it to normalize the keyframe progresses.
|
||||
ReadOnlySpan<KeyFrameInfo> keyFrames = this.keyFrames.AsSpan();
|
||||
TimeSpan duration = keyFrames[keyFrames.Length - 1].GetTimedProgress(default);
|
||||
ArraySegment<KeyFrameInfo> keyFrames = this.keyFrames.GetArraySegment();
|
||||
TimeSpan duration = keyFrames[keyFrames.Count - 1].GetTimedProgress(default);
|
||||
|
||||
return NormalizedKeyFrameAnimationBuilder<T>.GetAnimation(
|
||||
targetHint,
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -32,7 +31,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
TimeSpan? delay,
|
||||
TimeSpan duration,
|
||||
RepeatOption repeat,
|
||||
ReadOnlySpan<TKeyFrame> keyFrames)
|
||||
ArraySegment<TKeyFrame> keyFrames)
|
||||
where TKeyFrame : struct, IKeyFrameInfo
|
||||
{
|
||||
Timeline animation;
|
||||
|
@ -118,7 +117,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
}
|
||||
else
|
||||
{
|
||||
return ThrowHelper.ThrowInvalidOperationException<Timeline>("Invalid animation type");
|
||||
static Timeline ThrowInvalidOperationException() => throw new InvalidOperationException("Invalid animation type");
|
||||
|
||||
return ThrowInvalidOperationException();
|
||||
}
|
||||
|
||||
animation.BeginTime = delay;
|
||||
|
@ -163,7 +164,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
this.delay,
|
||||
default,
|
||||
this.repeat,
|
||||
this.keyFrames.AsSpan());
|
||||
this.keyFrames.GetArraySegment());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue GetValueAs<TValue>()
|
||||
{
|
||||
return Unsafe.As<T, TValue>(ref Unsafe.AsRef(in this.value));
|
||||
return (TValue)(object)this.value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -6,7 +6,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Numerics;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
@ -111,7 +110,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Axis.X => "AnchorPoint.X",
|
||||
Axis.Y => "AnchorPoint.Y",
|
||||
Axis.Z => "AnchorPoint.Z",
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -132,7 +131,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Axis.X => "Translation.X",
|
||||
Axis.Y => "Translation.Y",
|
||||
Axis.Z => "Translation.Z",
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -153,7 +152,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Axis.X => "Offset.X",
|
||||
Axis.Y => "Offset.Y",
|
||||
Axis.Z => "Offset.Z",
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -174,7 +173,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Axis.X => "Scale.X",
|
||||
Axis.Y => "Scale.Y",
|
||||
Axis.Z => "Scale.Z",
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -195,7 +194,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Axis.X => "CenterPoint.X",
|
||||
Axis.Y => "CenterPoint.Y",
|
||||
Axis.Z => "CenterPoint.Z",
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -217,7 +216,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Side.Bottom => nameof(InsetClip.BottomInset),
|
||||
Side.Right => nameof(InsetClip.RightInset),
|
||||
Side.Left => nameof(InsetClip.LeftInset),
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid clip side")
|
||||
_ => ThrowArgumentException<string>("Invalid clip side")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -238,7 +237,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
Axis.X => "Size.X",
|
||||
Axis.Y => "Size.Y",
|
||||
Axis.Z => "Size.Z",
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -257,7 +256,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Axis.X => nameof(CompositeTransform.TranslateX),
|
||||
Axis.Y => nameof(CompositeTransform.TranslateY),
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -270,7 +269,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Axis.X => nameof(CompositeTransform.ScaleX),
|
||||
Axis.Y => nameof(CompositeTransform.ScaleY),
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -283,7 +282,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Axis.X => nameof(CompositeTransform.CenterX),
|
||||
Axis.Y => nameof(CompositeTransform.CenterY),
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -296,9 +295,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Axis.X => nameof(FrameworkElement.Width),
|
||||
Axis.Y => nameof(FrameworkElement.Height),
|
||||
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
|
||||
_ => ThrowArgumentException<string>("Invalid axis")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws a new <see cref="ArgumentException"/> with a given message.
|
||||
/// </summary>
|
||||
private static T ThrowArgumentException<T>(string message)
|
||||
{
|
||||
throw new ArgumentException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using static Microsoft.Toolkit.Uwp.UI.Animations.AnimationExtensions;
|
||||
|
||||
|
@ -46,8 +46,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
EasingType.Quintic => new QuinticEase { EasingMode = easingMode },
|
||||
EasingType.Sine => new SineEase { EasingMode = easingMode },
|
||||
|
||||
_ => ThrowHelper.ThrowArgumentException<EasingFunctionBase?>("Invalid easing type")
|
||||
_ => ThrowArgumentException()
|
||||
};
|
||||
|
||||
static EasingFunctionBase ThrowArgumentException() => throw new ArgumentException("Invalid easing type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
@ -66,11 +66,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
visual.StartAnimation($"{nameof(Visual.Offset)}.{targetAxis}", animation);
|
||||
break;
|
||||
default:
|
||||
ThrowHelper.ThrowArgumentException("Invalid target property");
|
||||
ThrowArgumentException();
|
||||
break;
|
||||
}
|
||||
|
||||
return animation;
|
||||
|
||||
static ExpressionAnimation ThrowArgumentException() => throw new ArgumentException("Invalid target property");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
@ -52,7 +51,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
[Pure]
|
||||
public static RepeatOption Count(int count)
|
||||
{
|
||||
Guard.IsGreaterThanOrEqualTo(count, 0, nameof(count));
|
||||
if (count < 0)
|
||||
{
|
||||
ThrowArgumentOutOfRangeForCount();
|
||||
}
|
||||
|
||||
return new(count);
|
||||
}
|
||||
|
@ -76,7 +78,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
return Forever;
|
||||
}
|
||||
|
||||
return ThrowHelper.ThrowArgumentException<RepeatOption>("Invalid input text");
|
||||
return ThrowArgumentExceptionForText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -109,5 +111,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
|
||||
return (AnimationIterationBehavior.Count, this.value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws a new <see cref="ArgumentOutOfRangeException"/> when the constructor is invoked with an incorrect parameter.
|
||||
/// </summary>
|
||||
private static void ThrowArgumentOutOfRangeForCount()
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("The parameter \"count\" must be greater than or equal to 0.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws a new <see cref="ArgumentOutOfRangeException"/> when the constructor is invoked with an incorrect parameter.
|
||||
/// </summary>
|
||||
private static RepeatOption ThrowArgumentExceptionForText()
|
||||
{
|
||||
throw new ArgumentException("The input text is not valid to parse a new RepeatOption instance. It must be either a natural number or \"Forever\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
|
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
/// <inheritdoc/>
|
||||
public override async Task InvokeAsync(UIElement element, CancellationToken token)
|
||||
{
|
||||
Guard.IsNotNull(Animation, nameof(Animation));
|
||||
if (Animation is null)
|
||||
{
|
||||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
await base.InvokeAsync(element, token);
|
||||
|
||||
|
@ -72,6 +75,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
await Animation.StartAsync(token);
|
||||
}
|
||||
|
||||
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
|
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
/// <inheritdoc/>
|
||||
public override async Task InvokeAsync(UIElement element, CancellationToken token)
|
||||
{
|
||||
Guard.IsNotNull(Animation, nameof(Animation));
|
||||
if (Animation is null)
|
||||
{
|
||||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
await base.InvokeAsync(element, token);
|
||||
|
||||
|
@ -69,6 +72,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
Animation.Stop();
|
||||
}
|
||||
|
||||
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ using System.Diagnostics.Contracts;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
|
@ -218,10 +217,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
|
||||
if (ParentReference?.TryGetTarget(out parent) != true)
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException("The current animation collection isn't bound to a parent UIElement instance.");
|
||||
ThrowInvalidOperationException();
|
||||
}
|
||||
|
||||
return parent!;
|
||||
|
||||
static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current AnimationSet object isn't bound to a parent UIElement instance.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Hosting;
|
||||
|
@ -85,10 +84,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
|
||||
if (ParentReference?.TryGetTarget(out parent) != true)
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException("The current animation collection isn't bound to a parent UIElement instance.");
|
||||
ThrowInvalidOperationException();
|
||||
}
|
||||
|
||||
return parent!;
|
||||
|
||||
static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current ImplicitAnimationSet object isn't bound to a parent UIElement instance.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using System;
|
||||
using Microsoft.Toolkit.Uwp.UI.Animations;
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
|
|||
/// <inheritdoc/>
|
||||
public object Execute(object sender, object parameter)
|
||||
{
|
||||
Guard.IsNotNull(Animation, nameof(Animation));
|
||||
if (Animation is null)
|
||||
{
|
||||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
if (TargetObject is not null)
|
||||
{
|
||||
|
@ -65,6 +68,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
|
|||
}
|
||||
|
||||
return null!;
|
||||
|
||||
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using System;
|
||||
using Microsoft.Toolkit.Uwp.UI.Animations;
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
|
|||
/// <inheritdoc/>
|
||||
public object Execute(object sender, object parameter)
|
||||
{
|
||||
Guard.IsNotNull(Animation, nameof(Animation));
|
||||
if (Animation is null)
|
||||
{
|
||||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
if (TargetObject is not null)
|
||||
{
|
||||
|
@ -65,6 +68,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
|
|||
}
|
||||
|
||||
return null!;
|
||||
|
||||
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,11 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.ColorPickerConverters;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
@ -233,10 +230,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
T child = this.GetTemplateChild(childName) as T;
|
||||
if ((child == null) && isRequired)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(childName);
|
||||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
return child;
|
||||
|
||||
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(childName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -148,6 +148,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
var horizontalChange = e.Delta.Translation.X;
|
||||
var verticalChange = e.Delta.Translation.Y;
|
||||
|
||||
if (this.FlowDirection == FlowDirection.RightToLeft)
|
||||
{
|
||||
horizontalChange *= -1;
|
||||
}
|
||||
|
||||
if (_resizeDirection == GridResizeDirection.Columns)
|
||||
{
|
||||
if (HorizontalMove(horizontalChange))
|
||||
|
|
|
@ -5,11 +5,8 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
|
@ -54,9 +51,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
|
||||
public InterspersedObservableCollection(object itemsSource)
|
||||
{
|
||||
Guard.IsAssignableToType<IList>(itemsSource, nameof(itemsSource));
|
||||
if (!(itemsSource is IList list))
|
||||
{
|
||||
ThrowArgumentException();
|
||||
}
|
||||
|
||||
ItemsSource = itemsSource as IList;
|
||||
ItemsSource = list;
|
||||
|
||||
if (ItemsSource is INotifyCollectionChanged notifier)
|
||||
{
|
||||
|
@ -67,6 +67,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
};
|
||||
notifier.CollectionChanged += weakPropertyChangedListener.OnEvent;
|
||||
}
|
||||
|
||||
static void ThrowArgumentException() => throw new ArgumentNullException("The input items source must be assignable to the System.Collections.IList type.");
|
||||
}
|
||||
|
||||
private void ItemsSource_CollectionChanged(object source, NotifyCollectionChangedEventArgs eventArgs)
|
||||
|
@ -192,12 +194,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// <returns>Inner ItemsSource Index.</returns>
|
||||
private int ToInnerIndex(int outerIndex)
|
||||
{
|
||||
#if DEBUG
|
||||
Guard.IsInRange(outerIndex, 0, Count, nameof(outerIndex));
|
||||
Guard.IsFalse(_interspersedObjects.ContainsKey(outerIndex), nameof(outerIndex)); // We can't map a inserted key to the original collection!
|
||||
#endif
|
||||
if ((uint)outerIndex >= Count)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (_interspersedObjects.ContainsKey(outerIndex))
|
||||
{
|
||||
ThrowArgumentException();
|
||||
}
|
||||
|
||||
return outerIndex - _interspersedObjects.Keys.Count(key => key.Value <= outerIndex);
|
||||
|
||||
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(outerIndex));
|
||||
static void ThrowArgumentException() => throw new ArgumentException("The outer index can't be inserted as a key to the original collection.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -207,9 +217,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// <returns>Index into the entire collection.</returns>
|
||||
private int ToOuterIndex(int innerIndex)
|
||||
{
|
||||
#if DEBUG
|
||||
Guard.IsInRange(innerIndex, 0, ItemsSource.Count, nameof(innerIndex));
|
||||
#endif
|
||||
if ((uint)innerIndex >= ItemsSource.Count)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
var keys = _interspersedObjects.OrderBy(v => v.Key);
|
||||
|
||||
|
@ -226,6 +237,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
|
||||
return innerIndex;
|
||||
|
||||
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(innerIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -235,9 +248,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// <returns>Projected index in the entire collection.</returns>
|
||||
private int ToOuterIndexAfterRemoval(int innerIndexToProject)
|
||||
{
|
||||
#if DEBUG
|
||||
Guard.IsInRange(innerIndexToProject, 0, ItemsSource.Count + 1, nameof(innerIndexToProject));
|
||||
#endif
|
||||
if ((uint)innerIndexToProject >= ItemsSource.Count + 1)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
//// TODO: Deal with bounds (0 / Count)? Or is it the same?
|
||||
|
||||
|
@ -256,6 +270,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
|
||||
return innerIndexToProject;
|
||||
|
||||
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(innerIndexToProject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
using System.Collections;
|
||||
using System.Drawing;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
|
@ -28,9 +27,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// <param name="columns">The number of columns to track.</param>
|
||||
public TakenSpotsReferenceHolder(int rows, int columns)
|
||||
{
|
||||
Guard.IsGreaterThanOrEqualTo(rows, 0, nameof(rows));
|
||||
Guard.IsGreaterThanOrEqualTo(columns, 0, nameof(columns));
|
||||
|
||||
Height = rows;
|
||||
Width = columns;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
|
@ -63,8 +62,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
{
|
||||
Orientation.Vertical => new Rect(Position.V, Position.U, Size.V, Size.U),
|
||||
Orientation.Horizontal => new Rect(Position.U, Position.V, Size.U, Size.V),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<Rect>("unsupported orientation"),
|
||||
_ => ThrowArgumentException()
|
||||
};
|
||||
|
||||
private static Rect ThrowArgumentException() => throw new ArgumentException("The input orientation is not valid.");
|
||||
}
|
||||
|
||||
private struct Row
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -50,14 +49,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
if (Target is not TEffect target)
|
||||
{
|
||||
return ThrowHelper.ThrowArgumentNullException<AnimationBuilder>("The target effect is null, make sure to set the Target property");
|
||||
static AnimationBuilder ThrowArgumentNullException() => throw new ArgumentNullException("The target effect is null, make sure to set the Target property");
|
||||
|
||||
return ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
if (ExplicitTarget is not string explicitTarget)
|
||||
{
|
||||
return ThrowHelper.ThrowArgumentNullException<AnimationBuilder>(
|
||||
"The target effect cannot be animated at this time. If you're targeting one of the " +
|
||||
"built-in effects, make sure that the PipelineEffect.IsAnimatable property is set to true.");
|
||||
static AnimationBuilder ThrowArgumentNullException()
|
||||
{
|
||||
throw new ArgumentNullException(
|
||||
"The target effect cannot be animated at this time. If you're targeting one of the " +
|
||||
"built-in effects, make sure that the PipelineEffect.IsAnimatable property is set to true.");
|
||||
}
|
||||
|
||||
return ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
NormalizedKeyFrameAnimationBuilder<TKeyFrame>.Composition keyFrameBuilder = new(
|
||||
|
@ -70,7 +76,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
|
||||
CompositionAnimation animation = keyFrameBuilder.GetAnimation(target.Brush!, out _);
|
||||
|
||||
return builder.ExternalAnimation(target.Brush, animation);
|
||||
return builder.ExternalAnimation(target.Brush!, animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Numerics;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for CanvasDrawingSession.
|
||||
/// </summary>
|
||||
public static class CanvasDrawingSessionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws a circle of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="centerPoint">Center of the Circle</param>
|
||||
/// <param name="radius">Radius of the Circle</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawCircle(this CanvasDrawingSession session, Vector2 centerPoint, float radius, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawCircle(centerPoint, radius, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a circle of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the Center in x axis</param>
|
||||
/// <param name="y">Ordinate of the Center in the y axis</param>
|
||||
/// <param name="radius">Radius of the Circle</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawCircle(this CanvasDrawingSession session, float x, float y, float radius, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawCircle(x, y, radius, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws an Ellipse of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="centerPoint">Center of the Circle</param>
|
||||
/// <param name="radiusX">Radius in the X axis</param>
|
||||
/// <param name="radiusY">Radius in the Y axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawEllipse(this CanvasDrawingSession session, Vector2 centerPoint, float radiusX, float radiusY, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawEllipse(centerPoint, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws an Ellipse of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the Center on the x axis</param>
|
||||
/// <param name="y">Offset of the Center on the y axis</param>
|
||||
/// <param name="radiusX">Radius in the X axis</param>
|
||||
/// <param name="radiusY">Radius in the Y axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawEllipse(this CanvasDrawingSession session, float x, float y, float radiusX, float radiusY, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawEllipse(x, y, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a geometry relative to the origin, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="geometry">CanvasGeometry to render</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawGeometry(geometry, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a geometry relative to the specified position, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="geometry">CanvasGeometry to render</param>
|
||||
/// <param name="offset">Offset</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, Vector2 offset, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawGeometry(geometry, offset, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a geometry relative to the specified position, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="geometry">CanvasGeometry to render</param>
|
||||
/// <param name="x">Offset on the x axis</param>
|
||||
/// <param name="y">Offset on the y axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, float x, float y, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawGeometry(geometry, x, y, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a line between the specified positions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="point0">Starting position of the line</param>
|
||||
/// <param name="point1">Ending position of the line</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawLine(this CanvasDrawingSession session, Vector2 point0, Vector2 point1, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawLine(point0, point1, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a line between the specified positions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x0">Offset of Starting position of the line on x-axis</param>
|
||||
/// <param name="y0">Offset of Starting position of the line on y-axis</param>
|
||||
/// <param name="x1">Offset of Ending position of the line on x-axis</param>
|
||||
/// <param name="y1">Offset of Ending position of the line on y-axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawLine(this CanvasDrawingSession session, float x0, float y0, float x1, float y1, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawLine(x0, y0, x1, y1, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="rect">Rectangle dimensions</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawRectangle(this CanvasDrawingSession session, Rect rect, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawRectangle(rect, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Rectangle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Rectangle on the y-axis</param>
|
||||
/// <param name="w">Width of the Rectangle</param>
|
||||
/// <param name="h">Height of the Rectangle</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawRectangle(this CanvasDrawingSession session, float x, float y, float w, float h, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawRectangle(x, y, w, h, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a Rounded Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="rect">Rectangle dimensions</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawRoundedRectangle(this CanvasDrawingSession session, Rect rect, float radiusX, float radiusY, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawRoundedRectangle(rect, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a Rounded Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Rounded Rectangle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Rounded Rectangle on the y-axis</param>
|
||||
/// <param name="w">Width of the Rounded Rectangle</param>
|
||||
/// <param name="h">Height of the Rounded Rectangle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawRoundedRectangle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasStroke stroke)
|
||||
{
|
||||
session.DrawRoundedRectangle(x, y, w, h, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a Squircle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
|
||||
/// <param name="w">Width of the Squircle</param>
|
||||
/// <param name="h">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasStroke stroke)
|
||||
{
|
||||
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
|
||||
session.DrawGeometry(geometry, stroke);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a Squircle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
|
||||
/// <param name="w">Width of the Squircle</param>
|
||||
/// <param name="h">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="offset">Offset of the Squircle from the origin.</param>
|
||||
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
|
||||
/// color and stroke style.</param>
|
||||
public static void DrawSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, ICanvasStroke stroke)
|
||||
{
|
||||
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
|
||||
session.DrawGeometry(geometry, offset, stroke);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills a Squircle of the specified dimensions, using the given color.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
|
||||
/// <param name="w">Width of the Squircle</param>
|
||||
/// <param name="h">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="color">Color to fill the Squircle.</param>
|
||||
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Color color)
|
||||
{
|
||||
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
|
||||
session.FillGeometry(geometry, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills a Squircle of the specified dimensions, using the given brush.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
|
||||
/// <param name="w">Width of the Squircle</param>
|
||||
/// <param name="h">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="brush">Brush to fill the Squircle.</param>
|
||||
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasBrush brush)
|
||||
{
|
||||
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
|
||||
session.FillGeometry(geometry, brush);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills a Squircle of the specified dimensions, using the given color at specified offset.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
|
||||
/// <param name="w">Width of the Squircle</param>
|
||||
/// <param name="h">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="offset">Offset of the Squircle from the origin.</param>
|
||||
/// <param name="color">Color to fill the Squircle.</param>
|
||||
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, Color color)
|
||||
{
|
||||
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
|
||||
session.FillGeometry(geometry, offset, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills a Squircle of the specified dimensions, using the given brush at specified offset.
|
||||
/// </summary>
|
||||
/// <param name="session">CanvasDrawingSession</param>
|
||||
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
|
||||
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
|
||||
/// <param name="w">Width of the Squircle</param>
|
||||
/// <param name="h">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y axis</param>
|
||||
/// <param name="offset">Offset of the Squircle from the origin.</param>
|
||||
/// <param name="brush">Brush to fill the Squircle.</param>
|
||||
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, ICanvasBrush brush)
|
||||
{
|
||||
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
|
||||
session.FillGeometry(geometry, offset, brush);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,462 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines extension methods for CanvasPathBuilder.
|
||||
/// </summary>
|
||||
public static class CanvasPathBuilderExtensions
|
||||
{
|
||||
private const float SquircleFactor = 1.125f;
|
||||
private const float ControlPointFactor = 46f / 64f;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a line in the form of a cubic bezier. The control point of the quadratic bezier will be the endpoint of the line itself.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="end">Ending location of the line segment.</param>
|
||||
public static void AddLineAsQuadraticBezier(this CanvasPathBuilder pathBuilder, Vector2 end)
|
||||
{
|
||||
pathBuilder.AddQuadraticBezier(end, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a line in the form of a cubic bezier. The two control points of the cubic bezier will be the endpoints of the line itself.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="start">Starting location of the line segment.</param>
|
||||
/// <param name="end">Ending location of the line segment.</param>
|
||||
public static void AddLineAsCubicBezier(this CanvasPathBuilder pathBuilder, Vector2 start, Vector2 end)
|
||||
{
|
||||
pathBuilder.AddCubicBezier(start, end, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a circle figure to the path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="center">Center location of the circle.</param>
|
||||
/// <param name="radius">Radius of the circle.</param>
|
||||
public static void AddCircleFigure(this CanvasPathBuilder pathBuilder, Vector2 center, float radius)
|
||||
{
|
||||
pathBuilder.AddEllipseFigure(center.X, center.Y, radius, radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a circle figure to the path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="x">X coordinate of the center location of the circle.</param>
|
||||
/// <param name="y">Y coordinate of the center location of the circle.</param>
|
||||
/// <param name="radius">Radius of the circle.</param>
|
||||
public static void AddCircleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float radius)
|
||||
{
|
||||
pathBuilder.AddEllipseFigure(x, y, radius, radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an ellipse figure to the path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="center">Center location of the ellipse.</param>
|
||||
/// <param name="radiusX">Radius of the ellipse on the X-axis.</param>
|
||||
/// <param name="radiusY">Radius of the ellipse on the Y-axis.</param>
|
||||
public static void AddEllipseFigure(this CanvasPathBuilder pathBuilder, Vector2 center, float radiusX, float radiusY)
|
||||
{
|
||||
pathBuilder.AddEllipseFigure(center.X, center.Y, radiusX, radiusY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an ellipse figure to the path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="x">X coordinate of the center location of the ellipse.</param>
|
||||
/// <param name="y">Y coordinate of the center location of the ellipse.</param>
|
||||
/// <param name="radiusX">Radius of the ellipse on the X-axis.</param>
|
||||
/// <param name="radiusY">Radius of the ellipse on the Y-axis.</param>
|
||||
public static void AddEllipseFigure(this CanvasPathBuilder pathBuilder, float x, float y, float radiusX, float radiusY)
|
||||
{
|
||||
// Sanitize the radiusX by taking the absolute value
|
||||
radiusX = Math.Abs(radiusX);
|
||||
|
||||
// Sanitize the radiusY by taking the absolute value
|
||||
radiusY = Math.Abs(radiusY);
|
||||
|
||||
try
|
||||
{
|
||||
pathBuilder.BeginFigure(x + radiusX, y);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddEllipseFigure() method.
|
||||
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddEllipseFigure occurred, " +
|
||||
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
|
||||
"before calling CanvasPathBuilder.AddEllipseFigure, to end the previous figure.");
|
||||
|
||||
Throw();
|
||||
}
|
||||
|
||||
// First Semi-Ellipse
|
||||
pathBuilder.AddArc(new Vector2(x - radiusX, y), radiusX, radiusY, Scalar.Pi, CanvasSweepDirection.Clockwise, CanvasArcSize.Large);
|
||||
|
||||
// Second Semi-Ellipse
|
||||
pathBuilder.AddArc(new Vector2(x + radiusX, y), radiusX, radiusY, Scalar.Pi, CanvasSweepDirection.Clockwise, CanvasArcSize.Large);
|
||||
|
||||
// End Figure
|
||||
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a n-sided polygon figure to the path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="numSides">Number of sides of the polygon.</param>
|
||||
/// <param name="center">Center location of the polygon.</param>
|
||||
/// <param name="radius">Radius of the circle circumscribing the polygon i.e. the distance
|
||||
/// of each of the vertices of the polygon from the center.</param>
|
||||
public static void AddPolygonFigure(this CanvasPathBuilder pathBuilder, int numSides, Vector2 center, float radius)
|
||||
{
|
||||
pathBuilder.AddPolygonFigure(numSides, center.X, center.Y, radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a n-sided polygon figure to the path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="numSides">Number of sides of the polygon.</param>
|
||||
/// <param name="x">X coordinate of the center location of the polygon.</param>
|
||||
/// <param name="y">Y coordinate of the center location of the polygon.</param>
|
||||
/// <param name="radius">Radius of the circle circumscribing the polygon i.e. the distance
|
||||
/// of each of the vertices of the polygon from the center.</param>
|
||||
public static void AddPolygonFigure(this CanvasPathBuilder pathBuilder, int numSides, float x, float y, float radius)
|
||||
{
|
||||
// Sanitize the radius by taking the absolute value
|
||||
radius = Math.Abs(radius);
|
||||
|
||||
// A polygon should have at least 3 sides
|
||||
if (numSides <= 2)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Calculate the first vertex location based on the number of sides
|
||||
var angle = Scalar.TwoPi / numSides;
|
||||
var startAngle = numSides % 2 == 1 ? Scalar.PiByTwo : Scalar.PiByTwo - (angle / 2f);
|
||||
|
||||
var startX = x + (float)(radius * Math.Cos(startAngle));
|
||||
var startY = y - (float)(radius * Math.Sin(startAngle));
|
||||
|
||||
try
|
||||
{
|
||||
pathBuilder.BeginFigure(startX, startY);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
|
||||
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddPolygonFigure occurred, " +
|
||||
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
|
||||
"before calling CanvasPathBuilder.AddPolygonFigure, to end the previous figure.");
|
||||
|
||||
Throw();
|
||||
}
|
||||
|
||||
// Add lines to the remaining vertices
|
||||
for (var i = 1; i < numSides; i++)
|
||||
{
|
||||
var posX = x + (float)(radius * Math.Cos(startAngle + (i * angle)));
|
||||
var posY = y - (float)(radius * Math.Sin(startAngle + (i * angle)));
|
||||
pathBuilder.AddLine(posX, posY);
|
||||
}
|
||||
|
||||
// Add a line to the first vertex so that the lines join properly
|
||||
pathBuilder.AddLine(startX, startY);
|
||||
|
||||
// End the Figure
|
||||
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
|
||||
|
||||
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException($"Parameter {nameof(numSides)} must be greater than 2.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Rectangle to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="x">X offset of the TopLeft corner of the Rectangle</param>
|
||||
/// <param name="y">Y offset of the TopLeft corner of the Rectangle</param>
|
||||
/// <param name="width">Width of the Rectangle</param>
|
||||
/// <param name="height">Height of the Rectangle</param>
|
||||
public static void AddRectangleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height)
|
||||
{
|
||||
// Sanitize the width by taking the absolute value
|
||||
width = Math.Abs(width);
|
||||
|
||||
// Sanitize the height by taking the absolute value
|
||||
height = Math.Abs(height);
|
||||
|
||||
try
|
||||
{
|
||||
pathBuilder.BeginFigure(x, y);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
|
||||
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddRectangleFigure occurred, " +
|
||||
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
|
||||
"before calling CanvasPathBuilder.AddRectangleFigure, to end the previous figure.");
|
||||
|
||||
Throw();
|
||||
}
|
||||
|
||||
// Top Side
|
||||
pathBuilder.AddLine(x + width, y);
|
||||
|
||||
// Right Side
|
||||
pathBuilder.AddLine(x + width, y + height);
|
||||
|
||||
// Bottom Side
|
||||
pathBuilder.AddLine(x, y + height);
|
||||
|
||||
// Left Side
|
||||
pathBuilder.AddLine(x, y);
|
||||
|
||||
// End the Figure
|
||||
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RoundedRectangle to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="x">X offset of the TopLeft corner of the RoundedRectangle</param>
|
||||
/// <param name="y">Y offset of the TopLeft corner of the RoundedRectangle</param>
|
||||
/// <param name="width">Width of the RoundedRectangle</param>
|
||||
/// <param name="height">Height of the RoundedRectangle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x-axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y-axis</param>
|
||||
public static void AddRoundedRectangleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height, float radiusX, float radiusY)
|
||||
{
|
||||
// Sanitize the width by taking the absolute value
|
||||
width = Math.Abs(width);
|
||||
|
||||
// Sanitize the height by taking the absolute value
|
||||
height = Math.Abs(height);
|
||||
|
||||
var rect = new CanvasRoundRect(x, y, width, height, radiusX, radiusY);
|
||||
pathBuilder.AddRoundedRectangleFigure(ref rect, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RoundedRectangle to the Path. (To be used internally)
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="rect">CanvasRoundRect</param>
|
||||
/// <param name="raiseException">Flag to indicate whether exception should be raised</param>
|
||||
internal static void AddRoundedRectangleFigure(this CanvasPathBuilder pathBuilder, ref CanvasRoundRect rect, bool raiseException = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Begin path
|
||||
pathBuilder.BeginFigure(new Vector2(rect.LeftTopX, rect.LeftTopY));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
if (!raiseException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
|
||||
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddRoundedRectangleFigure occurred, " +
|
||||
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
|
||||
"before calling CanvasPathBuilder.AddRoundedRectangleFigure, to end the previous figure.");
|
||||
|
||||
Throw();
|
||||
}
|
||||
|
||||
// Top line
|
||||
pathBuilder.AddLine(new Vector2(rect.RightTopX, rect.RightTopY));
|
||||
|
||||
// Upper-right corner
|
||||
var radiusX = rect.TopRightX - rect.RightTopX;
|
||||
var radiusY = rect.TopRightY - rect.RightTopY;
|
||||
var center = new Vector2(rect.RightTopX, rect.TopRightY);
|
||||
pathBuilder.AddArc(center, radiusX, radiusY, 3f * Scalar.PiByTwo, Scalar.PiByTwo);
|
||||
|
||||
// Right line
|
||||
pathBuilder.AddLine(new Vector2(rect.BottomRightX, rect.BottomRightY));
|
||||
|
||||
// Lower-right corner
|
||||
radiusX = rect.BottomRightX - rect.RightBottomX;
|
||||
radiusY = rect.RightBottomY - rect.BottomRightY;
|
||||
center = new Vector2(rect.RightBottomX, rect.BottomRightY);
|
||||
pathBuilder.AddArc(center, radiusX, radiusY, 0f, Scalar.PiByTwo);
|
||||
|
||||
// Bottom line
|
||||
pathBuilder.AddLine(new Vector2(rect.LeftBottomX, rect.LeftBottomY));
|
||||
|
||||
// Lower-left corner
|
||||
radiusX = rect.LeftBottomX - rect.BottomLeftX;
|
||||
radiusY = rect.LeftBottomY - rect.BottomLeftY;
|
||||
center = new Vector2(rect.LeftBottomX, rect.BottomLeftY);
|
||||
pathBuilder.AddArc(center, radiusX, radiusY, Scalar.PiByTwo, Scalar.PiByTwo);
|
||||
|
||||
// Left line
|
||||
pathBuilder.AddLine(new Vector2(rect.TopLeftX, rect.TopLeftY));
|
||||
|
||||
// Upper-left corner
|
||||
radiusX = rect.LeftTopX - rect.TopLeftX;
|
||||
radiusY = rect.TopLeftY - rect.LeftTopY;
|
||||
center = new Vector2(rect.LeftTopX, rect.TopLeftY);
|
||||
pathBuilder.AddArc(center, radiusX, radiusY, 2f * Scalar.PiByTwo, Scalar.PiByTwo);
|
||||
|
||||
// End path
|
||||
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Squircle to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="x">X offset of the TopLeft corner of the Squircle</param>
|
||||
/// <param name="y">Y offset of the TopLeft corner of the Squircle</param>
|
||||
/// <param name="width">Width of the Squircle</param>
|
||||
/// <param name="height">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x-axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y-axis</param>
|
||||
public static void AddSquircleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height, float radiusX, float radiusY)
|
||||
{
|
||||
// Sanitize the width by taking the absolute value
|
||||
width = Math.Abs(width);
|
||||
|
||||
// Sanitize the height by taking the absolute value
|
||||
height = Math.Abs(height);
|
||||
|
||||
var rect = new CanvasRoundRect(x, y, width, height, radiusX * SquircleFactor, radiusY * SquircleFactor);
|
||||
pathBuilder.AddSquircleFigure(ref rect, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Squircle to the Path. (To be used internally)
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="rect">CanvasRoundRect</param>
|
||||
/// <param name="raiseException">Flag to indicate whether exception should be raised</param>
|
||||
internal static void AddSquircleFigure(this CanvasPathBuilder pathBuilder, ref CanvasRoundRect rect, bool raiseException = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Begin path
|
||||
pathBuilder.BeginFigure(new Vector2(rect.LeftTopX, rect.LeftTopY));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
if (!raiseException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
|
||||
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddSquircleFigure occurred, " +
|
||||
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
|
||||
"before calling CanvasPathBuilder.AddSquircleFigure, to end the previous figure.");
|
||||
|
||||
Throw();
|
||||
}
|
||||
|
||||
// Top line
|
||||
pathBuilder.AddLine(new Vector2(rect.RightTopX, rect.RightTopY));
|
||||
|
||||
// Upper-right corner
|
||||
var rightTopControlPoint = new Vector2(rect.RightTopX + ((rect.TopRightX - rect.RightTopX) * ControlPointFactor), rect.RightTopY);
|
||||
var topRightControlPoint = new Vector2(rect.TopRightX, rect.TopRightY - ((rect.TopRightY - rect.RightTopY) * ControlPointFactor));
|
||||
|
||||
// Top Right Curve
|
||||
pathBuilder.AddCubicBezier(rightTopControlPoint, topRightControlPoint, new Vector2(rect.TopRightX, rect.TopRightY));
|
||||
|
||||
// Right line
|
||||
pathBuilder.AddLine(new Vector2(rect.BottomRightX, rect.BottomRightY));
|
||||
|
||||
// Lower-right corner
|
||||
var bottomRightControlPoint = new Vector2(rect.BottomRightX, rect.BottomRightY + ((rect.RightBottomY - rect.BottomRightY) * ControlPointFactor));
|
||||
var rightBottomControlPoint = new Vector2(rect.RightBottomX + ((rect.BottomRightX - rect.RightBottomX) * ControlPointFactor), rect.RightBottomY);
|
||||
|
||||
// Bottom Right Curve
|
||||
pathBuilder.AddCubicBezier(bottomRightControlPoint, rightBottomControlPoint, new Vector2(rect.RightBottomX, rect.RightBottomY));
|
||||
|
||||
// Bottom line
|
||||
pathBuilder.AddLine(new Vector2(rect.LeftBottomX, rect.LeftBottomY));
|
||||
|
||||
// Lower-left corner
|
||||
var leftBottomControlPoint = new Vector2(rect.LeftBottomX - ((rect.LeftBottomX - rect.BottomLeftX) * ControlPointFactor), rect.LeftBottomY);
|
||||
var bottomLeftControlPoint = new Vector2(rect.BottomLeftX, rect.BottomLeftY + ((rect.LeftBottomY - rect.BottomLeftY) * ControlPointFactor));
|
||||
|
||||
// Bottom Left Curve
|
||||
pathBuilder.AddCubicBezier(leftBottomControlPoint, bottomLeftControlPoint, new Vector2(rect.BottomLeftX, rect.BottomLeftY));
|
||||
|
||||
// Left line
|
||||
pathBuilder.AddLine(new Vector2(rect.TopLeftX, rect.TopLeftY));
|
||||
|
||||
// Upper-left corner
|
||||
var topLeftControlPoint = new Vector2(rect.TopLeftX, rect.TopLeftY - ((rect.TopLeftY - rect.LeftTopY) * ControlPointFactor));
|
||||
var leftTopControlPoint = new Vector2(rect.LeftTopX - ((rect.LeftTopX - rect.TopLeftX) * ControlPointFactor), rect.LeftTopY);
|
||||
|
||||
// Top Left Curve
|
||||
pathBuilder.AddCubicBezier(topLeftControlPoint, leftTopControlPoint, new Vector2(rect.LeftTopX, rect.LeftTopY));
|
||||
|
||||
// End path
|
||||
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a path with the given collection of points.
|
||||
/// </summary>
|
||||
/// <param name="builder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="canvasFigureLoop">Specifies whether the figure is open or closed.
|
||||
/// This affects the appearance of fills and strokes, as well as geometry operations.</param>
|
||||
/// <param name="points">Collection of Vector2 points on the path.</param>
|
||||
/// <returns><see cref="CanvasPathBuilder"/> object</returns>
|
||||
public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builder, CanvasFigureLoop canvasFigureLoop, IEnumerable<Vector2> points)
|
||||
{
|
||||
var first = true;
|
||||
|
||||
foreach (var point in points)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
builder.BeginFigure(point);
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddLine(point);
|
||||
}
|
||||
}
|
||||
|
||||
builder.EndFigure(canvasFigureLoop);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a path with the given collection of points in the (x, y) pattern.
|
||||
/// </summary>
|
||||
/// <param name="builder"><see cref="CanvasPathBuilder"/></param>
|
||||
/// <param name="canvasFigureLoop">Specifies whether the figure is open or closed.
|
||||
/// This affects the appearance of fills and strokes, as well as geometry operations.</param>
|
||||
/// <param name="nodes">Collection of points in the (x, y) pattern on the path.</param>
|
||||
/// <returns><see cref="CanvasPathBuilder"/> object</returns>
|
||||
public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builder, CanvasFigureLoop canvasFigureLoop, IEnumerable<(float x, float y)> nodes)
|
||||
{
|
||||
var vectors = nodes.Select(n => new Vector2(n.x, n.y));
|
||||
return BuildPathWithLines(builder, canvasFigureLoop, vectors);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper Class for creating Win2d objects.
|
||||
/// </summary>
|
||||
public static class CanvasPathGeometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses the Path data string and converts it to CanvasGeometry.
|
||||
/// </summary>
|
||||
/// <param name="pathData">Path data</param>
|
||||
/// <returns><see cref="CanvasGeometry"/></returns>
|
||||
public static CanvasGeometry CreateGeometry(string pathData)
|
||||
{
|
||||
return CreateGeometry(null, pathData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the Path data string and converts it to CanvasGeometry.
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator"><see cref="ICanvasResourceCreator"/></param>
|
||||
/// <param name="pathData">Path data</param>
|
||||
/// <returns><see cref="CanvasGeometry"/></returns>
|
||||
public static CanvasGeometry CreateGeometry(ICanvasResourceCreator resourceCreator, string pathData)
|
||||
{
|
||||
using (new CultureShield("en-US"))
|
||||
{
|
||||
// Get the CanvasGeometry from the path data
|
||||
return CanvasGeometryParser.Parse(resourceCreator, pathData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Squircle geometry with the specified extents.
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">Resource creator</param>
|
||||
/// <param name="x">X offset of the TopLeft corner of the Squircle</param>
|
||||
/// <param name="y">Y offset of the TopLeft corner of the Squircle</param>
|
||||
/// <param name="width">Width of the Squircle</param>
|
||||
/// <param name="height">Height of the Squircle</param>
|
||||
/// <param name="radiusX">Corner Radius on the x-axis</param>
|
||||
/// <param name="radiusY">Corner Radius on the y-axis</param>
|
||||
/// <returns><see cref="CanvasGeometry"/></returns>
|
||||
public static CanvasGeometry CreateSquircle(ICanvasResourceCreator resourceCreator, float x, float y, float width, float height, float radiusX, float radiusY)
|
||||
{
|
||||
using var pathBuilder = new CanvasPathBuilder(resourceCreator);
|
||||
pathBuilder.AddSquircleFigure(x, y, width, height, radiusX, radiusY);
|
||||
return CanvasGeometry.CreatePath(pathBuilder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the given Brush data string and converts it to ICanvasBrush.
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator</param>
|
||||
/// <param name="brushData">Brush data in string format</param>
|
||||
/// <returns><see cref="ICanvasBrush"/></returns>
|
||||
public static ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator, string brushData)
|
||||
{
|
||||
using (new CultureShield("en-US"))
|
||||
{
|
||||
return CanvasBrushParser.Parse(resourceCreator, brushData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the given Stroke data string and converts it to ICanvasStroke.
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator</param>
|
||||
/// <param name="strokeData">Stroke data in string format</param>
|
||||
/// <returns><see cref="ICanvasStroke"/></returns>
|
||||
public static ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator, string strokeData)
|
||||
{
|
||||
using (new CultureShield("en-US"))
|
||||
{
|
||||
return CanvasStrokeParser.Parse(resourceCreator, strokeData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the give CanvasStrokeStyle data string and converts it to CanvasStrokeStyle.
|
||||
/// </summary>
|
||||
/// <param name="styleData">CanvasStrokeStyle data in string format</param>
|
||||
/// <returns><see cref="CanvasStrokeStyle"/> object</returns>
|
||||
public static CanvasStrokeStyle CreateStrokeStyle(string styleData)
|
||||
{
|
||||
using (new CultureShield("en-US"))
|
||||
{
|
||||
return CanvasStrokeStyleParser.Parse(styleData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the color string in Hexadecimal or HDR color format to the corresponding Color object.
|
||||
/// The hexadecimal color string should be in #RRGGBB or #AARRGGBB format.
|
||||
/// The '#' character is optional.
|
||||
/// The HDR color string should be in R G B A format.
|
||||
/// (R, G, B & A should have value in the range between 0 and 1, inclusive)
|
||||
/// </summary>
|
||||
/// <param name="colorString">Color string in Hexadecimal or HDR format</param>
|
||||
/// <returns>Color</returns>
|
||||
public static Color CreateColor(string colorString)
|
||||
{
|
||||
using (new CultureShield("en-US"))
|
||||
{
|
||||
return ColorParser.Parse(colorString);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector4 High Dynamic Range Color to Color object.
|
||||
/// Negative components of the Vector4 will be sanitized by taking the absolute
|
||||
/// value of the component. The HDR Color components should have value in
|
||||
/// the range between 0 and 1, inclusive. If they are more than 1, they
|
||||
/// will be clamped at 1.
|
||||
/// Vector4's X, Y, Z, W components match to Color's R, G, B, A components respectively.
|
||||
/// </summary>
|
||||
/// <param name="hdrColor">High Dynamic Range Color</param>
|
||||
/// <returns>Color</returns>
|
||||
public static Color CreateColor(Vector4 hdrColor)
|
||||
{
|
||||
using (new CultureShield("en-US"))
|
||||
{
|
||||
return ColorParser.Parse(hdrColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Numerics;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to represent the Stroke which can be used to render an outline on a <see cref="CanvasGeometry"/>
|
||||
/// </summary>
|
||||
public sealed class CanvasStroke : ICanvasStroke
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the brush with which the stroke will be rendered
|
||||
/// </summary>
|
||||
public ICanvasBrush Brush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the <see cref="CanvasStroke"/>
|
||||
/// </summary>
|
||||
public float Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Style of the <see cref="CanvasStroke"/>
|
||||
/// </summary>
|
||||
public CanvasStrokeStyle Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Transform matrix of the <see cref="CanvasStroke"/> brush.
|
||||
/// </summary>
|
||||
public Matrix3x2 Transform
|
||||
{
|
||||
get => GetTransform();
|
||||
|
||||
set => SetTransform(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
|
||||
/// </summary>
|
||||
/// <param name="brush">The brush with which the <see cref="CanvasStroke"/> will be rendered</param>
|
||||
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
|
||||
public CanvasStroke(ICanvasBrush brush, float strokeWidth = 1f)
|
||||
: this(brush, strokeWidth, new CanvasStrokeStyle())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
|
||||
/// </summary>
|
||||
/// <param name="brush">The brush with which the <see cref="CanvasStroke"/> will be rendered</param>
|
||||
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
|
||||
/// <param name="strokeStyle">Style of the <see cref="CanvasStroke"/></param>
|
||||
public CanvasStroke(ICanvasBrush brush, float strokeWidth, CanvasStrokeStyle strokeStyle)
|
||||
{
|
||||
Brush = brush;
|
||||
Width = strokeWidth;
|
||||
Style = strokeStyle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">ICanvasResourceCreator</param>
|
||||
/// <param name="strokeColor">Color of the <see cref="CanvasStroke"/></param>
|
||||
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
|
||||
public CanvasStroke(ICanvasResourceCreator device, Color strokeColor, float strokeWidth = 1f)
|
||||
: this(device, strokeColor, strokeWidth, new CanvasStrokeStyle())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">ICanvasResourceCreator</param>
|
||||
/// <param name="strokeColor">Color of the <see cref="CanvasStroke"/></param>
|
||||
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
|
||||
/// <param name="strokeStyle">Style of the <see cref="CanvasStroke"/></param>
|
||||
public CanvasStroke(ICanvasResourceCreator device, Color strokeColor, float strokeWidth, CanvasStrokeStyle strokeStyle)
|
||||
{
|
||||
Brush = new CanvasSolidColorBrush(device, strokeColor);
|
||||
Width = strokeWidth;
|
||||
Style = strokeStyle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="CanvasStroke"/>'s Transform.
|
||||
/// </summary>
|
||||
/// <param name="value">Transform matrix to set</param>
|
||||
private void SetTransform(Matrix3x2 value)
|
||||
{
|
||||
if (Brush != null)
|
||||
{
|
||||
Brush.Transform = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CanvasStroke"/>'s Transform. If stroke is null, then returns Matrix3x2.Identity.
|
||||
/// </summary>
|
||||
/// <returns>Transform matrix of the <see cref="CanvasStroke"/></returns>
|
||||
private Matrix3x2 GetTransform()
|
||||
{
|
||||
return Brush?.Transform ?? Matrix3x2.Identity;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Windows.UI.Composition;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for compositor to support Win2d Path Mini Language.
|
||||
/// </summary>
|
||||
public static class CompositorGeometryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CompositionPath"/> based on the specified path data.
|
||||
/// </summary>
|
||||
/// <param name="compositor"><see cref="Compositor"/></param>
|
||||
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
|
||||
/// <returns><see cref="CompositionPath"/></returns>
|
||||
public static CompositionPath CreatePath(this Compositor compositor, string pathData)
|
||||
{
|
||||
// Create CanvasGeometry
|
||||
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
|
||||
|
||||
// Create CompositionPath
|
||||
return new CompositionPath(geometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CompositionPathGeometry"/> based on the given path data.
|
||||
/// </summary>
|
||||
/// <param name="compositor"><see cref="Compositor"/></param>
|
||||
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
|
||||
/// <returns><see cref="CompositionPathGeometry"/></returns>
|
||||
public static CompositionPathGeometry CreatePathGeometry(this Compositor compositor, string pathData)
|
||||
{
|
||||
// Create CanvasGeometry
|
||||
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
|
||||
|
||||
// Create CompositionPathGeometry
|
||||
return compositor.CreatePathGeometry(new CompositionPath(geometry));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CompositionSpriteShape"/> based on the specified path data.
|
||||
/// </summary>
|
||||
/// <param name="compositor"><see cref="Compositor"/></param>
|
||||
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
|
||||
/// <returns><see cref="CompositionSpriteShape"/></returns>
|
||||
public static CompositionSpriteShape CreateSpriteShape(this Compositor compositor, string pathData)
|
||||
{
|
||||
// Create CanvasGeometry
|
||||
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
|
||||
|
||||
// Create CompositionPathGeometry
|
||||
var pathGeometry = compositor.CreatePathGeometry(new CompositionPath(geometry));
|
||||
|
||||
// Create CompositionSpriteShape
|
||||
return compositor.CreateSpriteShape(pathGeometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CompositionGeometricClip"/> from the specified <see cref="CanvasGeometry"/>.
|
||||
/// </summary>
|
||||
/// <param name="compositor"><see cref="Compositor"/></param>
|
||||
/// <param name="geometry"><see cref="CanvasGeometry"/></param>
|
||||
/// <returns>CompositionGeometricClip</returns>
|
||||
public static CompositionGeometricClip CreateGeometricClip(this Compositor compositor, CanvasGeometry geometry)
|
||||
{
|
||||
// Create the CompositionPath
|
||||
var path = new CompositionPath(geometry);
|
||||
|
||||
// Create the CompositionPathGeometry
|
||||
var pathGeometry = compositor.CreatePathGeometry(path);
|
||||
|
||||
// Create the CompositionGeometricClip
|
||||
return compositor.CreateGeometricClip(pathGeometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified path data and converts it to <see cref="CompositionGeometricClip"/>.
|
||||
/// </summary>
|
||||
/// <param name="compositor"><see cref="Compositor"/></param>
|
||||
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
|
||||
/// <returns><see cref="CompositionGeometricClip"/></returns>
|
||||
public static CompositionGeometricClip CreateGeometricClip(this Compositor compositor, string pathData)
|
||||
{
|
||||
// Create the CanvasGeometry from the path data
|
||||
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
|
||||
|
||||
// Create the CompositionGeometricClip
|
||||
return compositor.CreateGeometricClip(geometry);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure which encapsulates the details of each of the core points of the path of the rounded rectangle which is calculated based on
|
||||
/// either the given (Size, CornerRadius, BorderThickness and Padding) or (Size, RadiusX and RadiusY).
|
||||
/// </summary>
|
||||
internal struct CanvasRoundRect
|
||||
{
|
||||
private const float Factor = 0.5f;
|
||||
|
||||
private readonly float _leftTopWidth;
|
||||
private readonly float _topLeftHeight;
|
||||
private readonly float _topRightHeight;
|
||||
private readonly float _rightTopWidth;
|
||||
private readonly float _rightBottomWidth;
|
||||
private readonly float _bottomRightHeight;
|
||||
private readonly float _bottomLeftHeight;
|
||||
private readonly float _leftBottomWidth;
|
||||
|
||||
// This is the location of the properties within the Rect
|
||||
// |--LeftTop----------------------RightTop--|
|
||||
// | |
|
||||
// TopLeft TopRight
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// BottomLeft BottomRight
|
||||
// | |
|
||||
// |--LeftBottom----------------RightBottom--|
|
||||
internal float LeftTopX { get; private set; }
|
||||
|
||||
internal float LeftTopY { get; private set; }
|
||||
|
||||
internal float TopLeftX { get; private set; }
|
||||
|
||||
internal float TopLeftY { get; private set; }
|
||||
|
||||
internal float TopRightX { get; private set; }
|
||||
|
||||
internal float TopRightY { get; private set; }
|
||||
|
||||
internal float RightTopX { get; private set; }
|
||||
|
||||
internal float RightTopY { get; private set; }
|
||||
|
||||
internal float RightBottomX { get; private set; }
|
||||
|
||||
internal float RightBottomY { get; private set; }
|
||||
|
||||
internal float BottomRightX { get; private set; }
|
||||
|
||||
internal float BottomRightY { get; private set; }
|
||||
|
||||
internal float BottomLeftX { get; private set; }
|
||||
|
||||
internal float BottomLeftY { get; private set; }
|
||||
|
||||
internal float LeftBottomX { get; private set; }
|
||||
|
||||
internal float LeftBottomY { get; private set; }
|
||||
|
||||
internal float Width { get; }
|
||||
|
||||
internal float Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasRoundRect"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="origin">Origin of the Rect (absolute location of Top Left corner)</param>
|
||||
/// <param name="size">Size of the Rect</param>
|
||||
/// <param name="cornerRadius">CornerRadius</param>
|
||||
/// <param name="borderThickness">BorderThickness</param>
|
||||
/// <param name="padding">Padding</param>
|
||||
/// <param name="isOuterBorder">Flag to indicate whether outer or inner border needs
|
||||
/// to be calculated</param>
|
||||
internal CanvasRoundRect(Vector2 origin, Vector2 size, Vector4 cornerRadius, Vector4 borderThickness, Vector4 padding, bool isOuterBorder)
|
||||
: this()
|
||||
{
|
||||
Width = Math.Max(0f, size.X);
|
||||
Height = Math.Max(0f, size.Y);
|
||||
|
||||
var left = Factor * (borderThickness.X + padding.X);
|
||||
var top = Factor * (borderThickness.Y + padding.Y);
|
||||
var right = Factor * (borderThickness.Z + padding.Z);
|
||||
var bottom = Factor * (borderThickness.W + padding.W);
|
||||
|
||||
if (isOuterBorder)
|
||||
{
|
||||
// Top Left corner radius
|
||||
if (cornerRadius.X.IsZero())
|
||||
{
|
||||
_leftTopWidth = _topLeftHeight = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_leftTopWidth = cornerRadius.X + left;
|
||||
_topLeftHeight = cornerRadius.X + top;
|
||||
}
|
||||
|
||||
// Top Right corner radius
|
||||
if (cornerRadius.Y.IsZero())
|
||||
{
|
||||
_topRightHeight = _rightTopWidth = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_topRightHeight = cornerRadius.Y + top;
|
||||
_rightTopWidth = cornerRadius.Y + right;
|
||||
}
|
||||
|
||||
// Bottom Right corner radius
|
||||
if (cornerRadius.Z.IsZero())
|
||||
{
|
||||
_rightBottomWidth = _bottomRightHeight = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rightBottomWidth = cornerRadius.Z + right;
|
||||
_bottomRightHeight = cornerRadius.Z + bottom;
|
||||
}
|
||||
|
||||
// Bottom Left corner radius
|
||||
if (cornerRadius.W.IsZero())
|
||||
{
|
||||
_bottomLeftHeight = _leftBottomWidth = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_bottomLeftHeight = cornerRadius.W + bottom;
|
||||
_leftBottomWidth = cornerRadius.W + left;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_leftTopWidth = Math.Max(0f, cornerRadius.X - left);
|
||||
_topLeftHeight = Math.Max(0f, cornerRadius.X - top);
|
||||
_topRightHeight = Math.Max(0f, cornerRadius.Y - top);
|
||||
_rightTopWidth = Math.Max(0f, cornerRadius.Y - right);
|
||||
_rightBottomWidth = Math.Max(0f, cornerRadius.Z - right);
|
||||
_bottomRightHeight = Math.Max(0f, cornerRadius.Z - bottom);
|
||||
_bottomLeftHeight = Math.Max(0f, cornerRadius.W - bottom);
|
||||
_leftBottomWidth = Math.Max(0f, cornerRadius.W - left);
|
||||
}
|
||||
|
||||
// Calculate the anchor points
|
||||
ComputeCoordinates(origin.X, origin.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasRoundRect"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="origin">Top Left corner of the Rounded Rectangle</param>
|
||||
/// <param name="size">Dimensions of the Rounded Rectangle</param>
|
||||
/// <param name="radiusX">Radius of the corners on the x-axis</param>
|
||||
/// <param name="radiusY">Radius of the corners on the y-axis</param>
|
||||
internal CanvasRoundRect(Vector2 origin, Vector2 size, float radiusX, float radiusY)
|
||||
: this(origin.X, origin.Y, size.X, size.Y, radiusX, radiusY)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasRoundRect"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="x">X offset of the Top Left corner of the Rounded Rectangle</param>
|
||||
/// <param name="y">Y offset of the Top Left corner of the Rounded Rectangle</param>
|
||||
/// <param name="width">Width of the Rounded Rectangle.</param>
|
||||
/// <param name="height">Height of the Rounded Rectangle.</param>
|
||||
/// <param name="radiusX">Radius of the corners on the x-axis</param>
|
||||
/// <param name="radiusY">Radius of the corners on the y-axis</param>
|
||||
internal CanvasRoundRect(float x, float y, float width, float height, float radiusX, float radiusY)
|
||||
: this()
|
||||
{
|
||||
Width = Math.Max(0f, width);
|
||||
Height = Math.Max(0f, height);
|
||||
|
||||
// Sanitize the radii by taking the absolute value
|
||||
radiusX = Math.Min(Math.Abs(radiusX), width / 2f);
|
||||
radiusY = Math.Min(Math.Abs(radiusY), height / 2);
|
||||
|
||||
_leftTopWidth = radiusX;
|
||||
_rightTopWidth = radiusX;
|
||||
_rightBottomWidth = radiusX;
|
||||
_leftBottomWidth = radiusX;
|
||||
_topLeftHeight = radiusY;
|
||||
_topRightHeight = radiusY;
|
||||
_bottomRightHeight = radiusY;
|
||||
_bottomLeftHeight = radiusY;
|
||||
|
||||
ComputeCoordinates(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the coordinates of the crucial points on the CanvasRoundRect
|
||||
/// </summary>
|
||||
/// <param name="originX">X coordinate of the origin.</param>
|
||||
/// <param name="originY">Y coordinate of the origin.</param>
|
||||
private void ComputeCoordinates(float originX, float originY)
|
||||
{
|
||||
// compute the coordinates of the key points
|
||||
var leftTopX = _leftTopWidth;
|
||||
var leftTopY = 0f;
|
||||
var rightTopX = Width - _rightTopWidth;
|
||||
var rightTopY = 0f;
|
||||
var topRightX = Width;
|
||||
var topRightY = _topRightHeight;
|
||||
var bottomRightX = Width;
|
||||
var bottomRightY = Height - _bottomRightHeight;
|
||||
var rightBottomX = Width - _rightBottomWidth;
|
||||
var rightBottomY = Height;
|
||||
var leftBottomX = _leftBottomWidth;
|
||||
var leftBottomY = Height;
|
||||
var bottomLeftX = 0f;
|
||||
var bottomLeftY = Height - _bottomLeftHeight;
|
||||
var topLeftX = 0f;
|
||||
var topLeftY = _topLeftHeight;
|
||||
|
||||
// check anchors for overlap and resolve by partitioning corners according to
|
||||
// the percentage of each one.
|
||||
// top edge
|
||||
if (leftTopX > rightTopX)
|
||||
{
|
||||
var v = _leftTopWidth / (_leftTopWidth + _rightTopWidth) * Width;
|
||||
leftTopX = v;
|
||||
rightTopX = v;
|
||||
}
|
||||
|
||||
// right edge
|
||||
if (topRightY > bottomRightY)
|
||||
{
|
||||
var v = _topRightHeight / (_topRightHeight + _bottomRightHeight) * Height;
|
||||
topRightY = v;
|
||||
bottomRightY = v;
|
||||
}
|
||||
|
||||
// bottom edge
|
||||
if (leftBottomX > rightBottomX)
|
||||
{
|
||||
var v = _leftBottomWidth / (_leftBottomWidth + _rightBottomWidth) * Width;
|
||||
rightBottomX = v;
|
||||
leftBottomX = v;
|
||||
}
|
||||
|
||||
// left edge
|
||||
if (topLeftY > bottomLeftY)
|
||||
{
|
||||
var v = _topLeftHeight / (_topLeftHeight + _bottomLeftHeight) * Height;
|
||||
bottomLeftY = v;
|
||||
topLeftY = v;
|
||||
}
|
||||
|
||||
// Apply origin translation
|
||||
LeftTopX = leftTopX + originX;
|
||||
LeftTopY = leftTopY + originY;
|
||||
RightTopX = rightTopX + originX;
|
||||
RightTopY = rightTopY + originY;
|
||||
TopRightX = topRightX + originX;
|
||||
TopRightY = topRightY + originY;
|
||||
BottomRightX = bottomRightX + originX;
|
||||
BottomRightY = bottomRightY + originY;
|
||||
RightBottomX = rightBottomX + originX;
|
||||
RightBottomY = rightBottomY + originY;
|
||||
LeftBottomX = leftBottomX + originX;
|
||||
LeftBottomY = leftBottomY + originY;
|
||||
BottomLeftX = bottomLeftX + originX;
|
||||
BottomLeftY = bottomLeftY + originY;
|
||||
TopLeftX = topLeftX + originX;
|
||||
TopLeftY = topLeftY + originY;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum for the various PathFigures.
|
||||
/// </summary>
|
||||
internal enum PathFigureType
|
||||
{
|
||||
FillRule,
|
||||
PathFigure,
|
||||
EllipseFigure,
|
||||
PolygonFigure,
|
||||
RectangleFigure,
|
||||
RoundedRectangleFigure
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum for the various PathElements.
|
||||
/// </summary>
|
||||
internal enum PathElementType
|
||||
{
|
||||
MoveTo,
|
||||
Line,
|
||||
HorizontalLine,
|
||||
VerticalLine,
|
||||
QuadraticBezier,
|
||||
SmoothQuadraticBezier,
|
||||
CubicBezier,
|
||||
SmoothCubicBezier,
|
||||
Arc,
|
||||
ClosePath
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum for the various types of Brushes.
|
||||
/// </summary>
|
||||
internal enum BrushType
|
||||
{
|
||||
SolidColor,
|
||||
LinearGradient,
|
||||
RadialGradient,
|
||||
LinearGradientHdr,
|
||||
RadialGradientHdr
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum for the various types of GradientStop attributes.
|
||||
/// </summary>
|
||||
internal enum GradientStopAttributeType
|
||||
{
|
||||
Main,
|
||||
Additional,
|
||||
MainHdr,
|
||||
AdditionalHdr
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory class to instantiate various PathElements.
|
||||
/// </summary>
|
||||
internal static class PathElementFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a default Path Element for the given PathFigureType.
|
||||
/// </summary>
|
||||
/// <param name="figureType">PathFigureType</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
internal static ICanvasPathElement CreateDefaultPathElement(PathFigureType figureType)
|
||||
{
|
||||
if (figureType == PathFigureType.FillRule)
|
||||
{
|
||||
return new FillRuleElement();
|
||||
}
|
||||
|
||||
static ICanvasPathElement Throw() => throw new ArgumentException("Creation of Only Default FillRuleElement is supported.");
|
||||
|
||||
return Throw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default Path Element for the given PathElementType.
|
||||
/// </summary>
|
||||
/// <param name="elementType">PathElementType</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
internal static ICanvasPathElement CreateDefaultPathElement(PathElementType elementType)
|
||||
{
|
||||
if (elementType == PathElementType.ClosePath)
|
||||
{
|
||||
return new ClosePathElement();
|
||||
}
|
||||
|
||||
static ICanvasPathElement Throw() => throw new ArgumentException("Creation of Only Default ClosePathElement is supported.");
|
||||
|
||||
return Throw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a PathElement based on the PathFigureType.
|
||||
/// </summary>
|
||||
/// <param name="figureType">PathFigureType</param>
|
||||
/// <param name="match">Match object</param>
|
||||
/// <param name="index">Index of the path element in the Path data</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
internal static ICanvasPathElement CreatePathFigure(PathFigureType figureType, Match match, int index)
|
||||
{
|
||||
var element = CreatePathElement(figureType);
|
||||
element?.Initialize(match, index);
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a PathElement based on the PathFigureType.
|
||||
/// </summary>
|
||||
/// <param name="figureType">PathFigureType</param>
|
||||
/// <param name="capture">Capture object</param>
|
||||
/// <param name="index">Index of the capture</param>
|
||||
/// <param name="isRelative">Indicates whether the coordinates are absolute or relative</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
internal static ICanvasPathElement CreateAdditionalPathFigure(PathFigureType figureType, Capture capture, int index, bool isRelative)
|
||||
{
|
||||
var element = CreatePathElement(figureType);
|
||||
element?.InitializeAdditional(capture, index, isRelative);
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a PathElement based on the PathElementType.
|
||||
/// </summary>
|
||||
/// <param name="elementType">PathElementType</param>
|
||||
/// <param name="match">Match object</param>
|
||||
/// <param name="index">Index of the path element in the Path data</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
internal static ICanvasPathElement CreatePathElement(PathElementType elementType, Match match, int index)
|
||||
{
|
||||
var element = CreatePathElement(elementType);
|
||||
element?.Initialize(match, index);
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a PathElement based on the PathElementType.
|
||||
/// </summary>
|
||||
/// <param name="elementType">PathElementType</param>
|
||||
/// <param name="capture">Capture object</param>
|
||||
/// <param name="index">Index of the capture</param>
|
||||
/// <param name="isRelative">Indicates whether the coordinates are absolute or relative</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
internal static ICanvasPathElement CreateAdditionalPathElement(PathElementType elementType, Capture capture, int index, bool isRelative)
|
||||
{
|
||||
// Additional attributes in MoveTo Command must be converted
|
||||
// to Line commands
|
||||
if (elementType == PathElementType.MoveTo)
|
||||
{
|
||||
elementType = PathElementType.Line;
|
||||
}
|
||||
|
||||
var element = CreatePathElement(elementType);
|
||||
element?.InitializeAdditional(capture, index, isRelative);
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a PathElement based on the PathFigureType.
|
||||
/// </summary>
|
||||
/// <param name="figureType">PathFigureType</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
private static ICanvasPathElement CreatePathElement(PathFigureType figureType)
|
||||
{
|
||||
return figureType switch
|
||||
{
|
||||
PathFigureType.FillRule => new FillRuleElement(),
|
||||
PathFigureType.PathFigure => new CanvasPathFigure(),
|
||||
PathFigureType.EllipseFigure => new CanvasEllipseFigure(),
|
||||
PathFigureType.PolygonFigure => new CanvasPolygonFigure(),
|
||||
PathFigureType.RectangleFigure => new CanvasRectangleFigure(),
|
||||
PathFigureType.RoundedRectangleFigure => new CanvasRoundRectangleFigure(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(figureType), figureType, "Invalid PathFigureType!")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a PathElement based on the PathElementType.
|
||||
/// </summary>
|
||||
/// <param name="elementType">PathElementType</param>
|
||||
/// <returns>ICanvasPathElement</returns>
|
||||
private static ICanvasPathElement CreatePathElement(PathElementType elementType)
|
||||
{
|
||||
return elementType switch
|
||||
{
|
||||
PathElementType.MoveTo => new MoveToElement(),
|
||||
PathElementType.Line => new LineElement(),
|
||||
PathElementType.HorizontalLine => new HorizontalLineElement(),
|
||||
PathElementType.VerticalLine => new VerticalLineElement(),
|
||||
PathElementType.QuadraticBezier => new QuadraticBezierElement(),
|
||||
PathElementType.SmoothQuadraticBezier => new SmoothQuadraticBezierElement(),
|
||||
PathElementType.CubicBezier => new CubicBezierElement(),
|
||||
PathElementType.SmoothCubicBezier => new SmoothCubicBezierElement(),
|
||||
PathElementType.Arc => new ArcElement(),
|
||||
PathElementType.ClosePath => new ClosePathElement(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(elementType), elementType, "Invalid PathElementType!")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,602 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
[assembly: InternalsVisibleTo("UnitTests.UWP")]
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all the Regular Expressions which are used for parsing the Win2d Path Mini Language.
|
||||
/// </summary>
|
||||
internal static class RegexFactory
|
||||
{
|
||||
// Whitespace
|
||||
private const string Spacer = @"\s*";
|
||||
|
||||
// Whitespace or comma
|
||||
private const string SpaceOrComma = @"(?:\s+|\s*,\s*)";
|
||||
|
||||
// Whitespace or comma or a minus/plus sign (look ahead)
|
||||
private const string Sep = @"(?:\s+|\s*,\s*|(?=[-+.]))";
|
||||
|
||||
// Whitespace or comma or a '#' sign (look ahead)
|
||||
private const string ColorSep = @"(?:\s+|\s*,\s*|(?=[#]))";
|
||||
|
||||
// Positive Integer
|
||||
private const string Integer = @"[+-]?[0-9]+";
|
||||
|
||||
// Positive Integer
|
||||
private const string PositiveInteger = @"[+]?[0-9]+";
|
||||
|
||||
// Floating point number
|
||||
private const string Float = @"(?:[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
|
||||
|
||||
// Positive Floating point number
|
||||
private const string PositiveFloat = @"(?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
|
||||
|
||||
// Floating point number between 0 and 1, inclusive
|
||||
// private const string Float01 = @"(?:(?<!\d*[1-9]\.?0*)(?:(?:0+(?:\.\d+)?)|(?:\.\d+)|(?:1(?!\.0*[1-9]+)(?:\.0+)?)))";
|
||||
private const string Float01 = @"(?:" +
|
||||
@"(?:(?<!\d*[1-9]\.?0*)(?:(?:0+(?:\.\d+)?)|(?:1(?!\.0*[1-9]+)(?:\.0+)?)))|" +
|
||||
@"(?:(?<!\d*[1-9])(?:\.\d+))|" +
|
||||
@"(?:(?<=(?:\.\d+))(?:\.\d+))" +
|
||||
@")";
|
||||
|
||||
// Hexadecimal characters
|
||||
private const string Hex = "(?:[a-f]|[A-F]|[0-9])";
|
||||
|
||||
// Position
|
||||
private static readonly string Pos = $"{Float}{Sep}{Float}";
|
||||
|
||||
// MoveTo
|
||||
private static readonly string MoveTo = $"(?<MoveTo>[Mm]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Line
|
||||
private static readonly string Line = $"(?<Line>[Ll]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Horizontal Line
|
||||
private static readonly string HorizontalLine = $"(?<HorizontalLine>[Hh]{Spacer}{Float}(?:{Sep}{Float})*{Spacer})";
|
||||
|
||||
// Vertical Line
|
||||
private static readonly string VerticalLine = $"(?<VerticalLine>[Vv]{Spacer}{Float}(?:{Sep}{Float})*{Spacer})";
|
||||
|
||||
// Quadratic Bezier
|
||||
private static readonly string QuadraticBezier = $"(?<QuadraticBezier>[Qq]{Spacer}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Smooth Quadratic Bezier
|
||||
private static readonly string SmoothQuadraticBezier = $"(?<SmoothQuadraticBezier>[Tt]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Cubic Bezier
|
||||
private static readonly string CubicBezier = $"(?<CubicBezier>[Cc]{Spacer}{Pos}{Sep}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos}{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Smooth Cubic Bezier
|
||||
private static readonly string SmoothCubicBezier = $"(?<SmoothCubicBezier>[Ss]{Spacer}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Arc
|
||||
private static readonly string Arc = $"(?<Arc>[Aa]{Spacer}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos}" +
|
||||
$"(?:{Sep}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos})*{Spacer})";
|
||||
|
||||
// Close Path
|
||||
private static readonly string ClosePath = $"(?<ClosePath>[Zz]{Spacer})";
|
||||
|
||||
// CanvasPathFigure
|
||||
private static readonly string CanvasPathFigureRegexString =
|
||||
$"{MoveTo}" + // M x,y
|
||||
"(" +
|
||||
$"{Line}+|" + // L x,y
|
||||
$"{HorizontalLine}+|" + // H x
|
||||
$"{VerticalLine}+|" + // V y
|
||||
$"{QuadraticBezier}+|" + // Q x1,y1 x,y
|
||||
$"{SmoothQuadraticBezier}+|" + // T x,y
|
||||
$"{CubicBezier}+|" + // C x1,y1 x2,y2 x,y
|
||||
$"{SmoothCubicBezier}+|" + // S x2,y2 x,y
|
||||
$"{Arc}+|" + // A radX, radY, angle, isLargeArc, sweepDirection, x, y
|
||||
")+" +
|
||||
$"{ClosePath}?"; // Close Path (Optional)
|
||||
|
||||
// Fill Rule
|
||||
private static readonly string FillRule = $"{Spacer}(?<FillRule>[Ff]{Spacer}[01])";
|
||||
|
||||
// PathFigure
|
||||
private static readonly string PathFigure = $"{Spacer}(?<PathFigure>{CanvasPathFigureRegexString})";
|
||||
|
||||
// Ellipse Figure
|
||||
private static readonly string EllipseFigure = $"{Spacer}(?<EllipseFigure>[Oo]{Spacer}{Float}{Sep}{Float}{Sep}{Pos}" +
|
||||
$"(?:{Sep}{Float}{Sep}{Float}{Sep}{Pos})*)";
|
||||
|
||||
// Polygon Figure
|
||||
private static readonly string PolygonFigure = $"{Spacer}(?<PolygonFigure>[Pp]{Spacer}{Integer}{Sep}{Float}{Sep}{Pos}" +
|
||||
$"(?:{Sep}{Integer}{Sep}{Float}{Sep}{Pos})*)";
|
||||
|
||||
// Rectangle Figure
|
||||
private static readonly string RectangleFigure = $"{Spacer}(?<RectangleFigure>[Rr]{Spacer}{Pos}{Sep}{Float}{Sep}{Float}" +
|
||||
$"(?:{Sep}{Pos}{Sep}{Float}{Sep}{Float})*)";
|
||||
|
||||
// Rounded Rectangle Figure
|
||||
private static readonly string RoundedRectangleFigure = $"{Spacer}(?<RoundedRectangleFigure>[Uu]{Spacer}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float}" +
|
||||
$"(?:{Sep}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float})*)";
|
||||
|
||||
// CanvasGeometry
|
||||
private static readonly string CanvasGeometryRegexString =
|
||||
$"{FillRule}?" + // F0 or F1
|
||||
"(" +
|
||||
$"{PathFigure}+|" + // Path Figure
|
||||
$"{EllipseFigure}+|" + // O radX, radY, centerX, centerY
|
||||
$"{PolygonFigure}+|" + // P numSides, radius, centerX, centerY
|
||||
$"{RectangleFigure}+|" + // R x, y, width, height
|
||||
$"{RoundedRectangleFigure}+" + // U x, y, width, height, radiusX, radiusY
|
||||
")+";
|
||||
|
||||
// MoveTo
|
||||
private static readonly string MoveToAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
private static readonly string MoveToRegexString = $"{Spacer}(?<Main>(?<Command>[Mm]){Spacer}{MoveToAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos})*";
|
||||
|
||||
// Line
|
||||
private static readonly string LineAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
private static readonly string LineRegexString = $"{Spacer}(?<Main>(?<Command>[Ll]){Spacer}{LineAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos})*";
|
||||
|
||||
// Horizontal Line
|
||||
private static readonly string HorizontalLineAttributes = $"(?<X>{Float})";
|
||||
private static readonly string HorizontalLineRegexString = $"{Spacer}(?<Main>(?<Command>[Hh]){Spacer}{HorizontalLineAttributes})" +
|
||||
$"(?<Additional>{Sep}{Float})*";
|
||||
|
||||
// Vertical Line
|
||||
private static readonly string VerticalLineAttributes = $"(?<Y>{Float})";
|
||||
private static readonly string VerticalLineRegexString = $"{Spacer}(?<Main>(?<Command>[Vv]){Spacer}{VerticalLineAttributes})" +
|
||||
$"(?<Additional>{Sep}{Float})*";
|
||||
|
||||
// Quadratic Bezier
|
||||
private static readonly string QuadraticBezierAttributes = $"(?<X1>{Float}){Sep}(?<Y1>{Float}){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
private static readonly string QuadraticBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Qq]){Spacer}{QuadraticBezierAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos}{Sep}{Pos})*";
|
||||
|
||||
// Smooth Quadratic Bezier
|
||||
private static readonly string SmoothQuadraticBezierAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
private static readonly string SmoothQuadraticBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Tt]){Spacer}{SmoothQuadraticBezierAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos})*";
|
||||
|
||||
// Cubic Bezier
|
||||
private static readonly string CubicBezierAttributes = $"(?<X1>{Float}){Sep}(?<Y1>{Float}){Sep}(?<X2>{Float}){Sep}(?<Y2>{Float}){Sep}" +
|
||||
$"(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
|
||||
private static readonly string CubicBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Cc]){Spacer}{CubicBezierAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos}{Sep}{Pos}{Sep}{Pos})*";
|
||||
|
||||
// Smooth Cubic Bezier
|
||||
private static readonly string SmoothCubicBezierAttributes = $"(?<X2>{Float}){Sep}(?<Y2>{Float}){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
private static readonly string SmoothCubicBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Ss]){Spacer}{SmoothCubicBezierAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos}{Sep}{Pos})*";
|
||||
|
||||
// Arc
|
||||
private static readonly string ArcAttributes = $"(?<RadiusX>{Float}){Sep}(?<RadiusY>{Float}){Sep}(?<Angle>{Float}){SpaceOrComma}" +
|
||||
$"(?<IsLargeArc>[01]){SpaceOrComma}(?<SweepDirection>[01]){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
|
||||
private static readonly string ArcRegexString = $"{Spacer}(?<Main>(?<Command>[Aa]){Spacer}{ArcAttributes})" +
|
||||
$"(?<Additional>{Sep}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos})*";
|
||||
|
||||
// Close Path
|
||||
private static readonly string ClosePathRegexString = $"{Spacer}(?<Main>(?<Command>[Zz])){Spacer}";
|
||||
|
||||
// Fill Rule
|
||||
private static readonly string FillRuleRegexString = $"{Spacer}(?<Main>(?<Command>[Ff]){Spacer}(?<FillValue>[01]))";
|
||||
|
||||
// Path Figure
|
||||
private static readonly string PathFigureRegexString = $"{Spacer}(?<Main>{PathFigure})";
|
||||
|
||||
// Ellipse Figure
|
||||
private static readonly string EllipseFigureAttributes = $"(?<RadiusX>{Float}){Sep}(?<RadiusY>{Float}){Sep}" +
|
||||
$"(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
|
||||
private static readonly string EllipseFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Oo]){Spacer}{EllipseFigureAttributes})" +
|
||||
$"(?<Additional>{Sep}{Float}{Sep}{Float}{Sep}{Pos})*";
|
||||
|
||||
// Polygon Figure
|
||||
private static readonly string PolygonFigureAttributes = $"(?<Sides>{Integer}){Sep}(?<Radius>{Float}){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
|
||||
private static readonly string PolygonFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Pp]){Spacer}{PolygonFigureAttributes})" +
|
||||
$"(?<Additional>{Sep}{Integer}{Sep}{Float}{Sep}{Pos})*";
|
||||
|
||||
// Rectangle Figure
|
||||
private static readonly string RectangleFigureAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float}){Sep}(?<Width>{Float}){Sep}(?<Height>{Float})";
|
||||
private static readonly string RectangleFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Rr]){Spacer}{RectangleFigureAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos}{Sep}{Float}{Sep}{Float})*";
|
||||
|
||||
// Rectangle Figure
|
||||
private static readonly string RoundedRectangleFigureAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float}){Sep}(?<Width>{Float}){Sep}(?<Height>{Float})" +
|
||||
$"{Sep}(?<RadiusX>{Float}){Sep}(?<RadiusY>{Float})";
|
||||
|
||||
private static readonly string RoundedRectangleFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Uu]){Spacer}{RoundedRectangleFigureAttributes})" +
|
||||
$"(?<Additional>{Sep}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float})*";
|
||||
|
||||
// ARGB Color
|
||||
private static readonly string HexColor = $"(?:#?(?:{Hex}{{2}})?{Hex}{{6}})";
|
||||
|
||||
// Alpha
|
||||
private static readonly string Alpha = $"(?<Alpha>{Hex}{{2}})";
|
||||
|
||||
// Red
|
||||
private static readonly string Red = $"(?<Red>{Hex}{{2}})";
|
||||
|
||||
// Green
|
||||
private static readonly string Green = $"(?<Green>{Hex}{{2}})";
|
||||
|
||||
// Blue
|
||||
private static readonly string Blue = $"(?<Blue>{Hex}{{2}})";
|
||||
|
||||
// Hexadecimal Color
|
||||
private static readonly string RgbColor = $"(?<RgbColor>{HexColor})";
|
||||
|
||||
// HDR Color (Vector4 in which each component has a value between 0 and 1, inclusive)
|
||||
private static readonly string HdrColor = $"(?<HdrColor>{Float01}{Sep}{Float01}{Sep}{Float01}{Sep}{Float01})";
|
||||
|
||||
// Hexadecimal Color Attributes
|
||||
private static readonly string RgbColorAttributes = $"(?<RgbColor>#{{0,1}}{Alpha}{{0,1}}{Red}{Green}{Blue})";
|
||||
|
||||
// HDR Color Attributes (Vector4 in which each component has a value between 0 and 1, inclusive)
|
||||
private static readonly string HdrColorAttributes = $"(?<HdrColor>(?<X>{Float01}){Sep}(?<Y>{Float01}){Sep}(?<Z>{Float01}){Sep}(?<W>{Float01}))";
|
||||
|
||||
private static readonly string ColorRegexString = $"(?:{RgbColorAttributes}|{HdrColorAttributes})";
|
||||
|
||||
// Start Point
|
||||
private static readonly string StartPoint = $"(?<StartPoint>[Mm]{Spacer}{Pos}{Spacer})";
|
||||
|
||||
// End Point
|
||||
private static readonly string EndPoint = $"(?<EndPoint>[Zz]{Spacer}{Pos}{Spacer})";
|
||||
|
||||
// Opacity
|
||||
private static readonly string Opacity = $"(?<Opacity>[Oo]{Spacer}{Float01}{Spacer})";
|
||||
|
||||
// Alpha Mode
|
||||
private static readonly string AlphaMode = $"(?<AlphaMode>[Aa]{Spacer}[012]{Spacer})";
|
||||
|
||||
// Buffer Precision
|
||||
private static readonly string BufferPrecision = $"(?<BufferPrecision>[Bb]{Spacer}[01234]{Spacer})";
|
||||
|
||||
// Edge Behavior
|
||||
private static readonly string EdgeBehavior = $"(?<EdgeBehavior>[Ee]{Spacer}[012]{Spacer})";
|
||||
|
||||
// PreInterpolation Color Space
|
||||
private static readonly string PreColorSpace = $"(?<PreColorSpace>[Pp]{Spacer}[012]{Spacer})";
|
||||
|
||||
// PostInterpolation Color Space
|
||||
private static readonly string PostColorSpace = $"(?<PostColorSpace>[Rr]{Spacer}[012]{Spacer})";
|
||||
|
||||
// Radius in X-axis
|
||||
private static readonly string RadiusX = $"(?<RadiusX>{Float})";
|
||||
|
||||
// Radius in Y-axis
|
||||
private static readonly string RadiusY = $"(?<RadiusY>{Float})";
|
||||
|
||||
// Center location on X-axis
|
||||
private static readonly string CenterX = $"(?<CenterX>{Float})";
|
||||
|
||||
// Center location on Y-axis
|
||||
private static readonly string CenterY = $"(?<CenterY>{Float})";
|
||||
|
||||
// Origin Offset
|
||||
private static readonly string OriginOffset = $"(?<OriginOffset>[Ff]{Spacer}{Pos}{Spacer})";
|
||||
|
||||
// GradientStops
|
||||
private static readonly string GradientStops = $"(?<GradientStops>[Ss]{Spacer}{Float01}{ColorSep}{HexColor}(?:{Sep}{Float01}{ColorSep}{HexColor})*{Spacer})";
|
||||
|
||||
// GradientStopHdrs
|
||||
private static readonly string GradientStopHdrs = $"(?<GradientStops>[Ss]{Spacer}{Float01}{Sep}{HdrColor}(?:{Sep}{Float01}{Sep}{HdrColor})*{Spacer})";
|
||||
|
||||
// Solid Color Brush
|
||||
private static readonly string SolidColorBrush = $"(?<SolidColorBrush>[Ss][Cc]{Spacer}(?:{RgbColor}|{HdrColor}){Spacer}{Opacity}?)";
|
||||
|
||||
// LinearGradient
|
||||
private static readonly string LinearGradient = $"(?<LinearGradient>[Ll][Gg]{Spacer}{StartPoint}{EndPoint}" +
|
||||
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{PreColorSpace}?{PostColorSpace}?" +
|
||||
$"{GradientStops}+{Spacer})";
|
||||
|
||||
// RadialGradient
|
||||
private static readonly string RadialGradient = $"(?<RadialGradient>[Rr][Gg]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
|
||||
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{OriginOffset}?{PreColorSpace}?{PostColorSpace}?" +
|
||||
$"{GradientStops}+{Spacer})";
|
||||
|
||||
// LinearGradientHdr
|
||||
private static readonly string LinearGradientHdr = $"(?<LinearGradientHdr>[Ll][Hh]{Spacer}{StartPoint}{EndPoint}" +
|
||||
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{PreColorSpace}?{PostColorSpace}?" +
|
||||
$"{GradientStopHdrs}+{Spacer})";
|
||||
|
||||
// RadialGradientHdr
|
||||
private static readonly string RadialGradientHdr = $"(?<RadialGradientHdr>[Rr][Hh]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
|
||||
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{OriginOffset}?{PreColorSpace}?{PostColorSpace}?" +
|
||||
$"{GradientStopHdrs}+{Spacer})";
|
||||
|
||||
// Regex for the CanvasBrush
|
||||
private static readonly string CanvasBrushRegexString = $"(?<CanvasBrush>{SolidColorBrush}|{LinearGradient}|{RadialGradient}|{LinearGradientHdr}|{RadialGradientHdr})";
|
||||
|
||||
// Start Point
|
||||
private static readonly string StartPointAttr = $"(?:[Mm]{Spacer}(?<StartX>{Float}){Sep}(?<StartY>{Float}){Spacer})";
|
||||
|
||||
// End Point
|
||||
private static readonly string EndPointAttr = $"(?:[Zz]{Spacer}(?<EndX>{Float}){Sep}(?<EndY>{Float}){Spacer})";
|
||||
|
||||
// Opacity
|
||||
private static readonly string OpacityAttr = $"(?:[Oo]{Spacer}(?<Opacity>{Float01}){Spacer})";
|
||||
|
||||
// Alpha Mode
|
||||
private static readonly string AlphaModeAttr = $"(?:[Aa]{Spacer}(?<AlphaMode>[012]){Spacer})";
|
||||
|
||||
// Buffer Precision
|
||||
private static readonly string BufferPrecisionAttr = $"(?:[Bb]{Spacer}(?<BufferPrecision>[01234]){Spacer})";
|
||||
|
||||
// Edge Behavior
|
||||
private static readonly string EdgeBehaviorAttr = $"(?:[Ee]{Spacer}(?<EdgeBehavior>[012]){Spacer})";
|
||||
|
||||
// PreInterpolation Color Space
|
||||
private static readonly string PreColorSpaceAttr = $"(?:[Pp]{Spacer}(?<PreColorSpace>[012]){Spacer})";
|
||||
|
||||
// PostInterpolation Color Space
|
||||
private static readonly string PostColorSpaceAttr = $"(?:[Rr]{Spacer}(?<PostColorSpace>[012]){Spacer})";
|
||||
|
||||
// Origin Offset
|
||||
private static readonly string OriginOffsetAttr = $"(?<OriginOffset>[Ff]{Spacer}(?<OffsetX>{Float}){Sep}(?<OffsetY>{Float}){Spacer})";
|
||||
|
||||
// GradientStop Attributes
|
||||
private static readonly string GradientStopAttributes = $"(?<Position>{Float01}){ColorSep}{RgbColorAttributes}";
|
||||
private static readonly string GradientStopMainAttributes = $"(?<Main>[Ss]{Spacer}{GradientStopAttributes})";
|
||||
private static readonly string GradientStopRegexString = $"(?<GradientStops>{GradientStopMainAttributes}" + $"(?<Additional>{Sep}{Float01}{ColorSep}{HexColor})*{Spacer})";
|
||||
|
||||
// GradientStopHdr Attributes
|
||||
private static readonly string GradientStopHdrAttributes = $"(?<Position>{Float01}){Sep}{HdrColorAttributes}";
|
||||
private static readonly string GradientStopHdrMainAttributes = $"(?<Main>(?<Command>[Ss]){Spacer}{GradientStopHdrAttributes})";
|
||||
private static readonly string GradientStopHdrRegexString = $"(?<GradientStops>{GradientStopHdrMainAttributes}" + $"(?<Additional>{Sep}{Float01}{Sep}{HdrColor})*{Spacer})";
|
||||
|
||||
// Regex for SolidColorBrush Attributes
|
||||
private static readonly string SolidColorBrushRegexString = $"(?:[Ss][Cc]{Spacer}(?:{RgbColorAttributes}|{HdrColorAttributes}){Spacer}{OpacityAttr}?)";
|
||||
|
||||
// Regex for LinearGradient Attributes
|
||||
private static readonly string LinearGradientRegexString = $"[Ll][Gg]{Spacer}{StartPointAttr}{EndPointAttr}" +
|
||||
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?" +
|
||||
$"{EdgeBehaviorAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
|
||||
$"{GradientStops}+{Spacer}";
|
||||
|
||||
// Regex for RadialGradient Attributes
|
||||
private static readonly string RadialGradientRegexString = $"[Rr][Gg]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
|
||||
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?{EdgeBehaviorAttr}?" +
|
||||
$"{OriginOffsetAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
|
||||
$"{GradientStops}+{Spacer}";
|
||||
|
||||
// Regex for LinearGradientHdr Attributes
|
||||
private static readonly string LinearGradientHdrRegexString = $"[Ll][Hh]{Spacer}{StartPointAttr}{EndPointAttr}" +
|
||||
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?" +
|
||||
$"{EdgeBehaviorAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
|
||||
$"{GradientStopHdrs}+{Spacer}";
|
||||
|
||||
// Regex for RadialGradientHdr Attributes
|
||||
private static readonly string RadialGradientHdrRegexString = $"[Rr][Hh]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
|
||||
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?{EdgeBehaviorAttr}?" +
|
||||
$"{OriginOffsetAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
|
||||
$"{GradientStopHdrs}+{Spacer}";
|
||||
|
||||
// CanvasStrokeStyle attributes
|
||||
private static readonly string DashStyle = $"(?:[Dd][Ss]{Spacer}(?<DashStyle>[01234]){Spacer})";
|
||||
private static readonly string LineJoin = $"(?:[Ll][Jj]{Spacer}(?<LineJoin>[0123]){Spacer})";
|
||||
private static readonly string MiterLimit = $"(?:[Mm][Ll]{Spacer}(?<MiterLimit>{Float}){Spacer})";
|
||||
private static readonly string DashOffset = $"(?:[Dd][Oo]{Spacer}(?<DashOffset>{Float}){Spacer})";
|
||||
private static readonly string StartCap = $"(?:[Ss][Cc]{Spacer}(?<StartCap>[0123]){Spacer})";
|
||||
private static readonly string EndCap = $"(?:[Ee][Cc]{Spacer}(?<EndCap>[0123]){Spacer})";
|
||||
private static readonly string DashCap = $"(?:[Dd][Cc]{Spacer}(?<DashCap>[0123]){Spacer})";
|
||||
private static readonly string TransformBehavior = $"(?:[Tt][Bb]{Spacer}(?<TransformBehavior>[012]){Spacer})";
|
||||
private static readonly string CustomDashAttribute = $"(?<DashSize>{Float}){Sep}(?<SpaceSize>{Float})";
|
||||
private static readonly string CustomDashStyle = $"(?<CustomDashStyle>[Cc][Dd][Ss]{Spacer}(?<Main>{CustomDashAttribute})" + $"(?<Additional>{Sep}{Float}{Sep}{Float})*{Spacer})";
|
||||
|
||||
// CanvasStrokeStyle Regex
|
||||
private static readonly string CanvasStrokeStyleRegexString = $"(?<CanvasStrokeStyle>[Cc][Ss][Ss]{Spacer}{DashStyle}?{LineJoin}?{MiterLimit}?{DashOffset}?" +
|
||||
$"{StartCap}?{EndCap}?{DashCap}?{TransformBehavior}?{CustomDashStyle}?)";
|
||||
|
||||
// CanvasStroke Regex
|
||||
private static readonly string CanvasStrokeRegexString = $"(?<CanvasStroke>[Ss][Tt]{Spacer}" +
|
||||
$"(?<StrokeWidth>{Float}){Spacer}" +
|
||||
$"{CanvasBrushRegexString}{Spacer}" +
|
||||
$"{CanvasStrokeStyleRegexString}?)";
|
||||
|
||||
private static readonly Dictionary<PathFigureType, Regex> PathFigureRegexes;
|
||||
private static readonly Dictionary<PathFigureType, Regex> PathFigureAttributeRegexes;
|
||||
private static readonly Dictionary<PathElementType, Regex> PathElementRegexes;
|
||||
private static readonly Dictionary<PathElementType, Regex> PathElementAttributeRegexes;
|
||||
private static readonly Dictionary<BrushType, Regex> BrushRegexes;
|
||||
private static readonly Dictionary<GradientStopAttributeType, Regex> GradientStopAttributeRegexes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex to perform validation of Path data.
|
||||
/// </summary>
|
||||
public static Regex ValidationRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the CanvasGeometry string.
|
||||
/// </summary>
|
||||
public static Regex CanvasGeometryRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing Hexadecimal Color string.
|
||||
/// </summary>
|
||||
public static Regex ColorRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the ICanvasBrush string.
|
||||
/// </summary>
|
||||
public static Regex CanvasBrushRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the GradientStop string.
|
||||
/// </summary>
|
||||
public static Regex GradientStopRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the GradientStopHdr string.
|
||||
/// </summary>
|
||||
public static Regex GradientStopHdrRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the CanvasStrokeStyle string.
|
||||
/// </summary>
|
||||
public static Regex CanvasStrokeStyleRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the CanvasStroke string.
|
||||
/// </summary>
|
||||
public static Regex CanvasStrokeRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for parsing the CustomDashStyle attributes.
|
||||
/// </summary>
|
||||
public static Regex CustomDashAttributeRegex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes static members of the <see cref="RegexFactory"/> class.
|
||||
/// </summary>
|
||||
static RegexFactory()
|
||||
{
|
||||
PathFigureRegexes = new Dictionary<PathFigureType, Regex>
|
||||
{
|
||||
[PathFigureType.FillRule] = new Regex(FillRuleRegexString, RegexOptions.Compiled),
|
||||
[PathFigureType.PathFigure] = new Regex(PathFigureRegexString, RegexOptions.Compiled),
|
||||
[PathFigureType.EllipseFigure] = new Regex(EllipseFigureRegexString, RegexOptions.Compiled),
|
||||
[PathFigureType.PolygonFigure] = new Regex(PolygonFigureRegexString, RegexOptions.Compiled),
|
||||
[PathFigureType.RectangleFigure] = new Regex(RectangleFigureRegexString, RegexOptions.Compiled),
|
||||
[PathFigureType.RoundedRectangleFigure] = new Regex(RoundedRectangleFigureRegexString, RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
PathFigureAttributeRegexes = new Dictionary<PathFigureType, Regex>
|
||||
{
|
||||
// Not Applicable for FillRuleElement
|
||||
[PathFigureType.FillRule] = null,
|
||||
|
||||
// Not Applicable for CanvasPathFigure
|
||||
[PathFigureType.PathFigure] = null,
|
||||
[PathFigureType.EllipseFigure] = new Regex($"{Sep}{EllipseFigureAttributes}", RegexOptions.Compiled),
|
||||
[PathFigureType.PolygonFigure] = new Regex($"{Sep}{PolygonFigureAttributes}", RegexOptions.Compiled),
|
||||
[PathFigureType.RectangleFigure] = new Regex($"{Sep}{RectangleFigureAttributes}", RegexOptions.Compiled),
|
||||
[PathFigureType.RoundedRectangleFigure] = new Regex($"{Sep}{RoundedRectangleFigureAttributes}", RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
PathElementRegexes = new Dictionary<PathElementType, Regex>
|
||||
{
|
||||
[PathElementType.MoveTo] = new Regex(MoveToRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.Line] = new Regex(LineRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.HorizontalLine] = new Regex(HorizontalLineRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.VerticalLine] = new Regex(VerticalLineRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.QuadraticBezier] = new Regex(QuadraticBezierRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.SmoothQuadraticBezier] = new Regex(SmoothQuadraticBezierRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.CubicBezier] = new Regex(CubicBezierRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.SmoothCubicBezier] = new Regex(SmoothCubicBezierRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.Arc] = new Regex(ArcRegexString, RegexOptions.Compiled),
|
||||
[PathElementType.ClosePath] = new Regex(ClosePathRegexString, RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
PathElementAttributeRegexes = new Dictionary<PathElementType, Regex>
|
||||
{
|
||||
[PathElementType.MoveTo] = new Regex($"{Sep}{MoveToAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.Line] = new Regex($"{Sep}{LineAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.HorizontalLine] = new Regex($"{Sep}{HorizontalLineAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.VerticalLine] = new Regex($"{Sep}{VerticalLineAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.QuadraticBezier] = new Regex($"{Sep}{QuadraticBezierAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.SmoothQuadraticBezier] = new Regex($"{Sep}{SmoothQuadraticBezierAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.CubicBezier] = new Regex($"{Sep}{CubicBezierAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.SmoothCubicBezier] = new Regex($"{Sep}{SmoothCubicBezierAttributes}", RegexOptions.Compiled),
|
||||
[PathElementType.Arc] = new Regex($"{Sep}{ArcAttributes}", RegexOptions.Compiled),
|
||||
|
||||
// Not Applicable for ClosePathElement as it has no attributes
|
||||
[PathElementType.ClosePath] = null
|
||||
};
|
||||
|
||||
BrushRegexes = new Dictionary<BrushType, Regex>
|
||||
{
|
||||
[BrushType.SolidColor] = new Regex(SolidColorBrushRegexString, RegexOptions.Compiled),
|
||||
[BrushType.LinearGradient] = new Regex(LinearGradientRegexString, RegexOptions.Compiled),
|
||||
[BrushType.LinearGradientHdr] = new Regex(LinearGradientHdrRegexString, RegexOptions.Compiled),
|
||||
[BrushType.RadialGradient] = new Regex(RadialGradientRegexString, RegexOptions.Compiled),
|
||||
[BrushType.RadialGradientHdr] = new Regex(RadialGradientHdrRegexString, RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
GradientStopAttributeRegexes = new Dictionary<GradientStopAttributeType, Regex>
|
||||
{
|
||||
[GradientStopAttributeType.Main] = new Regex(GradientStopMainAttributes, RegexOptions.Compiled),
|
||||
[GradientStopAttributeType.Additional] = new Regex($"{Sep}{GradientStopAttributes}", RegexOptions.Compiled),
|
||||
[GradientStopAttributeType.MainHdr] = new Regex(GradientStopHdrMainAttributes, RegexOptions.Compiled),
|
||||
[GradientStopAttributeType.AdditionalHdr] = new Regex($"{Sep}{GradientStopHdrAttributes}", RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
ValidationRegex = new Regex(@"\s+");
|
||||
CanvasGeometryRegex = new Regex(CanvasGeometryRegexString, RegexOptions.Compiled);
|
||||
ColorRegex = new Regex(ColorRegexString, RegexOptions.Compiled);
|
||||
CanvasBrushRegex = new Regex(CanvasBrushRegexString, RegexOptions.Compiled);
|
||||
GradientStopRegex = new Regex(GradientStopRegexString, RegexOptions.Compiled);
|
||||
GradientStopHdrRegex = new Regex(GradientStopHdrRegexString, RegexOptions.Compiled);
|
||||
CanvasStrokeStyleRegex = new Regex(CanvasStrokeStyleRegexString, RegexOptions.Compiled);
|
||||
CanvasStrokeRegex = new Regex(CanvasStrokeRegexString, RegexOptions.Compiled);
|
||||
CustomDashAttributeRegex = new Regex($"{Sep}{CustomDashAttribute}", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for the given PathFigureType
|
||||
/// </summary>
|
||||
/// <param name="figureType">PathFigureType</param>
|
||||
/// <returns>Regex</returns>
|
||||
internal static Regex GetRegex(PathFigureType figureType)
|
||||
{
|
||||
return PathFigureRegexes[figureType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for the given PathElementType
|
||||
/// </summary>
|
||||
/// <param name="elementType">PathElementType</param>
|
||||
/// <returns>Regex</returns>
|
||||
internal static Regex GetRegex(PathElementType elementType)
|
||||
{
|
||||
return PathElementRegexes[elementType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for extracting attributes of the given PathFigureType
|
||||
/// </summary>
|
||||
/// <param name="figureType">PathFigureType</param>
|
||||
/// <returns>Regex</returns>
|
||||
internal static Regex GetAttributesRegex(PathFigureType figureType)
|
||||
{
|
||||
return PathFigureAttributeRegexes[figureType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for extracting attributes of the given PathElementType
|
||||
/// </summary>
|
||||
/// <param name="elementType">PathElementType</param>
|
||||
/// <returns>Regex</returns>
|
||||
internal static Regex GetAttributesRegex(PathElementType elementType)
|
||||
{
|
||||
return PathElementAttributeRegexes[elementType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting the attributes of the given BrushType
|
||||
/// </summary>
|
||||
/// <param name="brushType">BrushType</param>
|
||||
/// <returns>Regex</returns>
|
||||
internal static Regex GetAttributesRegex(BrushType brushType)
|
||||
{
|
||||
return BrushRegexes[brushType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting the attributes of the given
|
||||
/// GradientStopAttributeType
|
||||
/// </summary>
|
||||
/// <param name="gsAttrType">GradientStopAttributeType</param>
|
||||
/// <returns>Regex</returns>
|
||||
internal static Regex GetAttributesRegex(GradientStopAttributeType gsAttrType)
|
||||
{
|
||||
return GradientStopAttributeRegexes[gsAttrType];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Class which can be used to encapsulate code statement(s) so that they are executed in a specific culture.
|
||||
/// <para />
|
||||
/// Usage example:
|
||||
/// <para />
|
||||
/// The following code block will be executed using the French culture.
|
||||
/// <para />
|
||||
/// using (new CultureShield("fr-FR"))
|
||||
/// <para />
|
||||
/// {
|
||||
/// <para />
|
||||
/// ...
|
||||
/// <para />
|
||||
/// }
|
||||
/// </summary>
|
||||
internal readonly ref struct CultureShield
|
||||
{
|
||||
private readonly CultureInfo _prevCulture;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CultureShield"/> struct so that the encapsulated code statement(s) can be executed using the specified culture.
|
||||
/// <para />
|
||||
/// Usage example:
|
||||
/// <para />
|
||||
/// The following code block will be executed using the French culture.
|
||||
/// <para />
|
||||
/// using (new CultureShield("fr-FR"))
|
||||
/// <para />
|
||||
/// {
|
||||
/// <para />
|
||||
/// ...
|
||||
/// <para />
|
||||
/// }
|
||||
/// </summary>
|
||||
/// <param name="culture">The culture in which the encapsulated code statement(s) are to be executed.</param>
|
||||
internal CultureShield(string culture)
|
||||
{
|
||||
_prevCulture = CultureInfo.CurrentCulture;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the CultureShield object.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
CultureInfo.CurrentCulture = _prevCulture;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for all Brush Elements
|
||||
/// </summary>
|
||||
internal abstract class AbstractCanvasBrushElement : ICanvasBrushElement
|
||||
{
|
||||
#pragma warning disable SA1401 // Fields should be private
|
||||
protected float _opacity;
|
||||
#pragma warning restore SA1401 // Fields should be private
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Brush data defining the Brush Element
|
||||
/// </summary>
|
||||
public string Data { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of non-whitespace characters in
|
||||
/// the Brush Data
|
||||
/// </summary>
|
||||
public int ValidationCount { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Brush Element with the given Capture
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
public virtual void Initialize(Capture capture)
|
||||
{
|
||||
Data = capture.Value;
|
||||
|
||||
var regex = GetAttributesRegex();
|
||||
var match = regex.Match(Data);
|
||||
if (!match.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetAttributes(match);
|
||||
|
||||
// Get the number of non-whitespace characters in the data
|
||||
ValidationCount = RegexFactory.ValidationRegex.Replace(Data, string.Empty).Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the ICanvasBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>ICanvasBrush</returns>
|
||||
public abstract ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting Brush Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Regex</returns>
|
||||
protected abstract Regex GetAttributesRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Brush Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected abstract void GetAttributes(Match match);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for a Brush Element
|
||||
/// </summary>
|
||||
internal interface ICanvasBrushElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Brush data defining the Brush Element
|
||||
/// </summary>
|
||||
string Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of non-whitespace characters in
|
||||
/// the Brush Data
|
||||
/// </summary>
|
||||
int ValidationCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Brush Element with the given Capture
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
void Initialize(Capture capture);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the ICanvasBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>ICanvasBrush</returns>
|
||||
ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CanvasLinearGradientBrush with GradientStops
|
||||
/// </summary>
|
||||
internal sealed class LinearGradientBrushElement : AbstractCanvasBrushElement
|
||||
{
|
||||
private Vector2 _startPoint;
|
||||
private Vector2 _endPoint;
|
||||
private CanvasAlphaMode _alphaMode;
|
||||
private CanvasBufferPrecision _bufferPrecision;
|
||||
private CanvasEdgeBehavior _edgeBehavior;
|
||||
private CanvasColorSpace _preInterpolationColorSpace;
|
||||
private CanvasColorSpace _postInterpolationColorSpace;
|
||||
private List<CanvasGradientStop> _gradientStops;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinearGradientBrushElement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
public LinearGradientBrushElement(Capture capture)
|
||||
{
|
||||
// Set the default values
|
||||
_startPoint = Vector2.Zero;
|
||||
_endPoint = Vector2.Zero;
|
||||
_opacity = 1f;
|
||||
_alphaMode = (CanvasAlphaMode)0;
|
||||
_bufferPrecision = (CanvasBufferPrecision)0;
|
||||
_edgeBehavior = (CanvasEdgeBehavior)0;
|
||||
|
||||
// Default ColorSpace is sRGB
|
||||
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_gradientStops = new List<CanvasGradientStop>();
|
||||
|
||||
// Initialize
|
||||
Initialize(capture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the CanvasLinearGradientBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>An instance of <see cref="ICanvasBrush"/></returns>
|
||||
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
|
||||
{
|
||||
var brush = new CanvasLinearGradientBrush(
|
||||
resourceCreator,
|
||||
_gradientStops.ToArray(),
|
||||
_edgeBehavior,
|
||||
_alphaMode,
|
||||
_preInterpolationColorSpace,
|
||||
_postInterpolationColorSpace,
|
||||
_bufferPrecision)
|
||||
{
|
||||
StartPoint = _startPoint,
|
||||
EndPoint = _endPoint,
|
||||
Opacity = _opacity
|
||||
};
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting Brush Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Regex</returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(BrushType.LinearGradient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Brush Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
// Start Point
|
||||
float.TryParse(match.Groups["StartX"].Value, out var startX);
|
||||
float.TryParse(match.Groups["StartY"].Value, out var startY);
|
||||
_startPoint = new Vector2(startX, startY);
|
||||
|
||||
// End Point
|
||||
float.TryParse(match.Groups["EndX"].Value, out var endX);
|
||||
float.TryParse(match.Groups["EndY"].Value, out var endY);
|
||||
_endPoint = new Vector2(endX, endY);
|
||||
|
||||
// Opacity (optional)
|
||||
var group = match.Groups["Opacity"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(group.Value, out _opacity);
|
||||
}
|
||||
|
||||
// Alpha Mode (optional)
|
||||
group = match.Groups["AlphaMode"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _alphaMode);
|
||||
}
|
||||
|
||||
// Buffer Precision (optional)
|
||||
group = match.Groups["BufferPrecision"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _bufferPrecision);
|
||||
}
|
||||
|
||||
// Edge Behavior (optional)
|
||||
group = match.Groups["EdgeBehavior"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _edgeBehavior);
|
||||
}
|
||||
|
||||
// Pre Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PreColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// Post Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PostColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// GradientStops
|
||||
group = match.Groups["GradientStops"];
|
||||
if (group.Success)
|
||||
{
|
||||
_gradientStops.Clear();
|
||||
foreach (Capture capture in group.Captures)
|
||||
{
|
||||
var gradientMatch = RegexFactory.GradientStopRegex.Match(capture.Value);
|
||||
if (!gradientMatch.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float position;
|
||||
Color color;
|
||||
|
||||
// Main Attributes
|
||||
var main = gradientMatch.Groups["Main"];
|
||||
if (main.Success)
|
||||
{
|
||||
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Main).Match(main.Value);
|
||||
float.TryParse(mainMatch.Groups["Position"].Value, out position);
|
||||
color = ColorParser.Parse(mainMatch);
|
||||
|
||||
_gradientStops.Add(new CanvasGradientStop()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
|
||||
// Additional Attributes
|
||||
var additional = gradientMatch.Groups["Additional"];
|
||||
if (!additional.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Capture addCapture in additional.Captures)
|
||||
{
|
||||
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Additional).Match(addCapture.Value);
|
||||
float.TryParse(addMatch.Groups["Position"].Value, out position);
|
||||
color = ColorParser.Parse(addMatch);
|
||||
|
||||
_gradientStops.Add(new CanvasGradientStop()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the stops based on their position
|
||||
if (_gradientStops.Any())
|
||||
{
|
||||
_gradientStops = _gradientStops.OrderBy(g => g.Position).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CanvasLinearGradientBrush with GradientStopHdrs
|
||||
/// </summary>
|
||||
internal sealed class LinearGradientHdrBrushElement : AbstractCanvasBrushElement
|
||||
{
|
||||
private Vector2 _startPoint;
|
||||
private Vector2 _endPoint;
|
||||
private CanvasAlphaMode _alphaMode;
|
||||
private CanvasBufferPrecision _bufferPrecision;
|
||||
private CanvasEdgeBehavior _edgeBehavior;
|
||||
private CanvasColorSpace _preInterpolationColorSpace;
|
||||
private CanvasColorSpace _postInterpolationColorSpace;
|
||||
private List<CanvasGradientStopHdr> _gradientStopHdrs;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinearGradientHdrBrushElement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
public LinearGradientHdrBrushElement(Capture capture)
|
||||
{
|
||||
// Set the default values
|
||||
_startPoint = Vector2.Zero;
|
||||
_endPoint = Vector2.Zero;
|
||||
_opacity = 1f;
|
||||
_alphaMode = (CanvasAlphaMode)0;
|
||||
_bufferPrecision = (CanvasBufferPrecision)0;
|
||||
_edgeBehavior = (CanvasEdgeBehavior)0;
|
||||
|
||||
// Default ColorSpace is sRGB
|
||||
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_gradientStopHdrs = new List<CanvasGradientStopHdr>();
|
||||
|
||||
// Initialize
|
||||
Initialize(capture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the CanvasLinearGradientBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
|
||||
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
|
||||
{
|
||||
var brush = CanvasLinearGradientBrush.CreateHdr(
|
||||
resourceCreator,
|
||||
_gradientStopHdrs.ToArray(),
|
||||
_edgeBehavior,
|
||||
_alphaMode,
|
||||
_preInterpolationColorSpace,
|
||||
_postInterpolationColorSpace,
|
||||
_bufferPrecision);
|
||||
|
||||
brush.StartPoint = _startPoint;
|
||||
brush.EndPoint = _endPoint;
|
||||
brush.Opacity = _opacity;
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting Brush Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Regex</returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(BrushType.LinearGradientHdr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Brush Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
// Start Point
|
||||
float.TryParse(match.Groups["StartX"].Value, out var startX);
|
||||
float.TryParse(match.Groups["StartY"].Value, out var startY);
|
||||
_startPoint = new Vector2(startX, startY);
|
||||
|
||||
// End Point
|
||||
float.TryParse(match.Groups["EndX"].Value, out var endX);
|
||||
float.TryParse(match.Groups["EndY"].Value, out var endY);
|
||||
_endPoint = new Vector2(endX, endY);
|
||||
|
||||
// Opacity (optional)
|
||||
var group = match.Groups["Opacity"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(group.Value, out _opacity);
|
||||
}
|
||||
|
||||
// Alpha Mode (optional)
|
||||
group = match.Groups["AlphaMode"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _alphaMode);
|
||||
}
|
||||
|
||||
// Buffer Precision (optional)
|
||||
group = match.Groups["BufferPrecision"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _bufferPrecision);
|
||||
}
|
||||
|
||||
// Edge Behavior (optional)
|
||||
group = match.Groups["EdgeBehavior"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _edgeBehavior);
|
||||
}
|
||||
|
||||
// Pre Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PreColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// Post Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PostColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// GradientStopHdrs
|
||||
group = match.Groups["GradientStops"];
|
||||
if (group.Success)
|
||||
{
|
||||
_gradientStopHdrs.Clear();
|
||||
foreach (Capture capture in group.Captures)
|
||||
{
|
||||
var gradientMatch = RegexFactory.GradientStopHdrRegex.Match(capture.Value);
|
||||
if (!gradientMatch.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Main Attributes
|
||||
var main = gradientMatch.Groups["Main"];
|
||||
if (main.Success)
|
||||
{
|
||||
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.MainHdr)
|
||||
.Match(main.Value);
|
||||
float.TryParse(mainMatch.Groups["Position"].Value, out float position);
|
||||
float.TryParse(mainMatch.Groups["X"].Value, out float x);
|
||||
float.TryParse(mainMatch.Groups["Y"].Value, out float y);
|
||||
float.TryParse(mainMatch.Groups["Z"].Value, out float z);
|
||||
float.TryParse(mainMatch.Groups["W"].Value, out float w);
|
||||
|
||||
var color = new Vector4(x, y, z, w);
|
||||
|
||||
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
|
||||
// Additional Attributes
|
||||
var additional = gradientMatch.Groups["Additional"];
|
||||
if (!additional.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Capture addCapture in additional.Captures)
|
||||
{
|
||||
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.AdditionalHdr)
|
||||
.Match(addCapture.Value);
|
||||
float.TryParse(addMatch.Groups["Position"].Value, out float position);
|
||||
float.TryParse(addMatch.Groups["X"].Value, out float x);
|
||||
float.TryParse(addMatch.Groups["Y"].Value, out float y);
|
||||
float.TryParse(addMatch.Groups["Z"].Value, out float z);
|
||||
float.TryParse(addMatch.Groups["W"].Value, out float w);
|
||||
|
||||
var color = new Vector4(x, y, z, w);
|
||||
|
||||
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the stops based on their position
|
||||
if (_gradientStopHdrs.Any())
|
||||
{
|
||||
_gradientStopHdrs = _gradientStopHdrs.OrderBy(g => g.Position).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CanvasRadialGradientBrush with GradientStops
|
||||
/// </summary>
|
||||
internal sealed class RadialGradientBrushElement : AbstractCanvasBrushElement
|
||||
{
|
||||
private float _radiusX;
|
||||
private float _radiusY;
|
||||
private Vector2 _center;
|
||||
private Vector2 _originOffset;
|
||||
private CanvasAlphaMode _alphaMode;
|
||||
private CanvasBufferPrecision _bufferPrecision;
|
||||
private CanvasEdgeBehavior _edgeBehavior;
|
||||
private CanvasColorSpace _preInterpolationColorSpace;
|
||||
private CanvasColorSpace _postInterpolationColorSpace;
|
||||
private List<CanvasGradientStop> _gradientStops;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadialGradientBrushElement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
public RadialGradientBrushElement(Capture capture)
|
||||
{
|
||||
// Set the default values
|
||||
_radiusX = 0f;
|
||||
_radiusY = 0f;
|
||||
_center = Vector2.Zero;
|
||||
_originOffset = Vector2.Zero;
|
||||
_opacity = 1f;
|
||||
_alphaMode = (CanvasAlphaMode)0;
|
||||
_bufferPrecision = (CanvasBufferPrecision)0;
|
||||
_edgeBehavior = (CanvasEdgeBehavior)0;
|
||||
|
||||
// Default ColorSpace is sRGB
|
||||
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_gradientStops = new List<CanvasGradientStop>();
|
||||
|
||||
// Initialize
|
||||
Initialize(capture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the CanvasLinearGradientBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
|
||||
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
|
||||
{
|
||||
var brush = new CanvasRadialGradientBrush(
|
||||
resourceCreator,
|
||||
_gradientStops.ToArray(),
|
||||
_edgeBehavior,
|
||||
_alphaMode,
|
||||
_preInterpolationColorSpace,
|
||||
_postInterpolationColorSpace,
|
||||
_bufferPrecision)
|
||||
{
|
||||
RadiusX = _radiusX,
|
||||
RadiusY = _radiusY,
|
||||
Center = _center,
|
||||
OriginOffset = _originOffset,
|
||||
Opacity = _opacity
|
||||
};
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting Brush Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Regex</returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(BrushType.RadialGradient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Brush Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
// RadiusX
|
||||
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
|
||||
|
||||
// Sanitize by taking the absolute value
|
||||
_radiusX = Math.Abs(_radiusX);
|
||||
|
||||
// RadiusY
|
||||
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
|
||||
|
||||
// Sanitize by taking the absolute value
|
||||
_radiusY = Math.Abs(_radiusY);
|
||||
|
||||
// CenterX
|
||||
float.TryParse(match.Groups["CenterX"].Value, out var centerX);
|
||||
|
||||
// CenterY
|
||||
float.TryParse(match.Groups["CenterY"].Value, out var centerY);
|
||||
_center = new Vector2(centerX, centerY);
|
||||
|
||||
// Opacity (optional)
|
||||
var group = match.Groups["Opacity"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(group.Value, out _opacity);
|
||||
}
|
||||
|
||||
// Origin Offset (optional)
|
||||
group = match.Groups["OriginOffset"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(match.Groups["OffsetX"].Value, out var offsetX);
|
||||
float.TryParse(match.Groups["OffsetY"].Value, out var offsetY);
|
||||
_originOffset = new Vector2(offsetX, offsetY);
|
||||
}
|
||||
|
||||
// Alpha Mode (optional)
|
||||
group = match.Groups["AlphaMode"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _alphaMode);
|
||||
}
|
||||
|
||||
// Buffer Precision (optional)
|
||||
group = match.Groups["BufferPrecision"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _bufferPrecision);
|
||||
}
|
||||
|
||||
// Edge Behavior (optional)
|
||||
group = match.Groups["EdgeBehavior"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _edgeBehavior);
|
||||
}
|
||||
|
||||
// Pre Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PreColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// Post Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PostColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// Gradient Stops
|
||||
group = match.Groups["GradientStops"];
|
||||
if (group.Success)
|
||||
{
|
||||
_gradientStops.Clear();
|
||||
foreach (Capture capture in group.Captures)
|
||||
{
|
||||
var gradientMatch = RegexFactory.GradientStopRegex.Match(capture.Value);
|
||||
if (!gradientMatch.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float position;
|
||||
Color color;
|
||||
|
||||
// Main Attributes
|
||||
var main = gradientMatch.Groups["Main"];
|
||||
if (main.Success)
|
||||
{
|
||||
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Main).Match(main.Value);
|
||||
float.TryParse(mainMatch.Groups["Position"].Value, out position);
|
||||
color = ColorParser.Parse(mainMatch);
|
||||
|
||||
_gradientStops.Add(new CanvasGradientStop()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
|
||||
// Additional Attributes
|
||||
var additional = gradientMatch.Groups["Additional"];
|
||||
if (!additional.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Capture addCapture in additional.Captures)
|
||||
{
|
||||
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Additional).Match(addCapture.Value);
|
||||
float.TryParse(addMatch.Groups["Position"].Value, out position);
|
||||
color = ColorParser.Parse(addMatch);
|
||||
|
||||
_gradientStops.Add(new CanvasGradientStop()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the stops based on their position
|
||||
if (_gradientStops.Any())
|
||||
{
|
||||
_gradientStops = _gradientStops.OrderBy(g => g.Position).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CanvasRadialGradientBrush with GradientStopHdrs
|
||||
/// </summary>
|
||||
internal sealed class RadialGradientHdrBrushElement : AbstractCanvasBrushElement
|
||||
{
|
||||
private float _radiusX;
|
||||
private float _radiusY;
|
||||
private Vector2 _center;
|
||||
private Vector2 _originOffset;
|
||||
private CanvasAlphaMode _alphaMode;
|
||||
private CanvasBufferPrecision _bufferPrecision;
|
||||
private CanvasEdgeBehavior _edgeBehavior;
|
||||
private CanvasColorSpace _preInterpolationColorSpace;
|
||||
private CanvasColorSpace _postInterpolationColorSpace;
|
||||
private List<CanvasGradientStopHdr> _gradientStopHdrs;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadialGradientHdrBrushElement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
public RadialGradientHdrBrushElement(Capture capture)
|
||||
{
|
||||
// Set the default values
|
||||
_radiusX = 0f;
|
||||
_radiusY = 0f;
|
||||
_center = Vector2.Zero;
|
||||
_originOffset = Vector2.Zero;
|
||||
_opacity = 1f;
|
||||
_alphaMode = (CanvasAlphaMode)0;
|
||||
_bufferPrecision = (CanvasBufferPrecision)0;
|
||||
_edgeBehavior = (CanvasEdgeBehavior)0;
|
||||
|
||||
// Default ColorSpace is sRGB
|
||||
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
|
||||
_gradientStopHdrs = new List<CanvasGradientStopHdr>();
|
||||
|
||||
// Initialize
|
||||
Initialize(capture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the CanvasLinearGradientBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
|
||||
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
|
||||
{
|
||||
var brush = CanvasRadialGradientBrush.CreateHdr(
|
||||
resourceCreator,
|
||||
_gradientStopHdrs.ToArray(),
|
||||
_edgeBehavior,
|
||||
_alphaMode,
|
||||
_preInterpolationColorSpace,
|
||||
_postInterpolationColorSpace,
|
||||
_bufferPrecision);
|
||||
|
||||
brush.RadiusX = _radiusX;
|
||||
brush.RadiusY = _radiusY;
|
||||
brush.Center = _center;
|
||||
brush.OriginOffset = _originOffset;
|
||||
brush.Opacity = _opacity;
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting Brush Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Regex</returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(BrushType.RadialGradientHdr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Brush Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
// RadiusX
|
||||
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
|
||||
|
||||
// Sanitize by taking the absolute value
|
||||
_radiusX = Math.Abs(_radiusX);
|
||||
|
||||
// RadiusY
|
||||
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
|
||||
|
||||
// Sanitize by taking the absolute value
|
||||
_radiusY = Math.Abs(_radiusY);
|
||||
|
||||
// CenterX
|
||||
float.TryParse(match.Groups["CenterX"].Value, out var centerX);
|
||||
|
||||
// CenterY
|
||||
float.TryParse(match.Groups["CenterY"].Value, out var centerY);
|
||||
_center = new Vector2(centerX, centerY);
|
||||
|
||||
// Opacity (optional)
|
||||
var group = match.Groups["Opacity"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(group.Value, out _opacity);
|
||||
}
|
||||
|
||||
// Origin Offset (optional)
|
||||
group = match.Groups["OriginOffset"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(match.Groups["OffsetX"].Value, out var offsetX);
|
||||
float.TryParse(match.Groups["OffsetY"].Value, out var offsetY);
|
||||
_originOffset = new Vector2(offsetX, offsetY);
|
||||
}
|
||||
|
||||
// Alpha Mode (optional)
|
||||
group = match.Groups["AlphaMode"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _alphaMode);
|
||||
}
|
||||
|
||||
// Buffer Precision (optional)
|
||||
group = match.Groups["BufferPrecision"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _bufferPrecision);
|
||||
}
|
||||
|
||||
// Edge Behavior (optional)
|
||||
group = match.Groups["EdgeBehavior"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _edgeBehavior);
|
||||
}
|
||||
|
||||
// Pre Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PreColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// Post Interpolation ColorSpace (optional)
|
||||
group = match.Groups["PostColorSpace"];
|
||||
if (group.Success)
|
||||
{
|
||||
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
|
||||
}
|
||||
|
||||
// GradientStopHdrs
|
||||
group = match.Groups["GradientStops"];
|
||||
if (group.Success)
|
||||
{
|
||||
_gradientStopHdrs.Clear();
|
||||
foreach (Capture capture in group.Captures)
|
||||
{
|
||||
var gradientMatch = RegexFactory.GradientStopHdrRegex.Match(capture.Value);
|
||||
if (!gradientMatch.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float position;
|
||||
float x = 0, y = 0, z = 0, w = 0;
|
||||
var main = gradientMatch.Groups["Main"];
|
||||
if (main.Success)
|
||||
{
|
||||
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.MainHdr)
|
||||
.Match(main.Value);
|
||||
|
||||
float.TryParse(mainMatch.Groups["Position"].Value, out position);
|
||||
float.TryParse(mainMatch.Groups["X"].Value, out x);
|
||||
float.TryParse(mainMatch.Groups["Y"].Value, out y);
|
||||
float.TryParse(mainMatch.Groups["Z"].Value, out z);
|
||||
float.TryParse(mainMatch.Groups["W"].Value, out w);
|
||||
|
||||
var color = new Vector4(x, y, z, w);
|
||||
|
||||
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
|
||||
var additional = gradientMatch.Groups["Additional"];
|
||||
if (!additional.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Capture addCapture in additional.Captures)
|
||||
{
|
||||
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.AdditionalHdr)
|
||||
.Match(addCapture.Value);
|
||||
float.TryParse(addMatch.Groups["Position"].Value, out position);
|
||||
float.TryParse(addMatch.Groups["X"].Value, out x);
|
||||
float.TryParse(addMatch.Groups["Y"].Value, out y);
|
||||
float.TryParse(addMatch.Groups["Z"].Value, out z);
|
||||
float.TryParse(addMatch.Groups["W"].Value, out w);
|
||||
|
||||
var color = new Vector4(x, y, z, w);
|
||||
|
||||
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
|
||||
{
|
||||
Color = color,
|
||||
Position = position
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the stops based on their position
|
||||
if (_gradientStopHdrs.Any())
|
||||
{
|
||||
_gradientStopHdrs = _gradientStopHdrs.OrderBy(g => g.Position).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CanvasSolidColorBrush
|
||||
/// </summary>
|
||||
internal sealed class SolidColorBrushElement : AbstractCanvasBrushElement
|
||||
{
|
||||
private Color _color;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SolidColorBrushElement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
public SolidColorBrushElement(Capture capture)
|
||||
{
|
||||
// Set the default values
|
||||
_color = Colors.Transparent;
|
||||
_opacity = 1f;
|
||||
|
||||
Initialize(capture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the ICanvasBrush from the parsed data
|
||||
/// </summary>
|
||||
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
|
||||
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
|
||||
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
|
||||
{
|
||||
return new CanvasSolidColorBrush(resourceCreator, _color)
|
||||
{
|
||||
Opacity = _opacity
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Regex for extracting Brush Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Regex</returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(BrushType.SolidColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Brush Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
// Parse the Color
|
||||
_color = ColorParser.Parse(match);
|
||||
|
||||
// Opacity (optional)
|
||||
var group = match.Groups["Opacity"];
|
||||
if (group.Success)
|
||||
{
|
||||
float.TryParse(group.Value, out _opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for all Path Elements
|
||||
/// </summary>
|
||||
internal abstract class AbstractPathElement : ICanvasPathElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets index of the Path Element in the Path Data
|
||||
/// </summary>
|
||||
public int Index { get; protected set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path data defining the Path Element
|
||||
/// </summary>
|
||||
public string Data { get; protected set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets number of non-whitespace characters in
|
||||
/// the Path Element Data
|
||||
/// </summary>
|
||||
public int ValidationCount { get; protected set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the path element contains
|
||||
/// absolute or relative coordinates.
|
||||
/// </summary>
|
||||
public bool IsRelative { get; protected set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Path Element with the given Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
/// <param name="index">Index within the match</param>
|
||||
public virtual void Initialize(Match match, int index)
|
||||
{
|
||||
var main = match.Groups["Main"];
|
||||
Index = index;
|
||||
Data = main.Value;
|
||||
var command = match.Groups["Command"].Value[0];
|
||||
IsRelative = char.IsLower(command);
|
||||
|
||||
// Get the Path Element attributes
|
||||
GetAttributes(match);
|
||||
|
||||
// Get the number of non-whitespace characters in the data
|
||||
ValidationCount = RegexFactory.ValidationRegex.Replace(main.Value, string.Empty).Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Path Element with the given Capture
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
/// <param name="index">Index of the Path Element in the Path data.</param>
|
||||
/// <param name="isRelative">Indicates whether the Path Element coordinates are
|
||||
/// absolute or relative</param>
|
||||
public virtual void InitializeAdditional(Capture capture, int index, bool isRelative)
|
||||
{
|
||||
Index = index;
|
||||
Data = capture.Value;
|
||||
IsRelative = isRelative;
|
||||
|
||||
var match = GetAttributesRegex().Match(Data);
|
||||
if (match.Captures.Count != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the Path Element attributes
|
||||
GetAttributes(match);
|
||||
|
||||
// Get the number of non-whitespace characters in the data
|
||||
ValidationCount = RegexFactory.ValidationRegex.Replace(Data, string.Empty).Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Path Element to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder">CanvasPathBuilder object</param>
|
||||
/// <param name="currentPoint">The last active location in the Path before adding
|
||||
/// the Path Element</param>
|
||||
/// <param name="lastElement">The previous PathElement in the Path.</param>
|
||||
/// <returns>The latest location in the Path after adding the Path Element</returns>
|
||||
public abstract Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement);
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for extracting Path Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Instance of <see cref="Regex"/></returns>
|
||||
protected abstract Regex GetAttributesRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Path Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected abstract void GetAttributes(Match match);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing the Arc Element in a Path Geometry
|
||||
/// </summary>
|
||||
internal class ArcElement : AbstractPathElement
|
||||
{
|
||||
private float _radiusX;
|
||||
private float _radiusY;
|
||||
private float _angle;
|
||||
private CanvasArcSize _arcSize;
|
||||
private CanvasSweepDirection _sweepDirection;
|
||||
private float _x;
|
||||
private float _y;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArcElement"/> class.
|
||||
/// </summary>
|
||||
public ArcElement()
|
||||
{
|
||||
_radiusX = _radiusY = _angle = 0;
|
||||
_arcSize = CanvasArcSize.Small;
|
||||
_sweepDirection = CanvasSweepDirection.Clockwise;
|
||||
_sweepDirection = 0;
|
||||
_x = _y = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Path Element to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder">CanvasPathBuilder object</param>
|
||||
/// <param name="currentPoint">The last active location in the Path before adding
|
||||
/// the Path Element</param>
|
||||
/// <param name="lastElement">The previous PathElement in the Path.</param>
|
||||
/// <returns>The latest location in the Path after adding the Path Element</returns>
|
||||
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement)
|
||||
{
|
||||
// Calculate coordinates
|
||||
var point = new Vector2(_x, _y);
|
||||
if (IsRelative)
|
||||
{
|
||||
point += currentPoint;
|
||||
}
|
||||
|
||||
// Execute command
|
||||
pathBuilder.AddArc(point, _radiusX, _radiusY, _angle, _sweepDirection, _arcSize);
|
||||
|
||||
// Set Last Element
|
||||
lastElement = this;
|
||||
|
||||
// Return current point
|
||||
return point;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for extracting Path Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Instance of <see cref="Regex"/></returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(PathElementType.Arc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Path Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
|
||||
|
||||
// Sanitize by taking only the absolute value
|
||||
_radiusX = Math.Abs(_radiusX);
|
||||
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
|
||||
|
||||
// Sanitize by taking only the absolute value
|
||||
_radiusY = Math.Abs(_radiusY);
|
||||
|
||||
// Angle is provided in degrees
|
||||
float.TryParse(match.Groups["Angle"].Value, out _angle);
|
||||
|
||||
// Convert angle to radians as CanvasPathBuilder.AddArc() method
|
||||
// requires the angle to be in radians
|
||||
_angle *= Scalar.DegreesToRadians;
|
||||
Enum.TryParse(match.Groups["IsLargeArc"].Value, out _arcSize);
|
||||
Enum.TryParse(match.Groups["SweepDirection"].Value, out _sweepDirection);
|
||||
float.TryParse(match.Groups["X"].Value, out _x);
|
||||
float.TryParse(match.Groups["Y"].Value, out _y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing the Ellipse Figure in a Path Geometry
|
||||
/// </summary>
|
||||
internal class CanvasEllipseFigure : AbstractPathElement
|
||||
{
|
||||
private float _radiusX;
|
||||
private float _radiusY;
|
||||
private float _x;
|
||||
private float _y;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CanvasEllipseFigure"/> class.
|
||||
/// </summary>
|
||||
public CanvasEllipseFigure()
|
||||
{
|
||||
_radiusX = _radiusY = _x = _y = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Path Element to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder">CanvasPathBuilder object</param>
|
||||
/// <param name="currentPoint">The last active location in the Path before adding
|
||||
/// the EllipseFigure</param>
|
||||
/// <param name="lastElement">The previous PathElement in the Path.</param>
|
||||
/// <returns>The latest location in the Path after adding the EllipseFigure</returns>
|
||||
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement)
|
||||
{
|
||||
// Calculate coordinates
|
||||
var center = new Vector2(_x, _y);
|
||||
if (IsRelative)
|
||||
{
|
||||
center += currentPoint;
|
||||
}
|
||||
|
||||
// Execute command
|
||||
pathBuilder.AddEllipseFigure(center.X, center.Y, _radiusX, _radiusY);
|
||||
|
||||
// No need to update the lastElement or currentPoint here as we are creating
|
||||
// a separate closed figure here. So current point will not change.
|
||||
return currentPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for extracting Path Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Instance of <see cref="Regex"/></returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return RegexFactory.GetAttributesRegex(PathFigureType.EllipseFigure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Path Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
|
||||
|
||||
// Sanitize by taking the absolute value
|
||||
_radiusX = Math.Abs(_radiusX);
|
||||
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
|
||||
|
||||
// Sanitize by taking the absolute value
|
||||
_radiusY = Math.Abs(_radiusY);
|
||||
float.TryParse(match.Groups["X"].Value, out _x);
|
||||
float.TryParse(match.Groups["Y"].Value, out _y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
|
||||
{
|
||||
/// <summary>
|
||||
/// Class which contains a collection of ICanvasPathElements
|
||||
/// which can be used to create CanvasGeometry.
|
||||
/// </summary>
|
||||
internal class CanvasPathFigure : AbstractPathElement
|
||||
{
|
||||
// Collection of Path Elements
|
||||
private List<ICanvasPathElement> _elements;
|
||||
|
||||
public CanvasPathFigure()
|
||||
{
|
||||
_elements = new List<ICanvasPathElement>();
|
||||
ValidationCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Path Element with the given Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
/// <param name="index">Index of the path element in the Path data.</param>
|
||||
public override void Initialize(Match match, int index)
|
||||
{
|
||||
Index = index;
|
||||
var main = match.Groups["Main"];
|
||||
Data = main.Value;
|
||||
|
||||
var elements = new List<ICanvasPathElement>();
|
||||
foreach (PathElementType type in Enum.GetValues(typeof(PathElementType)))
|
||||
{
|
||||
foreach (Capture elementCapture in match.Groups[type.ToString()].Captures)
|
||||
{
|
||||
var elementRootIndex = elementCapture.Index;
|
||||
var regex = RegexFactory.GetRegex(type);
|
||||
var elementMatch = regex.Match(elementCapture.Value);
|
||||
var isRelative = false;
|
||||
|
||||
// Process the 'Main' Group which contains the Path Command and
|
||||
// corresponding attributes
|
||||
if (elementMatch.Groups["Main"].Captures.Count == 1)
|
||||
{
|
||||
var figure = PathElementFactory.CreatePathElement(type, elementMatch, elementRootIndex);
|
||||
elements.Add(figure);
|
||||
isRelative = figure.IsRelative;
|
||||
}
|
||||
|
||||
// Process the 'Additional' Group which contains just the attributes
|
||||
elements.AddRange(from Capture capture in elementMatch.Groups["Additional"].Captures
|
||||
select PathElementFactory.CreateAdditionalPathElement(type, capture, elementRootIndex + capture.Index, isRelative));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the path elements based on their index value
|
||||
_elements.AddRange(elements.OrderBy(e => e.Index));
|
||||
if (_elements.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the last path element in the figure is an ClosePathElement
|
||||
// which would indicate that the path needs to be closed. Otherwise,
|
||||
// add a default ClosePathElement at the end to indicate that the path
|
||||
// is not closed.
|
||||
var lastElement = _elements.ElementAt(_elements.Count - 1);
|
||||
if ((lastElement as ClosePathElement) == null)
|
||||
{
|
||||
_elements.Add(PathElementFactory.CreateDefaultPathElement(PathElementType.ClosePath));
|
||||
}
|
||||
|
||||
// Validation Count will be the cumulative sum of the validation count
|
||||
// of child elements of the PathFigure
|
||||
ValidationCount = _elements.Sum(x => x.ValidationCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Path Element with the given Capture
|
||||
/// </summary>
|
||||
/// <param name="capture">Capture object</param>
|
||||
/// <param name="index">Index of the Path Element in the Path data.</param>
|
||||
/// <param name="isRelative">Indicates whether the Path Element coordinates are
|
||||
/// absolute or relative</param>
|
||||
public override void InitializeAdditional(Capture capture, int index, bool isRelative)
|
||||
{
|
||||
// Do nothing as this scenario is not valid for CanvasPathFigure
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Path Element to the Path.
|
||||
/// </summary>
|
||||
/// <param name="pathBuilder">CanvasPathBuilder object</param>
|
||||
/// <param name="currentPoint">The last active location in the Path before adding
|
||||
/// the Path Element</param>
|
||||
/// <param name="lastElement">The previous PathElement in the Path.</param>
|
||||
/// <returns>The latest location in the Path after adding the Path Element</returns>
|
||||
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement)
|
||||
{
|
||||
foreach (var pathElement in _elements)
|
||||
{
|
||||
currentPoint = pathElement.CreatePath(pathBuilder, currentPoint, ref lastElement);
|
||||
}
|
||||
|
||||
return currentPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Regex for extracting Path Element Attributes
|
||||
/// </summary>
|
||||
/// <returns>Instance of <see cref="Regex"/></returns>
|
||||
protected override Regex GetAttributesRegex()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Path Element Attributes from the Match
|
||||
/// </summary>
|
||||
/// <param name="match">Match object</param>
|
||||
protected override void GetAttributes(Match match)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче