Browse Source

[SL.Core] initial primitive

af/octree-no-pixelmap
Scott Williams 9 years ago
parent
commit
0d277b8b5d
  1. 3
      .editorconfig
  2. 6
      SixLabors.Primitives.ruleset
  3. 71
      SixLabors.Primitives.sln
  4. 5
      SixLabors.Primitives.sln.DotSettings
  5. 31
      appveyor.yml
  6. 38
      build.cmd
  7. 21
      codecov.yml
  8. 13
      gitversion.yml
  9. 38
      src/Shared/AssemblyInfo.Common.cs
  10. 9
      src/Shared/stylecop.json
  11. 18
      src/SixLabors.Primitives/Constants.cs
  12. 183
      src/SixLabors.Primitives/Ellipse.cs
  13. 355
      src/SixLabors.Primitives/LongRational.cs
  14. 293
      src/SixLabors.Primitives/MathF.cs
  15. 360
      src/SixLabors.Primitives/Matrix.cs
  16. 257
      src/SixLabors.Primitives/Point.cs
  17. 233
      src/SixLabors.Primitives/PointF.cs
  18. 189
      src/SixLabors.Primitives/Rational.cs
  19. 467
      src/SixLabors.Primitives/Rectangle.cs
  20. 400
      src/SixLabors.Primitives/RectangleF.cs
  21. 189
      src/SixLabors.Primitives/SignedRational.cs
  22. 40
      src/SixLabors.Primitives/SixLabors.Primitives.csproj
  23. 225
      src/SixLabors.Primitives/Size.cs
  24. 190
      src/SixLabors.Primitives/SizeF.cs
  25. 9
      src/SixLabors.Primitives/stylecop.json
  26. 21
      tests/CodeCoverage/CodeCoverage.cmd
  27. 166
      tests/CodeCoverage/OpenCover.4.6.519/License.rtf
  28. BIN
      tests/CodeCoverage/OpenCover.4.6.519/MSBuild/OpenCover.MSBuild.dll
  29. 10
      tests/CodeCoverage/OpenCover.4.6.519/MSBuild/OpenCover.targets
  30. 6
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/.nuget/packages.config
  31. 53
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/Bom/Bom.csproj
  32. 17
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/Bom/BomManager.cs
  33. 36
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/Bom/Properties/AssemblyInfo.cs
  34. 31
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomSample.sln
  35. 39
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/BomManagerTests.cs
  36. 65
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/BomTest.csproj
  37. 36
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/Properties/AssemblyInfo.cs
  38. 4
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/packages.config
  39. 5
      tests/CodeCoverage/OpenCover.4.6.519/SampleSln/coverage.bat
  40. 137
      tests/CodeCoverage/OpenCover.4.6.519/docs/ReleaseNotes.txt
  41. 1138
      tests/CodeCoverage/OpenCover.4.6.519/docs/Usage.rtf
  42. 21
      tests/CodeCoverage/OpenCover.4.6.519/readme.txt
  43. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Autofac.Configuration.dll
  44. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Autofac.dll
  45. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/CrashReporter.NET.dll
  46. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Gendarme.Framework.dll
  47. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Gendarme.Rules.Maintainability.dll
  48. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Mono.Cecil.Mdb.dll
  49. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Mono.Cecil.Pdb.dll
  50. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/Mono.Cecil.dll
  51. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Console.exe
  52. 26
      tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Console.exe.config
  53. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Extensions.dll
  54. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Framework.dll
  55. 39
      tests/CodeCoverage/OpenCover.4.6.519/tools/log4net.config
  56. BIN
      tests/CodeCoverage/OpenCover.4.6.519/tools/log4net.dll
  57. 332
      tests/CodeCoverage/OpenCover.4.6.519/transform/simple_report.xslt
  58. 17
      tests/CodeCoverage/OpenCover.4.6.519/transform/transform.ps1
  59. 4
      tests/CodeCoverage/packages.config
  60. 192
      tests/SixLabors.Primitives.Tests/PointFTests.cs
  61. 252
      tests/SixLabors.Primitives.Tests/PointTests.cs
  62. 115
      tests/SixLabors.Primitives.Tests/RationalTests.cs
  63. 267
      tests/SixLabors.Primitives.Tests/RectangleFTests.cs
  64. 308
      tests/SixLabors.Primitives.Tests/RectangleTests.cs
  65. 122
      tests/SixLabors.Primitives.Tests/SignedRationalTests.cs
  66. 34
      tests/SixLabors.Primitives.Tests/SixLabors.Primitives.Tests.csproj
  67. 163
      tests/SixLabors.Primitives.Tests/SizeFTests.cs
  68. 195
      tests/SixLabors.Primitives.Tests/SizeTests.cs

3
.editorconfig

@ -0,0 +1,3 @@
[*.cs]
indent_style = space
indent_size = 4

6
SixLabors.Primitives.ruleset

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Shaper2D" ToolsVersion="14.0">
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1413" Action="None" />
</Rules>
</RuleSet>

71
SixLabors.Primitives.sln

@ -0,0 +1,71 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
appveyor.yml = appveyor.yml
build.cmd = build.cmd
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E574A07-F879-4811-9C41-5CBDC6BAFDB7}"
ProjectSection(SolutionItems) = preProject
src\Shared\AssemblyInfo.Common.cs = src\Shared\AssemblyInfo.Common.cs
src\Shared\stylecop.json = src\Shared\stylecop.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9F33164A-9EA9-4CB4-A384-A8A0A6DCA35D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SixLabors.Primitives", "src\SixLabors.Primitives\SixLabors.Primitives.csproj", "{09E744EC-4852-4FC7-BE78-C1B399F17967}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SixLabors.Primitives.Tests", "tests\SixLabors.Primitives.Tests\SixLabors.Primitives.Tests.csproj", "{F836E8E6-B4D9-4208-8346-140C74678B91}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{09E744EC-4852-4FC7-BE78-C1B399F17967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09E744EC-4852-4FC7-BE78-C1B399F17967}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09E744EC-4852-4FC7-BE78-C1B399F17967}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09E744EC-4852-4FC7-BE78-C1B399F17967}.Release|Any CPU.Build.0 = Release|Any CPU
{F836E8E6-B4D9-4208-8346-140C74678B91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F836E8E6-B4D9-4208-8346-140C74678B91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F836E8E6-B4D9-4208-8346-140C74678B91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F836E8E6-B4D9-4208-8346-140C74678B91}.Release|Any CPU.Build.0 = Release|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Release|Any CPU.Build.0 = Release|Any CPU
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Release|Any CPU.Build.0 = Release|Any CPU
{32D7A12E-B392-42CE-8EFB-1B685680F5B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32D7A12E-B392-42CE-8EFB-1B685680F5B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32D7A12E-B392-42CE-8EFB-1B685680F5B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32D7A12E-B392-42CE-8EFB-1B685680F5B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{09E744EC-4852-4FC7-BE78-C1B399F17967} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{999EDFB3-9FE4-4E09-B669-CB02E597EC20} = {9F33164A-9EA9-4CB4-A384-A8A0A6DCA35D}
{87E262FA-57FE-4AA7-853C-9DD91E769D4B} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{32D7A12E-B392-42CE-8EFB-1B685680F5B8} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal

5
SixLabors.Primitives.sln.DotSettings

@ -0,0 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StyleCop_002ESA1200/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StyleCop_002ESA1201/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StyleCop_002ESA1401/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StyleCop_002ESA1600/@EntryIndexedValue">DO_NOT_SHOW</s:String></wpf:ResourceDictionary>

31
appveyor.yml

@ -0,0 +1,31 @@
version: 0.0.{build}
image: Visual Studio 2017
install:
- choco install gitversion.portable -pre -y
before_build:
- cmd: dotnet --version
- ps: gitversion /l console /output buildserver
build_script:
- cmd: build.cmd
- cmd: tests\CodeCoverage\CodeCoverage.cmd
after_build:
- cmd: appveyor PushArtifact "artifacts\SixLabors.Primitives.%GitVersion_NuGetVersion%.nupkg"
deploy:
- provider: NuGet
server: https://www.myget.org/F/sixlabors/api/v2/package
symbol_server: https://www.myget.org/F/sixlabors/symbols/api/v2/package
api_key:
secure: SyrSERGrjkK21TSCsHtqke5279SMxXCg2NXKjR2qaErP0khEplwxPwE8Ch5bxzyf
artifact: /.*\.nupkg/
on:
branch: master
# prevent the double build when a branch has an active PR
skip_branch_with_pr: true
test: off

38
build.cmd

@ -0,0 +1,38 @@
@echo Off
if not "%GitVersion_NuGetVersion%" == "" (
dotnet restore /p:packageversion=%GitVersion_NuGetVersion%
)ELSE (
dotnet restore
)
ECHO Building nuget packages
if not "%GitVersion_NuGetVersion%" == "" (
dotnet build -c Release /p:packageversion=%GitVersion_NuGetVersion%
)ELSE (
dotnet build -c Release
)
if not "%errorlevel%"=="0" goto failure
dotnet test ./tests/SixLabors.Primitives.Tests/SixLabors.Primitives.Tests.csproj
if not "%GitVersion_NuGetVersion%" == "" (
dotnet pack ./src/SixLabors.Primitives/ -c Release --output ../../artifacts --no-build /p:packageversion=%GitVersion_NuGetVersion%
)ELSE (
dotnet pack ./src/SixLabors.Primitives/ -c Release --output ../../artifacts --no-build
)
if not "%errorlevel%"=="0" goto failure
:success
ECHO successfully built project
REM exit 0
goto end
:failure
ECHO failed to build.
REM exit -1
goto end
:end

21
codecov.yml

@ -0,0 +1,21 @@
codecov:
notify:
require_ci_to_pass: true
comment: off
coverage:
precision: 2
range:
- 70.0
- 100.0
round: down
status:
changes: false
patch: true
project: true
parsers:
gcov:
branch_detection:
conditional: true
loop: true
macro: false
method: false

13
gitversion.yml

@ -0,0 +1,13 @@
# to create a new package you create a new release/tag
# in github appveyor will build it without the -cixxx tag
# it will then be deployable cleanly to nuget.org
branches:
master:
tag: ci
mode: ContinuousDeployment
increment: Minor
prevent-increment-of-merged-branch-version: false
track-merge-target: true
ignore:
sha: []

38
src/Shared/AssemblyInfo.Common.cs

@ -0,0 +1,38 @@
// <copyright file="AssemblyInfo.Common.cs" company="Scott Williams">
// Copyright (c) Scott Williams and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyDescription("A cross-platform library for processing of image files; written in C#")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Scott Williams")]
[assembly: AssemblyProduct("SixLabors.Shapes")]
[assembly: AssemblyCopyright("Copyright (c) Scott Williams and contributors.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]
// Ensure the internals can be tested.
[assembly: InternalsVisibleTo("SixLabors.Shapes.Tests")]
[assembly: InternalsVisibleTo("SixLabors.Shapes.Benchmarks")]

9
src/Shared/stylecop.json

@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"documentationRules": {
"companyName": "Scott Williams",
"copyrightText": "Copyright (c) Scott Williams and contributors.\nLicensed under the Apache License, Version 2.0."
}
}
}

18
src/SixLabors.Primitives/Constants.cs

@ -0,0 +1,18 @@
// <copyright file="Constants.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
/// <summary>
/// Common constants used throughout the project
/// </summary>
internal static class Constants
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
public static readonly float Epsilon = 0.001f;
}
}

183
src/SixLabors.Primitives/Ellipse.cs

@ -0,0 +1,183 @@
// <copyright file="Ellipse.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an ellipse.
/// </summary>
public struct Ellipse : IEquatable<Ellipse>
{
/// <summary>
/// Represents a <see cref="Ellipse"/> that has X and Y values set to zero.
/// </summary>
public static readonly Ellipse Empty = default(Ellipse);
/// <summary>
/// The center point.
/// </summary>
private Point center;
/// <summary>
/// Initializes a new instance of the <see cref="Ellipse"/> struct.
/// </summary>
/// <param name="center">The center point.</param>
/// <param name="radiusX">The x-radius.</param>
/// <param name="radiusY">The y-radius.</param>
public Ellipse(Point center, float radiusX, float radiusY)
{
this.center = center;
this.RadiusX = radiusX;
this.RadiusY = radiusY;
}
/// <summary>
/// Gets the x-radius of this <see cref="Ellipse"/>.
/// </summary>
public float RadiusX { get; }
/// <summary>
/// Gets the y-radius of this <see cref="Ellipse"/>.
/// </summary>
public float RadiusY { get; }
/// <summary>
/// Gets a value indicating whether this <see cref="Ellipse"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Compares two <see cref="Ellipse"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Ellipse"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Ellipse"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Ellipse left, Ellipse right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Ellipse"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Ellipse"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Ellipse"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Ellipse left, Ellipse right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns the center point of the given <see cref="Ellipse"/>
/// </summary>
/// <param name="ellipse">The ellipse</param>
/// <returns><see cref="Vector2"/></returns>
public static Vector2 Center(Ellipse ellipse)
{
return new Vector2(ellipse.center.X, ellipse.center.Y);
}
/// <summary>
/// Determines if the specfied point is contained within the rectangular region defined by
/// this <see cref="Ellipse"/>.
/// </summary>
/// <param name="x">The x-coordinate of the given point.</param>
/// <param name="y">The y-coordinate of the given point.</param>
/// <returns>The <see cref="bool"/></returns>
public bool Contains(int x, int y)
{
if (this.RadiusX <= 0 || this.RadiusY <= 0)
{
return false;
}
// TODO: SIMD?
// This is a more general form of the circle equation
// X^2/a^2 + Y^2/b^2 <= 1
Point normalized = new Point(x - this.center.X, y - this.center.Y);
int nX = normalized.X;
int nY = normalized.Y;
return ((double)(nX * nX) / (this.RadiusX * this.RadiusX))
+ ((double)(nY * nY) / (this.RadiusY * this.RadiusY))
<= 1.0;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Ellipse [ Empty ]";
}
return
$"Ellipse [ RadiusX={this.RadiusX}, RadiusY={this.RadiusX}, Centre={this.center.X},{this.center.Y} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Ellipse)
{
return this.Equals((Ellipse)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(Ellipse other)
{
return this.center.Equals(other.center)
&& this.RadiusX.Equals(other.RadiusX)
&& this.RadiusY.Equals(other.RadiusY);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="ellipse">
/// The instance of <see cref="Point"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Ellipse ellipse)
{
unchecked
{
int hashCode = ellipse.center.GetHashCode();
hashCode = (hashCode * 397) ^ ellipse.RadiusX.GetHashCode();
hashCode = (hashCode * 397) ^ ellipse.RadiusY.GetHashCode();
return hashCode;
}
}
}
}

355
src/SixLabors.Primitives/LongRational.cs

@ -0,0 +1,355 @@
// <copyright file="LongRational.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.Globalization;
using System.Text;
/// <summary>
/// Represents a number that can be expressed as a fraction
/// </summary>
/// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks>
internal struct LongRational : IEquatable<LongRational>
{
/// <summary>
/// Initializes a new instance of the <see cref="LongRational"/> struct.
/// </summary>
/// <param name="numerator">
/// The number above the line in a vulgar fraction showing how many of the parts
/// indicated by the denominator are taken.
/// </param>
/// <param name="denominator">
/// The number below the line in a vulgar fraction; a divisor.
/// </param>
public LongRational(long numerator, long denominator)
: this(numerator, denominator, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LongRational"/> struct.
/// </summary>
/// <param name="numerator">
/// The number above the line in a vulgar fraction showing how many of the parts
/// indicated by the denominator are taken.
/// </param>
/// <param name="denominator">
/// The number below the line in a vulgar fraction; a divisor.
/// </param>
/// <param name="simplify">
/// Whether to attempt to simplify the fractional parts.
/// </param>
public LongRational(long numerator, long denominator, bool simplify)
: this()
{
this.Numerator = numerator;
this.Denominator = denominator;
if (simplify)
{
this.Simplify();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="LongRational"/> struct.
/// </summary>
/// <param name="value">The <see cref="double"/> to create the instance from.</param>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public LongRational(double value, bool bestPrecision)
: this()
{
if (double.IsNaN(value))
{
this.Numerator = this.Denominator = 0;
return;
}
if (double.IsPositiveInfinity(value))
{
this.Numerator = 1;
this.Denominator = 0;
return;
}
if (double.IsNegativeInfinity(value))
{
this.Numerator = -1;
this.Denominator = 0;
return;
}
this.Numerator = 1;
this.Denominator = 1;
double val = Math.Abs(value);
double df = this.Numerator / (double)this.Denominator;
double epsilon = bestPrecision ? double.Epsilon : .000001;
while (Math.Abs(df - val) > epsilon)
{
if (df < val)
{
this.Numerator++;
}
else
{
this.Denominator++;
this.Numerator = (int)(val * this.Denominator);
}
df = this.Numerator / (double)this.Denominator;
}
if (value < 0.0)
{
this.Numerator *= -1;
}
this.Simplify();
}
/// <summary>
/// Gets the numerator of a number.
/// </summary>
public long Numerator
{
get;
private set;
}
/// <summary>
/// Gets the denominator of a number.
/// </summary>
public long Denominator
{
get;
private set;
}
/// <summary>
/// Gets a value indicating whether this instance is indeterminate.
/// </summary>
public bool IsIndeterminate
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == 0;
}
}
/// <summary>
/// Gets a value indicating whether this instance is an integer (n, 1)
/// </summary>
public bool IsInteger => this.Denominator == 1;
/// <summary>
/// Gets a value indicating whether this instance is equal to negative infinity (-1, 0)
/// </summary>
public bool IsNegativeInfinity
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == -1;
}
}
/// <summary>
/// Gets a value indicating whether this instance is equal to positive infinity (1, 0)
/// </summary>
public bool IsPositiveInfinity
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == 1;
}
}
/// <summary>
/// Gets a value indicating whether this instance is equal to 0 (0, 1)
/// </summary>
public bool IsZero
{
get
{
if (this.Denominator != 1)
{
return false;
}
return this.Numerator == 0;
}
}
/// <inheritdoc/>
public bool Equals(LongRational other)
{
if (this.Denominator == other.Denominator)
{
return this.Numerator == other.Numerator;
}
if (this.Numerator == 0 && this.Denominator == 0)
{
return other.Numerator == 0 && other.Denominator == 0;
}
if (other.Numerator == 0 && other.Denominator == 0)
{
return this.Numerator == 0 && this.Denominator == 0;
}
return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
return this.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts the numeric value of this instance to its equivalent string representation using
/// the specified culture-specific format information.
/// </summary>
/// <param name="provider">
/// An object that supplies culture-specific formatting information.
/// </param>
/// <returns>The <see cref="string"/></returns>
public string ToString(IFormatProvider provider)
{
if (this.IsIndeterminate)
{
return "[ Indeterminate ]";
}
if (this.IsPositiveInfinity)
{
return "[ PositiveInfinity ]";
}
if (this.IsNegativeInfinity)
{
return "[ NegativeInfinity ]";
}
if (this.IsZero)
{
return "0";
}
if (this.IsInteger)
{
return this.Numerator.ToString(provider);
}
StringBuilder sb = new StringBuilder();
sb.Append(this.Numerator.ToString(provider));
sb.Append("/");
sb.Append(this.Denominator.ToString(provider));
return sb.ToString();
}
/// <summary>
/// Finds the greatest common divisor of two <see cref="long"/> values.
/// </summary>
/// <param name="left">The first value</param>
/// <param name="right">The second value</param>
/// <returns>The <see cref="long"/></returns>
private static long GreatestCommonDivisor(long left, long right)
{
return right == 0 ? left : GreatestCommonDivisor(right, left % right);
}
/// <summary>
/// Simplifies the <see cref="LongRational"/>
/// </summary>
private void Simplify()
{
if (this.IsIndeterminate)
{
return;
}
if (this.IsNegativeInfinity)
{
return;
}
if (this.IsPositiveInfinity)
{
return;
}
if (this.IsInteger)
{
return;
}
if (this.IsZero)
{
return;
}
if (this.Numerator == 0)
{
this.Denominator = 0;
return;
}
if (this.Numerator == this.Denominator)
{
this.Numerator = 1;
this.Denominator = 1;
}
long gcd = GreatestCommonDivisor(Math.Abs(this.Numerator), Math.Abs(this.Denominator));
if (gcd > 1)
{
this.Numerator = this.Numerator / gcd;
this.Denominator = this.Denominator / gcd;
}
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="rational">
/// The instance of <see cref="LongRational"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(LongRational rational)
{
return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode();
}
}
}

293
src/SixLabors.Primitives/MathF.cs

@ -0,0 +1,293 @@
// <copyright file="MathF.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Provides single-precision floating point constants and static methods for trigonometric, logarithmic, and other common mathematical functions.
/// </summary>
// ReSharper disable InconsistentNaming
internal static class MathF
{
/// <summary>
/// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π.
/// </summary>
public const float PI = (float)Math.PI;
/// <summary>
/// Returns the absolute value of a single-precision floating-point number.
/// </summary>
/// <param name="f">
/// A number that is greater than or equal to <see cref="F:System.Single.MinValue" />, but less than or equal to <see cref="F:System.Single.MaxValue" />.
/// </param>
/// <returns>
/// A single-precision floating-point number, x, such that 0 ≤ x ≤<see cref="F:System.Single.MaxValue" />.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Abs(float f)
{
return Math.Abs(f);
}
/// <summary>
/// Returns the angle whose tangent is the quotient of two specified numbers.
/// </summary>
/// <param name="y">The y coordinate of a point.</param>
/// <param name="x">The x coordinate of a point.</param>
/// <returns>
/// An angle, θ, measured in radians, such that -π≤θ≤π, and tan(θ) = y / x, where
/// (x, y) is a point in the Cartesian plane. Observe the following: For (x, y) in
/// quadrant 1, 0 &lt; θ &lt; π/2.For (x, y) in quadrant 2, π/2 &lt; θ≤π.For (x, y) in quadrant
/// 3, -π &lt; θ &lt; -π/2.For (x, y) in quadrant 4, -π/2 &lt; θ &lt; 0.For points on the boundaries
/// of the quadrants, the return value is the following:If y is 0 and x is not negative,
/// θ = 0.If y is 0 and x is negative, θ = π.If y is positive and x is 0, θ = π/2.If
/// y is negative and x is 0, θ = -π/2.If y is 0 and x is 0, θ = 0. If x or y is
/// <see cref="F:System.Single.NaN"/>, or if x and y are either <see cref="F:System.Single.PositiveInfinity"/> or
/// <see cref="F:System.Single.NegativeInfinity"/>, the method returns <see cref="F:System.Single.NaN"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Atan2(float y, float x)
{
return (float)Math.Atan2(y, x);
}
/// <summary>
/// Returns the smallest integral value that is greater than or equal to the specified single-precision floating-point number.
/// </summary>
/// <param name="f">A single-precision floating-point number.</param>
/// <returns>
/// The smallest integral value that is greater than or equal to <paramref name="f" />.
/// If <paramref name="f" /> is equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NegativeInfinity" />,
/// or <see cref="F:System.Single.PositiveInfinity" />, that value is returned.
/// Note that this method returns a <see cref="T:System.Single" /> instead of an integral type.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Ceiling(float f)
{
return (float)Math.Ceiling(f);
}
/// <summary>
/// Returns the cosine of the specified angle.
/// </summary>
/// <param name="f">An angle, measured in radians.</param>
/// <returns>
/// The cosine of <paramref name="f"/>. If <paramref name="f"/> is equal to <see cref="F:System.Float.NaN"/>, <see cref="F:System.Float.NegativeInfinity"/>,
/// or <see cref="F:System.Float.PositiveInfinity"/>, this method returns <see cref="F:System.Float.NaN"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Cos(float f)
{
return (float)Math.Cos(f);
}
/// <summary>
/// Converts a degree (360-periodic) angle to a radian (2*Pi-periodic) angle.
/// </summary>
/// <param name="degree">The angle in degrees.</param>
/// <returns>
/// The <see cref="float"/> representing the degree as radians.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DegreeToRadian(float degree)
{
return degree * (PI / 180F);
}
/// <summary>
/// Returns e raised to the specified power.
/// </summary>
/// <param name="f">A number specifying a power.</param>
/// <returns>
/// The number e raised to the power <paramref name="f" />.
/// If <paramref name="f" /> equals <see cref="F:System.Single.NaN" /> or <see cref="F:System.Single.PositiveInfinity" />, that value is returned.
/// If <paramref name="f" /> equals <see cref="F:System.Single.NegativeInfinity" />, 0 is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Exp(float f)
{
return (float)Math.Exp(f);
}
/// <summary>
/// Returns the largest integer less than or equal to the specified single-precision floating-point number.
/// </summary>
/// <param name="f">A single-precision floating-point number. </param>
/// <returns>
/// The largest integer less than or equal to <paramref name="f" />.
/// If <paramref name="f" /> is equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NegativeInfinity" />,
/// or <see cref="F:System.Single.PositiveInfinity" />, that value is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Floor(float f)
{
return (float)Math.Floor(f);
}
/// <summary>
/// Returns the larger of two single-precision floating-point numbers.
/// </summary>
/// <param name="val1">The first of two single-precision floating-point numbers to compare. </param>
/// <param name="val2">The second of two single-precision floating-point numbers to compare. </param>
/// <returns>
/// Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is larger.
/// If <paramref name="val1" />, or <paramref name="val2" />, or both <paramref name="val1" /> and <paramref name="val2" /> are
/// equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NaN" /> is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Max(float val1, float val2)
{
return Math.Max(val1, val2);
}
/// <summary>
/// Returns the smaller of two single-precision floating-point numbers.
/// </summary>
/// <param name="val1">The first of two single-precision floating-point numbers to compare. </param>
/// <param name="val2">The second of two single-precision floating-point numbers to compare. </param>
/// <returns>
/// Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is smaller.
/// If <paramref name="val1" />, <paramref name="val2" />, or both <paramref name="val1" /> and <paramref name="val2" /> are equal
/// to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NaN" /> is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Min(float val1, float val2)
{
return Math.Min(val1, val2);
}
/// <summary>
/// Returns a specified number raised to the specified power.
/// </summary>
/// <param name="x">A single-precision floating-point number to be raised to a power. </param>
/// <param name="y">A single-precision floating-point number that specifies a power. </param>
/// <returns>The number <paramref name="x" /> raised to the power <paramref name="y" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Pow(float x, float y)
{
return (float)Math.Pow(x, y);
}
/// <summary>
/// Converts a radian (2*Pi-periodic) angle to a degree (360-periodic) angle.
/// </summary>
/// <param name="radian">The angle in radians.</param>
/// <returns>
/// The <see cref="float"/> representing the degree as radians.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float RadianToDegree(float radian)
{
return radian / (PI / 180F);
}
/// <summary>
/// Rounds a single-precision floating-point value to the nearest integral value.
/// </summary>
/// <param name="f">A single-precision floating-point number to be rounded.</param>
/// <returns>
/// The integer nearest <paramref name="f" />.
/// If the fractional component of <paramref name="f" /> is halfway between two integers, one of which is even and the other odd, then the even number is returned.
/// Note that this method returns a <see cref="T:System.Single" /> instead of an integral type.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Round(float f)
{
return (float)Math.Round(f);
}
/// <summary>
/// Rounds a single-precision floating-point value to the nearest integer.
/// A parameter specifies how to round the value if it is midway between two numbers.
/// </summary>
/// <param name="f">A single-precision floating-point number to be rounded. </param>
/// <param name="mode">Specification for how to round <paramref name="f" /> if it is midway between two other numbers.</param>
/// <returns>
/// The integer nearest <paramref name="f" />. If <paramref name="f" /> is halfway between two integers, one of which is even
/// and the other odd, then <paramref name="mode" /> determines which of the two is returned.
/// Note that this method returns a <see cref="T:System.Single" /> instead of an integral type.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// <paramref name="mode" /> is not a valid value of <see cref="T:System.MidpointRounding" />.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Round(float f, MidpointRounding mode)
{
return (float)Math.Round(f, mode);
}
/// <summary>
/// Returns the sine of the specified angle.
/// </summary>
/// <param name="f">An angle, measured in radians.</param>
/// <returns>
/// The sine of <paramref name="f" />.
/// If <paramref name="f" /> is equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NegativeInfinity" />,
/// or <see cref="F:System.Single.PositiveInfinity" />, this method returns <see cref="F:System.Single.NaN" />.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Sin(float f)
{
return (float)Math.Sin(f);
}
/// <summary>
/// Returns the result of a normalized sine cardinal function for the given value.
/// SinC(x) = sin(pi*x)/(pi*x).
/// </summary>
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
/// <returns>
/// The sine cardinal of <paramref name="f" />.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float SinC(float f)
{
if (Abs(f) > Constants.Epsilon)
{
f *= PI;
return Clean(Sin(f) / f);
}
return 1F;
}
/// <summary>
/// Returns the square root of a specified number.
/// </summary>
/// <param name="f">The number whose square root is to be found.</param>
/// <returns>
/// One of the values in the following table.
/// <paramref name="f" /> parameter Return value Zero or positive The positive square root of <paramref name="f" />.
/// Negative <see cref="F:System.Single.NaN" />Equals <see cref="F:System.Single.NaN" />
/// <see cref="F:System.Single.NaN" />Equals <see cref="F:System.Single.PositiveInfinity" />
/// <see cref="F:System.Single.PositiveInfinity" />
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Sqrt(float f)
{
return (float)Math.Sqrt(f);
}
/// <summary>
/// Ensures that any passed float is correctly rounded to zero
/// </summary>
/// <param name="x">The value to clean.</param>
/// <returns>
/// The <see cref="float"/>
/// </returns>.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Clean(float x)
{
if (Abs(x) < Constants.Epsilon)
{
return 0F;
}
return x;
}
}
}

360
src/SixLabors.Primitives/Matrix.cs

@ -0,0 +1,360 @@
// <copyright file="Matrix.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// A Matrix object for applying matrix transforms to primitives.
/// </summary>
public struct Matrix : IEquatable<Matrix>
{
private Matrix3x2 backingMatrix;
private static readonly Matrix _identity = new Matrix
{
backingMatrix = Matrix3x2.Identity
};
/// <summary>
/// Returns the multiplicative identity matrix.
/// </summary>
public static Matrix Identity => _identity;
/// <summary>
/// Returns whether the matrix is the identity matrix.
/// </summary>
public bool IsIdentity => this.backingMatrix.IsIdentity;
/// <summary>
/// Gets or sets the translation component of this matrix.
/// </summary>
public Vector2 Translation => this.backingMatrix.Translation;
/// <summary>
/// Constructs a Matrix3x2 from the given components.
/// </summary>
public Matrix(float m11, float m12,
float m21, float m22,
float m31, float m32)
{
this.backingMatrix = new Matrix3x2(m11, m12, m21, m22, m31, m32);
}
/// <summary>
/// Creates a translation matrix from the given vector.
/// </summary>
/// <param name="position">The translation position.</param>
/// <returns>A translation matrix.</returns>
public static Matrix CreateTranslation(PointF position) => Matrix3x2.CreateTranslation(position);
/// <summary>
/// Creates a translation matrix from the given X and Y components.
/// </summary>
/// <param name="xPosition">The X position.</param>
/// <param name="yPosition">The Y position.</param>
/// <returns>A translation matrix.</returns>
public static Matrix CreateTranslation(float xPosition, float yPosition) => Matrix3x2.CreateTranslation(xPosition, yPosition);
/// <summary>
/// Creates a scale matrix from the given X and Y components.
/// </summary>
/// <param name="xScale">Value to scale by on the X-axis.</param>
/// <param name="yScale">Value to scale by on the Y-axis.</param>
/// <returns>A scaling matrix.</returns>
public static Matrix CreateScale(float xScale, float yScale) => Matrix3x2.CreateScale(xScale, yScale);
/// <summary>
/// Creates a scale matrix that is offset by a given center point.
/// </summary>
/// <param name="xScale">Value to scale by on the X-axis.</param>
/// <param name="yScale">Value to scale by on the Y-axis.</param>
/// <param name="centerPoint">The center point.</param>
/// <returns>A scaling matrix.</returns>
public static Matrix CreateScale(float xScale, float yScale, PointF centerPoint) => Matrix3x2.CreateScale(xScale, yScale, centerPoint);
/// <summary>
/// Creates a scale matrix from the given vector scale.
/// </summary>
/// <param name="scales">The scale to use.</param>
/// <returns>A scaling matrix.</returns>
public static Matrix CreateScale(SizeF scales) => Matrix3x2.CreateScale(scales);
/// <summary>
/// Creates a scale matrix from the given vector scale with an offset from the given center point.
/// </summary>
/// <param name="scales">The scale to use.</param>
/// <param name="centerPoint">The center offset.</param>
/// <returns>A scaling matrix.</returns>
public static Matrix CreateScale(SizeF scales, PointF centerPoint) => Matrix3x2.CreateScale(scales, centerPoint);
/// <summary>
/// Creates a scale matrix that scales uniformly with the given scale.
/// </summary>
/// <param name="scale">The uniform scale to use.</param>
/// <returns>A scaling matrix.</returns>
public static Matrix CreateScale(float scale) => Matrix3x2.CreateScale(scale);
/// <summary>
/// Creates a scale matrix that scales uniformly with the given scale with an offset from the given center.
/// </summary>
/// <param name="scale">The uniform scale to use.</param>
/// <param name="centerPoint">The center offset.</param>
/// <returns>A scaling matrix.</returns>
public static Matrix CreateScale(float scale, PointF centerPoint) => Matrix3x2.CreateScale(scale, centerPoint);
/// <summary>
/// Creates a skew matrix from the given angles in radians.
/// </summary>
/// <param name="radiansX">The X angle, in radians.</param>
/// <param name="radiansY">The Y angle, in radians.</param>
/// <returns>A skew matrix.</returns>
public static Matrix CreateSkew(float radiansX, float radiansY) => Matrix3x2.CreateSkew(radiansX, radiansY);
/// <summary>
/// Creates a skew matrix from the given angles in radians.
/// </summary>
/// <param name="degreesX">The X angle, in degrees.</param>
/// <param name="degreesY">The Y angle, in degrees.</param>
/// <returns>A skew matrix.</returns>
public static Matrix CreateSkewDegrees(float degreesX, float degreesY) => Matrix3x2.CreateSkew(MathF.DegreeToRadian(degreesX), MathF.DegreeToRadian(degreesY));
/// <summary>
/// Creates a skew matrix from the given angles in radians and a center point.
/// </summary>
/// <param name="radiansX">The X angle, in radians.</param>
/// <param name="radiansY">The Y angle, in radians.</param>
/// <param name="centerPoint">The center point.</param>
/// <returns>A skew matrix.</returns>
public static Matrix CreateSkew(float radiansX, float radiansY, PointF centerPoint) => Matrix3x2.CreateSkew(radiansX, radiansY, centerPoint);
/// <summary>
/// Creates a skew matrix from the given angles in radians and a center point.
/// </summary>
/// <param name="degreesX">The X angle, in degrees.</param>
/// <param name="degreesY">The Y angle, in degrees.</param>
/// <param name="centerPoint">The center point.</param>
/// <returns>A skew matrix.</returns>
public static Matrix CreateSkewDegrees(float degreesX, float degreesY, PointF centerPoint) => Matrix3x2.CreateSkew(MathF.DegreeToRadian(degreesX), MathF.DegreeToRadian(degreesY), centerPoint);
/// <summary>
/// Creates a rotation matrix using the given rotation in radians.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>A rotation matrix.</returns>
public static Matrix CreateRotation(float radians) => System.Numerics.Matrix3x2.CreateRotation(radians);
/// <summary>
/// Creates a rotation matrix using the given rotation in radians.
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>A rotation matrix.</returns>
public static Matrix CreateRotationDegrees(float degrees) => System.Numerics.Matrix3x2.CreateRotation(MathF.DegreeToRadian(degrees));
/// <summary>
/// Creates a rotation matrix using the given rotation in radians and a center point.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The center point.</param>
/// <returns>A rotation matrix.</returns>
public static Matrix CreateRotation(float radians, PointF centerPoint) => System.Numerics.Matrix3x2.CreateRotation(radians, centerPoint);
/// <summary>
/// Creates a rotation matrix using the given rotation in radians and a center point.
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <param name="centerPoint">The center point.</param>
/// <returns>A rotation matrix.</returns>
public static Matrix CreateRotationDegrees(float degrees, PointF centerPoint) => Matrix3x2.CreateRotation(MathF.DegreeToRadian(degrees), centerPoint);
/// <summary>
/// Calculates the determinant for this matrix.
/// The determinant is calculated by expanding the matrix with a third column whose values are (0,0,1).
/// </summary>
/// <returns>The determinant.</returns>
public float GetDeterminant() => this.backingMatrix.GetDeterminant();
/// <summary>
/// Attempts to invert the given matrix. If the operation succeeds, the inverted matrix is stored in the result parameter.
/// </summary>
/// <param name="matrix">The source matrix.</param>
/// <param name="result">The output matrix.</param>
/// <returns>True if the operation succeeded, False otherwise.</returns>
public static bool Invert(Matrix matrix, out Matrix result)
{
Matrix3x2 m;
var b = System.Numerics.Matrix3x2.Invert(matrix.backingMatrix, out m);
result = m;
return b;
}
/// <summary>
/// Linearly interpolates from matrix1 to matrix2, based on the third parameter.
/// </summary>
/// <param name="matrix1">The first source matrix.</param>
/// <param name="matrix2">The second source matrix.</param>
/// <param name="amount">The relative weighting of matrix2.</param>
/// <returns>The interpolated matrix.</returns>
public static Matrix Lerp(Matrix matrix1, Matrix matrix2, float amount) => Matrix3x2.Lerp(matrix1.backingMatrix, matrix2.backingMatrix, amount);
/// <summary>
/// Negates the given matrix by multiplying all values by -1.
/// </summary>
/// <param name="value">The source matrix.</param>
/// <returns>The negated matrix.</returns>
public static Matrix Negate(Matrix value) => -value.backingMatrix;
/// <summary>
/// Adds each matrix element in value1 with its corresponding element in value2.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>The matrix containing the summed values.</returns>
public static Matrix Add(Matrix value1, Matrix value2) => value1.backingMatrix + value2.backingMatrix;
/// <summary>
/// Subtracts each matrix element in value2 from its corresponding element in value1.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>The matrix containing the resulting values.</returns>
public static Matrix Subtract(Matrix value1, Matrix value2) => value1.backingMatrix - value2.backingMatrix;
/// <summary>
/// Multiplies two matrices together and returns the resulting matrix.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>The product matrix.</returns>
public static Matrix Multiply(Matrix value1, Matrix value2) => value1.backingMatrix * value2.backingMatrix;
/// <summary>
/// Scales all elements in a matrix by the given scalar factor.
/// </summary>
/// <param name="value1">The source matrix.</param>
/// <param name="value2">The scaling value to use.</param>
/// <returns>The resulting matrix.</returns>
public static Matrix Multiply(Matrix value1, float value2) => value1.backingMatrix * value2;
/// <summary>
/// Negates the given matrix by multiplying all values by -1.
/// </summary>
/// <param name="value">The source matrix.</param>
/// <returns>The negated matrix.</returns>
public static Matrix operator -(Matrix value) => -value.backingMatrix;
/// <summary>
/// Adds each matrix element in value1 with its corresponding element in value2.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>The matrix containing the summed values.</returns>
public static Matrix operator +(Matrix value1, Matrix value2) => value1.backingMatrix + value2.backingMatrix;
/// <summary>
/// Subtracts each matrix element in value2 from its corresponding element in value1.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>The matrix containing the resulting values.</returns>
public static Matrix operator -(Matrix value1, Matrix value2) => value1.backingMatrix - value2.backingMatrix;
/// <summary>
/// Multiplies two matrices together and returns the resulting matrix.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>The product matrix.</returns>
public static Matrix operator *(Matrix value1, Matrix value2) => value1.backingMatrix * value2.backingMatrix;
/// <summary>
/// Scales all elements in a matrix by the given scalar factor.
/// </summary>
/// <param name="value1">The source matrix.</param>
/// <param name="value2">The scaling value to use.</param>
/// <returns>The resulting matrix.</returns>
public static Matrix operator *(Matrix value1, float value2) => value1.backingMatrix * value2;
/// <summary>
/// Returns a boolean indicating whether the given matrices are equal.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>True if the matrices are equal; False otherwise.</returns>
public static bool operator ==(Matrix value1, Matrix value2) => value1.backingMatrix == value2.backingMatrix;
/// <summary>
/// Returns a boolean indicating whether the given matrices are not equal.
/// </summary>
/// <param name="value1">The first source matrix.</param>
/// <param name="value2">The second source matrix.</param>
/// <returns>True if the matrices are not equal; False if they are equal.</returns>
public static bool operator !=(Matrix value1, Matrix value2)
{
return value1.backingMatrix != value2.backingMatrix;
}
/// <summary>
/// Returns a boolean indicating whether the matrix is equal to the other given matrix.
/// </summary>
/// <param name="other">The other matrix to test equality against.</param>
/// <returns>True if this matrix is equal to other; False otherwise.</returns>
public bool Equals(Matrix other)
{
return this.backingMatrix == other.backingMatrix;
}
/// <summary>
/// Returns a boolean indicating whether the given Object is equal to this matrix instance.
/// </summary>
/// <param name="obj">The Object to compare against.</param>
/// <returns>True if the Object is equal to this matrix; False otherwise.</returns>
public override bool Equals(object obj)
{
if (obj is Matrix)
{
return Equals((Matrix)obj);
}
return false;
}
/// <summary>
/// Returns a String representing this matrix instance.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString() => this.backingMatrix.ToString();
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode() => this.backingMatrix.GetHashCode();
/// <summary>
/// Creates a <see cref="Matrix3x2"/> with the values of the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The matrix.</param>
/// <returns>
/// The <see cref="Matrix3x2"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Matrix3x2(Matrix matrix) => matrix.backingMatrix;
/// <summary>
/// Creates a <see cref="Matrix3x2"/> with the values of the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The matrix.</param>
/// <returns>
/// The <see cref="Matrix"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Matrix(Matrix3x2 matrix) => new Matrix { backingMatrix = matrix };
}
}

257
src/SixLabors.Primitives/Point.cs

@ -0,0 +1,257 @@
// <copyright file="Point.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents an ordered pair of integer x- and y-coordinates that defines a point in
/// a two-dimensional plane.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct Point : IEquatable<Point>
{
/// <summary>
/// Represents a <see cref="Point"/> that has X and Y values set to zero.
/// </summary>
public static readonly Point Empty = default(Point);
/// <summary>
/// Initializes a new instance of the <see cref="Point"/> struct.
/// </summary>
/// <param name="value">The horizontal and vertical position of the point.</param>
public Point(int value)
: this()
{
this.X = LowInt16(value);
this.Y = HighInt16(value);
}
/// <summary>
/// Initializes a new instance of the <see cref="Point"/> struct.
/// </summary>
/// <param name="x">The horizontal position of the point.</param>
/// <param name="y">The vertical position of the point.</param>
public Point(int x, int y)
: this()
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Initializes a new instance of the <see cref="Point"/> struct from the given <see cref="Size"/>.
/// </summary>
/// <param name="size">The size</param>
public Point(Size size)
{
this.X = size.Width;
this.Y = size.Height;
}
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="Point"/>.
/// </summary>
public int X { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="Point"/>.
/// </summary>
public int Y { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="Point"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="PointF"/> with the coordinates of the specified <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator PointF(Point point) => new PointF(point.X, point.Y);
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y);
/// <summary>
/// Creates a <see cref="Size"/> with the coordinates of the specified <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Size(Point point) => new Size(point.X, point.Y);
/// <summary>
/// Translates a <see cref="Point"/> by a given <see cref="Size"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Point"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point operator +(Point point, Size size) => Add(point, size);
/// <summary>
/// Translates a <see cref="Point"/> by the negative of a given <see cref="Size"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point operator -(Point point, Size size) => Subtract(point, size);
/// <summary>
/// Compares two <see cref="Point"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Point"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Point"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Point left, Point right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Point"/> objects for inequality.
/// </summary>
/// <param name="left">The <see cref="Point"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Point"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Point left, Point right) => !left.Equals(right);
/// <summary>
/// Translates a <see cref="Point"/> by the negative of a given <see cref="Size"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height));
/// <summary>
/// Translates a <see cref="Point"/> by the negative of a given <see cref="Size"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height));
/// <summary>
/// Converts a <see cref="PointF"/> to a <see cref="Point"/> by performing a ceiling operation on all the coordinates.
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y)));
/// <summary>
/// Converts a <see cref="PointF"/> to a <see cref="Point"/> by performing a round operation on all the coordinates.
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y)));
/// <summary>
/// Converts a <see cref="PointF"/> to a <see cref="Point"/> by performing a truncate operation on all the coordinates.
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y));
/// <summary>
/// Converts a <see cref="Vector2"/> to a <see cref="Point"/> by performing a round operation on all the coordinates.
/// </summary>
/// <param name="vector">The vector</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y)));
/// <summary>
/// Rotates a point around the given rotation matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Rotate(Point point, Matrix3x2 rotation) => Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation));
/// <summary>
/// Skews a point using the given skew matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="skew">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Skew(Point point, Matrix3x2 skew) => Round(Vector2.Transform(new Vector2(point.X, point.Y), skew));
/// <summary>
/// Translates this <see cref="Point"/> by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(int dx, int dy)
{
unchecked
{
this.X += dx;
this.Y += dy;
}
}
/// <summary>
/// Translates this <see cref="Point"/> by the specified amount.
/// </summary>
/// <param name="point">The <see cref="Point"/> used offset this <see cref="Point"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(Point point) => this.Offset(point.X, point.Y);
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Point [ Empty ]";
}
return $"Point [ X={this.X}, Y={this.Y} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Point && this.Equals((Point)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Point other) => this.X == other.X && this.Y == other.Y;
private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff));
private static short LowInt16(int n) => unchecked((short)(n & 0xffff));
private int GetHashCode(Point point) => point.X ^ point.Y;
}
}

233
src/SixLabors.Primitives/PointF.cs

@ -0,0 +1,233 @@
// <copyright file="PointF.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents an ordered pair of single precision floating point x- and y-coordinates that defines a point in
/// a two-dimensional plane.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct PointF : IEquatable<PointF>
{
/// <summary>
/// Represents a <see cref="PointF"/> that has X and Y values set to zero.
/// </summary>
public static readonly PointF Empty = default(PointF);
/// <summary>
/// Initializes a new instance of the <see cref="PointF"/> struct.
/// </summary>
/// <param name="x">The horizontal position of the point.</param>
/// <param name="y">The vertical position of the point.</param>
public PointF(float x, float y)
: this()
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Initializes a new instance of the <see cref="PointF"/> struct from the given <see cref="SizeF"/>.
/// </summary>
/// <param name="size">The size</param>
public PointF(SizeF size)
{
this.X = size.Width;
this.Y = size.Height;
}
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="PointF"/>.
/// </summary>
public float X { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="PointF"/>.
/// </summary>
public float Y { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="PointF"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="PointF"/>.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>
/// The <see cref="Vector2"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator PointF(Vector2 vector) => new PointF(vector.X, vector.Y);
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="PointF"/>.
/// </summary>
/// <param name="point">The point.</param>
/// <returns>
/// The <see cref="Vector2"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector2(PointF point) => new Vector2(point.X, point.Y);
/// <summary>
/// Creates a <see cref="Point"/> with the coordinates of the specified <see cref="PointF"/> by truncating each of the coordinates.
/// </summary>
/// <param name="point">The point.</param>
/// <returns>
/// The <see cref="Point"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Point(PointF point) => Point.Truncate(point);
/// <summary>
/// Translates a <see cref="PointF"/> by a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="PointF"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF operator +(PointF point, SizeF size) => Add(point, size);
/// <summary>
/// Translates a <see cref="PointF"/> by the negative of a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF operator -(PointF point, SizeF size) => Subtract(point, size);
/// <summary>
/// Compares two <see cref="PointF"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="PointF"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="PointF"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(PointF left, PointF right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="PointF"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="PointF"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="PointF"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(PointF left, PointF right) => !left.Equals(right);
/// <summary>
/// Translates a <see cref="PointF"/> by the negative of a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Add(PointF point, SizeF size) => new PointF(point.X + size.Width, point.Y + size.Height);
/// <summary>
/// Translates a <see cref="PointF"/> by the negative of a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Subtract(PointF point, SizeF size) => new PointF(point.X - size.Width, point.Y - size.Height);
/// <summary>
/// Rotates a point around the given rotation matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns>The rotated <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Rotate(PointF point, Matrix rotation) => Vector2.Transform(new Vector2(point.X, point.Y), rotation);
/// <summary>
/// Skews a point using the given skew matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="skew">Rotation matrix used</param>
/// <returns>The rotated <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Skew(PointF point, Matrix skew) => Vector2.Transform(new Vector2(point.X, point.Y), skew);
/// <summary>
/// Translates this <see cref="PointF"/> by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(float dx, float dy)
{
this.X += dx;
this.Y += dy;
}
/// <summary>
/// Translates this <see cref="PointF"/> by the specified amount.
/// </summary>
/// <param name="point">The <see cref="PointF"/> used offset this <see cref="PointF"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(PointF point) => this.Offset(point.X, point.Y);
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "PointF [ Empty ]";
}
return $"PointF [ X={this.X}, Y={this.Y} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is PointF && this.Equals((PointF)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="point">
/// The instance of <see cref="PointF"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(PointF point) => point.X.GetHashCode() ^ point.Y.GetHashCode();
}
}

189
src/SixLabors.Primitives/Rational.cs

@ -0,0 +1,189 @@
// <copyright file="Rational.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.Globalization;
/// <summary>
/// Represents a number that can be expressed as a fraction.
/// </summary>
/// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks>
public struct Rational : IEquatable<Rational>
{
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="value">The <see cref="uint"/> to create the rational from.</param>
public Rational(uint value)
: this(value, 1)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
public Rational(uint numerator, uint denominator)
: this(numerator, denominator, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
/// <param name="simplify">Specified if the rational should be simplified.</param>
public Rational(uint numerator, uint denominator, bool simplify)
{
LongRational rational = new LongRational(numerator, denominator, simplify);
this.Numerator = (uint)rational.Numerator;
this.Denominator = (uint)rational.Denominator;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="value">The <see cref="double"/> to create the instance from.</param>
public Rational(double value)
: this(value, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="value">The <see cref="double"/> to create the instance from.</param>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public Rational(double value, bool bestPrecision)
{
LongRational rational = new LongRational(Math.Abs(value), bestPrecision);
this.Numerator = (uint)rational.Numerator;
this.Denominator = (uint)rational.Denominator;
}
/// <summary>
/// Gets the numerator of a number.
/// </summary>
public uint Numerator { get; }
/// <summary>
/// Gets the denominator of a number.
/// </summary>
public uint Denominator { get; }
/// <summary>
/// Determines whether the specified <see cref="Rational"/> instances are considered equal.
/// </summary>
/// <param name="left">The first <see cref="Rational"/> to compare.</param>
/// <param name="right"> The second <see cref="Rational"/> to compare.</param>
/// <returns>The <see cref="bool"/></returns>
public static bool operator ==(Rational left, Rational right)
{
return Rational.Equals(left, right);
}
/// <summary>
/// Determines whether the specified <see cref="Rational"/> instances are not considered equal.
/// </summary>
/// <param name="left">The first <see cref="Rational"/> to compare.</param>
/// <param name="right"> The second <see cref="Rational"/> to compare.</param>
/// <returns>The <see cref="bool"/></returns>
public static bool operator !=(Rational left, Rational right)
{
return !Rational.Equals(left, right);
}
/// <summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
/// </summary>
/// <param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
/// <returns>
/// The <see cref="Rational"/>.
/// </returns>
public static Rational FromDouble(double value)
{
return new Rational(value, false);
}
/// <summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
/// </summary>
/// <param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
/// <returns>
/// The <see cref="Rational"/>.
/// </returns>
public static Rational FromDouble(double value, bool bestPrecision)
{
return new Rational(value, bestPrecision);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Rational)
{
return this.Equals((Rational)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(Rational other)
{
LongRational left = new LongRational(this.Numerator, this.Denominator);
LongRational right = new LongRational(other.Numerator, other.Denominator);
return left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
LongRational self = new LongRational(this.Numerator, this.Denominator);
return self.GetHashCode();
}
/// <summary>
/// Converts a rational number to the nearest <see cref="double"/>.
/// </summary>
/// <returns>
/// The <see cref="double"/>.
/// </returns>
public double ToDouble()
{
return this.Numerator / (double)this.Denominator;
}
/// <inheritdoc/>
public override string ToString()
{
return this.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts the numeric value of this instance to its equivalent string representation using
/// the specified culture-specific format information.
/// </summary>
/// <param name="provider">
/// An object that supplies culture-specific formatting information.
/// </param>
/// <returns>The <see cref="string"/></returns>
public string ToString(IFormatProvider provider)
{
LongRational rational = new LongRational(this.Numerator, this.Denominator);
return rational.ToString(provider);
}
}
}

467
src/SixLabors.Primitives/Rectangle.cs

@ -0,0 +1,467 @@
// <copyright file="Rectangle.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores a set of four integers that represent the location and size of a rectangle.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct Rectangle : IEquatable<Rectangle>
{
/// <summary>
/// Represents a <see cref="Rectangle"/> that has X, Y, Width, and Height values set to zero.
/// </summary>
public static readonly Rectangle Empty = default(Rectangle);
/// <summary>
/// Initializes a new instance of the <see cref="Rectangle"/> struct.
/// </summary>
/// <param name="x">The horizontal position of the rectangle.</param>
/// <param name="y">The vertical position of the rectangle.</param>
/// <param name="width">The width of the rectangle.</param>
/// <param name="height">The height of the rectangle.</param>
public Rectangle(int x, int y, int width, int height)
{
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rectangle"/> struct.
/// </summary>
/// <param name="point">
/// The <see cref="Point"/> which specifies the rectangles point in a two-dimensional plane.
/// </param>
/// <param name="size">
/// The <see cref="Size"/> which specifies the rectangles height and width.
/// </param>
public Rectangle(Point point, Size size)
{
this.X = point.X;
this.Y = point.Y;
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="Rectangle"/>.
/// </summary>
public int X { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="Rectangle"/>.
/// </summary>
public int Y { get; set; }
/// <summary>
/// Gets or sets the width of this <see cref="Rectangle"/>.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="Rectangle"/>.
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this <see cref="Rectangle"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Point Location
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Point(this.X, this.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.X = value.X;
this.Y = value.Y;
}
}
/// <summary>
/// Gets or sets the size of this <see cref="Rectangle"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Size Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Size(this.Width, this.Height);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.Width = value.Width;
this.Height = value.Height;
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="Rectangle"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Gets the y-coordinate of the top edge of this <see cref="Rectangle"/>.
/// </summary>
public int Top
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Y;
}
}
/// <summary>
/// Gets the x-coordinate of the right edge of this <see cref="Rectangle"/>.
/// </summary>
public int Right
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return unchecked(this.X + this.Width);
}
}
/// <summary>
/// Gets the y-coordinate of the bottom edge of this <see cref="Rectangle"/>.
/// </summary>
public int Bottom
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return unchecked(this.Y + this.Height);
}
}
/// <summary>
/// Gets the x-coordinate of the left edge of this <see cref="Rectangle"/>.
/// </summary>
public int Left
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.X;
}
}
/// <summary>
/// Creates a <see cref="RectangleF"/> with the coordinates of the specified <see cref="Rectangle"/>.
/// </summary>
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator RectangleF(Rectangle rectangle) => new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
/// <summary>
/// Creates a <see cref="Vector4"/> with the coordinates of the specified <see cref="Rectangle"/>.
/// </summary>
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector4(Rectangle rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
/// <summary>
/// Compares two <see cref="Rectangle"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Rectangle"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rectangle"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Rectangle"/> objects for inequality.
/// </summary>
/// <param name="left">The <see cref="Rectangle"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rectangle"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right);
/// <summary>
/// Creates a new <see cref="Rectangle"/> with the specified location and size. </summary>
/// <param name="left">The left coordinate of the rectangle</param>
/// <param name="top">The top coordinate of the rectangle</param>
/// <param name="right">The right coordinate of the rectangle</param>
/// <param name="bottom">The bottom coordinate of the rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once InconsistentNaming
public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top));
/// <summary>
/// Returns the center point of the given <see cref="Rectangle"/>
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Center(Rectangle rectangle) => new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
/// <summary>
/// Creates a rectangle that represents the intersection between <paramref name="a"/> and
/// <paramref name="b"/>. If there is no intersection, an empty rectangle is returned.
/// </summary>
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Intersect(Rectangle a, Rectangle b)
{
int x1 = Math.Max(a.X, b.X);
int x2 = Math.Min(a.Right, b.Right);
int y1 = Math.Max(a.Y, b.Y);
int y2 = Math.Min(a.Bottom, b.Bottom);
if (x2 >= x1 && y2 >= y1)
{
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
return Empty;
}
/// <summary>
/// Creates a <see cref="Rectangle"/> that is inflated by the specified amount.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <param name="x">The amount to inflate the width by</param>
/// <param name="y">The amount to inflate the height by</param>
/// <returns>A new <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Inflate(Rectangle rectangle, int x, int y)
{
Rectangle r = rectangle;
r.Inflate(x, y);
return r;
}
/// <summary>
/// Converts a <see cref="RectangleF"/> to a <see cref="Rectangle"/> by performing a ceiling operation on all the coordinates.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Ceiling(RectangleF rectangle)
{
unchecked
{
return new Rectangle(
(int)MathF.Ceiling(rectangle.X),
(int)MathF.Ceiling(rectangle.Y),
(int)MathF.Ceiling(rectangle.Width),
(int)MathF.Ceiling(rectangle.Height));
}
}
/// <summary>
/// Converts a <see cref="RectangleF"/> to a <see cref="Rectangle"/> by performing a truncate operation on all the coordinates.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Truncate(RectangleF rectangle)
{
unchecked
{
return new Rectangle(
(int)rectangle.X,
(int)rectangle.Y,
(int)rectangle.Width,
(int)rectangle.Height);
}
}
/// <summary>
/// Converts a <see cref="RectangleF"/> to a <see cref="Rectangle"/> by performing a round operation on all the coordinates.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Round(RectangleF rectangle)
{
unchecked
{
return new Rectangle(
(int)MathF.Round(rectangle.X),
(int)MathF.Round(rectangle.Y),
(int)MathF.Round(rectangle.Width),
(int)MathF.Round(rectangle.Height));
}
}
/// <summary>
/// Creates a rectangle that represents the union between <paramref name="a"/> and <paramref name="b"/>.
/// </summary>
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Union(Rectangle a, Rectangle b)
{
int x1 = Math.Min(a.X, b.X);
int x2 = Math.Max(a.Right, b.Right);
int y1 = Math.Min(a.Y, b.Y);
int y2 = Math.Max(a.Bottom, b.Bottom);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Creates a Rectangle that represents the intersection between this Rectangle and the <paramref name="rectangle"/>.
/// </summary>
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Intersect(Rectangle rectangle)
{
Rectangle result = Intersect(rectangle, this);
this.X = result.X;
this.Y = result.Y;
this.Width = result.Width;
this.Height = result.Height;
}
/// <summary>
/// Inflates this <see cref="Rectangle"/> by the specified amount.
/// </summary>
/// <param name="width">The width</param>
/// <param name="height">The height</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(int width, int height)
{
unchecked
{
this.X -= width;
this.Y -= height;
this.Width += 2 * width;
this.Height += 2 * height;
}
}
/// <summary>
/// Inflates this <see cref="Rectangle"/> by the specified amount.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(Size size) => this.Inflate(size.Width, size.Height);
/// <summary>
/// Determines if the specfied point is contained within the rectangular region defined by
/// this <see cref="Rectangle"/>.
/// </summary>
/// <param name="x">The x-coordinate of the given point.</param>
/// <param name="y">The y-coordinate of the given point.</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int x, int y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom;
/// <summary>
/// Determines if the specified point is contained within the rectangular region defined by this <see cref="Rectangle"/> .
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(Point point) => this.Contains(point.X, point.Y);
/// <summary>
/// Determines if the rectangular region represented by <paramref name="rectangle"/> is entirely contained
/// within the rectangular region represented by this <see cref="Rectangle"/> .
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(Rectangle rectangle) =>
(this.X <= rectangle.X) && (rectangle.Right <= this.Right) &&
(this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom);
/// <summary>
/// Determines if the specfied <see cref="Rectangle"/> intersects the rectangular region defined by
/// this <see cref="Rectangle"/>.
/// </summary>
/// <param name="rectangle">The other Rectange </param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IntersectsWith(Rectangle rectangle) =>
(rectangle.X < this.Right) && (this.X < rectangle.Right) &&
(rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(Point point) => this.Offset(point.X, point.Y);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(int dx, int dy)
{
unchecked
{
this.X += dx;
this.Y += dy;
}
}
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Rectangle [ Empty ]";
}
return $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Rectangle && this.Equals((Rectangle)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rectangle other) => this.X == other.X && this.Y == other.Y && this.Width == other.Width && this.Height == other.Height;
private int GetHashCode(Rectangle rectangle)
{
unchecked
{
int hashCode = rectangle.X;
hashCode = (hashCode * 397) ^ rectangle.Y;
hashCode = (hashCode * 397) ^ rectangle.Width;
hashCode = (hashCode * 397) ^ rectangle.Height;
return hashCode;
}
}
}
}

400
src/SixLabors.Primitives/RectangleF.cs

@ -0,0 +1,400 @@
// <copyright file="RectangleF.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores a set of four single precision floating points that represent the location and size of a rectangle.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct RectangleF : IEquatable<RectangleF>
{
/// <summary>
/// Represents a <see cref="RectangleF"/> that has X, Y, Width, and Height values set to zero.
/// </summary>
public static readonly RectangleF Empty = default(RectangleF);
/// <summary>
/// Initializes a new instance of the <see cref="RectangleF"/> struct.
/// </summary>
/// <param name="x">The horizontal position of the rectangle.</param>
/// <param name="y">The vertical position of the rectangle.</param>
/// <param name="width">The width of the rectangle.</param>
/// <param name="height">The height of the rectangle.</param>
public RectangleF(float x, float y, float width, float height)
{
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="RectangleF"/> struct.
/// </summary>
/// <param name="point">
/// The <see cref="Point"/> which specifies the rectangles point in a two-dimensional plane.
/// </param>
/// <param name="size">
/// The <see cref="Size"/> which specifies the rectangles height and width.
/// </param>
public RectangleF(PointF point, SizeF size)
{
this.X = point.X;
this.Y = point.Y;
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="RectangleF"/>.
/// </summary>
public float X { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="RectangleF"/>.
/// </summary>
public float Y { get; set; }
/// <summary>
/// Gets or sets the width of this <see cref="RectangleF"/>.
/// </summary>
public float Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="RectangleF"/>.
/// </summary>
public float Height { get; set; }
/// <summary>
/// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this <see cref="RectangleF"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public PointF Location
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new PointF(this.X, this.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.X = value.X;
this.Y = value.Y;
}
}
/// <summary>
/// Gets or sets the size of this <see cref="RectangleF"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public SizeF Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new SizeF(this.Width, this.Height);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.Width = value.Width;
this.Height = value.Height;
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="RectangleF"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => (this.Width <= 0) || (this.Height <= 0);
/// <summary>
/// Gets the y-coordinate of the top edge of this <see cref="RectangleF"/>.
/// </summary>
public float Top
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Y;
}
}
/// <summary>
/// Gets the x-coordinate of the right edge of this <see cref="RectangleF"/>.
/// </summary>
public float Right
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.X + this.Width;
}
}
/// <summary>
/// Gets the y-coordinate of the bottom edge of this <see cref="RectangleF"/>.
/// </summary>
public float Bottom
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Y + this.Height;
}
}
/// <summary>
/// Gets the x-coordinate of the left edge of this <see cref="RectangleF"/>.
/// </summary>
public float Left
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.X;
}
}
/// <summary>
/// Creates a <see cref="Rectangle"/> with the coordinates of the specified <see cref="RectangleF"/> by truncating each coordinate.
/// </summary>
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Rectangle(RectangleF rectangle) => Rectangle.Truncate(rectangle);
/// <summary>
/// Compares two <see cref="RectangleF"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="RectangleF"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="RectangleF"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(RectangleF left, RectangleF right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="RectangleF"/> objects for inequality.
/// </summary>
/// <param name="left">The <see cref="RectangleF"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="RectangleF"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(RectangleF left, RectangleF right) => !left.Equals(right);
/// <summary>
/// Creates a new <see cref="RectangleF"/> with the specified location and size. </summary>
/// <param name="left">The left coordinate of the rectangle</param>
/// <param name="top">The top coordinate of the rectangle</param>
/// <param name="right">The right coordinate of the rectangle</param>
/// <param name="bottom">The bottom coordinate of the rectangle</param>
/// <returns>The <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once InconsistentNaming
public static RectangleF FromLTRB(float left, float top, float right, float bottom) => new RectangleF(left, top, right - left, bottom - top);
/// <summary>
/// Returns the center point of the given <see cref="RectangleF"/>
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Center(RectangleF rectangle) => new PointF(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
/// <summary>
/// Creates a rectangle that represents the intersection between <paramref name="a"/> and
/// <paramref name="b"/>. If there is no intersection, an empty rectangle is returned.
/// </summary>
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RectangleF Intersect(RectangleF a, RectangleF b)
{
float x1 = MathF.Max(a.X, b.X);
float x2 = MathF.Min(a.Right, b.Right);
float y1 = MathF.Max(a.Y, b.Y);
float y2 = MathF.Min(a.Bottom, b.Bottom);
if (x2 >= x1 && y2 >= y1)
{
return new RectangleF(x1, y1, x2 - x1, y2 - y1);
}
return Empty;
}
/// <summary>
/// Creates a <see cref="RectangleF"/> that is inflated by the specified amount.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <param name="x">The amount to inflate the width by</param>
/// <param name="y">The amount to inflate the height by</param>
/// <returns>A new <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RectangleF Inflate(RectangleF rectangle, float x, float y)
{
RectangleF r = rectangle;
r.Inflate(x, y);
return r;
}
/// <summary>
/// Creates a rectangle that represents the union between <paramref name="a"/> and <paramref name="b"/>.
/// </summary>
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RectangleF Union(RectangleF a, RectangleF b)
{
float x1 = MathF.Min(a.X, b.X);
float x2 = MathF.Max(a.Right, b.Right);
float y1 = MathF.Min(a.Y, b.Y);
float y2 = MathF.Max(a.Bottom, b.Bottom);
return new RectangleF(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Creates a RectangleF that represents the intersection between this RectangleF and the <paramref name="rectangle"/>.
/// </summary>
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Intersect(RectangleF rectangle)
{
RectangleF result = Intersect(rectangle, this);
this.X = result.X;
this.Y = result.Y;
this.Width = result.Width;
this.Height = result.Height;
}
/// <summary>
/// Inflates this <see cref="RectangleF"/> by the specified amount.
/// </summary>
/// <param name="width">The width</param>
/// <param name="height">The height</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(float width, float height)
{
this.X -= width;
this.Y -= height;
this.Width += 2 * width;
this.Height += 2 * height;
}
/// <summary>
/// Inflates this <see cref="RectangleF"/> by the specified amount.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(SizeF size) => this.Inflate(size.Width, size.Height);
/// <summary>
/// Determines if the specfied point is contained within the rectangular region defined by
/// this <see cref="RectangleF"/>.
/// </summary>
/// <param name="x">The x-coordinate of the given point.</param>
/// <param name="y">The y-coordinate of the given point.</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(float x, float y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom;
/// <summary>
/// Determines if the specified point is contained within the rectangular region defined by this <see cref="RectangleF"/> .
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(PointF point) => this.Contains(point.X, point.Y);
/// <summary>
/// Determines if the rectangular region represented by <paramref name="rectangle"/> is entirely contained
/// within the rectangular region represented by this <see cref="RectangleF"/> .
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(RectangleF rectangle) =>
(this.X <= rectangle.X) && (rectangle.Right <= this.Right) &&
(this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom);
/// <summary>
/// Determines if the specfied <see cref="RectangleF"/> intersects the rectangular region defined by
/// this <see cref="RectangleF"/>.
/// </summary>
/// <param name="rectangle">The other Rectange </param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IntersectsWith(RectangleF rectangle) =>
(rectangle.X < this.Right) && (this.X < rectangle.Right) &&
(rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(PointF point) => this.Offset(point.X, point.Y);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(float dx, float dy)
{
this.X += dx;
this.Y += dy;
}
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "RectangleF [ Empty ]";
}
return $"RectangleF [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is RectangleF && this.Equals((RectangleF)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(RectangleF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Width.Equals(other.Width) && this.Height.Equals(other.Height);
private int GetHashCode(RectangleF rectangle)
{
unchecked
{
int hashCode = rectangle.X.GetHashCode();
hashCode = (hashCode * 397) ^ rectangle.Y.GetHashCode();
hashCode = (hashCode * 397) ^ rectangle.Width.GetHashCode();
hashCode = (hashCode * 397) ^ rectangle.Height.GetHashCode();
return hashCode;
}
}
}
}

189
src/SixLabors.Primitives/SignedRational.cs

@ -0,0 +1,189 @@
// <copyright file="SignedRational.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.Globalization;
/// <summary>
/// Represents a number that can be expressed as a fraction.
/// </summary>
/// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks>
public struct SignedRational : IEquatable<SignedRational>
{
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="value">The <see cref="int"/> to create the rational from.</param>
public SignedRational(int value)
: this(value, 1)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
public SignedRational(int numerator, int denominator)
: this(numerator, denominator, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
/// <param name="simplify">Specified if the rational should be simplified.</param>
public SignedRational(int numerator, int denominator, bool simplify)
{
LongRational rational = new LongRational(numerator, denominator, simplify);
this.Numerator = (int)rational.Numerator;
this.Denominator = (int)rational.Denominator;
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="value">The <see cref="double"/> to create the instance from.</param>
public SignedRational(double value)
: this(value, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="value">The <see cref="double"/> to create the instance from.</param>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public SignedRational(double value, bool bestPrecision)
{
LongRational rational = new LongRational(value, bestPrecision);
this.Numerator = (int)rational.Numerator;
this.Denominator = (int)rational.Denominator;
}
/// <summary>
/// Gets the numerator of a number.
/// </summary>
public int Numerator { get; }
/// <summary>
/// Gets the denominator of a number.
/// </summary>
public int Denominator { get; }
/// <summary>
/// Determines whether the specified <see cref="SignedRational"/> instances are considered equal.
/// </summary>
/// <param name="left">The first <see cref="SignedRational"/> to compare.</param>
/// <param name="right"> The second <see cref="SignedRational"/> to compare.</param>
/// <returns>The <see cref="bool"/></returns>
public static bool operator ==(SignedRational left, SignedRational right)
{
return SignedRational.Equals(left, right);
}
/// <summary>
/// Determines whether the specified <see cref="SignedRational"/> instances are not considered equal.
/// </summary>
/// <param name="left">The first <see cref="SignedRational"/> to compare.</param>
/// <param name="right"> The second <see cref="SignedRational"/> to compare.</param>
/// <returns>The <see cref="bool"/></returns>
public static bool operator !=(SignedRational left, SignedRational right)
{
return !SignedRational.Equals(left, right);
}
/// <summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
/// </summary>
/// <param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
/// <returns>
/// The <see cref="SignedRational"/>.
/// </returns>
public static SignedRational FromDouble(double value)
{
return new SignedRational(value, false);
}
/// <summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
/// </summary>
/// <param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
/// <returns>
/// The <see cref="SignedRational"/>.
/// </returns>
public static SignedRational FromDouble(double value, bool bestPrecision)
{
return new SignedRational(value, bestPrecision);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is SignedRational)
{
return this.Equals((SignedRational)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(SignedRational other)
{
LongRational left = new LongRational(this.Numerator, this.Denominator);
LongRational right = new LongRational(other.Numerator, other.Denominator);
return left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
LongRational self = new LongRational(this.Numerator, this.Denominator);
return self.GetHashCode();
}
/// <summary>
/// Converts a rational number to the nearest <see cref="double"/>.
/// </summary>
/// <returns>
/// The <see cref="double"/>.
/// </returns>
public double ToDouble()
{
return this.Numerator / (double)this.Denominator;
}
/// <inheritdoc/>
public override string ToString()
{
return this.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts the numeric value of this instance to its equivalent string representation using
/// the specified culture-specific format information.
/// </summary>
/// <param name="provider">
/// An object that supplies culture-specific formatting information.
/// </param>
/// <returns>The <see cref="string"/></returns>
public string ToString(IFormatProvider provider)
{
LongRational rational = new LongRational(this.Numerator, this.Denominator);
return rational.ToString(provider);
}
}
}

40
src/SixLabors.Primitives/SixLabors.Primitives.csproj

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Low level primitives for use across Six Labors projects..</Description>
<AssemblyTitle>SixLabors.Primitives</AssemblyTitle>
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.1.0-alpha1</VersionPrefix>
<Authors>Six Labors</Authors>
<TargetFramework>netstandard1.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.Primitives</AssemblyName>
<PackageId>SixLabors.Primitives</PackageId>
<PackageTags>rectangle;point;size,primitives</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/Home/master/logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/SixLabors/Primitives</PackageProjectUrl>
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/SixLabors/Primitives</RepositoryUrl>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<DebugType Condition="$(codecov) == 'true'">full</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Numerics.Vectors" Version="4.3.0" />
</ItemGroup>
</Project>

225
src/SixLabors.Primitives/Size.cs

@ -0,0 +1,225 @@
// <copyright file="Size.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores an ordered pair of integers, which specify a height and width.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct Size : IEquatable<Size>
{
/// <summary>
/// Represents a <see cref="Size"/> that has Width and Height values set to zero.
/// </summary>
public static readonly Size Empty = default(Size);
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct.
/// </summary>
/// <param name="value">The width and height of the size</param>
public Size(int value)
: this()
{
this.Width = value;
this.Height = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct.
/// </summary>
/// <param name="width">The width of the size.</param>
/// <param name="height">The height of the size.</param>
public Size(int width, int height)
{
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct.
/// </summary>
/// <param name="size">The size</param>
public Size(Size size)
: this()
{
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct from the given <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
public Size(Point point)
{
this.Width = point.X;
this.Height = point.Y;
}
/// <summary>
/// Gets or sets the width of this <see cref="Size"/>.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="Size"/>.
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="Size"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="SizeF"/> with the dimensions of the specified <see cref="Size"/>.
/// </summary>
/// <param name="size">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SizeF(Size size) => new SizeF(size.Width, size.Height);
/// <summary>
/// Converts the given <see cref="Size"/> into a <see cref="Point"/>.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Point(Size size) => new Point(size.Width, size.Height);
/// <summary>
/// Computes the sum of adding two sizes.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Size"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size operator +(Size left, Size right) => Add(left, right);
/// <summary>
/// Computes the difference left by subtracting one size from another.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Size"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size operator -(Size left, Size right) => Subtract(left, right);
/// <summary>
/// Compares two <see cref="Size"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Size"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Size"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Size left, Size right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Size"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Size"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Size"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Size left, Size right) => !left.Equals(right);
/// <summary>
/// Performs vector addition of two <see cref="Size"/> objects.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Add(Size left, Size right) => new Size(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height));
/// <summary>
/// Contracts a <see cref="Size"/> by another <see cref="Size"/>
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Subtract(Size left, Size right) => new Size(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height));
/// <summary>
/// Converts a <see cref="SizeF"/> to a <see cref="Size"/> by performing a ceiling operation on all the dimensions.
/// </summary>
/// <param name="size">The size</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Ceiling(SizeF size) => new Size(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height)));
/// <summary>
/// Converts a <see cref="SizeF"/> to a <see cref="Size"/> by performing a round operation on all the dimensions.
/// </summary>
/// <param name="size">The size</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Round(SizeF size) => new Size(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height)));
/// <summary>
/// Converts a <see cref="SizeF"/> to a <see cref="Size"/> by performing a round operation on all the dimensions.
/// </summary>
/// <param name="size">The size</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Truncate(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height));
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Size [ Empty ]";
}
return $"Size [ Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Size && this.Equals((Size)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Size other) => this.Width == other.Width && this.Height == other.Height;
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="size">
/// The instance of <see cref="Size"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Size size) => size.Width ^ size.Height;
}
}

190
src/SixLabors.Primitives/SizeF.cs

@ -0,0 +1,190 @@
// <copyright file="SizeF.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores an ordered pair of single precision floating points, which specify a height and width.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct SizeF : IEquatable<SizeF>
{
/// <summary>
/// Represents a <see cref="SizeF"/> that has Width and Height values set to zero.
/// </summary>
public static readonly SizeF Empty = default(SizeF);
/// <summary>
/// Initializes a new instance of the <see cref="SizeF"/> struct.
/// </summary>
/// <param name="width">The width of the size.</param>
/// <param name="height">The height of the size.</param>
public SizeF(float width, float height)
{
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="SizeF"/> struct.
/// </summary>
/// <param name="size">The size</param>
public SizeF(SizeF size)
: this()
{
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="SizeF"/> struct from the given <see cref="PointF"/>.
/// </summary>
/// <param name="point">The point</param>
public SizeF(PointF point)
{
this.Width = point.X;
this.Height = point.Y;
}
/// <summary>
/// Gets or sets the width of this <see cref="SizeF"/>.
/// </summary>
public float Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="SizeF"/>.
/// </summary>
public float Height { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="SizeF"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="Size"/> with the dimensions of the specified <see cref="SizeF"/> by truncating each of the dimensions.
/// </summary>
/// <param name="size">The size.</param>
/// <returns>
/// The <see cref="Size"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Size(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height));
/// <summary>
/// Converts the given <see cref="SizeF"/> into a <see cref="PointF"/>.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator PointF(SizeF size) => new PointF(size.Width, size.Height);
/// <summary>
/// Computes the sum of adding two sizes.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="SizeF"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF operator +(SizeF left, SizeF right) => Add(left, right);
/// <summary>
/// Computes the difference left by subtracting one size from another.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="SizeF"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF operator -(SizeF left, SizeF right) => Subtract(left, right);
/// <summary>
/// Compares two <see cref="SizeF"/> objects for equality.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(SizeF left, SizeF right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="SizeF"/> objects for inequality.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(SizeF left, SizeF right) => !left.Equals(right);
/// <summary>
/// Performs vector addition of two <see cref="SizeF"/> objects.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="SizeF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF Add(SizeF left, SizeF right) => new SizeF(left.Width + right.Width, left.Height + right.Height);
/// <summary>
/// Contracts a <see cref="SizeF"/> by another <see cref="SizeF"/>
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="SizeF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF Subtract(SizeF left, SizeF right) => new SizeF(left.Width - right.Width, left.Height - right.Height);
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "SizeF [ Empty ]";
}
return $"SizeF [ Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is SizeF && this.Equals((SizeF)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height);
private int GetHashCode(SizeF size) => size.Width.GetHashCode() ^ size.Height.GetHashCode();
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="PointF"/>.
/// </summary>
/// <param name="point">The point.</param>
/// <returns>
/// The <see cref="Vector2"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector2(SizeF point) => new Vector2(point.Width, point.Height);
}
}

9
src/SixLabors.Primitives/stylecop.json

@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"documentationRules": {
"companyName": "Six Labors",
"copyrightText": "Copyright (c) Six Labors and contributors.\nLicensed under the Apache License, Version 2.0."
}
}
}

21
tests/CodeCoverage/CodeCoverage.cmd

@ -0,0 +1,21 @@
@echo off
cd tests\CodeCoverage
nuget restore packages.config -PackagesDirectory .
cd ..
cd ..
dotnet restore SixLabors.Primitives.sln
dotnet build SixLabors.Primitives.sln --no-incremental -c debug /p:codecov=true
rem The -threshold options prevents this taking ages...
rem tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\SixLabors.Shapes.Tests\SixLabors.Shapes.Tests.csproj --no-build -c Release /p:codecov=true" -threshold:10 -register:user -filter:"+[SixLabors.Shapes*]*" -excludebyattribute:*.ExcludeFromCodeCoverage* -hideskipped:All -returntargetcode -output:.\SixLabors.Shapes.Coverage.xml
tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\SixLabors.Primitives.Tests\SixLabors.Primitives.Tests.csproj --no-build -c debug" -searchdirs:"tests\SixLabors.Shapes.Tests\bin\Release\netcoreapp1.1" -register:user -output:.\SixLabors.Shapes.Coverage.xml -hideskipped:All -returntargetcode -oldStyle -filter:"+[SixLabors.Primitives*]*"
if %errorlevel% neq 0 exit /b %errorlevel%
SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%
pip install codecov
codecov -f "SixLabors.Primitives.Coverage.xml"

166
tests/CodeCoverage/OpenCover.4.6.519/License.rtf

@ -0,0 +1,166 @@
{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang3081\deflangfe3081\themelang3081\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}
{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}
{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}
{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f40\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f379\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f380\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}
{\f382\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f383\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f386\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f387\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}
{\f409\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f410\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f412\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f413\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
{\f416\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f417\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;}{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;}
{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fhimajor\f31536\fbidi \froman\fcharset163\fprq2 Cambria (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}
{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;
\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp
\f31506\fs22\lang3081\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa200\sl276\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang3081\langfe1033\cgrid\langnp3081\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive
\ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang3081\langfe1033\cgrid\langnp3081\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}
{\*\rsidtbl \rsid11275983\rsid14818796\rsid15367925\rsid16724145}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Shaun}{\operator Shaun}
{\creatim\yr2012\mo1\dy5\hr7\min52}{\revtim\yr2012\mo2\dy21\hr8\min10}{\version3}{\edmins2}{\nofpages1}{\nofwords157}{\nofchars900}{\*\company Microsoft}{\nofcharsws1055}{\vern49273}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/word
ml}}\paperw11906\paperh16838\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect
\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen
\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1440\dgvorigin1440\dghshow1\dgvshow1
\jexpand\viewkind1\viewscale60\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct
\asianbrkrule\rsidroot15367925\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0
{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2
\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6
\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang
{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14818796 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0
\f31506\fs22\lang3081\langfe1033\cgrid\langnp3081\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid14818796 Copyright (c) 2011}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16724145 -2012}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid14818796 Shaun Wilde
\par Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is fu
rnished to do so, subject to the following conditions:
\par The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
\par THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid11275983
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210030dd4329a8060000a41b0000160000007468656d652f7468656d652f
7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87
615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad
79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b
5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab
999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9
699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586
8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6
0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f
9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be
15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979
3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d
32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a
f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86
e877f0034e16bafb0e258ebb4faf06b769e888340b103d331115bebc4eb813bf83291b63624a0d1475a756c734f9bbc2cd28546ecbe1e20a3794ca175f3fae90
fb6d2dd99bb07b55e5ccf68942bd0877b23c77b908e8db5f9db7f024d9239010f35bd4bbe2fcae387bfff9e2bc289f2fbe24cfaa301468dd8bd846dbb4ddf1c2
ae7b4c191ba8292337a469bc25ec3d411f06f53a73e224c5292c8de0516732307070a1c0660d125c7d44553488700a4d7bddd3444299910e254ab984c3a219ae
a4adf1d0f82b7bd46cea4388ad1c12ab5d1ed8e1153d9c9f350a3246aad01c6873462b9ac05999ad5cc988826eafc3acae853a33b7ba11cd1445875ba1b236b1
399483c90bd560b0b0263435085a21b0f22a9cf9356b38ec6046026d77eba3dc2dc60b17e92219e180643ed27acffba86e9c94c7ca9c225a0f1b0cfae0788ad5
4adc5a9aec1b703b8b93caec1a0bd8e5de7b132fe5113cf312503b998e2c2927274bd051db6b35979b1ef271daf6c6704e86c73805af4bdd476216c26593af84
0dfb5393d964f9cc9bad5c313709ea70f561ed3ea7b053075221d51696910d0d339585004b34272bff7213cc7a510a5454a3b349b1b206c1f0af490176745d4b
c663e2abb2b34b23da76f6352ba57ca2881844c1111ab189d8c7e07e1daaa04f40255c77988aa05fe06e4e5bdb4cb9c5394bbaf28d98c1d971ccd20867e556a7
689ec9166e0a522183792b8907ba55ca6e943bbf2a26e52f48957218ffcf54d1fb09dc3eac04da033e5c0d0b8c74a6b43d2e54c4a10aa511f5fb021a07533b20
5ae07e17a621a8e082dafc17e450ffb739676998b48643a4daa7211214f623150942f6a02c99e83b85583ddbbb2c4996113211551257a656ec1139246ca86be0
aadedb3d1441a89b6a929501833b197fee7b9641a3503739e57c732a59b1f7da1cf8a73b1f9bcca0945b874d4393dbbf10b1680f66bbaa5d6f96e77b6f59113d
316bb31a795600b3d256d0cad2fe354538e7566b2bd69cc6cbcd5c38f0e2bcc63058344429dc2121fd07f63f2a7c66bf76e80d75c8f7a1b622f878a18941d840
545fb28d07d205d20e8ea071b283369834296bdaac75d256cb37eb0bee740bbe278cad253b8bbfcf69eca23973d939b97891c6ce2cecd8da8e2d343578f6648a
c2d0383fc818c798cf64e52f597c740f1cbd05df0c264c49134cf09d4a60e8a107260f20f92d47b374e32f000000ffff0300504b030414000600080000002100
0dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f7
8277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89
d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd500
1996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0f
bfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6
a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a
0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021
0030dd4329a8060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d001400060008
00000021000dd1909fb60000001b0100002700000000000000000000000000b20900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ad0a00000000}
{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;
\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;
\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7;
\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font;
\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision;
\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6;
\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;
\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;
\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000
4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000
d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000e00b
b50e14f0cc01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000105000000000000}}

BIN
tests/CodeCoverage/OpenCover.4.6.519/MSBuild/OpenCover.MSBuild.dll

Binary file not shown.

10
tests/CodeCoverage/OpenCover.4.6.519/MSBuild/OpenCover.targets

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OpenCoverMSBuildTasksPath Condition="'$(OpenCoverMSBuildTasksPath)' == ''">$(MSBuildExtensionsPath32)\OpenCover</OpenCoverMSBuildTasksPath>
<OpenCoverMSBuildTasksLib>$(OpenCoverMSBuildTasksPath)\OpenCover.MSBuild.dll</OpenCoverMSBuildTasksLib>
</PropertyGroup>
<UsingTask AssemblyFile="$(OpenCoverMSBuildTasksLib)" TaskName="OpenCover.MSBuild.OpenCover" />
</Project>

6
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/.nuget/packages.config

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit.Runners" version="2.6.4" />
<package id="OpenCover" version="4.6.519" />
<package id="ReportGenerator" version="2.1.8.0" />
</packages>

53
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/Bom/Bom.csproj

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Bom</RootNamespace>
<AssemblyName>Bom</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BomManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

17
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/Bom/BomManager.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bom
{
public class BomManager
{
public int MethodToTest(IEnumerable<int> collection)
{
return (collection ?? new int[0]).Sum();
}
}
}

36
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/Bom/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Bom")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Bom")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2b4e3956-c04a-42f8-a367-87bc16ffa08d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

31
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomSample.sln

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bom", "Bom\Bom.csproj", "{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BomTest", "BomTest\BomTest.csproj", "{E25E828A-5D71-4E95-AEBC-7AD21315DFEC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{A3CD3B1D-4C14-4A8C-A57F-A265BC49FB29}"
ProjectSection(SolutionItems) = preProject
.nuget\packages.config = .nuget\packages.config
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}.Release|Any CPU.Build.0 = Release|Any CPU
{E25E828A-5D71-4E95-AEBC-7AD21315DFEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E25E828A-5D71-4E95-AEBC-7AD21315DFEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E25E828A-5D71-4E95-AEBC-7AD21315DFEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E25E828A-5D71-4E95-AEBC-7AD21315DFEC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

39
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/BomManagerTests.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bom;
using NUnit.Framework;
namespace BomTest
{
[TestFixture]
public class BomManagerTests
{
[Test]
public void Sum_Is_Zero_When_No_Entries()
{
var bomManager = new BomManager();
Assert.AreEqual(0, bomManager.MethodToTest(new Collection<int>()));
}
[Test]
[TestCase(new[] { 0 }, 0)]
[TestCase(new[] { 1 }, 1)]
[TestCase(new[] { 1, 2, 3 }, 6)]
public void Sum_Is_Calculated_Correctly_When_Entries_Supplied(int[] data, int expected)
{
var bomManager = new BomManager();
Assert.AreEqual(expected, bomManager.MethodToTest(new Collection<int>(data)));
}
[Test]
public void Sum_Is_Zero_When_Null_Collection()
{
var bomManager = new BomManager();
Assert.AreEqual(0, bomManager.MethodToTest(null));
}
}
}

65
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/BomTest.csproj

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E25E828A-5D71-4E95-AEBC-7AD21315DFEC}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BomTest</RootNamespace>
<AssemblyName>BomTest</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BomManagerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bom\Bom.csproj">
<Project>{0C1E1E72-A92D-4B64-83B1-FEF1D05B8605}</Project>
<Name>Bom</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

36
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BomTest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BomTest")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("688e9792-727d-4e39-a0ae-93461aa13b49")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

4
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/BomTest/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages>

5
tests/CodeCoverage/OpenCover.4.6.519/SampleSln/coverage.bat

@ -0,0 +1,5 @@
.\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe -register:user "-filter:+[Bom]* -[*Test]*" "-target:.\packages\NUnit.Runners.2.6.4\tools\nunit-console-x86.exe" "-targetargs:/noshadow .\BomTest\bin\Debug\BomTest.dll"
.\packages\ReportGenerator.2.1.8.0\tools\ReportGenerator.exe "-reports:results.xml" "-targetdir:.\coverage"
pause

137
tests/CodeCoverage/OpenCover.4.6.519/docs/ReleaseNotes.txt

@ -0,0 +1,137 @@
Version 4.6.519
#528 -safemode:on to address incomplete coverage due to thread based buffers (feature)
#521 add exclude paths (feature)
#376 protect buffer allocation in multithreaded environment (fix)
#335 allow short wait timeout to be configured (feature)
#310 improved reporting - hide branches due to iterators (feature)
#352 improved reporting - hide branches due to async (feature)
#363 calculate npath comlexity (feature)
#282 exclude by process (feature)
#246 auto crash reports (feature)
#329 address ArgumentOutOfRangeException (potentially related to #274) (fix for VS2015)
#335 error on unrecognized arguments/prefixes (fix)
#328 exclude types when declaredtype is excluded (fix-feature)
#302 ignore branches in known autogenerated sequences (feature)
Version 4.6.166
#323 push releases and candidates to github via appveyor (prj-mgmt)
#315 update nuget package (fix for VS2015)
#320 update packages (fix for VS2015)
#304 add switch to control timeout wait (feature)
#307 add -version to args (feature)
#305 add corclr_profiler_path support (feature)
#303 support for test cases and theories during track by test (feature)
#295 exclude assembly by attribute (feature)
#288 report (to console) if can't ready body of method (diag)
#287 fix crash (fix)
#283 Add visited class/method to summary entity (feature)
#274 Use thread based buffers for performance improvement (fix)
Version 4.5.3723
#244 support ApplicationUnderTest.Launch to propagate required OPENCOVER environment variables (feature)
#256 support Microsoft Fakes (feature) - beta support until we bed feature in (feature)
#248 address issue with Mono.Cecil and latest PDB (.NET 4.6) version (fix)
#252 use AppVeyor for building code and pull requests (prj-mgmt)
Version 4.5.3522
#243 null reference exception when excluding branch point in using finally block (fix)
Version 4.5.3427
#234 improved IIS support (fix)
#237 handle multiple files for a method e.g. during code contract re-writes (fix)
#228 add MDB support (feature)
#226 remove branch points on methods without sequence points (fix)
#225 Enable filters to use regular expressions (feature)
#218 Auto tag release notes (prj-mgmt)
#116 output results to accumulate with previous coverage file activate by -mergeoutput (feature)
Version 4.5.3207
#209 The number of WaitHandles must be less than or equal to 64 (fix)
#208 Line Number for Branch Points (feature)
#207 "using" statement causes incorrect Branch Coverage (fix)
#201 NETWORK SERVICE support (feature)
Version 4.5.2506
#188 Bring back COR_PRF_DISABLE_ALL_NGEN_IMAGES
#190 Compiler generated "Empty" Branch Points feature high close on next release
#191 SequencePoint FileID [CodeContractClass/For]
Version 4.5.2316
#170 - Overflow fixed
#188 - re-introduced COR_PRF_DISABLE_ALL_NGEN_IMAGES
#174 / #176 - pass arguments as multiple variable
Version 4.5.1923
#168 - skip auto implemented properties
#164 - allow registryless loading
#163 - improved error messages
Version 4.5.1604
#156 - prepend targetdir when applying test strategies (silverlight)
Version 4.5.1528
#158 - fix app domain crash due to timeout of proxy
Version 4.5.1403
#154 - Add xUnit to the list of supported strategies for the Cover by Test feature
#150 - fix for Xslt issue
Build Environment now uses BDD tests to ensure the packages have all assemblies required to run
Version 4.5.1314
#148 - Fix issue with nuget and zip packages and missing Autofac assembly.
Version 4.5.1313
#118 - Fix communication issue between profiler and host when many processes are vying for the channel
- improved thread management
- only check if method needs tracing if coverbytest option is utilised
Version 4.5.1310
#128 - Add threshold limits (optional commandline) to reduce reporting of visits
#135 - Add performance counters (admin privileges required) around the memory processing queues
Version 4.5.1302
Update version number to reflect 4.5 support
Fix bug in summaries
Version 4.0.1229
Supports .NET 4.5 (not windows store apps)
#120 - Built in Summary Reports - useful for build systems
Version 4.0.1128
#125 - Hide compiler generated method when no source remains after skipping
#107 - fix 'sporadic' crash when dealing with Generic methods.
Version 4.0.1118
#137 - Fix instrumentation issue when dealing with typed clauses
#107 - fix 'sporadic' crash when dealing with Generic methods.
Version 4.0.1107
#133 - Remove skipped File/Module/Class/Methods from report
#130 - Support for 'returntargetcode' switch in msbuild task
#126, #127, #132 - ReportGenerator upgrades
#122 - filter from file instead of command line list option (#123 patch)
Version 4.0.804
#117 - fix filter crash with anonymous types
#110 - fix timeout issues due to performance woes in dealing with large number of types/methods
Version 4.0.724
#94 - remove thread that "may" have been the cause of the nunit-agent.exe closedown issue - switched to a shared memory buffer per child process/profiler object instantiated
#108 - merge pull request - ToolPath property for MSBuild command
Version 4.0.519
#102 - add msbuild parts to zip and nuget package
#99 - exclude anonymous functions if containing method is excluded
#97 - fix crash based on receiving corrupt data (sequence point with id==0)
#88 - only use COR_PRF_DISABLE_TRANSPARENCY_CHECKS_UNDER_FULL_TRUST if oldStyle instrumentation
Version 4.0.408
#83 - build a zip package
#88 - provide a switch for "old school" instrumentation
#95 - fix for namespaces with spaces
Version 4.0.301.10
#78 - fix for endfault/endfinally
#71 - detect disabled service

1138
tests/CodeCoverage/OpenCover.4.6.519/docs/Usage.rtf

File diff suppressed because it is too large

21
tests/CodeCoverage/OpenCover.4.6.519/readme.txt

@ -0,0 +1,21 @@
Welcome to OpenCover.
OpenCover is an open source (MIT licence) code coverage utility for all .NET
Frameworks (2 and above, including Silverlight). It can handle all .NET
languages as long as the compiled assemblies use the .NET Runtime.
It is most commonly used to gather coverage data of code that is being
exercised by unit testing i.e. nunit, mstest etc.
We recommend that you view the documents that have also been installed
alongside the utility to get started or you can look at the documentation
provided on the wiki https://github.com/OpenCover/opencover/wiki/Usage.
Currently OpenCover has no visualization of its results and we recommend that
your use ReportGenerator (2.1.8 and above) to view the results against your code.
A sample project showing a possible way to use OpenCover with ReportGenerator
has also been provided for your convenience.
If you have any issues or feature requests with OpenCover please raise them
with the project owners on https://github.com/opencover/opencover.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Autofac.Configuration.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Autofac.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/CrashReporter.NET.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Gendarme.Framework.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Gendarme.Rules.Maintainability.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Mono.Cecil.Mdb.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Mono.Cecil.Pdb.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/Mono.Cecil.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Console.exe

Binary file not shown.

26
tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Console.exe.config

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration" />
</configSections>
<autofac>
<modules>
<module type="OpenCover.Extensions.RegisterStrategiesModule, OpenCover.Extensions" />
</modules>
</autofac>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Mono.Cecil" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.9.6.0" newVersion="0.9.6.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Extensions.dll

Binary file not shown.

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/OpenCover.Framework.dll

Binary file not shown.

39
tests/CodeCoverage/OpenCover.4.6.519/tools/log4net.config

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="ERROR" />
<foreColor value="White" />
<backColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="DEBUG" />
<backColor value="Green" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message%newline" />
</layout>
</appender>
<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="OpenCover: (Host) %message" />
</layout>
</appender>
<logger name="DebugLogger" additivity="false">
<level value="ALL" />
<appender-ref ref="OutputDebugStringAppender" />
</logger>
<root>
<appender-ref ref="ColoredConsoleAppender" />
</root>
</log4net>
</configuration>

BIN
tests/CodeCoverage/OpenCover.4.6.519/tools/log4net.dll

Binary file not shown.

332
tests/CodeCoverage/OpenCover.4.6.519/transform/simple_report.xslt

@ -0,0 +1,332 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This report was provided by pawan52tiwari (https://github.com/pawan52tiwari)
see https://github.com/sawilde/opencover/issues/93
sample usage:
powershell -noexit -file transform.ps1 -xsl simple_report.xslt -xml ..\results\opencovertests.xml -output ..\results\simple_output.html
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="html"/>
<xsl:variable name="covered.lines" select="count(/CoverageSession/Modules/Module/Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0])" />
<xsl:variable name="uncovered.lines" select="count(/CoverageSession/Modules/Module/Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc = 0])" />
<xsl:variable name="coverable.Lines" select="count(/CoverageSession/Modules/Module/Classes/Class/Methods/Method/SequencePoints/SequencePoint)" />
<xsl:template match="/">
<html><body>
<h2 class="sectionheader">Code Coverage Report</h2>
<table class="overview">
<colgroup>
<col width="130" />
<col />
</colgroup>
<tr>
<td class="sectionheader">
Generated on:
</td>
<td>
<xsl:value-of select="/@date"/>
</td>
</tr>
<tr>
<td class="sectionheader">
Parser:
</td>
<td>
Pawan Tiwari's Parser
</td>
</tr>
<tr>
<td class="sectionheader">
Assemblies:
</td>
<td>
<xsl:value-of select="count(/CoverageSession/Modules/Module/ModuleName)"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
Files:
</td>
<td>
<xsl:value-of select="count(/CoverageSession/Modules/Module/Files/File)"/>
</td>
</tr>
<tr>
<td class="sectionheader">
Coverage:
</td>
<td>
<xsl:value-of select="$covered.lines div ($uncovered.lines + $covered.lines) * 100"/>%
</td>
</tr>
<tr>
<td class="sectionheader">
Covered lines:
</td>
<td>
<xsl:value-of select="$covered.lines"/>
</td>
</tr>
<tr>
<td class="sectionheader">
UnCovered lines:
</td>
<td>
<xsl:value-of select="$uncovered.lines"/>
</td>
</tr>
<tr>
<td class="sectionheader">
Coverable lines:
</td>
<td>
<xsl:value-of select="$coverable.Lines"/>
</td>
</tr>
<tr>
<td class="sectionheader">
Total lines:
</td>
<td>
Yet To be discovered
</td>
</tr>
</table>
<h2 class="sectionheader">
Assemblies
</h2>
<p class="toggleClasses">
<a id="collapseAllClasses" style="text-decoration: none;color:red;font-size:10px" href="#">Collapse all classes</a> | <a id="expandAllClasses" style="text-decoration: none;color:red;font-size:10px"
href="#">Expand all classes</a>
</p>
<table class="overview">
<colgroup>
<col />
<col width="60" />
<col width="105" />
</colgroup>
<xsl:for-each select="/CoverageSession/Modules/Module">
<xsl:sort select="ModuleName" order="ascending"/>
<xsl:sort select="ModuleName"/>
<xsl:variable name="ModulenameVariable" select="ModuleName"></xsl:variable>
<xsl:variable name="FileLocationLink" select="."></xsl:variable>
<tr class="expanded">
<th>
<a href="#" class="toggleClassesInAssembly" style="text-decoration: none;color:red;font-size:10px" title="Collapse/Expand classes"></a>
<xsl:value-of select="ModuleName"/>
<a href="#" class="toggleAssemblyDetails" style="text-decoration: none;color:red;font-size:10px" title="Show details of assembly">Details</a>
<div class="detailspopup">
<table class="overview">
<colgroup>
<col width="130" />
<col />
</colgroup>
<tr>
<td class="sectionheader">
Classes:
</td>
<td>
<xsl:value-of select="count(Classes/Class/FullName[not(contains(text(),'&lt;'))])"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
Covered lines:
</td>
<td>
<xsl:value-of select="count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0])"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
Coverable lines:
</td>
<td>
<xsl:value-of select="count(Classes/Class/Methods/Method/SequencePoints/SequencePoint)" />
</td>
</tr>
<tr>
<td class="sectionheader">
Coverage:
</td>
<td>
<xsl:choose>
<xsl:when test="(count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0])) &gt; 0">
<xsl:value-of select="count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0]) div (count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0])) * 100"/>%
</xsl:when>
<xsl:otherwise>
0
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</table>
</div>
</th>
<th title="LineCoverage">
<xsl:if test="(Classes/Class/Methods/Method)">
<xsl:value-of select="round(count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0]) div (count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0])) * 100)"/>%
</xsl:if>
</th>
<td>
<xsl:variable name="width" select="count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0]) div (count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Classes/Class/Methods/Method/SequencePoints/SequencePoint[@vc > 0])) * 100"></xsl:variable>
<table class="coverage">
<tr>
<td class="green" style="width: {$width}px;">
&#160;
</td>
<td class="red" style="width: {100-$width}px;">
&#160;
</td>
</tr>
</table>
</td>
</tr>
<xsl:for-each select="Classes/Class">
<xsl:if test="FullName[not(contains(text(),'&lt;'))]">
<tr class="classrow">
<td align="center">
<h3 class="sectionheader">
Class Name:<xsl:value-of select="FullName"></xsl:value-of>
</h3>
</td>
<td title="LineCoverage">
<xsl:value-of select="round(count(Methods/Method/SequencePoints/SequencePoint[@vc > 0]) div (count(Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Methods/Method/SequencePoints/SequencePoint[@vc > 0])) * 100)"/>%
</td>
<td>
<table class="coverage">
<tr width="100px">
<xsl:variable name="Line.CoveragerClass" select="round(count(Methods/Method/SequencePoints/SequencePoint[@vc > 0]) div (count(Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Methods/Method/SequencePoints/SequencePoint[@vc > 0])) * 100)"></xsl:variable>
<td class="green" style="width: {$Line.CoveragerClass +9}px;">
&#160;
</td>
<td class="red" style="width: {100- $Line.CoveragerClass}px;">
&#160;
</td>
</tr>
</table>
</td>
</tr>
<tr class="classrow">
<td colspan="3">
<table class="overview">
<colgroup>
<col width="130" />
<col />
</colgroup>
<tr>
<td class="sectionheader">
Class:
</td>
<td>
<xsl:value-of select="FullName"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
Assembly:
</td>
<td>
<xsl:value-of select="$ModulenameVariable"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
File(s):
</td>
<td>
<xsl:variable name="CounterForFile" select="position()"></xsl:variable>
<xsl:value-of select="$FileLocationLink/Files/File[$CounterForFile]/@fullPath"/>
<xsl:variable name="FilePathVariable" select="//Files/File[@uid=($CounterForFile -1)]/@fullPath"></xsl:variable>
<a href="file:///{$FilePathVariable}">
<!--<xsl:value-of select="$FilePathVariable"></xsl:value-of>-->
</a>
</td>
</tr>
<tr>
<td class="sectionheader">
Coverage:
</td>
<td>
<xsl:variable name="Covered.lines" select="count(Methods/Method/SequencePoints/SequencePoint[@vc > 0])"></xsl:variable>
<xsl:value-of select="count(Methods/Method/SequencePoints/SequencePoint[@vc > 0]) div (count(Methods/Method/SequencePoints/SequencePoint[@vc = 0]) + count(Methods/Method/SequencePoints/SequencePoint[@vc > 0])) * 100"/>%
</td>
</tr>
<tr>
<td class="sectionheader">
Covered lines:
</td>
<td>
<xsl:value-of select="count(Methods/Method/SequencePoints/SequencePoint[@vc > 0])"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
Coverable lines:
</td>
<td>
<xsl:value-of select="count(Methods/Method/SequencePoints/SequencePoint)"></xsl:value-of>
</td>
</tr>
<tr>
<td class="sectionheader">
Total lines:
</td>
<td>
51
</td>
</tr>
</table>
</td>
</tr>
<tr class="classrow">
<td colspan="3">
<table class="overview">
<tr>
<td class="sectionheader">
Method
</td>
<td class="sectionheader">
Cyclomatic Complexity
</td>
<td class="sectionheader">
Sequence Coverage
</td>
<td class="sectionheader">
Branch Coverage
</td>
<td class="sectionheader">
Static Method
</td>
</tr>
<xsl:for-each select="Methods/Method[@isConstructor='false']">
<tr>
<td>
<xsl:variable name="indexvariable" select="string-length(substring-before(Name, '::'))"/>
<xsl:value-of disable-output-escaping="yes" select="substring(Name,$indexvariable +3)"></xsl:value-of>
</td>
<td>
<xsl:value-of disable-output-escaping="yes" select="@cyclomaticComplexity"></xsl:value-of>
</td>
<td>
<xsl:value-of disable-output-escaping="yes" select="@sequenceCoverage"></xsl:value-of>
</td>
<td>
<xsl:value-of disable-output-escaping="yes" select="@branchCoverage"></xsl:value-of>
</td>
<td>
<xsl:value-of disable-output-escaping="yes" select="@isStatic"></xsl:value-of>
</td>
</tr>
</xsl:for-each>
</table>
</td>
</tr>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>

17
tests/CodeCoverage/OpenCover.4.6.519/transform/transform.ps1

@ -0,0 +1,17 @@
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$xsl,
[Parameter(Mandatory=$True)]
[string]$xml,
[Parameter(Mandatory=$True)]
[string]$output
)
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$xslt.Load($xsl);
$xslt.Transform($xml, $output);
Write-Host "The file has been transformed."

4
tests/CodeCoverage/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="OpenCover" version="4.6.519" />
</packages>

192
tests/SixLabors.Primitives.Tests/PointFTests.cs

@ -0,0 +1,192 @@
// <copyright file="PointFTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests.Numerics
{
using System;
using System.Globalization;
using System.Numerics;
using System.Reflection;
using Xunit;
public class PointFTests
{
[Fact]
public void DefaultConstructorTest()
{
Assert.Equal(PointF.Empty, new PointF());
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(float.MinValue, float.MaxValue)]
[InlineData(0.0, 0.0)]
public void NonDefaultConstructorTest(float x, float y)
{
var p1 = new PointF(x, y);
Assert.Equal(x, p1.X);
Assert.Equal(y, p1.Y);
}
[Fact]
public void IsEmptyDefaultsTest()
{
Assert.True(PointF.Empty.IsEmpty);
Assert.True(new PointF().IsEmpty);
Assert.True(new PointF(0, 0).IsEmpty);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
public void IsEmptyRandomTest(float x, float y)
{
Assert.False(new PointF(x, y).IsEmpty);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void CoordinatesTest(float x, float y)
{
var p = new PointF(x, y);
Assert.Equal(x, p.X);
Assert.Equal(y, p.Y);
p.X = 10;
Assert.Equal(10, p.X);
p.Y = -10.123f;
Assert.Equal(-10.123, p.Y, 3);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue, int.MaxValue, int.MinValue)]
[InlineData(float.MinValue, float.MaxValue, int.MinValue, int.MaxValue)]
[InlineData(0, 0, 0, 0)]
public void ArithmeticTestWithSize(float x, float y, int x1, int y1)
{
var p = new PointF(x, y);
var s = new Size(x1, y1);
var addExpected = new PointF(x + x1, y + y1);
var subExpected = new PointF(x - x1, y - y1);
Assert.Equal(addExpected, p + s);
Assert.Equal(subExpected, p - s);
Assert.Equal(addExpected, PointF.Add(p, s));
Assert.Equal(subExpected, PointF.Subtract(p, s));
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MaxValue)]
[InlineData(0, 0)]
public void ArithmeticTestWithSizeF(float x, float y)
{
var p = new PointF(x, y);
var s = new SizeF(y, x);
var addExpected = new PointF(x + y, y + x);
var subExpected = new PointF(x - y, y - x);
Assert.Equal(addExpected, p + s);
Assert.Equal(subExpected, p - s);
Assert.Equal(addExpected, PointF.Add(p, s));
Assert.Equal(subExpected, PointF.Subtract(p, s));
}
[Fact]
public void RotateTest()
{
var p = new PointF(13, 17);
Matrix matrix = Matrix.CreateRotationDegrees(45, PointF.Empty);
var pout = PointF.Rotate(p, matrix);
Assert.Equal(new PointF(-2.82842732F, 21.2132034F), pout);
}
[Fact]
public void SkewTest()
{
var p = new PointF(13, 17);
Matrix matrix = Matrix.CreateSkewDegrees(45, 45, PointF.Empty);
var pout = PointF.Skew(p, matrix);
Assert.Equal(new PointF(30, 30), pout);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MaxValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void EqualityTest(float x, float y)
{
var pLeft = new PointF(x, y);
var pRight = new PointF(y, x);
if (x == y)
{
Assert.True(pLeft == pRight);
Assert.False(pLeft != pRight);
Assert.True(pLeft.Equals(pRight));
Assert.True(pLeft.Equals((object)pRight));
Assert.Equal(pLeft.GetHashCode(), pRight.GetHashCode());
return;
}
Assert.True(pLeft != pRight);
Assert.False(pLeft == pRight);
Assert.False(pLeft.Equals(pRight));
Assert.False(pLeft.Equals((object)pRight));
}
[Fact]
public static void EqualityTest_NotPointF()
{
var point = new PointF(0, 0);
Assert.False(point.Equals(null));
Assert.False(point.Equals(0));
// If PointF implements IEquatable<PointF> (e.g. in .NET Core), then structs that are implicitly
// convertible to var can potentially be equal.
// See https://github.com/dotnet/corefx/issues/5255.
bool expectsImplicitCastToPointF = typeof(IEquatable<PointF>).IsAssignableFrom(point.GetType());
Assert.Equal(expectsImplicitCastToPointF, point.Equals(new Point(0, 0)));
Assert.False(point.Equals((object)new Point(0, 0))); // No implicit cast
}
[Fact]
public static void GetHashCodeTest()
{
var point = new PointF(10, 10);
Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode());
Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode());
Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode());
}
[Fact]
public void ToStringTest()
{
var p = new PointF(5.1F, -5.123F);
Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString());
}
[Fact]
public void ToStringEmptyTest()
{
var p = new PointF(0, 0);
Assert.Equal("PointF [ Empty ]", p.ToString());
}
}
}

252
tests/SixLabors.Primitives.Tests/PointTests.cs

@ -0,0 +1,252 @@
// <copyright file="PointTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using System.Globalization;
using System.Numerics;
using Xunit;
public class PointTests
{
[Fact]
public void DefaultConstructorTest()
{
Assert.Equal(Point.Empty, new Point());
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void NonDefaultConstructorTest(int x, int y)
{
var p1 = new Point(x, y);
var p2 = new Point(new Size(x, y));
Assert.Equal(p1, p2);
}
[Theory]
[InlineData(int.MaxValue)]
[InlineData(int.MinValue)]
[InlineData(0)]
public void SingleIntConstructorTest(int x)
{
var p1 = new Point(x);
var p2 = new Point(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF)));
Assert.Equal(p1, p2);
}
[Fact]
public void IsEmptyDefaultsTest()
{
Assert.True(Point.Empty.IsEmpty);
Assert.True(new Point().IsEmpty);
Assert.True(new Point(0, 0).IsEmpty);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
public void IsEmptyRandomTest(int x, int y)
{
Assert.False(new Point(x, y).IsEmpty);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void CoordinatesTest(int x, int y)
{
var p = new Point(x, y);
Assert.Equal(x, p.X);
Assert.Equal(y, p.Y);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void PointFConversionTest(int x, int y)
{
PointF p = new Point(x, y);
Assert.Equal(new PointF(x, y), p);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void SizeConversionTest(int x, int y)
{
var sz = (Size)new Point(x, y);
Assert.Equal(new Size(x, y), sz);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void ArithmeticTest(int x, int y)
{
Point addExpected, subExpected, p = new Point(x, y);
var s = new Size(y, x);
unchecked
{
addExpected = new Point(x + y, y + x);
subExpected = new Point(x - y, y - x);
}
Assert.Equal(addExpected, p + s);
Assert.Equal(subExpected, p - s);
Assert.Equal(addExpected, Point.Add(p, s));
Assert.Equal(subExpected, Point.Subtract(p, s));
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void PointFMathematicalTest(float x, float y)
{
var pf = new PointF(x, y);
Point pCeiling, pTruncate, pRound;
unchecked
{
pCeiling = new Point((int)MathF.Ceiling(x), (int)MathF.Ceiling(y));
pTruncate = new Point((int)x, (int)y);
pRound = new Point((int)MathF.Round(x), (int)MathF.Round(y));
}
Assert.Equal(pCeiling, Point.Ceiling(pf));
Assert.Equal(pRound, Point.Round(pf));
Assert.Equal(pTruncate, (Point)pf);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void OffsetTest(int x, int y)
{
var p1 = new Point(x, y);
var p2 = new Point(y, x);
p1.Offset(p2);
Assert.Equal(unchecked(p2.X + p2.Y), p1.X);
Assert.Equal(p1.X, p1.Y);
p2.Offset(x, y);
Assert.Equal(p1, p2);
}
[Fact]
public void RotateTest()
{
var p = new Point(13, 17);
Matrix matrix = Matrix.CreateRotationDegrees(45, Point.Empty);
var pout = Point.Rotate(p, matrix);
Assert.Equal(new Point(-3, 21), pout);
}
[Fact]
public void SkewTest()
{
var p = new Point(13, 17);
Matrix3x2 matrix = Matrix.CreateSkewDegrees(45, 45, Point.Empty);
var pout = Point.Skew(p, matrix);
Assert.Equal(new Point(30, 30), pout);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void EqualityTest(int x, int y)
{
var p1 = new Point(x, y);
var p2 = new Point(x / 2 - 1, y / 2 - 1);
var p3 = new Point(x, y);
Assert.True(p1 == p3);
Assert.True(p1 != p2);
Assert.True(p2 != p3);
Assert.True(p1.Equals(p3));
Assert.False(p1.Equals(p2));
Assert.False(p2.Equals(p3));
Assert.True(p1.Equals((object)p3));
Assert.False(p1.Equals((object)p2));
Assert.False(p2.Equals((object)p3));
Assert.Equal(p1.GetHashCode(), p3.GetHashCode());
}
[Fact]
public static void EqualityTest_NotPoint()
{
var point = new Point(0, 0);
Assert.False(point.Equals(null));
Assert.False(point.Equals(0));
Assert.False(point.Equals(new PointF(0, 0)));
}
[Fact]
public static void GetHashCodeTest()
{
var point = new Point(10, 10);
Assert.Equal(point.GetHashCode(), new Point(10, 10).GetHashCode());
Assert.NotEqual(point.GetHashCode(), new Point(20, 10).GetHashCode());
Assert.NotEqual(point.GetHashCode(), new Point(10, 20).GetHashCode());
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(1, -2, 3, -4)]
public void ConversionTest(int x, int y, int width, int height)
{
var rect = new Rectangle(x, y, width, height);
RectangleF rectF = rect;
Assert.Equal(x, rectF.X);
Assert.Equal(y, rectF.Y);
Assert.Equal(width, rectF.Width);
Assert.Equal(height, rectF.Height);
}
[Fact]
public void ToStringTest()
{
var p = new Point(5, -5);
Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Point [ X={0}, Y={1} ]", p.X, p.Y), p.ToString());
}
[Fact]
public void ToStringEmptyTest()
{
var p = new Point(0, 0);
Assert.Equal("Point [ Empty ]", p.ToString());
}
}
}

115
tests/SixLabors.Primitives.Tests/RationalTests.cs

@ -0,0 +1,115 @@
// <copyright file="RationalTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using Xunit;
/// <summary>
/// Tests the <see cref="Rational"/> struct.
/// </summary>
public class RationalTests
{
/// <summary>
/// Tests the equality operators for equality.
/// </summary>
[Fact]
public void AreEqual()
{
Rational r1 = new Rational(3, 2);
Rational r2 = new Rational(3, 2);
Assert.Equal(r1, r2);
Assert.True(r1 == r2);
Rational r3 = new Rational(7.55);
Rational r4 = new Rational(755, 100);
Rational r5 = new Rational(151, 20);
Assert.Equal(r3, r4);
Assert.Equal(r4, r5);
}
/// <summary>
/// Tests the equality operators for inequality.
/// </summary>
[Fact]
public void AreNotEqual()
{
Rational first = new Rational(0, 100);
Rational second = new Rational(100, 100);
Assert.NotEqual(first, second);
Assert.True(first != second);
}
/// <summary>
/// Tests whether the Rational constructor correctly assign properties.
/// </summary>
[Fact]
public void ConstructorAssignsProperties()
{
Rational rational = new Rational(7, 55);
Assert.Equal(7U, rational.Numerator);
Assert.Equal(55U, rational.Denominator);
rational = new Rational(755, 100);
Assert.Equal(151U, rational.Numerator);
Assert.Equal(20U, rational.Denominator);
rational = new Rational(755, 100, false);
Assert.Equal(755U, rational.Numerator);
Assert.Equal(100U, rational.Denominator);
rational = new Rational(-7.55);
Assert.Equal(151U, rational.Numerator);
Assert.Equal(20U, rational.Denominator);
rational = new Rational(7);
Assert.Equal(7U, rational.Numerator);
Assert.Equal(1U, rational.Denominator);
}
[Fact]
public void Fraction()
{
Rational first = new Rational(1.0 / 1600);
Rational second = new Rational(1.0 / 1600, true);
Assert.False(first.Equals(second));
}
[Fact]
public void ToDouble()
{
Rational rational = new Rational(0, 0);
Assert.Equal(double.NaN, rational.ToDouble());
rational = new Rational(2, 0);
Assert.Equal(double.PositiveInfinity, rational.ToDouble());
}
[Fact]
public void ToStringRepresention()
{
Rational rational = new Rational(0, 0);
Assert.Equal("[ Indeterminate ]", rational.ToString());
rational = new Rational(double.PositiveInfinity);
Assert.Equal("[ PositiveInfinity ]", rational.ToString());
rational = new Rational(double.NegativeInfinity);
Assert.Equal("[ PositiveInfinity ]", rational.ToString());
rational = new Rational(0, 1);
Assert.Equal("0", rational.ToString());
rational = new Rational(2, 1);
Assert.Equal("2", rational.ToString());
rational = new Rational(1, 2);
Assert.Equal("1/2", rational.ToString());
}
}
}

267
tests/SixLabors.Primitives.Tests/RectangleFTests.cs

@ -0,0 +1,267 @@
// <copyright file="RectangleFTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using System;
using System.Globalization;
using System.Reflection;
using Xunit;
/// <summary>
/// Tests the <see cref="RectangleF"/> struct.
/// </summary>
public class RectangleFTests
{
[Fact]
public void DefaultConstructorTest()
{
Assert.Equal(RectangleF.Empty, new RectangleF());
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(float.MaxValue, 0, 0, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void NonDefaultConstructorTest(float x, float y, float width, float height)
{
var rect1 = new RectangleF(x, y, width, height);
var p = new PointF(x, y);
var s = new SizeF(width, height);
var rect2 = new RectangleF(p, s);
Assert.Equal(rect1, rect2);
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(float.MaxValue, 0, 0, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void FromLTRBTest(float left, float top, float right, float bottom)
{
var expected = new RectangleF(left, top, right - left, bottom - top);
var actual = RectangleF.FromLTRB(left, top, right, bottom);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(float.MaxValue, 0, 0, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void DimensionsTest(float x, float y, float width, float height)
{
var rect = new RectangleF(x, y, width, height);
var p = new PointF(x, y);
var s = new SizeF(width, height);
Assert.Equal(p, rect.Location);
Assert.Equal(s, rect.Size);
Assert.Equal(x, rect.X);
Assert.Equal(y, rect.Y);
Assert.Equal(width, rect.Width);
Assert.Equal(height, rect.Height);
Assert.Equal(x, rect.Left);
Assert.Equal(y, rect.Top);
Assert.Equal(x + width, rect.Right);
Assert.Equal(y + height, rect.Bottom);
}
[Fact]
public void IsEmptyTest()
{
Assert.True(RectangleF.Empty.IsEmpty);
Assert.True(new RectangleF().IsEmpty);
Assert.True(new RectangleF(1, -2, -10, 10).IsEmpty);
Assert.True(new RectangleF(1, -2, 10, -10).IsEmpty);
Assert.True(new RectangleF(1, -2, 0, 0).IsEmpty);
Assert.False(new RectangleF(0, 0, 10, 10).IsEmpty);
}
[Theory]
[InlineData(0, 0)]
[InlineData(float.MaxValue, float.MinValue)]
public static void LocationSetTest(float x, float y)
{
var point = new PointF(x, y);
var rect = new RectangleF(10, 10, 10, 10) { Location = point };
Assert.Equal(point, rect.Location);
Assert.Equal(point.X, rect.X);
Assert.Equal(point.Y, rect.Y);
}
[Theory]
[InlineData(0, 0)]
[InlineData(float.MaxValue, float.MinValue)]
public static void SizeSetTest(float x, float y)
{
var size = new SizeF(x, y);
var rect = new RectangleF(10, 10, 10, 10) { Size = size };
Assert.Equal(size, rect.Size);
Assert.Equal(size.Width, rect.Width);
Assert.Equal(size.Height, rect.Height);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(float.MaxValue, 0, 0, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void EqualityTest(float x, float y, float width, float height)
{
var rect1 = new RectangleF(x, y, width, height);
var rect2 = new RectangleF(width, height, x, y);
Assert.True(rect1 != rect2);
Assert.False(rect1 == rect2);
Assert.False(rect1.Equals(rect2));
Assert.False(rect1.Equals((object)rect2));
}
[Fact]
public static void EqualityTestNotRectangleF()
{
var rectangle = new RectangleF(0, 0, 0, 0);
Assert.False(rectangle.Equals(null));
Assert.False(rectangle.Equals(0));
// If RectangleF implements IEquatable<RectangleF> (e.g. in .NET Core), then classes that are implicitly
// convertible to RectangleF can potentially be equal.
// See https://github.com/dotnet/corefx/issues/5255.
bool expectsImplicitCastToRectangleF = typeof(IEquatable<RectangleF>).IsAssignableFrom(rectangle.GetType());
Assert.Equal(expectsImplicitCastToRectangleF, rectangle.Equals(new Rectangle(0, 0, 0, 0)));
Assert.False(rectangle.Equals((object)new Rectangle(0, 0, 0, 0))); // No implicit cast
}
[Fact]
public static void GetHashCodeTest()
{
var rect1 = new RectangleF(10, 10, 10, 10);
var rect2 = new RectangleF(10, 10, 10, 10);
Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new RectangleF(20, 10, 10, 10).GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 20, 10, 10).GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 20, 10).GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 10, 20).GetHashCode());
}
[Theory]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void ContainsTest(float x, float y, float width, float height)
{
var rect = new RectangleF(x, y, width, height);
float X = (x + width) / 2;
float Y = (y + height) / 2;
var p = new PointF(X, Y);
var r = new RectangleF(X, Y, width / 2, height / 2);
Assert.False(rect.Contains(X, Y));
Assert.False(rect.Contains(p));
Assert.False(rect.Contains(r));
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(float.MaxValue / 2, float.MinValue / 2, float.MinValue / 2, float.MaxValue / 2)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void InflateTest(float x, float y, float width, float height)
{
var rect = new RectangleF(x, y, width, height);
var inflatedRect = new RectangleF(x - width, y - height, width + 2 * width, height + 2 * height);
rect.Inflate(width, height);
Assert.Equal(inflatedRect, rect);
var s = new SizeF(x, y);
inflatedRect = RectangleF.Inflate(rect, x, y);
rect.Inflate(s);
Assert.Equal(inflatedRect, rect);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue, float.MaxValue / 2, float.MinValue / 2)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void IntersectTest(float x, float y, float width, float height)
{
var rect1 = new RectangleF(x, y, width, height);
var rect2 = new RectangleF(y, x, width, height);
var expectedRect = RectangleF.Intersect(rect1, rect2);
rect1.Intersect(rect2);
Assert.Equal(expectedRect, rect1);
Assert.False(rect1.IntersectsWith(expectedRect));
}
[Fact]
public static void IntersectIntersectingRectsTest()
{
var rect1 = new RectangleF(0, 0, 5, 5);
var rect2 = new RectangleF(1, 1, 3, 3);
var expected = new RectangleF(1, 1, 3, 3);
Assert.Equal(expected, RectangleF.Intersect(rect1, rect2));
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(float.MaxValue, 0, 0, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void UnionTest(float x, float y, float width, float height)
{
var a = new RectangleF(x, y, width, height);
var b = new RectangleF(width, height, x, y);
float x1 = Math.Min(a.X, b.X);
float x2 = Math.Max(a.X + a.Width, b.X + b.Width);
float y1 = Math.Min(a.Y, b.Y);
float y2 = Math.Max(a.Y + a.Height, b.Y + b.Height);
var expectedRectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1);
Assert.Equal(expectedRectangle, RectangleF.Union(a, b));
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)]
[InlineData(float.MaxValue, 0, 0, float.MaxValue)]
[InlineData(0, float.MinValue, float.MaxValue, 0)]
public void OffsetTest(float x, float y, float width, float height)
{
var r1 = new RectangleF(x, y, width, height);
var expectedRect = new RectangleF(x + width, y + height, width, height);
var p = new PointF(width, height);
r1.Offset(p);
Assert.Equal(expectedRect, r1);
expectedRect.Offset(p);
r1.Offset(width, height);
Assert.Equal(expectedRect, r1);
}
[Fact]
public void ToStringTest()
{
var r = new RectangleF(5, 5.1F, 1.3F, 1);
Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString());
}
[InlineData(0, 0, 0, 0)]
[InlineData(5, -5, 0.2, -1.3)]
public void ToStringTestEmpty(float x, float y, float width, float height)
{
var r = new RectangleF(x, y, width, height);
Assert.Equal("RectangleF [ Empty ]", r.ToString());
}
}
}

308
tests/SixLabors.Primitives.Tests/RectangleTests.cs

@ -0,0 +1,308 @@
// <copyright file="RectangleTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using System;
using System.Globalization;
using Xunit;
/// <summary>
/// Tests the <see cref="Rectangle"/> struct.
/// </summary>
public class RectangleTests
{
[Fact]
public void DefaultConstructorTest()
{
Assert.Equal(Rectangle.Empty, new Rectangle());
}
[Theory]
[InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)]
[InlineData(int.MaxValue, 0, int.MinValue, 0)]
[InlineData(0, 0, 0, 0)]
[InlineData(0, int.MinValue, 0, int.MaxValue)]
public void NonDefaultConstructorTest(int x, int y, int width, int height)
{
var rect1 = new Rectangle(x, y, width, height);
var rect2 = new Rectangle(new Point(x, y), new Size(width, height));
Assert.Equal(rect1, rect2);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)]
[InlineData(int.MaxValue, 0, int.MinValue, 0)]
[InlineData(0, 0, 0, 0)]
[InlineData(0, int.MinValue, 0, int.MaxValue)]
public void FromLTRBTest(int left, int top, int right, int bottom)
{
var rect1 = new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top));
var rect2 = Rectangle.FromLTRB(left, top, right, bottom);
Assert.Equal(rect1, rect2);
}
[Fact]
public void EmptyTest()
{
Assert.True(Rectangle.Empty.IsEmpty);
Assert.True(new Rectangle(0, 0, 0, 0).IsEmpty);
Assert.True(new Rectangle().IsEmpty);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)]
[InlineData(int.MaxValue, 0, int.MinValue, 0)]
[InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)]
[InlineData(0, int.MinValue, 0, int.MaxValue)]
public void NonEmptyTest(int x, int y, int width, int height)
{
Assert.False(new Rectangle(x, y, width, height).IsEmpty);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)]
[InlineData(int.MaxValue, 0, int.MinValue, 0)]
[InlineData(0, 0, 0, 0)]
[InlineData(0, int.MinValue, 0, int.MaxValue)]
[InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)]
public void DimensionsTest(int x, int y, int width, int height)
{
var rect = new Rectangle(x, y, width, height);
Assert.Equal(new Point(x, y), rect.Location);
Assert.Equal(new Size(width, height), rect.Size);
Assert.Equal(x, rect.X);
Assert.Equal(y, rect.Y);
Assert.Equal(width, rect.Width);
Assert.Equal(height, rect.Height);
Assert.Equal(x, rect.Left);
Assert.Equal(y, rect.Top);
Assert.Equal(unchecked(x + width), rect.Right);
Assert.Equal(unchecked(y + height), rect.Bottom);
var p = new Point(width, height);
var s = new Size(x, y);
rect.Location = p;
rect.Size = s;
Assert.Equal(p, rect.Location);
Assert.Equal(s, rect.Size);
Assert.Equal(width, rect.X);
Assert.Equal(height, rect.Y);
Assert.Equal(x, rect.Width);
Assert.Equal(y, rect.Height);
Assert.Equal(width, rect.Left);
Assert.Equal(height, rect.Top);
Assert.Equal(unchecked(x + width), rect.Right);
Assert.Equal(unchecked(y + height), rect.Bottom);
}
[Theory]
[InlineData(0, 0)]
[InlineData(int.MaxValue, int.MinValue)]
public static void LocationSetTest(int x, int y)
{
var point = new Point(x, y);
var rect = new Rectangle(10, 10, 10, 10) { Location = point };
Assert.Equal(point, rect.Location);
Assert.Equal(point.X, rect.X);
Assert.Equal(point.Y, rect.Y);
}
[Theory]
[InlineData(0, 0)]
[InlineData(int.MaxValue, int.MinValue)]
public static void SizeSetTest(int x, int y)
{
var size = new Size(x, y);
var rect = new Rectangle(10, 10, 10, 10) { Size = size };
Assert.Equal(size, rect.Size);
Assert.Equal(size.Width, rect.Width);
Assert.Equal(size.Height, rect.Height);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)]
[InlineData(int.MaxValue, 0, int.MinValue, 0)]
[InlineData(0, int.MinValue, 0, int.MaxValue)]
[InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)]
public void EqualityTest(int x, int y, int width, int height)
{
var rect1 = new Rectangle(x, y, width, height);
var rect2 = new Rectangle(width / 2, height / 2, x, y);
Assert.True(rect1 != rect2);
Assert.False(rect1 == rect2);
Assert.False(rect1.Equals(rect2));
Assert.False(rect1.Equals((object)rect2));
}
[Fact]
public static void EqualityTestNotRectangle()
{
var rectangle = new Rectangle(0, 0, 0, 0);
Assert.False(rectangle.Equals(null));
Assert.False(rectangle.Equals(0));
Assert.False(rectangle.Equals(new RectangleF(0, 0, 0, 0)));
}
[Fact]
public static void GetHashCodeTest()
{
var rect1 = new Rectangle(10, 10, 10, 10);
var rect2 = new Rectangle(10, 10, 10, 10);
Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new Rectangle(20, 10, 10, 10).GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 20, 10, 10).GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 20, 10).GetHashCode());
Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 10, 20).GetHashCode());
}
[Theory]
[InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)]
[InlineData(0, 0, 0, 0)]
public void RectangleFConversionTest(float x, float y, float width, float height)
{
var rect = new RectangleF(x, y, width, height);
Rectangle rCeiling, rTruncate, rRound;
unchecked
{
rCeiling = new Rectangle((int)Math.Ceiling(x), (int)Math.Ceiling(y),
(int)Math.Ceiling(width), (int)Math.Ceiling(height));
rTruncate = new Rectangle((int)x, (int)y, (int)width, (int)height);
rRound = new Rectangle((int)Math.Round(x), (int)Math.Round(y),
(int)Math.Round(width), (int)Math.Round(height));
}
Assert.Equal(rCeiling, Rectangle.Ceiling(rect));
Assert.Equal(rTruncate, Rectangle.Truncate(rect));
Assert.Equal(rRound, Rectangle.Round(rect));
}
[Theory]
[InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)]
[InlineData(0, int.MinValue, int.MaxValue, 0)]
public void ContainsTest(int x, int y, int width, int height)
{
var rect = new Rectangle(unchecked(2 * x - width), unchecked(2 * y - height), width, height);
var p = new Point(x, y);
var r = new Rectangle(x, y, width / 2, height / 2);
Assert.False(rect.Contains(x, y));
Assert.False(rect.Contains(p));
Assert.False(rect.Contains(r));
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)]
[InlineData(0, int.MinValue, int.MaxValue, 0)]
public void InflateTest(int x, int y, int width, int height)
{
Rectangle inflatedRect, rect = new Rectangle(x, y, width, height);
unchecked
{
inflatedRect = new Rectangle(x - width, y - height, width + 2 * width, height + 2 * height);
}
Assert.Equal(inflatedRect, Rectangle.Inflate(rect, width, height));
rect.Inflate(width, height);
Assert.Equal(inflatedRect, rect);
var s = new Size(x, y);
unchecked
{
inflatedRect = new Rectangle(rect.X - x, rect.Y - y, rect.Width + 2 * x, rect.Height + 2 * y);
}
rect.Inflate(s);
Assert.Equal(inflatedRect, rect);
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)]
[InlineData(0, int.MinValue, int.MaxValue, 0)]
public void IntersectTest(int x, int y, int width, int height)
{
var rect = new Rectangle(x, y, width, height);
var expectedRect = Rectangle.Intersect(rect, rect);
rect.Intersect(rect);
Assert.Equal(expectedRect, rect);
Assert.False(rect.IntersectsWith(expectedRect));
}
[Fact]
public static void IntersectIntersectingRectsTest()
{
var rect1 = new Rectangle(0, 0, 5, 5);
var rect2 = new Rectangle(1, 1, 3, 3);
var expected = new Rectangle(1, 1, 3, 3);
Assert.Equal(expected, Rectangle.Intersect(rect1, rect2));
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)]
[InlineData(int.MaxValue, 0, 0, int.MaxValue)]
[InlineData(0, int.MinValue, int.MaxValue, 0)]
public void UnionTest(int x, int y, int width, int height)
{
var a = new Rectangle(x, y, width, height);
var b = new Rectangle(width, height, x, y);
int x1 = Math.Min(a.X, b.X);
int x2 = Math.Max(a.X + a.Width, b.X + b.Width);
int y1 = Math.Min(a.Y, b.Y);
int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height);
var expectedRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1);
Assert.Equal(expectedRectangle, Rectangle.Union(a, b));
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)]
[InlineData(int.MaxValue, 0, 0, int.MaxValue)]
[InlineData(0, int.MinValue, int.MaxValue, 0)]
public void OffsetTest(int x, int y, int width, int height)
{
var r1 = new Rectangle(x, y, width, height);
var expectedRect = new Rectangle(x + width, y + height, width, height);
var p = new Point(width, height);
r1.Offset(p);
Assert.Equal(expectedRect, r1);
expectedRect.Offset(p);
r1.Offset(width, height);
Assert.Equal(expectedRect, r1);
}
[Fact]
public void ToStringTest()
{
var r = new Rectangle(5, -5, 0, 1);
Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Rectangle [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString());
}
[Fact]
public void ToStringTestEmpty()
{
var r = new Rectangle(0, 0, 0, 0);
Assert.Equal("Rectangle [ Empty ]", r.ToString());
}
}
}

122
tests/SixLabors.Primitives.Tests/SignedRationalTests.cs

@ -0,0 +1,122 @@
// <copyright file="RationalTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using Xunit;
/// <summary>
/// Tests the <see cref="SignedRational"/> struct.
/// </summary>
public class SignedRationalTests
{
/// <summary>
/// Tests the equality operators for equality.
/// </summary>
[Fact]
public void AreEqual()
{
SignedRational r1 = new SignedRational(3, 2);
SignedRational r2 = new SignedRational(3, 2);
Assert.Equal(r1, r2);
Assert.True(r1 == r2);
SignedRational r3 = new SignedRational(7.55);
SignedRational r4 = new SignedRational(755, 100);
SignedRational r5 = new SignedRational(151, 20);
Assert.Equal(r3, r4);
Assert.Equal(r4, r5);
}
/// <summary>
/// Tests the equality operators for inequality.
/// </summary>
[Fact]
public void AreNotEqual()
{
SignedRational first = new SignedRational(0, 100);
SignedRational second = new SignedRational(100, 100);
Assert.NotEqual(first, second);
Assert.True(first != second);
}
/// <summary>
/// Tests whether the Rational constructor correctly assign properties.
/// </summary>
[Fact]
public void ConstructorAssignsProperties()
{
SignedRational rational = new SignedRational(7, -55);
Assert.Equal(7, rational.Numerator);
Assert.Equal(-55, rational.Denominator);
rational = new SignedRational(-755, 100);
Assert.Equal(-151, rational.Numerator);
Assert.Equal(20, rational.Denominator);
rational = new SignedRational(-755, -100, false);
Assert.Equal(-755, rational.Numerator);
Assert.Equal(-100, rational.Denominator);
rational = new SignedRational(-151, -20);
Assert.Equal(-151, rational.Numerator);
Assert.Equal(-20, rational.Denominator);
rational = new SignedRational(-7.55);
Assert.Equal(-151, rational.Numerator);
Assert.Equal(20, rational.Denominator);
rational = new SignedRational(7);
Assert.Equal(7, rational.Numerator);
Assert.Equal(1, rational.Denominator);
}
[Fact]
public void Fraction()
{
SignedRational first = new SignedRational(1.0 / 1600);
SignedRational second = new SignedRational(1.0 / 1600, true);
Assert.False(first.Equals(second));
}
[Fact]
public void ToDouble()
{
SignedRational rational = new SignedRational(0, 0);
Assert.Equal(double.NaN, rational.ToDouble());
rational = new SignedRational(2, 0);
Assert.Equal(double.PositiveInfinity, rational.ToDouble());
rational = new SignedRational(-2, 0);
Assert.Equal(double.NegativeInfinity, rational.ToDouble());
}
[Fact]
public void ToStringRepresention()
{
SignedRational rational = new SignedRational(0, 0);
Assert.Equal("[ Indeterminate ]", rational.ToString());
rational = new SignedRational(double.PositiveInfinity);
Assert.Equal("[ PositiveInfinity ]", rational.ToString());
rational = new SignedRational(double.NegativeInfinity);
Assert.Equal("[ NegativeInfinity ]", rational.ToString());
rational = new SignedRational(0, 1);
Assert.Equal("0", rational.ToString());
rational = new SignedRational(2, 1);
Assert.Equal("2", rational.ToString());
rational = new SignedRational(1, 2);
Assert.Equal("1/2", rational.ToString());
}
}
}

34
tests/SixLabors.Primitives.Tests/SixLabors.Primitives.Tests.csproj

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>0.0.0</VersionPrefix>
<TargetFrameworks>netcoreapp1.1</TargetFrameworks>
<AssemblyName>SixLabors.Shapes.Tests</AssemblyName>
<PackageId>SixLabors.Shapes.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<DebugType Condition="$(codecov) != ''">full</DebugType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SixLabors.Primitives\SixLabors.Primitives.csproj" />
<ProjectReference Include="..\..\src\SixLabors.Shapes\SixLabors.Shapes.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="Moq" Version="4.7.1" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

163
tests/SixLabors.Primitives.Tests/SizeFTests.cs

@ -0,0 +1,163 @@
// <copyright file="SizeTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using System;
using System.Globalization;
using System.Reflection;
using Xunit;
public class SizeFTests
{
[Fact]
public void DefaultConstructorTest()
{
Assert.Equal(SizeF.Empty, new SizeF());
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void NonDefaultConstructorAndDimensionsTest(float width, float height)
{
var s1 = new SizeF(width, height);
var p1 = new PointF(width, height);
var s2 = new SizeF(s1);
Assert.Equal(s1, s2);
Assert.Equal(s1, new SizeF(p1));
Assert.Equal(s2, new SizeF(p1));
Assert.Equal(width, s1.Width);
Assert.Equal(height, s1.Height);
s1.Width = 10;
Assert.Equal(10, s1.Width);
s1.Height = -10.123f;
Assert.Equal(-10.123, s1.Height, 3);
}
[Fact]
public void IsEmptyDefaultsTest()
{
Assert.True(SizeF.Empty.IsEmpty);
Assert.True(new SizeF().IsEmpty);
Assert.True(new SizeF(0, 0).IsEmpty);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
public void IsEmptyRandomTest(float width, float height)
{
Assert.False(new SizeF(width, height).IsEmpty);
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void ArithmeticTest(float width, float height)
{
var s1 = new SizeF(width, height);
var s2 = new SizeF(height, width);
var addExpected = new SizeF(width + height, width + height);
var subExpected = new SizeF(width - height, height - width);
Assert.Equal(addExpected, s1 + s2);
Assert.Equal(addExpected, SizeF.Add(s1, s2));
Assert.Equal(subExpected, s1 - s2);
Assert.Equal(subExpected, SizeF.Subtract(s1, s2));
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void EqualityTest(float width, float height)
{
var sLeft = new SizeF(width, height);
var sRight = new SizeF(height, width);
if (width == height)
{
Assert.True(sLeft == sRight);
Assert.False(sLeft != sRight);
Assert.True(sLeft.Equals(sRight));
Assert.True(sLeft.Equals((object)sRight));
Assert.Equal(sLeft.GetHashCode(), sRight.GetHashCode());
return;
}
Assert.True(sLeft != sRight);
Assert.False(sLeft == sRight);
Assert.False(sLeft.Equals(sRight));
Assert.False(sLeft.Equals((object)sRight));
}
[Fact]
public static void EqualityTest_NotSizeF()
{
var size = new SizeF(0, 0);
Assert.False(size.Equals(null));
Assert.False(size.Equals(0));
// If SizeF implements IEquatable<SizeF> (e.g in .NET Core), then classes that are implicitly
// convertible to SizeF can potentially be equal.
// See https://github.com/dotnet/corefx/issues/5255.
bool expectsImplicitCastToSizeF = typeof(IEquatable<SizeF>).IsAssignableFrom(size.GetType());
Assert.Equal(expectsImplicitCastToSizeF, size.Equals(new Size(0, 0)));
Assert.False(size.Equals((object)new Size(0, 0))); // No implicit cast
}
[Fact]
public static void GetHashCodeTest()
{
var size = new SizeF(10, 10);
Assert.Equal(size.GetHashCode(), new SizeF(10, 10).GetHashCode());
Assert.NotEqual(size.GetHashCode(), new SizeF(20, 10).GetHashCode());
Assert.NotEqual(size.GetHashCode(), new SizeF(10, 20).GetHashCode());
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void ConversionTest(float width, float height)
{
var s1 = new SizeF(width, height);
var p1 = (PointF)s1;
var s2 = new Size(unchecked((int)width), unchecked((int)height));
Assert.Equal(new PointF(width, height), p1);
Assert.Equal(p1, (PointF)s1);
Assert.Equal(s2, (Size)s1);
}
[Fact]
public void ToStringTest()
{
var sz = new SizeF(10, 5);
Assert.Equal(string.Format(CultureInfo.CurrentCulture, "SizeF [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString());
}
[Fact]
public void ToStringTestEmpty()
{
var sz = new SizeF(0, 0);
Assert.Equal("SizeF [ Empty ]", sz.ToString());
}
}
}

195
tests/SixLabors.Primitives.Tests/SizeTests.cs

@ -0,0 +1,195 @@
// <copyright file="SizeTests.cs" company="Six Labors">
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.Primitives.Tests
{
using System.Globalization;
using Xunit;
/// <summary>
/// Tests the <see cref="Size"/> struct.
/// </summary>
public class SizeTests
{
[Fact]
public void DefaultConstructorTest()
{
Assert.Equal(Size.Empty, new Size());
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void NonDefaultConstructorTest(int width, int height)
{
var s1 = new Size(width, height);
var s2 = new Size(new Point(width, height));
Assert.Equal(s1, s2);
s1.Width = 10;
Assert.Equal(10, s1.Width);
s1.Height = -10;
Assert.Equal(-10, s1.Height);
}
[Fact]
public void IsEmptyDefaultsTest()
{
Assert.True(Size.Empty.IsEmpty);
Assert.True(new Size().IsEmpty);
Assert.True(new Size(0, 0).IsEmpty);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
public void IsEmptyRandomTest(int width, int height)
{
Assert.False(new Size(width, height).IsEmpty);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void DimensionsTest(int width, int height)
{
var p = new Size(width, height);
Assert.Equal(width, p.Width);
Assert.Equal(height, p.Height);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void PointFConversionTest(int width, int height)
{
SizeF sz = new Size(width, height);
Assert.Equal(new SizeF(width, height), sz);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void SizeConversionTest(int width, int height)
{
var sz = (Point)new Size(width, height);
Assert.Equal(new Point(width, height), sz);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void ArithmeticTest(int width, int height)
{
var sz1 = new Size(width, height);
var sz2 = new Size(height, width);
Size addExpected, subExpected;
unchecked
{
addExpected = new Size(width + height, height + width);
subExpected = new Size(width - height, height - width);
}
Assert.Equal(addExpected, sz1 + sz2);
Assert.Equal(subExpected, sz1 - sz2);
Assert.Equal(addExpected, Size.Add(sz1, sz2));
Assert.Equal(subExpected, Size.Subtract(sz1, sz2));
}
[Theory]
[InlineData(float.MaxValue, float.MinValue)]
[InlineData(float.MinValue, float.MinValue)]
[InlineData(float.MaxValue, float.MaxValue)]
[InlineData(0, 0)]
public void PointFMathematicalTest(float width, float height)
{
var szF = new SizeF(width, height);
Size pCeiling, pTruncate, pRound;
unchecked
{
pCeiling = new Size((int)MathF.Ceiling(width), (int)MathF.Ceiling(height));
pTruncate = new Size((int)width, (int)height);
pRound = new Size((int)MathF.Round(width), (int)MathF.Round(height));
}
Assert.Equal(pCeiling, Size.Ceiling(szF));
Assert.Equal(pRound, Size.Round(szF));
Assert.Equal(pTruncate, (Size)szF);
}
[Theory]
[InlineData(int.MaxValue, int.MinValue)]
[InlineData(int.MinValue, int.MinValue)]
[InlineData(int.MaxValue, int.MaxValue)]
[InlineData(0, 0)]
public void EqualityTest(int width, int height)
{
var p1 = new Size(width, height);
var p2 = new Size(unchecked(width - 1), unchecked(height - 1));
var p3 = new Size(width, height);
Assert.True(p1 == p3);
Assert.True(p1 != p2);
Assert.True(p2 != p3);
Assert.True(p1.Equals(p3));
Assert.False(p1.Equals(p2));
Assert.False(p2.Equals(p3));
Assert.True(p1.Equals((object)p3));
Assert.False(p1.Equals((object)p2));
Assert.False(p2.Equals((object)p3));
Assert.Equal(p1.GetHashCode(), p3.GetHashCode());
}
[Fact]
public static void EqualityTest_NotSize()
{
var size = new Size(0, 0);
Assert.False(size.Equals(null));
Assert.False(size.Equals(0));
Assert.False(size.Equals(new SizeF(0, 0)));
}
[Fact]
public static void GetHashCodeTest()
{
var size = new Size(10, 10);
Assert.Equal(size.GetHashCode(), new Size(10, 10).GetHashCode());
Assert.NotEqual(size.GetHashCode(), new Size(20, 10).GetHashCode());
Assert.NotEqual(size.GetHashCode(), new Size(10, 20).GetHashCode());
}
[Fact]
public void ToStringTest()
{
var sz = new Size(10, 5);
Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Size [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString());
}
[Fact]
public void ToStringTestEmpty()
{
var sz = new Size(0, 0);
Assert.Equal("Size [ Empty ]", sz.ToString());
}
}
}
Loading…
Cancel
Save