Added the Win2d Path Geometry parser.
This commit is contained in:
Родитель
49a88c7d57
Коммит
cec5bd67a5
|
@ -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>
|
||||
|
|
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png
Normal file
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.5 KiB |
|
@ -0,0 +1,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 & 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 & 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 & 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 & 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 & Z and Y & 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 & Z and Y & 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);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче