Added the Win2d Path Geometry parser.

This commit is contained in:
Ratish Philip 2020-09-26 09:24:08 -07:00
Родитель 49a88c7d57
Коммит cec5bd67a5
53 изменённых файлов: 7613 добавлений и 0 удалений

Просмотреть файл

@ -272,6 +272,7 @@
<Content Include="Icons\More.png" />
<Content Include="Icons\Notifications.png" />
<Content Include="Icons\Services.png" />
<Content Include="SamplePages\CanvasPathGeometry\CanvasPathGeometry.png" />
<Content Include="SamplePages\TilesBrush\TilesBrush.png" />
<Content Include="SamplePages\Eyedropper\Eyedropper.png" />
<Content Include="SamplePages\OnDevice\OnDevice.png" />
@ -509,6 +510,9 @@
<Compile Include="SamplePages\AutoFocusBehavior\AutoFocusBehaviorPage.xaml.cs">
<DependentUpon>AutoFocusBehaviorPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\CanvasPathGeometry\CanvasPathGeometryPage.xaml.cs">
<DependentUpon>CanvasPathGeometryPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\EnumValuesExtension\EnumValuesExtensionPage.xaml.cs">
<DependentUpon>EnumValuesExtensionPage.xaml</DependentUpon>
</Compile>
@ -993,6 +997,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\CanvasPathGeometry\CanvasPathGeometryPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\EnumValuesExtension\EnumValuesExtensionPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.5 KiB

Просмотреть файл

@ -0,0 +1,119 @@
<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:xaml="using:Microsoft.Graphics.Canvas.UI.Xaml"
mc:Ignorable="d">
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="0.1*" />
<RowDefinition Height="48" />
<RowDefinition Height="0.9*" />
</Grid.RowDefinitions>
<TextBox x:Name="InputData"
Margin="10,3"
FontFamily="Courier New"
FontSize="14"
PlaceholderText="Enter SVG/XAML Path Data and press Parse. (Or select a sample from the Samples list below)"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionHighlightColor="#007aff"
Text="{x:Bind InputText, Mode=TwoWay}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="1"
HorizontalAlignment="Left"
Orientation="Horizontal">
<TextBlock Margin="12,3"
VerticalAlignment="Center"
Text="Samples: " />
<ComboBox x:Name="SampleList"
Width="150"
Height="30"
Margin="0,4,10,4"
VerticalAlignment="Center"
SelectionChanged="{x:Bind OnSampleChanged}" />
</StackPanel>
<Button Grid.Row="1"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Center"
Click="{x:Bind OnClearCanvas}"
Content="Clear" />
<Button Grid.Row="1"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Right"
Click="{x:Bind OnParseData}"
Content="Parse" />
<Pivot x:Name="RootPivot"
Grid.Row="2">
<PivotItem Foreground="Black"
Header="Canvas">
<ScrollViewer x:Name="RenderScroll"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<xaml: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="2"
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,253 @@
// 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.Media.Geometry;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
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 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;
public string InputText { get; set; }
public CanvasPathGeometryPage()
{
this.InitializeComponent();
_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")
};
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",
};
SampleList.ItemsSource = sampleList;
StrokeList.ItemsSource = colorList;
FillList.ItemsSource = colorList;
SampleList.SelectedIndex = 0;
StrokeThickness.Value = 1;
StrokeList.SelectedIndex = 1;
FillList.SelectedIndex = 0;
_selectionChanged = false;
}
private void OnParseData(object sender, RoutedEventArgs e)
{
_data = InputData.Text;
_isParsing = true;
SampleList.SelectedIndex = 0;
RenderCanvas.Invalidate();
_isParsing = false;
}
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
if (string.IsNullOrWhiteSpace(_data))
{
CommandsList.Text = string.Empty;
return;
}
if (_errorGeometry == null)
{
_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, _logger);
CommandsList.Text = _logger?.ToString() ?? string.Empty;
args.DrawingSession.FillGeometry(geometry, _fillColor);
args.DrawingSession.DrawGeometry(geometry, _strokeColor, _strokeThickness);
}
catch (Exception)
{
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
CommandsList.Text = "Parsing error! Invalid input data!";
}
}
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 OnSampleChanged(object sender, SelectionChangedEventArgs e)
{
if (SampleList.SelectedIndex > 0)
{
InputData.Text = _samples.ElementAt(SampleList.SelectedIndex);
if (!_selectionChanged)
{
StrokeThickness.Value = 4;
StrokeList.SelectedIndex = 1;
FillList.SelectedIndex = 5;
_selectionChanged = false;
}
_data = InputData.Text;
RenderCanvas.Invalidate();
}
else if (!this._isParsing)
{
InputData.Text = string.Empty;
_data = string.Empty;
RenderCanvas.Invalidate();
}
}
private void OnClearCanvas(object sender, RoutedEventArgs e)
{
InputData.Text = string.Empty;
OnParseData(this, null);
}
}
}

Просмотреть файл

@ -875,6 +875,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",

Просмотреть файл

@ -0,0 +1,322 @@
// 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,456 @@
// 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 method 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.
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.");
}
// 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);
if (numSides < 3)
{
throw new ArgumentException("A polygon should have at least 3 sides", nameof(numSides));
}
// 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.
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.");
}
// 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);
}
/// <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.
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.");
}
// 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(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, CanvasRoundRect rect, bool raiseException = false)
{
try
{
// Begin path
pathBuilder.BeginFigure(rect.LeftTop);
}
catch (ArgumentException)
{
if (!raiseException)
{
return;
}
// An ArgumentException will be raised if another figure was already begun( and not ended)
// before calling AddPolygonFigure() method.
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.");
}
// Top line
pathBuilder.AddLine(rect.RightTop);
// Upper-right corner
var radiusX = rect.TopRight.X - rect.RightTop.X;
var radiusY = rect.TopRight.Y - rect.RightTop.Y;
var center = new Vector2(rect.RightTop.X, rect.TopRight.Y);
pathBuilder.AddArc(center, radiusX, radiusY, 3f * Scalar.PiByTwo, Scalar.PiByTwo);
// Right line
pathBuilder.AddLine(rect.BottomRight);
// Lower-right corner
radiusX = rect.BottomRight.X - rect.RightBottom.X;
radiusY = rect.RightBottom.Y - rect.BottomRight.Y;
center = new Vector2(rect.RightBottom.X, rect.BottomRight.Y);
pathBuilder.AddArc(center, radiusX, radiusY, 0f, Scalar.PiByTwo);
// Bottom line
pathBuilder.AddLine(rect.LeftBottom);
// Lower-left corner
radiusX = rect.LeftBottom.X - rect.BottomLeft.X;
radiusY = rect.LeftBottom.Y - rect.BottomLeft.Y;
center = new Vector2(rect.LeftBottom.X, rect.BottomLeft.Y);
pathBuilder.AddArc(center, radiusX, radiusY, Scalar.PiByTwo, Scalar.PiByTwo);
// Left line
pathBuilder.AddLine(rect.TopLeft);
// Upper-left corner
radiusX = rect.LeftTop.X - rect.TopLeft.X;
radiusY = rect.TopLeft.Y - rect.LeftTop.Y;
center = new Vector2(rect.LeftTop.X, rect.TopLeft.Y);
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(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, CanvasRoundRect rect, bool raiseException = false)
{
try
{
// Begin path
pathBuilder.BeginFigure(rect.LeftTop);
}
catch (ArgumentException)
{
if (!raiseException)
{
return;
}
// An ArgumentException will be raised if another figure was already begun( and not ended)
// before calling AddPolygonFigure() method.
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.");
}
// Top line
pathBuilder.AddLine(rect.RightTop);
// Upper-right corner
var rightTopControlPoint = new Vector2(rect.RightTop.X + ((rect.TopRight.X - rect.RightTop.X) * ControlPointFactor), rect.RightTop.Y);
var topRightControlPoint = new Vector2(rect.TopRight.X, rect.TopRight.Y - ((rect.TopRight.Y - rect.RightTop.Y) * ControlPointFactor));
// Top Right Curve
pathBuilder.AddCubicBezier(rightTopControlPoint, topRightControlPoint, rect.TopRight);
// Right line
pathBuilder.AddLine(rect.BottomRight);
// Lower-right corner
var bottomRightControlPoint = new Vector2(rect.BottomRight.X, rect.BottomRight.Y + ((rect.RightBottom.Y - rect.BottomRight.Y) * ControlPointFactor));
var rightBottomControlPoint = new Vector2(rect.RightBottom.X + ((rect.BottomRight.X - rect.RightBottom.X) * ControlPointFactor), rect.RightBottom.Y);
// Bottom Right Curve
pathBuilder.AddCubicBezier(bottomRightControlPoint, rightBottomControlPoint, rect.RightBottom);
// Bottom line
pathBuilder.AddLine(rect.LeftBottom);
// Lower-left corner
var leftBottomControlPoint = new Vector2(rect.LeftBottom.X - ((rect.LeftBottom.X - rect.BottomLeft.X) * ControlPointFactor), rect.LeftBottom.Y);
var bottomLeftControlPoint = new Vector2(rect.BottomLeft.X, rect.BottomLeft.Y + ((rect.LeftBottom.Y - rect.BottomLeft.Y) * ControlPointFactor));
// Bottom Left Curve
pathBuilder.AddCubicBezier(leftBottomControlPoint, bottomLeftControlPoint, rect.BottomLeft);
// Left line
pathBuilder.AddLine(rect.TopLeft);
// Upper-left corner
var topLeftControlPoint = new Vector2(rect.TopLeft.X, rect.TopLeft.Y - ((rect.TopLeft.Y - rect.LeftTop.Y) * ControlPointFactor));
var leftTopControlPoint = new Vector2(rect.LeftTop.X - ((rect.LeftTop.X - rect.TopLeft.X) * ControlPointFactor), rect.LeftTop.Y);
// Top Left Curve
pathBuilder.AddCubicBezier(topLeftControlPoint, leftTopControlPoint, rect.LeftTop);
// 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,176 @@
// 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;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Common;
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="resourceCreator">ICanvasResourceCreator</param>
/// <param name="pathData">Path data</param>
/// <param name="logger">(Optional) For logging purpose. To log the set of
/// CanvasPathBuilder commands, used for creating the CanvasGeometry, in
/// string format.</param>
/// <returns><see cref="CanvasGeometry"/></returns>
public static CanvasGeometry CreateGeometry(ICanvasResourceCreator resourceCreator, string pathData, StringBuilder logger = null)
{
using (new CultureShield("en-US"))
{
// Log command
var resourceStr = resourceCreator == null ? "null" : "resourceCreator";
logger?.AppendLine($"using (var pathBuilder = new CanvasPathBuilder({resourceStr}))");
logger?.AppendLine("{");
// Get the CanvasGeometry from the path data
var geometry = CanvasGeometryParser.Parse(resourceCreator, pathData, logger);
// Log command
logger?.AppendLine("}");
return geometry;
}
}
/// <summary>
/// Parses the Path data string and converts it to CanvasGeometry.
/// </summary>
/// <param name="pathData">Path data</param>
/// <param name="logger">(Optional) For logging purpose. To log the set of
/// CanvasPathBuilder commands, used for creating the CanvasGeometry, in
/// string format.</param>
/// <returns><see cref="CanvasGeometry"/></returns>
public static CanvasGeometry CreateGeometry(string pathData, StringBuilder logger = null)
{
return CreateGeometry(null, pathData, logger);
}
/// <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 &amp; 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>
/// Attempts to convert 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 &amp; A should have value in the range between 0 and 1, inclusive)
/// </summary>
/// <param name="colorString">Color string in Hexadecimal or HDR format</param>
/// <param name="color">Output Color object</param>
/// <returns>True if successful, otherwise False</returns>
public static bool TryCreateColor(string colorString, out Color color)
{
using (new CultureShield("en-US"))
{
return ColorParser.TryParse(colorString, out color);
}
}
/// <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,119 @@
// 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
{
return 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,240 @@
// 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 Vector2 LeftTop { get; private set; }
internal Vector2 TopLeft { get; private set; }
internal Vector2 TopRight { get; private set; }
internal Vector2 RightTop { get; private set; }
internal Vector2 RightBottom { get; private set; }
internal Vector2 BottomRight { get; private set; }
internal Vector2 BottomLeft { get; private set; }
internal Vector2 LeftBottom { get; private set; }
internal Vector2 Size { 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()
{
Size = size;
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);
}
/// <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()
{
Size = new Vector2(width, 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(new Vector2(x, y));
}
private void ComputeCoordinates(Vector2 origin)
{
// compute the coordinates of the key points
var leftTop = new Vector2(_leftTopWidth, 0);
var rightTop = new Vector2(Size.X - _rightTopWidth, 0);
var topRight = new Vector2(Size.X, _topRightHeight);
var bottomRight = new Vector2(Size.X, Size.Y - _bottomRightHeight);
var rightBottom = new Vector2(Size.X - _rightBottomWidth, Size.Y);
var leftBottom = new Vector2(_leftBottomWidth, Size.Y);
var bottomLeft = new Vector2(0, Size.Y - _bottomLeftHeight);
var topLeft = new Vector2(0, _topLeftHeight);
// check anchors for overlap and resolve by partitioning corners according to
// the percentage of each one.
// top edge
if (leftTop.X > rightTop.X)
{
var v = _leftTopWidth / (_leftTopWidth + _rightTopWidth) * Size.X;
leftTop.X = v;
rightTop.X = v;
}
// right edge
if (topRight.Y > bottomRight.Y)
{
var v = _topRightHeight / (_topRightHeight + _bottomRightHeight) * Size.Y;
topRight.Y = v;
bottomRight.Y = v;
}
// bottom edge
if (leftBottom.X > rightBottom.X)
{
var v = _leftBottomWidth / (_leftBottomWidth + _rightBottomWidth) * Size.X;
rightBottom.X = v;
leftBottom.X = v;
}
// left edge
if (topLeft.Y > bottomLeft.Y)
{
var v = _topLeftHeight / (_topLeftHeight + _bottomLeftHeight) * Size.Y;
bottomLeft.Y = v;
topLeft.Y = v;
}
// Apply origin translation
LeftTop = leftTop + origin;
RightTop = rightTop + origin;
TopRight = topRight + origin;
BottomRight = bottomRight + origin;
RightBottom = rightBottom + origin;
LeftBottom = leftBottom + origin;
BottomLeft = bottomLeft + origin;
TopLeft = topLeft + origin;
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
// 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,203 @@
// 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)
{
ICanvasPathElement result;
switch (figureType)
{
case PathFigureType.FillRule:
result = new FillRuleElement();
break;
default:
throw new ArgumentException("Creation of Only Default FillRuleElement is supported.", nameof(figureType));
}
return result;
}
/// <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)
{
ICanvasPathElement result;
switch (elementType)
{
case PathElementType.ClosePath:
result = new ClosePathElement();
break;
default:
throw new ArgumentException("Creation of Only Default ClosePathElement is supported.", nameof(elementType));
}
return result;
}
/// <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)
{
ICanvasPathElement result = null;
switch (figureType)
{
case PathFigureType.FillRule:
result = new FillRuleElement();
break;
case PathFigureType.PathFigure:
result = new CanvasPathFigure();
break;
case PathFigureType.EllipseFigure:
result = new CanvasEllipseFigure();
break;
case PathFigureType.PolygonFigure:
result = new CanvasPolygonFigure();
break;
case PathFigureType.RectangleFigure:
result = new CanvasRectangleFigure();
break;
case PathFigureType.RoundedRectangleFigure:
result = new CanvasRoundRectangleFigure();
break;
}
return result;
}
/// <summary>
/// Instantiates a PathElement based on the PathElementType
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <returns>ICanvasPathElement</returns>
private static ICanvasPathElement CreatePathElement(PathElementType elementType)
{
ICanvasPathElement result = null;
switch (elementType)
{
case PathElementType.MoveTo:
result = new MoveToElement();
break;
case PathElementType.Line:
result = new LineElement();
break;
case PathElementType.HorizontalLine:
result = new HorizontalLineElement();
break;
case PathElementType.VerticalLine:
result = new VerticalLineElement();
break;
case PathElementType.QuadraticBezier:
result = new QuadraticBezierElement();
break;
case PathElementType.SmoothQuadraticBezier:
result = new SmoothQuadraticBezierElement();
break;
case PathElementType.CubicBezier:
result = new CubicBezierElement();
break;
case PathElementType.SmoothCubicBezier:
result = new SmoothCubicBezierElement();
break;
case PathElementType.Arc:
result = new ArcElement();
break;
case PathElementType.ClosePath:
result = new ClosePathElement();
break;
}
return result;
}
}
}

Просмотреть файл

@ -0,0 +1,604 @@
// 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.Text.RegularExpressions;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
{
/// <summary>
/// Contains all the Regular Expressions which are
/// used for parsing the CanvasGeometry Mini-Language
/// </summary>
internal static class RegexFactory
{
// Whitespace
private const string Spacer = @"\s*";
// Whitespace or comma
private const string SoC = @"(?:\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}{SoC}[01]{SoC}[01]{Sep}{Pos}" +
$"(?:{Sep}{Float}{Sep}{Float}{Sep}{Float}{SoC}[01]{SoC}[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}){SoC}" +
$"(?<IsLargeArc>[01]){SoC}(?<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}{SoC}[01]{SoC}[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>#?{Alpha}?{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
public static readonly string GradientStopAttributes = $"(?<Position>{Float01}){ColorSep}{RgbColorAttributes}";
public static readonly string GradientStopMainAttributes = $"(?<Main>[Ss]{Spacer}{GradientStopAttributes})";
public 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,61 @@
// 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.Common
{
/// <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>
public class CultureShield : IDisposable
{
private readonly CultureInfo _prevCulture;
/// <summary>
/// Initializes a new instance of the <see cref="CultureShield"/> class 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>
public 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,114 @@
// 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 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
{
#pragma warning disable SA1401 // Fields should be private
protected readonly string Indent = new string(' ', 4);
#pragma warning restore SA1401 // Fields should be private
/// <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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger);
/// <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,108 @@
// 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;
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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var point = new Vector2(_x, _y);
if (IsRelative)
{
point += currentPoint;
}
// Execute command
pathBuilder.AddArc(point, _radiusX, _radiusY, _angle, _sweepDirection, _arcSize);
// Log command
logger?.Append($"{Indent}pathBuilder.AddArc(new Vector2({point.X}, {point.Y}), ");
logger?.AppendLine($"{_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,90 @@
// 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;
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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</param>
/// <returns>The latest location in the Path after adding the EllipseFigure</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement, StringBuilder logger)
{
// Calculate coordinates
var center = new Vector2(_x, _y);
if (IsRelative)
{
center += currentPoint;
}
// Execute command
pathBuilder.AddEllipseFigure(center.X, center.Y, _radiusX, _radiusY);
// Log command
logger?.AppendLine();
logger?.AppendLine($"{Indent}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,142 @@
// 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;
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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Log command
logger?.AppendLine();
foreach (var pathElement in _elements)
{
currentPoint = pathElement.CreatePath(pathBuilder, currentPoint, ref lastElement, logger);
}
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
}
}
}

Просмотреть файл

@ -0,0 +1,88 @@
// 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;
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 Polygon Figure in a Path Geometry
/// </summary>
internal class CanvasPolygonFigure : AbstractPathElement
{
private int _numSides;
private float _radius;
private float _x;
private float _y;
public CanvasPolygonFigure()
{
_numSides = 0;
_radius = _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 PolygonFigure</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</param>
/// <returns>The latest location in the Path after adding the PolygonFigure</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement, StringBuilder logger)
{
// Calculate coordinates
var center = new Vector2(_x, _y);
if (IsRelative)
{
center += currentPoint;
}
// Execute command
pathBuilder.AddPolygonFigure(_numSides, center.X, center.Y, _radius);
// Log command
logger?.AppendLine();
logger?.AppendLine($"{Indent}pathBuilder.AddPolygonFigure({_numSides}, {center.X}, {center.Y}, {_radius});");
// 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.PolygonFigure);
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
int.TryParse(match.Groups["Sides"].Value, out _numSides);
// Sanitize by taking the absolute value
_numSides = Math.Abs(_numSides);
float.TryParse(match.Groups["Radius"].Value, out _radius);
// Sanitize by taking the absolute value
_radius = Math.Abs(_radius);
float.TryParse(match.Groups["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// 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;
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 Rectangle Figure in a Path Geometry
/// </summary>
internal sealed class CanvasRectangleFigure : AbstractPathElement
{
private float _x;
private float _y;
private float _width;
private float _height;
/// <summary>
/// Initializes a new instance of the <see cref="CanvasRectangleFigure"/> class.
/// </summary>
public CanvasRectangleFigure()
{
_x = _y = _width = _height = 0f;
}
/// <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 PolygonFigure</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</param>
/// <returns>The latest location in the Path after adding the PolygonFigure</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement, StringBuilder logger)
{
// Calculate coordinates
var topLeft = new Vector2(_x, _y);
if (IsRelative)
{
topLeft += currentPoint;
}
// Execute command
pathBuilder.AddRectangleFigure(topLeft.X, topLeft.Y, _width, _height);
// Log command
logger?.AppendLine();
logger?.AppendLine($"{Indent}pathBuilder.AddRectangleFigure({topLeft.X}, {topLeft.Y}, {_width}, {_height});");
// 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.RectangleFigure);
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// X
float.TryParse(match.Groups["X"].Value, out _x);
// Y
float.TryParse(match.Groups["Y"].Value, out _y);
// Width
float.TryParse(match.Groups["Width"].Value, out _width);
// Sanitize by taking the absolute value
_width = Math.Abs(_width);
// Height
float.TryParse(match.Groups["Height"].Value, out _height);
// Sanitize by taking the absolute value
_height = Math.Abs(_height);
}
}
}

Просмотреть файл

@ -0,0 +1,111 @@
// 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;
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 RoundRectangle Figure in a Path Geometry
/// </summary>
internal sealed class CanvasRoundRectangleFigure : AbstractPathElement
{
private float _x;
private float _y;
private float _width;
private float _height;
private float _radiusX;
private float _radiusY;
/// <summary>
/// Initializes a new instance of the <see cref="CanvasRoundRectangleFigure"/> class.
/// </summary>
public CanvasRoundRectangleFigure()
{
_x = _y = _width = _height = _radiusX = _radiusY = 0f;
}
/// <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 PolygonFigure</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</param>
/// <returns>The latest location in the Path after adding the PolygonFigure</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement, StringBuilder logger)
{
// Calculate coordinates
var topLeft = new Vector2(_x, _y);
if (IsRelative)
{
topLeft += currentPoint;
}
// Execute command
pathBuilder.AddRoundedRectangleFigure(topLeft.X, topLeft.Y, _width, _height, _radiusX, _radiusY);
// Log command
logger?.AppendLine();
logger?.AppendLine($"{Indent}pathBuilder.AddRoundedRectangleFigure({topLeft.X}, {topLeft.Y}, {_width}, {_height}, {_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.RoundedRectangleFigure);
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// X
float.TryParse(match.Groups["X"].Value, out _x);
// Y
float.TryParse(match.Groups["Y"].Value, out _y);
// Width
float.TryParse(match.Groups["Width"].Value, out _width);
// Sanitize by taking the absolute value
_width = Math.Abs(_width);
// Height
float.TryParse(match.Groups["Height"].Value, out _height);
// Sanitize by taking the absolute value
_height = Math.Abs(_height);
// 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);
}
}
}

Просмотреть файл

@ -0,0 +1,108 @@
// 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 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 ClosePath command in a Path Geometry
/// </summary>
internal class ClosePathElement : AbstractPathElement
{
private bool _isFigureClosed;
/// <summary>
/// Initializes a new instance of the <see cref="ClosePathElement"/> class.
/// </summary>
public ClosePathElement()
{
_isFigureClosed = false;
}
/// <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)
{
var main = match.Groups["Main"];
Index = index;
Data = main.Value;
var command = match.Groups["Command"];
// If the Command is captured, it means that 'Z' is provided and hence
// the figure must be closed
if (command.Captures.Count == 1)
{
_isFigureClosed = true;
}
// 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 override void InitializeAdditional(Capture capture, int index, bool isRelative)
{
// Do nothing as this scenario is not valid for this Path Element
}
/// <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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Execute command
pathBuilder.EndFigure(_isFigureClosed ? CanvasFigureLoop.Closed : CanvasFigureLoop.Open);
// Log command
var cmd = _isFigureClosed ? "CanvasFigureLoop.Closed" : "CanvasFigureLoop.Open";
logger?.AppendLine($"{Indent}pathBuilder.EndFigure({cmd});");
// Set Last Element
lastElement = this;
// Return current point
return currentPoint;
}
/// <summary>
/// Get the Regex for extracting Path Element Attributes
/// </summary>
/// <returns>Instance of <see cref="Regex"/></returns>
protected override Regex GetAttributesRegex()
{
// Attributes are not present
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
}
}
}

Просмотреть файл

@ -0,0 +1,113 @@
// 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 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 Cubic Bezier Element in a Path Geometry
/// </summary>
internal class CubicBezierElement : AbstractPathElement
{
private float _x1;
private float _y1;
private float _x2;
private float _y2;
private float _x;
private float _y;
private Vector2 _absoluteControlPoint2;
/// <summary>
/// Initializes a new instance of the <see cref="CubicBezierElement"/> class.
/// </summary>
public CubicBezierElement()
{
_x1 = _y1 = 0;
_x2 = _y2 = 0;
_x = _y = 0;
_absoluteControlPoint2 = Vector2.Zero;
}
/// <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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var controlPoint1 = new Vector2(_x1, _y1);
var controlPoint2 = new Vector2(_x2, _y2);
var point = new Vector2(_x, _y);
if (IsRelative)
{
controlPoint1 += currentPoint;
controlPoint2 += currentPoint;
point += currentPoint;
}
// Save the second absolute control point so that it can be used by the following
// SmoothCubicBezierElement (if any)
_absoluteControlPoint2 = controlPoint2;
// Execute command
pathBuilder.AddCubicBezier(controlPoint1, controlPoint2, point);
// Log command
logger?.Append($"{Indent}pathBuilder.AddCubicBezier(new Vector2({controlPoint1.X}, {controlPoint1.Y})");
logger?.Append($", new Vector2({controlPoint2.X}, {controlPoint2.Y})");
logger?.AppendLine($", new Vector2({point.X}, {point.Y}));");
// Set Last Element
lastElement = this;
// Return current point
return point;
}
/// <summary>
/// Gets the Second Control Point of this Cubic Bezier
/// </summary>
/// <returns>Vector2</returns>
public Vector2 GetControlPoint()
{
return _absoluteControlPoint2;
}
/// <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.CubicBezier);
}
/// <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["X1"].Value, out _x1);
float.TryParse(match.Groups["Y1"].Value, out _y1);
float.TryParse(match.Groups["X2"].Value, out _x2);
float.TryParse(match.Groups["Y2"].Value, out _y2);
float.TryParse(match.Groups["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
// 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;
using System.Text.RegularExpressions;
using Microsoft.Graphics.Canvas.Geometry;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
{
/// <summary>
/// Class representing the Fill Rule Element in a Path Geometry
/// </summary>
internal class FillRuleElement : AbstractPathElement
{
private CanvasFilledRegionDetermination _fillValue;
/// <summary>
/// Initializes a new instance of the <see cref="FillRuleElement"/> class.
/// </summary>
public FillRuleElement()
{
_fillValue = CanvasFilledRegionDetermination.Alternate;
}
/// <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 this Path Element
}
/// <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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Execute command
pathBuilder.SetFilledRegionDetermination(_fillValue);
// Log command
logger?.AppendLine($"{Indent}pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.{_fillValue});");
// Set Last Element
lastElement = this;
// Return current point
return currentPoint;
}
/// <summary>
/// Get the Regex for extracting Path Element Attributes
/// </summary>
/// <returns>Instance of <see cref="Regex"/></returns>
protected override Regex GetAttributesRegex()
{
// Not applicable for this Path Element
return null;
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
Enum.TryParse(match.Groups["FillValue"].Value, out _fillValue);
}
}
}

Просмотреть файл

@ -0,0 +1,75 @@
// 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 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 Horizontal Line Element in a Path Geometry
/// </summary>
internal class HorizontalLineElement : AbstractPathElement
{
private float _x;
/// <summary>
/// Initializes a new instance of the <see cref="HorizontalLineElement"/> class.
/// </summary>
public HorizontalLineElement()
{
_x = 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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var point = IsRelative ?
new Vector2(currentPoint.X + _x, currentPoint.Y) : new Vector2(_x, currentPoint.Y);
// Execute command
pathBuilder.AddLine(point);
// Log command
logger?.AppendLine($"{Indent}pathBuilder.AddLine(new Vector2({point.X}, {point.Y}));");
// 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.HorizontalLine);
}
/// <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["X"].Value, out _x);
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
// 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 System.Text.RegularExpressions;
using Microsoft.Graphics.Canvas.Geometry;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
{
/// <summary>
/// Interface for a Path Element which serves
/// as a building block for CanvasPathGeometry
/// </summary>
internal interface ICanvasPathElement
{
/// <summary>
/// Gets index of the Path Element in the Path Data
/// </summary>
int Index { get; }
/// <summary>
/// Gets path data defining the Path Element
/// </summary>
string Data { get; }
/// <summary>
/// Gets number of non-whitespace characters in
/// the Path Element Data
/// </summary>
int ValidationCount { get; }
/// <summary>
/// Gets a value indicating whether the path element contains
/// absolute or relative coordinates.
/// </summary>
bool IsRelative { get; }
/// <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>
void Initialize(Match match, int index);
/// <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>
void InitializeAdditional(Capture capture, int index, bool isRelative);
/// <summary>
/// Adds the Path Element to the PathBuilder.
/// </summary>
/// <param name="pathBuilder">CanvasPathBuilder object</param>
/// <param name="currentPoint">The current point on the path before the path element is added</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</param>
/// <returns>The current point on the path after the path element is added</returns>
Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement, StringBuilder logger);
}
}

Просмотреть файл

@ -0,0 +1,80 @@
// 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 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 Line Element in a Path Geometry
/// </summary>
internal class LineElement : AbstractPathElement
{
private float _x;
private float _y;
/// <summary>
/// Initializes a new instance of the <see cref="LineElement"/> class.
/// </summary>
public LineElement()
{
_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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var point = new Vector2(_x, _y);
if (IsRelative)
{
point += currentPoint;
}
// Execute command
pathBuilder.AddLine(point);
// Log command
logger?.AppendLine($"{Indent}pathBuilder.AddLine(new Vector2({point.X}, {point.Y}));");
// 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.Line);
}
/// <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["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,93 @@
// 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 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 MoveTo Command in a Path Geometry
/// </summary>
internal class MoveToElement : AbstractPathElement
{
private float _x;
private float _y;
/// <summary>
/// Initializes a new instance of the <see cref="MoveToElement"/> class.
/// </summary>
public MoveToElement()
{
_x = _y = 0;
}
/// <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 MoveTo Command
// Additional coordinates specified with MoveTo will be converted to Line Commands
}
/// <summary>
/// Adds the Path Element to the PathBuilder.
/// </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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var point = new Vector2(_x, _y);
if (IsRelative)
{
point += currentPoint;
}
// Execute command
pathBuilder.BeginFigure(point);
// Log command
logger?.AppendLine($"{Indent}pathBuilder.BeginFigure(new Vector2({point.X}, {point.Y}));");
// 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.MoveTo);
}
/// <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["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,104 @@
// 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 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 Quadratic Bezier Element in a Path Geometry
/// </summary>
internal class QuadraticBezierElement : AbstractPathElement
{
private float _x1;
private float _y1;
private float _x;
private float _y;
private Vector2 _absoluteControlPoint;
/// <summary>
/// Initializes a new instance of the <see cref="QuadraticBezierElement"/> class.
/// </summary>
public QuadraticBezierElement()
{
_x1 = _y1 = 0;
_x = _y = 0;
_absoluteControlPoint = Vector2.Zero;
}
/// <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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var controlPoint = new Vector2(_x1, _y1);
var point = new Vector2(_x, _y);
if (IsRelative)
{
controlPoint += currentPoint;
point += currentPoint;
}
// Save the absolute control point so that it can be used by the following
// SmoothQuadraticBezierElement (if any)
_absoluteControlPoint = controlPoint;
// Execute command
pathBuilder.AddQuadraticBezier(controlPoint, point);
// Log command
logger?.Append($"{Indent}pathBuilder.AddQuadraticBezier(new Vector2({controlPoint.X}, {controlPoint.Y})");
logger?.AppendLine($", new Vector2({point.X}, {point.Y}));");
// Set Last Element
lastElement = this;
// Return current point
return point;
}
/// <summary>
/// Gets the Control Point of this Quadratic Bezier
/// </summary>
/// <returns>Vector2</returns>
public Vector2 GetControlPoint()
{
return _absoluteControlPoint;
}
/// <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.QuadraticBezier);
}
/// <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["X1"].Value, out _x1);
float.TryParse(match.Groups["Y1"].Value, out _y1);
float.TryParse(match.Groups["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,125 @@
// 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 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 Smooth Cubic Bezier Element in a Path Geometry
/// </summary>
internal class SmoothCubicBezierElement : AbstractPathElement
{
private float _x2;
private float _y2;
private float _x;
private float _y;
private Vector2 _absoluteControlPoint2;
/// <summary>
/// Initializes a new instance of the <see cref="SmoothCubicBezierElement"/> class.
/// </summary>
public SmoothCubicBezierElement()
{
_x2 = _y2 = 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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
// Check if the last element was a Cubic Bezier
Vector2 controlPoint1;
if (lastElement is CubicBezierElement cubicBezier)
{
// Reflect the second control point of the cubic bezier over the current point. The
// resulting point will be the first control point of this Bezier.
controlPoint1 = Utils.Reflect(cubicBezier.GetControlPoint(), currentPoint);
}
// Or if the last element was s Smooth Cubic Bezier
else
{
// If the last element was a Smooth Cubic Bezier then reflect its second control point
// over the current point. The resulting point will be the first control point of this
// Bezier. Otherwise, if the last element was not a Smooth Cubic Bezier then the
// currentPoint will be the first control point of this Bezier
controlPoint1 = lastElement is SmoothCubicBezierElement smoothCubicBezier
? Utils.Reflect(smoothCubicBezier.GetControlPoint(), currentPoint)
: currentPoint;
}
var controlPoint2 = new Vector2(_x2, _y2);
var point = new Vector2(_x, _y);
if (IsRelative)
{
controlPoint2 += currentPoint;
point += currentPoint;
}
// Save the second absolute control point so that it can be used by the following
// SmoothCubicBezierElement (if any)
_absoluteControlPoint2 = controlPoint2;
// Execute command
pathBuilder.AddCubicBezier(controlPoint1, controlPoint2, point);
// Log command
logger?.Append($"{Indent}pathBuilder.AddCubicBezier(new Vector2({controlPoint1.X}, {controlPoint1.Y})");
logger?.Append($", new Vector2({controlPoint2.X}, {controlPoint2.Y})");
logger?.AppendLine($", new Vector2({point.X}, {point.Y}));");
// Set Last Element
lastElement = this;
// Return current point
return point;
}
/// <summary>
/// Gets the Second Control Point of this Cubic Bezier
/// </summary>
/// <returns>Vector2</returns>
public Vector2 GetControlPoint()
{
return _absoluteControlPoint2;
}
/// <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.SmoothCubicBezier);
}
/// <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["X2"].Value, out _x2);
float.TryParse(match.Groups["Y2"].Value, out _y2);
float.TryParse(match.Groups["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,113 @@
// 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 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 Smooth Quadratic Bezier Element in a Path Geometry
/// </summary>
internal class SmoothQuadraticBezierElement : AbstractPathElement
{
private float _x;
private float _y;
private Vector2 _absoluteControlPoint;
/// <summary>
/// Initializes a new instance of the <see cref="SmoothQuadraticBezierElement"/> class.
/// </summary>
public SmoothQuadraticBezierElement()
{
_x = _y = 0;
_absoluteControlPoint = Vector2.Zero;
}
/// <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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
// Check if the last element was a Quadratic Bezier
if (lastElement is QuadraticBezierElement quadBezier)
{
// Reflect the control point of the Quadratic Bezier over the current point. The
// resulting point will be the control point of this Bezier.
_absoluteControlPoint = Utils.Reflect(quadBezier.GetControlPoint(), currentPoint);
}
// Or if the last element was s Smooth Quadratic Bezier
else
{
// If the last element was a Smooth Quadratic Bezier then reflect its control point
// over the current point. The resulting point will be the control point of this
// Bezier. Otherwise, if the last element was not a Smooth Quadratic Bezier,
// then the currentPoint will be the control point of this Bezier.
_absoluteControlPoint = lastElement is SmoothQuadraticBezierElement smoothQuadBezier
? Utils.Reflect(smoothQuadBezier.GetControlPoint(), currentPoint)
: currentPoint;
}
var point = new Vector2(_x, _y);
if (IsRelative)
{
point += currentPoint;
}
// Execute command
pathBuilder.AddQuadraticBezier(_absoluteControlPoint, point);
// Log command
logger?.Append($"{Indent}pathBuilder.AddQuadraticBezier(new Vector2({_absoluteControlPoint.X},");
logger?.AppendLine($" {_absoluteControlPoint.Y}), new Vector2({point.X}, {point.Y}));");
// Set Last Element
lastElement = this;
// Return current point
return point;
}
/// <summary>
/// Gets the Control Point of this Quadratic Bezier
/// </summary>
/// <returns>Vector2</returns>
public Vector2 GetControlPoint()
{
return _absoluteControlPoint;
}
/// <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.SmoothQuadraticBezier);
}
/// <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["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,76 @@
// 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;
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 Vertical Line Element in a Path Geometry
/// </summary>
internal class VerticalLineElement : AbstractPathElement
{
private float _y;
/// <summary>
/// Initializes a new instance of the <see cref="VerticalLineElement"/> class.
/// </summary>
public VerticalLineElement()
{
_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>
/// <param name="logger">For logging purpose. To log the set of CanvasPathBuilder
/// commands, used for creating the CanvasGeometry, in string format.</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, StringBuilder logger)
{
// Calculate coordinates
var point = IsRelative ?
new Vector2(currentPoint.X, currentPoint.Y + _y) : new Vector2(currentPoint.X, _y);
// Execute command
pathBuilder.AddLine(point);
// Log command
logger?.AppendLine($"{Indent}pathBuilder.AddLine(new Vector2({point.X}, {point.Y}));");
// 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.VerticalLine);
}
/// <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["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,66 @@
// 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.Toolkit.Uwp.UI.Media.Geometry.Core;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke
{
/// <summary>
/// Abstract base class for Stroke Element
/// </summary>
internal abstract class AbstractCanvasStrokeElement : ICanvasStrokeElement
{
/// <summary>
/// Gets or sets the Stroke data defining the Brush Element
/// </summary>
public string Data { get; protected set; }
/// <summary>
/// Gets or sets the number of non-whitespace characters in
/// the Stroke Data
/// </summary>
public int ValidationCount { get; protected set; }
/// <summary>
/// Initializes the Stroke Element with the given Capture
/// </summary>
/// <param name="match">Match object</param>
public virtual void Initialize(Match match)
{
Data = match.Value;
if (!match.Success)
{
return;
}
GetAttributes(match);
// Update the validation count
Validate();
}
/// <summary>
/// Gets the number of non-whitespace characters in the data
/// </summary>
protected virtual void Validate()
{
ValidationCount = RegexFactory.ValidationRegex.Replace(Data, string.Empty).Length;
}
/// <summary>
/// Creates the ICanvasStroke from the parsed data
/// </summary>
/// <returns>ICanvasStroke</returns>
public abstract ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator);
/// <summary>
/// Gets the Stroke Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected abstract void GetAttributes(Match match);
}
}

Просмотреть файл

@ -0,0 +1,104 @@
// 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.Graphics.Canvas;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke
{
/// <summary>
/// Represents a Stroke Element
/// </summary>
internal sealed class CanvasStrokeElement : AbstractCanvasStrokeElement
{
private float _width;
private ICanvasBrushElement _brush;
private ICanvasStrokeStyleElement _style;
private int _widthValidationCount;
/// <summary>
/// Initializes a new instance of the <see cref="CanvasStrokeElement"/> class.
/// </summary>
/// <param name="match">Match object</param>
public CanvasStrokeElement(Match match)
{
_width = 1f;
_brush = null;
_style = null;
_widthValidationCount = 0;
Initialize(match);
}
/// <summary>
/// Creates the ICanvasStroke from the parsed data
/// </summary>
/// <returns>ICanvasStroke</returns>
public override ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator)
{
return new CanvasStroke(_brush.CreateBrush(resourceCreator), _width, _style.Style);
}
/// <summary>
/// Gets the Stroke Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// Stroke Width
var group = match.Groups["StrokeWidth"];
float.TryParse(group.Value, out _width);
// Sanitize by taking the absolute value
_width = Math.Abs(_width);
_widthValidationCount = RegexFactory.ValidationRegex.Replace(group.Value, string.Empty).Length;
// Stroke Brush
group = match.Groups["CanvasBrush"];
if (group.Success)
{
_brush = CanvasBrushParser.Parse(group.Value);
}
// If the ICanvasBrushElement was not created, then the ICanvasStroke cannot be created
if (_brush == null)
{
throw new NullReferenceException($"Unable to create a valid ICanvasBrush for the " +
$"ICanvasStroke with the following Brush data - '{group.Value}'");
}
// Stroke Style
_style = CanvasStrokeStyleParser.Parse(match);
}
/// <summary>
/// Gets the number of non-whitespace characters in the data
/// </summary>
protected override void Validate()
{
// Add 2 to the Validation Count to include the stroke command 'ST'
ValidationCount += 2;
// StrokeWidth Validation Count
ValidationCount += _widthValidationCount;
// Stroke Brush Validation Count
if (_brush != null)
{
ValidationCount += _brush.ValidationCount;
}
// Stroke Style Validation Count
if (_style != null)
{
ValidationCount += _style.ValidationCount;
}
}
}
}

Просмотреть файл

@ -0,0 +1,182 @@
// 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.RegularExpressions;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke
{
/// <summary>
/// Represents a CanvasStrokeStyle Element
/// </summary>
internal sealed class CanvasStrokeStyleElement : ICanvasStrokeStyleElement
{
/// <summary>
/// Gets the Stroke data defining the Brush Element
/// </summary>
public string Data { get; private set; }
/// <summary>
/// Gets the number of non-whitespace characters in
/// the Stroke Data
/// </summary>
public int ValidationCount { get; private set; }
/// <summary>
/// Gets the CanvasStrokeStyle obtained by parsing
/// the style data.
/// </summary>
public CanvasStrokeStyle Style { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="CanvasStrokeStyleElement"/> class.
/// </summary>
/// <param name="match">The matching data</param>
public CanvasStrokeStyleElement(Match match)
{
Data = match.Groups["CanvasStrokeStyle"].Value;
Style = new CanvasStrokeStyle();
Initialize(match);
// Get the number of non-whitespace characters in the data
ValidationCount = RegexFactory.ValidationRegex.Replace(Data, string.Empty).Length;
}
/// <summary>
/// Initializes the Stroke Element with the given Capture
/// </summary>
/// <param name="match">Match object</param>
public void Initialize(Match match)
{
var group = match.Groups["CanvasStrokeStyle"];
if (group.Success)
{
// DashStyle
group = match.Groups["DashStyle"];
if (group.Success)
{
Enum.TryParse(group.Value, out CanvasDashStyle dashStyle);
Style.DashStyle = dashStyle;
}
// LineJoin
group = match.Groups["LineJoin"];
if (group.Success)
{
Enum.TryParse(group.Value, out CanvasLineJoin lineJoin);
Style.LineJoin = lineJoin;
}
// MiterLimit
group = match.Groups["MiterLimit"];
if (group.Success)
{
float.TryParse(group.Value, out var miterLimit);
// Sanitize by taking the absolute value
Style.MiterLimit = Math.Abs(miterLimit);
}
// DashOffset
group = match.Groups["DashOffset"];
if (group.Success)
{
float.TryParse(group.Value, out var dashOffset);
Style.DashOffset = dashOffset;
}
// StartCap
group = match.Groups["StartCap"];
if (group.Success)
{
Enum.TryParse(group.Value, out CanvasCapStyle capStyle);
Style.StartCap = capStyle;
}
// EndCap
group = match.Groups["EndCap"];
if (group.Success)
{
Enum.TryParse(group.Value, out CanvasCapStyle capStyle);
Style.EndCap = capStyle;
}
// DashCap
group = match.Groups["DashCap"];
if (group.Success)
{
Enum.TryParse(group.Value, out CanvasCapStyle capStyle);
Style.DashCap = capStyle;
}
// TransformBehavior
group = match.Groups["TransformBehavior"];
if (group.Success)
{
Enum.TryParse(group.Value, out CanvasStrokeTransformBehavior transformBehavior);
Style.TransformBehavior = transformBehavior;
}
// CustomDashStyle
group = match.Groups["CustomDashStyle"];
if (group.Success)
{
List<float> dashes = new List<float>();
group = match.Groups["Main"];
if (group.Success)
{
if (float.TryParse(match.Groups["DashSize"].Value, out var dashSize))
{
// Sanitize by taking the absolute value
dashes.Add(Math.Abs(dashSize));
}
if (float.TryParse(match.Groups["SpaceSize"].Value, out var spaceSize))
{
// Sanitize by taking the absolute value
dashes.Add(Math.Abs(spaceSize));
}
}
group = match.Groups["Additional"];
if (group.Success)
{
foreach (Capture capture in group.Captures)
{
var dashMatch = RegexFactory.CustomDashAttributeRegex.Match(capture.Value);
if (!dashMatch.Success)
{
continue;
}
if (float.TryParse(dashMatch.Groups["DashSize"].Value, out var dashSize))
{
// Sanitize by taking the absolute value
dashes.Add(Math.Abs(dashSize));
}
if (float.TryParse(dashMatch.Groups["SpaceSize"].Value, out var spaceSize))
{
// Sanitize by taking the absolute value
dashes.Add(Math.Abs(spaceSize));
}
}
}
// Any valid dashes?
if (dashes.Any())
{
Style.CustomDashStyle = dashes.ToArray();
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// 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;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke
{
/// <summary>
/// Interface for Stroke Element
/// </summary>
internal interface ICanvasStrokeElement
{
/// <summary>
/// Gets the Stroke data defining the Brush Element
/// </summary>
string Data { get; }
/// <summary>
/// Gets the number of non-whitespace characters in
/// the Stroke Data
/// </summary>
int ValidationCount { get; }
/// <summary>
/// Initializes the Stroke Element with the given Capture
/// </summary>
/// <param name="match">Match object</param>
void Initialize(Match match);
/// <summary>
/// Creates the ICanvasStroke from the parsed data
/// </summary>
/// <returns>ICanvasStroke</returns>
ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator);
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// 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.Geometry;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke
{
/// <summary>
/// Interface for the CanvasStrokeStyle Element
/// </summary>
internal interface ICanvasStrokeStyleElement
{
/// <summary>
/// Gets the Stroke data defining the Brush Element
/// </summary>
string Data { get; }
/// <summary>
/// Gets the number of non-whitespace characters in
/// the Stroke Data
/// </summary>
int ValidationCount { get; }
/// <summary>
/// Gets the CanvasStrokeStyle obtained by parsing
/// the style data.
/// </summary>
CanvasStrokeStyle Style { get; }
/// <summary>
/// Initializes the Stroke Element with the given Capture
/// </summary>
/// <param name="match">Match object</param>
void Initialize(Match match);
}
}

Просмотреть файл

@ -0,0 +1,37 @@
// 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.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Interface to represent the Stroke which
/// can be used to render an outline on a <see cref="CanvasGeometry"/>
/// </summary>
public interface ICanvasStroke
{
/// <summary>
/// Gets or sets the brush with which the <see cref="CanvasStroke"/> will be rendered
/// </summary>
ICanvasBrush Brush { get; set; }
/// <summary>
/// Gets or sets the width of the <see cref="CanvasStroke"/>
/// </summary>
float Width { get; set; }
/// <summary>
/// Gets or sets the Style of the <see cref="CanvasStroke"/>
/// </summary>
CanvasStrokeStyle Style { get; set; }
/// <summary>
/// Gets or sets transform matrix of the <see cref="CanvasStroke"/> brush.
/// </summary>
Matrix3x2 Transform { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,103 @@
// 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 Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
{
/// <summary>
/// Parser for ICanvasBrush
/// </summary>
internal static class CanvasBrushParser
{
/// <summary>
/// Parses the Brush data string and converts it into ICanvasBrushElement
/// </summary>
/// <param name="brushData">Brush data</param>
/// <returns>ICanvasBrushElement</returns>
internal static ICanvasBrushElement Parse(string brushData)
{
var matches = RegexFactory.CanvasBrushRegex.Matches(brushData);
// If no match is found or no captures in the match, then it means
// that the brush data is invalid.
if ((matches == null) || (matches.Count == 0))
{
throw new ArgumentException($"Invalid Brush data!\nBrush Data: {brushData}", nameof(brushData));
}
// If the match contains more than one captures, it means that there
// are multiple brushes present in the brush data. There should
// be only one brush defined in the brush data.
if (matches.Count > 1)
{
throw new ArgumentException("Multiple Brushes defined in Brush Data! " +
"There should be only one Brush definition within the Brush Data. " +
"You can either remove Brush definitions or split the Brush Data " +
"into multiple Brush Data and call the CanvasPathGeometry.CreateBrush() method on each of them." +
$"\nBrush Data: {brushData}");
}
// There should be only one match
var match = matches[0];
AbstractCanvasBrushElement brushElement = null;
if (match.Groups["SolidColorBrush"].Success)
{
brushElement = new SolidColorBrushElement(match.Groups["SolidColorBrush"].Captures[0]);
}
else if (match.Groups["LinearGradient"].Success)
{
brushElement = new LinearGradientBrushElement(match.Groups["LinearGradient"].Captures[0]);
}
else if (match.Groups["LinearGradientHdr"].Success)
{
brushElement = new LinearGradientHdrBrushElement(match.Groups["LinearGradientHdr"].Captures[0]);
}
else if (match.Groups["RadialGradient"].Success)
{
brushElement = new RadialGradientBrushElement(match.Groups["RadialGradient"].Captures[0]);
}
else if (match.Groups["RadialGradientHdr"].Success)
{
brushElement = new RadialGradientHdrBrushElement(match.Groups["RadialGradientHdr"].Captures[0]);
}
if (brushElement == null)
{
return null;
}
// Perform validation to check if there are any invalid characters in the brush data that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(brushData, string.Empty).Length;
var postValidationCount = brushElement.ValidationCount;
if (preValidationCount != postValidationCount)
{
throw new ArgumentException($"Brush data contains invalid characters!\nBrush Data: {brushData}", nameof(brushData));
}
return brushElement;
}
/// <summary>
/// Parses the Brush data string and converts it into ICanvasBrush
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator</param>
/// <param name="brushData">Brush data string</param>
/// <returns>ICanvasBrush</returns>
internal static ICanvasBrush Parse(ICanvasResourceCreator resourceCreator, string brushData)
{
// Parse the brush data to get the ICanvasBrushElement
var brushElement = Parse(brushData);
// Create ICanvasBrush from the brushElement
return brushElement.CreateBrush(resourceCreator);
}
}
}

Просмотреть файл

@ -0,0 +1,123 @@
// 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;
using System.Text.RegularExpressions;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
{
/// <summary>
/// Parser for CanvasGeometry
/// </summary>
internal static class CanvasGeometryParser
{
/// <summary>
/// Parses the Path data in string format and converts it to CanvasGeometry.
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator</param>
/// <param name="pathData">Path data</param>
/// <param name="logger">(Optional) For logging purpose. To log the set of
/// CanvasPathBuilder commands, used for creating the CanvasGeometry, in
/// string format.</param>
/// <returns>CanvasGeometry</returns>
public static CanvasGeometry Parse(ICanvasResourceCreator resourceCreator, string pathData, StringBuilder logger = null)
{
var pathFigures = new List<ICanvasPathElement>();
var matches = RegexFactory.CanvasGeometryRegex.Matches(pathData);
// If no match is found or no captures in the match, then it means
// that the path data is invalid.
if ((matches == null) || (matches.Count == 0))
{
throw new ArgumentException($"Invalid Path data!\nPath Data: {pathData}", nameof(pathData));
}
// If the match contains more than one captures, it means that there
// are multiple FillRuleElements present in the path data. There can
// be only one FillRuleElement in the path data (at the beginning).
if (matches.Count > 1)
{
throw new ArgumentException("Multiple FillRule elements present in Path Data! " +
"There should be only one FillRule within the Path Data. " +
"You can either remove additional FillRule elements or split the Path Data " +
"into multiple Path Data and call the CanvasPathGeometry.CreateGeometry() method on each of them." +
$"\nPath Data: {pathData}");
}
var figures = new List<ICanvasPathElement>();
foreach (PathFigureType type in Enum.GetValues(typeof(PathFigureType)))
{
foreach (Capture figureCapture in matches[0].Groups[type.ToString()].Captures)
{
var figureRootIndex = figureCapture.Index;
var regex = RegexFactory.GetRegex(type);
var figureMatch = regex.Match(figureCapture.Value);
if (!figureMatch.Success)
{
continue;
}
// Process the 'Main' Group which contains the Path Command and
// corresponding attributes
var figure = PathElementFactory.CreatePathFigure(type, figureMatch, figureRootIndex);
figures.Add(figure);
// Process the 'Additional' Group which contains just the attributes
figures.AddRange(from Capture capture in figureMatch.Groups["Additional"].Captures
select PathElementFactory.CreateAdditionalPathFigure(type, capture, figureRootIndex + capture.Index, figure.IsRelative));
}
}
// Sort the figures by their indices
pathFigures.AddRange(figures.OrderBy(f => f.Index));
if (pathFigures.Count > 0)
{
// Check if the first element in the _figures list is a FillRuleElement
// which would indicate the fill rule to be followed while creating the
// path. If it is not present, then insert a default FillRuleElement at
// the beginning.
if ((pathFigures.ElementAt(0) as FillRuleElement) == null)
{
pathFigures.Insert(0, PathElementFactory.CreateDefaultPathElement(PathFigureType.FillRule));
}
}
// Perform validation to check if there are any invalid characters in the path data that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(pathData, string.Empty).Length;
var postValidationCount = pathFigures.Sum(x => x.ValidationCount);
if (preValidationCount != postValidationCount)
{
throw new ArgumentException($"Path data contains invalid characters!\nPath Data: {pathData}", nameof(pathData));
}
if (pathFigures.Count == 0)
{
return null;
}
ICanvasPathElement lastElement = null;
var currentPoint = Vector2.Zero;
using var pathBuilder = new CanvasPathBuilder(resourceCreator);
foreach (var pathFigure in pathFigures)
{
currentPoint = pathFigure.CreatePath(pathBuilder, currentPoint, ref lastElement, logger);
}
return CanvasGeometry.CreatePath(pathBuilder);
}
}
}

Просмотреть файл

@ -0,0 +1,77 @@
// 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 Microsoft.Graphics.Canvas;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
{
/// <summary>
/// Parser for CanvasStroke
/// </summary>
internal static class CanvasStrokeParser
{
/// <summary>
/// Parses the Stroke Data string and converts it into ICanvasStrokeElement
/// </summary>
/// <param name="strokeData">Stroke Data string</param>
/// <returns>ICanvasStrokeElement</returns>
internal static ICanvasStrokeElement Parse(string strokeData)
{
var matches = RegexFactory.CanvasStrokeRegex.Matches(strokeData);
// If no match is found or no captures in the match, then it means
// that the stroke data is invalid.
if ((matches == null) || (matches.Count == 0))
{
throw new ArgumentException($"Invalid Stroke data!\nStroke Data: {strokeData}", nameof(strokeData));
}
// If the match contains more than one captures, it means that there
// are multiple CanvasStrokes present in the stroke data. There should
// be only one CanvasStroke defined in the stroke data.
if (matches.Count > 1)
{
throw new ArgumentException("Multiple CanvasStrokes defined in Stroke Data! " +
"There should be only one CanvasStroke definition within the Stroke Data. " +
"You can either remove CanvasStroke definitions or split the Stroke Data " +
"into multiple Stroke Data and call the CanvasPathGeometry.CreateStroke() method on each of them." +
$"\nStroke Data: {strokeData}");
}
// There should be only one match
var match = matches[0];
var strokeElement = new CanvasStrokeElement(match);
// Perform validation to check if there are any invalid characters in the stroke data that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(strokeData, string.Empty).Length;
var postValidationCount = strokeElement.ValidationCount;
if (preValidationCount != postValidationCount)
{
throw new ArgumentException($"Stroke data contains invalid characters!\nStroke Data: {strokeData}", nameof(strokeData));
}
return strokeElement;
}
/// <summary>
/// Parses the Stroke Data string and converts it into CanvasStroke
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator</param>
/// <param name="strokeData">Stroke Data string</param>
/// <returns>ICanvasStroke</returns>
internal static ICanvasStroke Parse(ICanvasResourceCreator resourceCreator, string strokeData)
{
// Parse the stroke data to create the ICanvasStrokeElement
var strokeElement = Parse(strokeData);
// Create the CanvasStroke from the strokeElement
return strokeElement.CreateStroke(resourceCreator);
}
}
}

Просмотреть файл

@ -0,0 +1,74 @@
// 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.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
{
/// <summary>
/// Parser for the CanvasStrokeStyle
/// </summary>
internal static class CanvasStrokeStyleParser
{
/// <summary>
/// Parses the given style data and converts it to CanvasStrokeStyle
/// </summary>
/// <param name="styleData">Style data</param>
/// <returns>CanvasStrokeStyle</returns>
internal static CanvasStrokeStyle Parse(string styleData)
{
var matches = RegexFactory.CanvasStrokeStyleRegex.Matches(styleData);
// If no match is found or no captures in the match, then it means
// that the style data is invalid.
if ((matches == null) || (matches.Count == 0))
{
throw new ArgumentException($"Invalid CanvasStrokeStyle data!\nCanvasStrokeStyle Data: {styleData}", nameof(styleData));
}
// If the match contains more than one captures, it means that there
// are multiple CanvasStrokeStyles present in the CanvasStrokeStyle data. There should
// be only one CanvasStrokeStyle defined in the CanvasStrokeStyle data.
if (matches.Count > 1)
{
throw new ArgumentException("Multiple CanvasStrokeStyles defined in CanvasStrokeStyle Data! " +
"There should be only one CanvasStrokeStyle definition within the CanvasStrokeStyle Data. " +
"You can either remove CanvasStrokeStyle definitions or split the CanvasStrokeStyle Data " +
"into multiple CanvasStrokeStyle Data and call the CanvasPathGeometry.CreateStrokeStyle() method on each of them." +
$"\nCanvasStrokeStyle Data: {styleData}");
}
// There should be only one match
var match = matches[0];
var styleElement = new CanvasStrokeStyleElement(match);
// Perform validation to check if there are any invalid characters in the brush data that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(styleData, string.Empty).Length;
var postValidationCount = styleElement.ValidationCount;
if (preValidationCount != postValidationCount)
{
throw new ArgumentException($"CanvasStrokeStyle data contains invalid characters!\nCanvasStrokeStyle Data: {styleData}", nameof(styleData));
}
return styleElement.Style;
}
/// <summary>
/// Parses and constructs a ICanvasStrokeStyleElement from the
/// specified Match object.
/// </summary>
/// <param name="match">Match object</param>
/// <returns>ICanvasStrokeStyleElement</returns>
internal static ICanvasStrokeStyleElement Parse(Match match)
{
return new CanvasStrokeStyleElement(match);
}
}
}

Просмотреть файл

@ -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;
using System.Numerics;
using System.Text.RegularExpressions;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
using Windows.UI;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
{
/// <summary>
/// Parser for Color
/// </summary>
internal static class ColorParser
{
/// <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 &amp; 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>
internal static Color Parse(string colorString)
{
var match = RegexFactory.ColorRegex.Match(colorString);
if (!match.Success)
{
throw new ArgumentException("Invalid Hexadecimal string!", nameof(colorString));
}
return Parse(match);
}
/// <summary>
/// Attempts to convert 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 &amp; A should have value in the range between 0 and 1, inclusive)
/// </summary>
/// <param name="colorString">Color string in Hexadecimal or HDR format</param>
/// <param name="color">Output Color object</param>
/// <returns>True if successful, otherwise False</returns>
internal static bool TryParse(string colorString, out Color color)
{
var match = RegexFactory.ColorRegex.Match(colorString);
if (!match.Success)
{
return false;
}
color = Parse(match);
return true;
}
/// <summary>
/// Converts a Vector4 High Dynamic Range Color
/// to Color. 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.
/// </summary>
/// <param name="hdrColor">High Dynamic Range Color</param>
/// <returns>Color</returns>
internal static Color Parse(Vector4 hdrColor)
{
// Vector4's X, Y, Z, W components match to
// Color's R, G, B, A components respectively
return Parse(hdrColor.X, hdrColor.Y, hdrColor.Z, hdrColor.W);
}
/// <summary>
/// Converts the given HDR color values to Color.
/// </summary>
/// <param name="x">Red Component</param>
/// <param name="y">Green Component</param>
/// <param name="z">Blue Component</param>
/// <param name="w">Alpha Component</param>
/// <returns>Instance of Color.</returns>
internal static Color Parse(float x, float y, float z, float w)
{
var r = (byte)Math.Min(Math.Abs(x) * 255f, 255f);
var g = (byte)Math.Min(Math.Abs(y) * 255f, 255f);
var b = (byte)Math.Min(Math.Abs(z) * 255f, 255f);
var a = (byte)Math.Min(Math.Abs(w) * 255f, 255f);
return Color.FromArgb(a, r, g, b);
}
/// <summary>
/// Parses and constructs a Color object from the specified
/// Match object.
/// </summary>
/// <param name="match">Match object</param>
/// <returns>Color</returns>
internal static Color Parse(Match match)
{
if (match.Groups["RgbColor"].Success)
{
// Alpha component
byte alpha = 255;
var alphaStr = match.Groups["Alpha"].Value;
if (!string.IsNullOrWhiteSpace(alphaStr))
{
alpha = (byte)Convert.ToInt32(alphaStr, 16);
}
// Red component
var red = (byte)Convert.ToInt32(match.Groups["Red"].Value, 16);
// Green component
var green = (byte)Convert.ToInt32(match.Groups["Green"].Value, 16);
// Blue component
var blue = (byte)Convert.ToInt32(match.Groups["Blue"].Value, 16);
return Color.FromArgb(alpha, red, green, blue);
}
if (match.Groups["HdrColor"].Success)
{
float.TryParse(match.Groups["X"].Value, out var x);
float.TryParse(match.Groups["Y"].Value, out var y);
float.TryParse(match.Groups["Z"].Value, out var z);
float.TryParse(match.Groups["W"].Value, out var w);
return Parse(x, y, z, w);
}
return Colors.Transparent;
}
}
}

Просмотреть файл

@ -0,0 +1,64 @@
// 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;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Class containing some constants
/// represented as Floating point numbers.
/// </summary>
public static class Scalar
{
// Pi related floating point constants
/// <summary>
/// (float)Math.PI radians ( or 180 degrees).
/// </summary>
public const float Pi = (float)Math.PI;
/// <summary>
/// Two times (float)Math.PI radians ( or 360 degrees).
/// </summary>
public const float TwoPi = 2f * Pi;
/// <summary>
/// Half of (float)Math.PI radians ( or 90 degrees).
/// </summary>
public const float PiByTwo = Pi / 2f;
/// <summary>
/// One third of (float)Math.PI radians ( or 60 degrees).
/// </summary>
public const float PiByThree = Pi / 3f;
/// <summary>
/// One fourth of (float)Math.PI radians ( or 45 degrees).
/// </summary>
public const float PiByFour = Pi / 4f;
/// <summary>
/// One sixth of (float)Math.PI radians ( or 30 degrees).
/// </summary>
public const float PiBySix = Pi / 6f;
/// <summary>
/// Three times half of (float)Math.PI radians ( or 270 degrees).
/// </summary>
public const float ThreePiByTwo = 3f * Pi / 2f;
// Conversion constants
/// <summary>
/// 1 degree in radians.
/// </summary>
public const float DegreesToRadians = Pi / 180f;
/// <summary>
/// 1 radian in degrees.
/// </summary>
public const float RadiansToDegrees = 180f / Pi;
}
}

Просмотреть файл

@ -0,0 +1,984 @@
// 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.Runtime.InteropServices;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Class containing collection of useful methods
/// for various types
/// </summary>
public static class Utils
{
// Constant values
// Smallest double value such that 1.0 + DoubleEpsilon != 1.0
internal const double DoubleEpsilon = 2.2204460492503131e-016;
// Number close to zero, where float.MinValue is -float.MaxValue
internal const float FloatMin = 1.175494351e-38F;
/// <summary>
/// Returns whether or not two doubles are "close".
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
public static bool IsCloseTo(this double value1, double value2)
{
// In case they are Infinities or NaN (then epsilon check does not work)
if ((double.IsInfinity(value1) &&
double.IsInfinity(value2)) ||
(IsNaN(value1) && IsNaN(value2)))
{
return true;
}
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DoubleEpsilon
var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DoubleEpsilon;
var delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// Returns whether or not the first double is less than the second double.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
/// <returns>
/// bool - the result of the LessThan comparision.
/// </returns>
public static bool IsLessThan(double value1, double value2)
{
return (value1 < value2) && !value1.IsCloseTo(value2);
}
/// <summary>
/// Returns whether or not the first double is greater than the second double.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
/// <returns>
/// bool - the result of the GreaterThan comparision.
/// </returns>
public static bool IsGreaterThan(this double value1, double value2)
{
return (value1 > value2) && !value1.IsCloseTo(value2);
}
/// <summary>
/// Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
/// but this is faster.
/// </summary>
/// <param name="value"> The double to compare to 1. </param>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
public static bool IsOne(this double value)
{
return Math.Abs(value - 1.0) < 10.0 * DoubleEpsilon;
}
/// <summary>
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
/// but this is faster.
/// </summary>
/// <param name="value"> The double to compare to 0. </param>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
public static bool IsZero(this double value)
{
return Math.Abs(value) < 10.0 * DoubleEpsilon;
}
/// <summary>
/// Returns whether or not two floats are "close".
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
public static bool IsCloseTo(this float value1, float value2)
{
// In case they are Infinities or NaN (then epsilon check does not work)
if ((float.IsInfinity(value1) &&
float.IsInfinity(value2)) ||
(float.IsNaN(value1) && float.IsNaN(value2)))
{
return true;
}
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < FloatMin
var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * FloatMin;
var delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// Returns whether or not the first float is less than the second float.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
/// <returns>
/// bool - the result of the LessThan comparision.
/// </returns>
public static bool IsLessThan(float value1, float value2)
{
return (value1 < value2) && !value1.IsCloseTo(value2);
}
/// <summary>
/// Returns whether or not the first float is greater than the second float.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
/// <returns>
/// bool - the result of the GreaterThan comparision.
/// </returns>
public static bool IsGreaterThan(this float value1, float value2)
{
return (value1 > value2) && !value1.IsCloseTo(value2);
}
/// <summary>
/// Returns whether or not the float is "close" to 1. Same as AreClose(float, 1),
/// but this is faster.
/// </summary>
/// <param name="value"> The float to compare to 1. </param>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
public static bool IsOne(this float value)
{
return Math.Abs(value - 1.0) < 10.0 * FloatMin;
}
/// <summary>
/// IsZero - Returns whether or not the float is "close" to 0. Same as AreClose(float, 0),
/// but this is faster.
/// </summary>
/// <param name="value"> The float to compare to 0. </param>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
public static bool IsZero(this float value)
{
return Math.Abs(value) < 10.0 * FloatMin;
}
/// <summary>
/// Compares two points for fuzzy equality. This function
/// helps compensate for the fact that double values can
/// acquire error when operated upon
/// </summary>
/// <param name='point1'>The first point to compare</param>
/// <param name='point2'>The second point to compare</param>
/// <returns>Whether or not the two points are equal</returns>
public static bool IsCloseTo(this Point point1, Point point2)
{
return point1.X.IsCloseTo(point2.X) && point1.Y.IsCloseTo(point2.Y);
}
/// <summary>
/// Compares two Size instances for fuzzy equality. This function
/// helps compensate for the fact that double values can
/// acquire error when operated upon
/// </summary>
/// <param name='size1'>The first size to compare</param>
/// <param name='size2'>The second size to compare</param>
/// <returns>Whether or not the two Size instances are equal</returns>
public static bool IsCloseTo(this Size size1, Size size2)
{
return size1.Width.IsCloseTo(size2.Width) && size1.Height.IsCloseTo(size2.Height);
}
/// <summary>
/// Compares two rectangles for fuzzy equality. This function
/// helps compensate for the fact that double values can
/// acquire error when operated upon
/// </summary>
/// <param name='rect1'>The first rectangle to compare</param>
/// <param name='rect2'>The second rectangle to compare</param>
/// <returns>Whether or not the two rectangles are equal</returns>
public static bool IsCloseTo(this Rect rect1, Rect rect2)
{
// If they're both empty, don't bother with the double logic.
if (rect1.IsEmpty)
{
return rect2.IsEmpty;
}
// At this point, rect1 isn't empty, so the first thing we can test is rect2.IsEmpty, followed by property-wise compares.
return (!rect2.IsEmpty)
&& rect1.X.IsCloseTo(rect2.X) && rect1.Y.IsCloseTo(rect2.Y)
&& rect1.Height.IsCloseTo(rect2.Height) && rect1.Width.IsCloseTo(rect2.Width);
}
/// <summary>
/// Restricts the given single precision number to be within the
/// specified min and max values (inclusive).
/// </summary>
/// <param name="num">Given single precision number</param>
/// <param name="min">Minimum value</param>
/// <param name="max">Maximum value</param>
/// <returns>Clamped value</returns>
public static float Clamp(this float num, float min, float max)
{
if (min > max)
{
var temp = min;
min = max;
max = temp;
}
return Math.Max(min, Math.Min(max, num));
}
/// <summary>
/// Restricts the given double precision number to be within the
/// specified min and max values (inclusive).
/// </summary>
/// <param name="num">Given double precision number</param>
/// <param name="min">Minimum value</param>
/// <param name="max">Maximum value</param>
/// <returns>Clamped value</returns>
public static double Clamp(this double num, double min, double max)
{
if (min > max)
{
var temp = min;
min = max;
max = temp;
}
return Math.Max(min, Math.Min(max, num));
}
[StructLayout(LayoutKind.Explicit)]
private struct NanUnion
{
[FieldOffset(0)]
internal double DoubleValue;
[FieldOffset(0)]
internal ulong UintValue;
}
/// <summary>
/// Faster check for NaN ( faster than double.IsNaN() )
/// IEEE 754 : If the argument is any value in the range 0x7ff0000000000001L through 0x7fffffffffffffffL
/// or in the range 0xfff0000000000001L through 0xffffffffffffffffL, the result will be NaN.
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>True/False</returns>
public static bool IsNaN(double value)
{
var t = new NanUnion
{
DoubleValue = value
};
var exp = t.UintValue & 0xfff0000000000000;
var man = t.UintValue & 0x000fffffffffffff;
return (exp == 0x7ff0000000000000 || exp == 0xfff0000000000000) && (man != 0);
}
/// <summary>
/// Rounds the given value based on the DPI scale
/// </summary>
/// <param name="value">Value to round</param>
/// <param name="dpiScale">DPI Scale</param>
/// <returns>The rounded value</returns>
public static double RoundLayoutValue(double value, double dpiScale)
{
double newValue;
// If DPI == 1, don't use DPI-aware rounding.
if (!dpiScale.IsCloseTo(1.0))
{
newValue = Math.Round(value * dpiScale) / dpiScale;
// If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue), use the original value.
if (IsNaN(newValue) ||
double.IsInfinity(newValue) ||
newValue.IsCloseTo(double.MaxValue))
{
newValue = value;
}
}
else
{
newValue = Math.Round(value);
}
return newValue;
}
/// <summary>
/// Calculates the linear interpolated value based on the given values.
/// </summary>
/// <param name="start">Starting value.</param>
/// <param name="end">Ending value.</param>
/// <param name="amount">Weight-age given to the ending value.</param>
/// <returns>Linear interpolated value.</returns>
public static float Lerp(this float start, float end, float amount)
{
return start + ((end - start) * amount);
}
/// <summary>
/// Verifies if this Thickness contains only valid values
/// The set of validity checks is passed as parameters.
/// </summary>
/// <param name='thick'>Thickness value</param>
/// <param name='allowNegative'>allows negative values</param>
/// <param name='allowNaN'>allows double.NaN</param>
/// <param name='allowPositiveInfinity'>allows double.PositiveInfinity</param>
/// <param name='allowNegativeInfinity'>allows double.NegativeInfinity</param>
/// <returns>Whether or not the thickness complies to the range specified</returns>
public static bool IsValid(this Thickness thick, bool allowNegative, bool allowNaN, bool allowPositiveInfinity, bool allowNegativeInfinity)
{
if (!allowNegative)
{
if (thick.Left < 0d || thick.Right < 0d || thick.Top < 0d || thick.Bottom < 0d)
{
return false;
}
}
if (!allowNaN)
{
if (IsNaN(thick.Left) || IsNaN(thick.Right)
|| IsNaN(thick.Top) || IsNaN(thick.Bottom))
{
return false;
}
}
if (!allowPositiveInfinity)
{
if (double.IsPositiveInfinity(thick.Left) || double.IsPositiveInfinity(thick.Right)
|| double.IsPositiveInfinity(thick.Top) || double.IsPositiveInfinity(thick.Bottom))
{
return false;
}
}
if (!allowNegativeInfinity)
{
if (double.IsNegativeInfinity(thick.Left) || double.IsNegativeInfinity(thick.Right)
|| double.IsNegativeInfinity(thick.Top) || double.IsNegativeInfinity(thick.Bottom))
{
return false;
}
}
return true;
}
/// <summary>
/// Method to add up the left and right size as width, as well as the top and bottom size as height
/// </summary>
/// <param name="thick">Thickness</param>
/// <returns>Size</returns>
public static Size CollapseThickness(this Thickness thick)
{
return new Size(thick.Left + thick.Right, thick.Top + thick.Bottom);
}
/// <summary>
/// Verifies if the Thickness contains only zero values
/// </summary>
/// <param name="thick">Thickness</param>
/// <returns>Size</returns>
public static bool IsZero(this Thickness thick)
{
return thick.Left.IsZero()
&& thick.Top.IsZero()
&& thick.Right.IsZero()
&& thick.Bottom.IsZero();
}
/// <summary>
/// Verifies if all the values in Thickness are same
/// </summary>
/// <param name="thick">Thickness</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsUniform(this Thickness thick)
{
return thick.Left.IsCloseTo(thick.Top)
&& thick.Left.IsCloseTo(thick.Right)
&& thick.Left.IsCloseTo(thick.Bottom);
}
/// <summary>
/// Converts the Thickness object to Vector4. If the Thickness
/// object's component have values NaN, PositiveInfinity or
/// NegativeInfinity, then Vector4.Zero will be returned.
/// </summary>
/// <param name="thickness">Thickness object</param>
/// <returns>Vector4</returns>
public static Vector4 ToVector4(this Thickness thickness)
{
if (thickness.IsValid(true, false, false, false))
{
// Sanitize the component by taking only
return new Vector4(
(float)thickness.Left,
(float)thickness.Top,
(float)thickness.Right,
(float)thickness.Bottom);
}
return Vector4.Zero;
}
/// <summary>
/// Converts the Thickness object to Vector4. If the Thickness
/// object contains negative components they will be converted
/// to positive values. If the Thickness object's component
/// have values NaN, PositiveInfinity or NegativeInfinity,
/// then Vector4.Zero will be returned.
/// </summary>
/// <param name="thickness">Thickness object</param>
/// <returns>Vector2</returns>
public static Vector4 ToAbsVector4(this Thickness thickness)
{
if (thickness.IsValid(true, false, false, false))
{
// Sanitize the component by taking only
return new Vector4(
Math.Abs((float)thickness.Left),
Math.Abs((float)thickness.Top),
Math.Abs((float)thickness.Right),
Math.Abs((float)thickness.Bottom));
}
return Vector4.Zero;
}
/// <summary>
/// Gets the top left corner of the thickness structure.
/// </summary>
/// <param name="thickness">Thickness object</param>
/// <returns>Vector2</returns>
public static Vector2 GetOffset(this Thickness thickness)
{
return new Vector2((float)thickness.Left, (float)thickness.Top);
}
/// <summary>
/// Verifies if this CornerRadius contains only valid values
/// The set of validity checks is passed as parameters.
/// </summary>
/// <param name='corner'>CornerRadius value</param>
/// <param name='allowNegative'>allows negative values</param>
/// <param name='allowNaN'>allows double.NaN</param>
/// <param name='allowPositiveInfinity'>allows double.PositiveInfinity</param>
/// <param name='allowNegativeInfinity'>allows double.NegativeInfinity</param>
/// <returns>Whether or not the CornerRadius complies to the range specified</returns>
public static bool IsValid(this CornerRadius corner, bool allowNegative, bool allowNaN, bool allowPositiveInfinity, bool allowNegativeInfinity)
{
if (!allowNegative)
{
if (corner.TopLeft < 0d || corner.TopRight < 0d || corner.BottomLeft < 0d || corner.BottomRight < 0d)
{
return false;
}
}
if (!allowNaN)
{
if (IsNaN(corner.TopLeft) || IsNaN(corner.TopRight) ||
IsNaN(corner.BottomLeft) || IsNaN(corner.BottomRight))
{
return false;
}
}
if (!allowPositiveInfinity)
{
if (double.IsPositiveInfinity(corner.TopLeft) || double.IsPositiveInfinity(corner.TopRight) ||
double.IsPositiveInfinity(corner.BottomLeft) || double.IsPositiveInfinity(corner.BottomRight))
{
return false;
}
}
if (!allowNegativeInfinity)
{
if (double.IsNegativeInfinity(corner.TopLeft) || double.IsNegativeInfinity(corner.TopRight) ||
double.IsNegativeInfinity(corner.BottomLeft) || double.IsNegativeInfinity(corner.BottomRight))
{
return false;
}
}
return true;
}
/// <summary>
/// Verifies if the CornerRadius contains only zero values
/// </summary>
/// <param name="corner">CornerRadius</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsZero(this CornerRadius corner)
{
return corner.TopLeft.IsZero()
&& corner.TopRight.IsZero()
&& corner.BottomRight.IsZero()
&& corner.BottomLeft.IsZero();
}
/// <summary>
/// Verifies if the CornerRadius contains same values
/// </summary>
/// <param name="corner">CornerRadius</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsUniform(this CornerRadius corner)
{
var topLeft = corner.TopLeft;
return topLeft.IsCloseTo(corner.TopRight) &&
topLeft.IsCloseTo(corner.BottomLeft) &&
topLeft.IsCloseTo(corner.BottomRight);
}
/// <summary>
/// Converts the given corner value to a valid positive value.
/// Returns zero if the corner value is Infinity or NaN or 0.
/// </summary>
/// <param name="corner">Corner value</param>
/// <returns>Valid Corner value</returns>
public static double ConvertToValidCornerValue(double corner)
{
if (IsNaN(corner) ||
double.IsInfinity(corner) ||
(corner < 0d))
{
return 0d;
}
return corner;
}
/// <summary>
/// Converts the CornerRadius object to Vector4. If the CornerRadius
/// object's component have values NaN, PositiveInfinity or
/// NegativeInfinity, then Vector4.Zero will be returned.
/// </summary>
/// <param name="corner">CornerRadius object</param>
/// <returns>Vector4</returns>
public static Vector4 ToVector4(this CornerRadius corner)
{
if (corner.IsValid(true, false, false, false))
{
return new Vector4(
(float)corner.TopLeft,
(float)corner.TopRight,
(float)corner.BottomRight,
(float)corner.BottomLeft);
}
return Vector4.Zero;
}
/// <summary>
/// Converts the CornerRadius object to Vector4. If the CornerRadius
/// object contains negative components they will be converted
/// to positive values. If the Thickness object's component
/// have values NaN, PositiveInfinity or NegativeInfinity,
/// then Vector4.Zero will be returned.
/// </summary>
/// <param name="corner">CornerRadius object</param>
/// <returns>Vector4</returns>
public static Vector4 ToAbsVector4(this CornerRadius corner)
{
if (corner.IsValid(true, false, false, false))
{
// Sanitize the component by taking only
return new Vector4(
(float)Math.Abs(corner.TopLeft),
(float)Math.Abs(corner.TopRight),
(float)Math.Abs(corner.BottomRight),
(float)Math.Abs(corner.BottomLeft));
}
return Vector4.Zero;
}
/// <summary>
/// Deflates rectangle by given thickness
/// </summary>
/// <param name="rect">Rectangle</param>
/// <param name="thick">Thickness</param>
/// <returns>Deflated Rectangle</returns>
public static Rect Deflate(this Rect rect, Thickness thick)
{
return new Rect(
rect.Left + thick.Left,
rect.Top + thick.Top,
Math.Max(0.0, rect.Width - thick.Left - thick.Right),
Math.Max(0.0, rect.Height - thick.Top - thick.Bottom));
}
/// <summary>
/// Inflates rectangle by given thickness
/// </summary>
/// <param name="rect">Rectangle</param>
/// <param name="thick">Thickness</param>
/// <returns>Inflated Rectangle</returns>
public static Rect Inflate(this Rect rect, Thickness thick)
{
return new Rect(
rect.Left - thick.Left,
rect.Top - thick.Top,
Math.Max(0.0, rect.Width + thick.Left + thick.Right),
Math.Max(0.0, rect.Height + thick.Top + thick.Bottom));
}
/// <summary>
/// Verifies if the given brush is a SolidColorBrush and
/// its color does not include transparency.
/// </summary>
/// <param name="brush">Brush</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsOpaqueSolidColorBrush(this Brush brush)
{
return (brush as SolidColorBrush)?.Color.A == 0xff;
}
/// <summary>
/// Verifies if the given brush is the same as the otherBrush.
/// </summary>
/// <param name="brush">Given <see cref="Brush"/></param>
/// <param name="otherBrush">The <see cref="Brush"/> to match it with</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsEqualTo(this Brush brush, Brush otherBrush)
{
if (brush.GetType() != otherBrush.GetType())
{
return false;
}
if (ReferenceEquals(brush, otherBrush))
{
return true;
}
// Are both instances of SolidColorBrush
if ((brush is SolidColorBrush solidBrushA) && (otherBrush is SolidColorBrush solidBrushB))
{
return (solidBrushA.Color == solidBrushB.Color)
&& solidBrushA.Opacity.IsCloseTo(solidBrushB.Opacity);
}
// Are both instances of LinearGradientBrush
if ((brush is LinearGradientBrush linGradBrushA) && (otherBrush is LinearGradientBrush linGradBrushB))
{
var result = (linGradBrushA.ColorInterpolationMode == linGradBrushB.ColorInterpolationMode)
&& (linGradBrushA.EndPoint == linGradBrushB.EndPoint)
&& (linGradBrushA.MappingMode == linGradBrushB.MappingMode)
&& linGradBrushA.Opacity.IsCloseTo(linGradBrushB.Opacity)
&& (linGradBrushA.StartPoint == linGradBrushB.StartPoint)
&& (linGradBrushA.SpreadMethod == linGradBrushB.SpreadMethod)
&& (linGradBrushA.GradientStops.Count == linGradBrushB.GradientStops.Count);
if (!result)
{
return false;
}
for (var i = 0; i < linGradBrushA.GradientStops.Count; i++)
{
result = (linGradBrushA.GradientStops[i].Color == linGradBrushB.GradientStops[i].Color)
&& linGradBrushA.GradientStops[i].Offset.IsCloseTo(linGradBrushB.GradientStops[i].Offset);
if (!result)
{
break;
}
}
return result;
}
// Are both instances of ImageBrush
if ((brush is ImageBrush imgBrushA) && (otherBrush is ImageBrush imgBrushB))
{
var result = (imgBrushA.AlignmentX == imgBrushB.AlignmentX)
&& (imgBrushA.AlignmentY == imgBrushB.AlignmentY)
&& imgBrushA.Opacity.IsCloseTo(imgBrushB.Opacity)
&& (imgBrushA.Stretch == imgBrushB.Stretch)
&& (imgBrushA.ImageSource == imgBrushB.ImageSource);
return result;
}
return false;
}
/// <summary>
/// Compares one URI with another URI
/// </summary>
/// <param name="uri">URI to compare with</param>
/// <param name="otherUri">URI to compare</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsEqualTo(this Uri uri, Uri otherUri)
{
return
Uri.Compare(uri, otherUri, UriComponents.AbsoluteUri, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0;
}
/// <summary>
/// Reflects point 'a' over point 'b'
/// </summary>
/// <param name="a">Point to be reflected</param>
/// <param name="b">Point of reflection</param>
/// <returns>Reflected point</returns>
public static Vector2 Reflect(Vector2 a, Vector2 b)
{
// Let 'c' be the reflected point. Then point 'b'
// becomes the middle point between 'a' and 'c'.
// As per MidPoint formula,
// b.X = (a.X + c.X) / 2 and
// b.Y = (a.Y + c.Y) / 2
// Therefore, c.X = 2 * b.X - a.X
// c.y = 2 * b.Y - a.Y
return new Vector2((2f * b.X) - a.X, (2f * b.Y) - a.Y);
}
/// <summary>
/// Converts a Vector2 structure (x,y) to Vector3 structure (x, y, 0)
/// </summary>
/// <param name="v">Input Vector2</param>
/// <returns>Vector3</returns>
public static Vector3 ToVector3(this Vector2 v)
{
return new Vector3(v, 0);
}
/// <summary>
/// Verifies if the Vector4 contains only zero values
/// </summary>
/// <param name="vector">Vector4</param>
/// <returns>true if yes, otherwise false</returns>
public static bool IsZero(this Vector4 vector)
{
return vector.X.IsZero()
&& vector.Y.IsZero()
&& vector.Z.IsZero()
&& vector.W.IsZero();
}
/// <summary>
/// Useful in converting the four components
/// of Thickness or Padding to two components
/// by taking a sum of alternate components
/// (X &amp; Z and Y &amp; W).
/// </summary>
/// <param name="vector">Vector4</param>
/// <returns>Vector3</returns>
public static Vector2 Collapse(this Vector4 vector)
{
return new Vector2(vector.X + vector.Z, vector.Y + vector.W);
}
/// <summary>
/// Useful in converting the four components
/// of Thickness or Padding to two components
/// by adding alternate components -
/// (X &amp; Z and Y &amp; W).
/// </summary>
/// <param name="vector">Vector4</param>
/// <returns>Size</returns>
public static Size ToSize(this Vector4 vector)
{
return new Size(vector.X + vector.Z, vector.Y + vector.W);
}
/// <summary>
/// Converts the Vector4 to Thickness - Left(X), Top(Y), Right(Z), Bottom(W)
/// </summary>
/// <param name="vector">Vector4</param>
/// <returns>Thickness</returns>
public static Thickness ToThickness(this Vector4 vector)
{
return new Thickness(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Converts the Vector4 to CornerRadius - TopLeft(X), TopRight(Y), BottomRight(Z), BottomLeft(W)
/// </summary>
/// <param name="vector">Vector4</param>
/// <returns>CornerRadius</returns>
public static CornerRadius ToCornerRadius(this Vector4 vector)
{
return new CornerRadius(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Calculates the linear interpolated Color based on the given Color values.
/// </summary>
/// <param name="colorFrom">Source Color.</param>
/// <param name="colorTo">Target Color.</param>
/// <param name="amount">Weightage given to the target color.</param>
/// <returns>Linear Interpolated Color.</returns>
public static Color Lerp(this Color colorFrom, Color colorTo, float amount)
{
// Convert colorFrom components to lerp-able floats
float sa = colorFrom.A,
sr = colorFrom.R,
sg = colorFrom.G,
sb = colorFrom.B;
// Convert colorTo components to lerp-able floats
float ea = colorTo.A,
er = colorTo.R,
eg = colorTo.G,
eb = colorTo.B;
// lerp the colors to get the difference
byte a = (byte)Math.Max(0, Math.Min(255, sa.Lerp(ea, amount))),
r = (byte)Math.Max(0, Math.Min(255, sr.Lerp(er, amount))),
g = (byte)Math.Max(0, Math.Min(255, sg.Lerp(eg, amount))),
b = (byte)Math.Max(0, Math.Min(255, sb.Lerp(eb, amount)));
// return the new color
return Color.FromArgb(a, r, g, b);
}
/// <summary>
/// Darkens the color by the given percentage.
/// </summary>
/// <param name="color">Source color.</param>
/// <param name="amount">Percentage to darken. Value should be between 0 and 1.</param>
/// <returns>Color</returns>
public static Color DarkerBy(this Color color, float amount)
{
return color.Lerp(Colors.Black, amount);
}
/// <summary>
/// Lightens the color by the given percentage.
/// </summary>
/// <param name="color">Source color.</param>
/// <param name="amount">Percentage to lighten. Value should be between 0 and 1.</param>
/// <returns>Color</returns>
public static Color LighterBy(this Color color, float amount)
{
return color.Lerp(Colors.White, amount);
}
/// <summary>
/// Converts the Point structure P (X,Y) to Vector3 structure
/// V (P.X, P.Y, 0);
/// </summary>
/// <param name="p">Point structure</param>
/// <returns>Vector3</returns>
public static Vector3 ToVector3(this Point p)
{
return new Vector3((float)p.X, (float)p.Y, 0f);
}
/// <summary>
/// Calculates the best size that can fit in the destination area based on the given stretch and
/// alignment options.
/// </summary>
/// <param name="srcWidth">Width of the source.</param>
/// <param name="srcHeight">Height of the source.</param>
/// <param name="destWidth">Width of the destination area.</param>
/// <param name="destHeight">Height of the destination area.</param>
/// <param name="stretch">Defines how the source should stretch to fit the destination.</param>
/// <param name="horizontalAlignment">Horizontal Alignment</param>
/// <param name="verticalAlignment">Vertical Alignment</param>
/// <returns>The best fitting Rectangle in the destination area.</returns>
public static Rect GetOptimumSize(double srcWidth, double srcHeight, double destWidth, double destHeight, Stretch stretch, AlignmentX horizontalAlignment, AlignmentY verticalAlignment)
{
var ratio = srcWidth / srcHeight;
var targetWidth = 0d;
var targetHeight = 0d;
// Stretch Mode
switch (stretch)
{
case Stretch.None:
targetWidth = srcWidth;
targetHeight = srcHeight;
break;
case Stretch.Fill:
targetWidth = destWidth;
targetHeight = destHeight;
break;
case Stretch.Uniform:
// If width is greater than height
if (ratio > 1.0)
{
targetHeight = Math.Min(destWidth / ratio, destHeight);
targetWidth = targetHeight * ratio;
}
else
{
targetWidth = Math.Min(destHeight * ratio, destWidth);
targetHeight = targetWidth / ratio;
}
break;
case Stretch.UniformToFill:
// If width is greater than height
if (ratio > 1.0)
{
targetHeight = Math.Max(destWidth / ratio, destHeight);
targetWidth = targetHeight * ratio;
}
else
{
targetWidth = Math.Max(destHeight * ratio, destWidth);
targetHeight = targetWidth / ratio;
}
break;
}
var left = 0d;
switch (horizontalAlignment)
{
case AlignmentX.Left:
left = 0;
break;
case AlignmentX.Center:
left = (destWidth - targetWidth) / 2.0;
break;
case AlignmentX.Right:
left = destWidth - targetWidth;
break;
}
var top = 0d;
switch (verticalAlignment)
{
case AlignmentY.Top:
top = 0;
break;
case AlignmentY.Center:
top = (destHeight - targetHeight) / 2.0;
break;
case AlignmentY.Bottom:
top = destHeight - targetHeight;
break;
}
return new Rect(left, top, targetWidth, targetHeight);
}
}
}