зеркало из https://github.com/SixLabors/Shapes.git
Migrate shaper code from ImageSharp to Shaper2D
This commit is contained in:
Родитель
12d3d5a9be
Коммит
10cf98fab8
|
@ -0,0 +1,3 @@
|
|||
[*.cs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2012 James South
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
# <img src="build/icons/imagesharp-logo-64.png" width="52" height="52"/> ImageSharp
|
||||
|
||||
**ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`.
|
||||
|
||||
> **ImageSharp is still in early stages (alpha) but progress has been pretty quick. As such, please do not use on production environments until the library reaches release candidate status. Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp).**
|
||||
|
||||
[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/APACHE-2.0-LICENSE.txt)
|
||||
[![GitHub issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues)
|
||||
[![GitHub stars](https://img.shields.io/github/stars/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/stargazers)
|
||||
[![GitHub forks](https://img.shields.io/github/forks/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/network)
|
||||
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Twitter](https://img.shields.io/twitter/url/https/github.com/JimBobSquarePants/ImageSharp.svg?style=social)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fJimBobSquarePants%2fImageSharp&via=james_m_south)
|
||||
|
||||
|
||||
| |Build Status|Code Coverage|
|
||||
|-------------|:----------:|:-----------:|
|
||||
|**Linux/Mac**|[![Build Status](https://travis-ci.org/JimBobSquarePants/ImageSharp.svg)](https://travis-ci.org/JimBobSquarePants/ImageSharp)|[![Code coverage](https://codecov.io/gh/JimBobSquarePants/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/JimBobSquarePants/ImageSharp)|
|
||||
|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/hu6d1gdpxdw0q360/branch/master?svg=true)](https://ci.appveyor.com/project/JamesSouth/imagesharp/branch/master)|[![Code coverage](https://codecov.io/gh/JimBobSquarePants/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/JimBobSquarePants/ImageSharp)|
|
||||
|
||||
|
||||
### Installation
|
||||
At present the code is pre-release but when ready it will be available on [Nuget](http://www.nuget.org).
|
||||
|
||||
**Pre-release downloads**
|
||||
|
||||
We already have a [MyGet package repository](https://www.myget.org/gallery/imagesharp) - for bleeding-edge / development NuGet releases.
|
||||
|
||||
### Packages
|
||||
|
||||
The **ImageSharp** library is made up of multiple packages, to make **ImageSharp** do anything useful you will want to make sure you include at least one format as a dependency otherwise you will not be able to save/load any images.
|
||||
|
||||
Packages include:
|
||||
- **ImageSharp**
|
||||
Contains the Image classes, Colors, Primitives, Bootstrapper, IImageFormat interface, and other core functionality.
|
||||
- **ImageSharp.Formats.Jpeg**
|
||||
The jpeg decoder/encoder (Auto registered)
|
||||
- **ImageSharp.Formats.Png**
|
||||
The png decoder/encoder (Auto registered)
|
||||
- **ImageSharp.Formats.Gif**
|
||||
The gif decoder/encoder (Auto registered)
|
||||
- **ImageSharp.Formats.Bmp**
|
||||
The bmp decoder/encoder (Auto registered)
|
||||
- **ImageSharp.Processing**
|
||||
Contains methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image.
|
||||
Contains methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions.
|
||||
- **ImageSharp.Drawing**
|
||||
Brushes and various drawing algorithms.
|
||||
|
||||
### Manual build
|
||||
|
||||
If you prefer, you can compile ImageSharp yourself (please do and help!), you'll need:
|
||||
|
||||
- [Visual Studio 2015 with Update 3 (or above)](https://www.visualstudio.com/news/releasenotes/vs2015-update3-vs)
|
||||
- The [.NET Core 1.0 SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link.
|
||||
|
||||
To clone it locally click the "Clone in Windows" button above or run the following git commands.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/JimBobSquarePants/ImageSharp
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
There's plenty there and more coming. Check out the [current features](features.md)!
|
||||
|
||||
### API
|
||||
|
||||
Without the constraints of `System.Drawing` We have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks. Images and processors are thread safe usable in parallel processing utilizing all the availables cores.
|
||||
|
||||
Many `Image` methods are also fluent.
|
||||
|
||||
Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix.
|
||||
|
||||
```csharp
|
||||
using (FileStream stream = File.OpenRead("foo.jpg"))
|
||||
using (FileStream output = File.OpenWrite("bar.jpg"))
|
||||
{
|
||||
Image image = new Image(stream);
|
||||
image.Resize(image.Width / 2, image.Height / 2)
|
||||
.Grayscale()
|
||||
.Save(output);
|
||||
}
|
||||
```
|
||||
|
||||
Individual processors can be initialised and apply processing against images. This allows nesting which brings the potential for powerful combinations of processing methods:
|
||||
|
||||
```csharp
|
||||
new BrightnessProcessor(50).Apply(sourceImage, sourceImage.Bounds);
|
||||
```
|
||||
|
||||
Setting individual pixel values is perfomed as follows:
|
||||
|
||||
```csharp
|
||||
Image image = new Image(400, 400);
|
||||
using (var pixels = image.Lock())
|
||||
{
|
||||
pixels[200, 200] = Color.White;
|
||||
}
|
||||
```
|
||||
|
||||
For advanced usage the `Image<TColor>` and `PixelAccessor<TColor>` classes are available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame.
|
||||
|
||||
All in all this should allow image processing to be much more accessible to developers which has always been my goal from the start.
|
||||
|
||||
### How can you help?
|
||||
|
||||
Please... Spread the word, contribute algorithms, submit performance improvements, unit tests.
|
||||
|
||||
Performance is a biggie, if you know anything about the new vector types and can apply some fancy new stuff with that it would be awesome.
|
||||
|
||||
There's a lot of developers out there who could write this stuff a lot better and faster than I and I would love to see what we collectively can come up with so please, if you can help in any way it would be most welcome and benificial for all.
|
||||
|
||||
### The ImageSharp Team
|
||||
|
||||
Grand High Eternal Dictator
|
||||
- [Scott Williams](https://github.com/jimbobsquarepants)
|
||||
|
||||
Core Team
|
||||
- [Dirk Lemstra](https://github.com/dlemstra)
|
||||
- [Jeavon Leopold](https://github.com/jeavon)
|
||||
- [Anton Firsov](https://github.com/antonfirsov)
|
||||
- [Olivia Ifrim](https://github.com/olivif)
|
||||
- [Scott Williams](https://github.com/tocsoft)
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="ImageSharp" ToolsVersion="14.0">
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
|
||||
<Rule Id="SA1413" Action="None" />
|
||||
</Rules>
|
||||
</RuleSet>
|
38
Shaper2D.sln
38
Shaper2D.sln
|
@ -3,9 +3,26 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shaper2D", "Shaper2D\Shaper2D.csproj", "{8EC582C9-750F-48BC-B48E-B3F89A5BA7B7}"
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
global.json = global.json
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Items", "Items", "{20777789-C567-4304-BB8D-11FAF5328812}"
|
||||
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("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Shaper2D", "src\Shaper2D\Shaper2D.xproj", "{2E33181E-6E28-4662-A801-E2E7DC206029}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Shaper2D.Tests", "tests\Shaper2D.Tests\Shaper2D.Tests.xproj", "{F836E8E6-B4D9-4208-8346-140C74678B91}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -13,12 +30,21 @@ Global
|
|||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8EC582C9-750F-48BC-B48E-B3F89A5BA7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8EC582C9-750F-48BC-B48E-B3F89A5BA7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8EC582C9-750F-48BC-B48E-B3F89A5BA7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8EC582C9-750F-48BC-B48E-B3F89A5BA7B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2E33181E-6E28-4662-A801-E2E7DC206029}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2E33181E-6E28-4662-A801-E2E7DC206029}.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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
|
||||
{2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
|
||||
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"projects": [ "src" ],
|
||||
"sdk": {
|
||||
"version": "1.0.0-preview2-003121"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// <copyright file="BezierLineSegment.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line segment that conistst of control points that will be rendered as a cubic bezier curve
|
||||
/// </summary>
|
||||
/// <seealso cref="Shaper2D.ILineSegment" />
|
||||
public class BezierLineSegment : ILineSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// The segments per curve.
|
||||
/// code for this taken from <see href="http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/"/>
|
||||
/// </summary>
|
||||
private const int SegmentsPerCurve = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The line points.
|
||||
/// </summary>
|
||||
private readonly Point[] linePoints;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BezierLineSegment"/> class.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
public BezierLineSegment(params Point[] points)
|
||||
{
|
||||
Guard.NotNull(points, nameof(points));
|
||||
Guard.MustBeGreaterThanOrEqualTo(points.Length, 4, nameof(points));
|
||||
|
||||
this.linePoints = this.GetDrawingPoints(points);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current <see cref="ILineSegment" /> a simple linear path.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
||||
/// </returns>
|
||||
public ImmutableArray<Point> AsSimpleLinearPath()
|
||||
{
|
||||
return ImmutableArray.Create(this.linePoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the drawing points along the line.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="T:Vector2[]"/>.
|
||||
/// </returns>
|
||||
private Point[] GetDrawingPoints(Point[] controlPoints)
|
||||
{
|
||||
// TODO we need to calculate an optimal SegmentsPerCurve value
|
||||
// depending on the calcualted length of this curve
|
||||
int curveCount = (controlPoints.Length - 1) / 3;
|
||||
int finalPointCount = (SegmentsPerCurve * curveCount) + 1; // we have SegmentsPerCurve for each curve plus the origon point;
|
||||
|
||||
Point[] drawingPoints = new Point[finalPointCount];
|
||||
|
||||
int position = 0;
|
||||
int targetPoint = controlPoints.Length - 3;
|
||||
for (int i = 0; i < targetPoint; i += 3)
|
||||
{
|
||||
Vector2 p0 = controlPoints[i];
|
||||
Vector2 p1 = controlPoints[i + 1];
|
||||
Vector2 p2 = controlPoints[i + 2];
|
||||
Vector2 p3 = controlPoints[i + 3];
|
||||
|
||||
// only do this for the first end point. When i != 0, this coincides with the end point of the previous segment,
|
||||
if (i == 0)
|
||||
{
|
||||
drawingPoints[position++] = this.CalculateBezierPoint(0, p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
for (int j = 1; j <= SegmentsPerCurve; j++)
|
||||
{
|
||||
float t = j / (float)SegmentsPerCurve;
|
||||
drawingPoints[position++] = this.CalculateBezierPoint(t, p0, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
return drawingPoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the bezier point along the line.
|
||||
/// </summary>
|
||||
/// <param name="t">The position within the line.</param>
|
||||
/// <param name="p0">The p 0.</param>
|
||||
/// <param name="p1">The p 1.</param>
|
||||
/// <param name="p2">The p 2.</param>
|
||||
/// <param name="p3">The p 3.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Vector2"/>.
|
||||
/// </returns>
|
||||
private Vector2 CalculateBezierPoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3)
|
||||
{
|
||||
float u = 1 - t;
|
||||
float tt = t * t;
|
||||
float uu = u * u;
|
||||
float uuu = uu * u;
|
||||
float ttt = tt * t;
|
||||
|
||||
Vector2 p = uuu * p0; // first term
|
||||
|
||||
p += 3 * uu * t * p1; // second term
|
||||
p += 3 * u * tt * p2; // third term
|
||||
p += ttt * p3; // fourth term
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// <copyright file="BezierPolygon.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a polygon made up exclusivly of a single close cubic Bezier curve.
|
||||
/// </summary>
|
||||
public sealed class BezierPolygon : IShape
|
||||
{
|
||||
private Polygon innerPolygon;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BezierPolygon"/> class.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
public BezierPolygon(params Point[] points)
|
||||
{
|
||||
this.innerPolygon = new Polygon(new BezierLineSegment(points));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
public Rectangle Bounds => this.innerPolygon.Bounds;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum intersections.
|
||||
/// </value>
|
||||
public int MaxIntersections => this.innerPolygon.MaxIntersections;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths that make up this shape
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The paths.
|
||||
/// </value>
|
||||
public IEnumerable<IPath> Paths => this.innerPolygon.Paths;
|
||||
|
||||
/// <summary>
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// The distance from the shape.
|
||||
/// </returns>
|
||||
public float Distance(Point point) => this.innerPolygon.Distance(point);
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point of the line.</param>
|
||||
/// <param name="end">The end point of the line.</param>
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>
|
||||
/// The number of intersections populated into the buffer.
|
||||
/// </returns>
|
||||
public int FindIntersections(Point start, Point end, Point[] buffer, int count, int offset)
|
||||
{
|
||||
return this.innerPolygon.FindIntersections(start, end, buffer, count, offset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
// <copyright file="ComplexPolygon.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
using PolygonClipper;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a complex polygon made up of one or more outline
|
||||
/// polygons and one or more holes to punch out of them.
|
||||
/// </summary>
|
||||
/// <seealso cref="Shaper2D.IShape" />
|
||||
public sealed class ComplexPolygon : IShape
|
||||
{
|
||||
private const float ClipperScaleFactor = 100f;
|
||||
private IShape[] shapes;
|
||||
private IEnumerable<IPath> paths;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ComplexPolygon"/> class.
|
||||
/// </summary>
|
||||
/// <param name="outline">The outline.</param>
|
||||
/// <param name="holes">The holes.</param>
|
||||
public ComplexPolygon(IShape outline, params IShape[] holes)
|
||||
: this(new[] { outline }, holes)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ComplexPolygon"/> class.
|
||||
/// </summary>
|
||||
/// <param name="outlines">The outlines.</param>
|
||||
/// <param name="holes">The holes.</param>
|
||||
public ComplexPolygon(IShape[] outlines, IShape[] holes)
|
||||
{
|
||||
Guard.NotNull(outlines, nameof(outlines));
|
||||
Guard.MustBeGreaterThanOrEqualTo(outlines.Length, 1, nameof(outlines));
|
||||
|
||||
this.MaxIntersections = this.FixAndSetShapes(outlines, holes);
|
||||
|
||||
float minX = this.shapes.Min(x => x.Bounds.Left);
|
||||
float maxX = this.shapes.Max(x => x.Bounds.Right);
|
||||
float minY = this.shapes.Min(x => x.Bounds.Top);
|
||||
float maxY = this.shapes.Max(x => x.Bounds.Bottom);
|
||||
|
||||
this.Bounds = new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths that make up this shape
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The paths.
|
||||
/// </value>
|
||||
public IEnumerable<IPath> Paths => this.paths;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
public Rectangle Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum intersections.
|
||||
/// </value>
|
||||
public int MaxIntersections { get; }
|
||||
|
||||
/// <summary>
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// Returns the distance from thr shape to the point
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Due to the clipping we did during construction we know that out shapes do not overlap at there edges
|
||||
/// therefore for apoint to be in more that one we must be in a hole of another, theoretically this could
|
||||
/// then flip again to be in a outlin inside a hole inside an outline :)
|
||||
/// </remarks>
|
||||
float IShape.Distance(Point point)
|
||||
{
|
||||
float dist = float.MaxValue;
|
||||
bool inside = false;
|
||||
foreach (IShape shape in this.shapes)
|
||||
{
|
||||
float d = shape.Distance(point);
|
||||
|
||||
if (d <= 0)
|
||||
{
|
||||
// we are inside a poly
|
||||
d = -d; // flip the sign
|
||||
inside ^= true; // flip the inside flag
|
||||
}
|
||||
|
||||
if (d < dist)
|
||||
{
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (inside)
|
||||
{
|
||||
return -dist;
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
||||
/// populate a buffer for all points on all the polygons, that make up this complex shape,
|
||||
/// that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point of the line.</param>
|
||||
/// <param name="end">The end point of the line.</param>
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>
|
||||
/// The number of intersections populated into the buffer.
|
||||
/// </returns>
|
||||
public int FindIntersections(Point start, Point end, Point[] buffer, int count, int offset)
|
||||
{
|
||||
int totalAdded = 0;
|
||||
for (int i = 0; i < this.shapes.Length; i++)
|
||||
{
|
||||
int added = this.shapes[i].FindIntersections(start, end, buffer, count, offset);
|
||||
count -= added;
|
||||
offset += added;
|
||||
totalAdded += added;
|
||||
}
|
||||
|
||||
return totalAdded;
|
||||
}
|
||||
|
||||
private void AddPoints(Clipper clipper, IShape shape, PolyType polyType)
|
||||
{
|
||||
// if the path is already the shape use it directly and skip the path loop.
|
||||
if (shape is IPath)
|
||||
{
|
||||
clipper.AddPath(
|
||||
(IPath)shape,
|
||||
polyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (IPath path in shape.Paths)
|
||||
{
|
||||
clipper.AddPath(
|
||||
path,
|
||||
polyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPoints(Clipper clipper, IEnumerable<IShape> shapes, PolyType polyType)
|
||||
{
|
||||
foreach (IShape shape in shapes)
|
||||
{
|
||||
this.AddPoints(clipper, shape, polyType);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractOutlines(PolyNode tree, List<IShape> shapes, List<IPath> paths)
|
||||
{
|
||||
if (tree.Contour.Any())
|
||||
{
|
||||
// if the source path is set then we clipper retained the full path intact thus we can freely
|
||||
// use it and get any shape optimisations that are availible.
|
||||
if (tree.SourcePath != null)
|
||||
{
|
||||
shapes.Add((IShape)tree.SourcePath);
|
||||
paths.Add(tree.SourcePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygon polygon = new Polygon(new LinearLineSegment(tree.Contour.Select(x => new Point(x)).ToArray()));
|
||||
|
||||
shapes.Add(polygon);
|
||||
paths.Add(polygon);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PolyNode c in tree.Children)
|
||||
{
|
||||
this.ExtractOutlines(c, shapes, paths);
|
||||
}
|
||||
}
|
||||
|
||||
private int FixAndSetShapes(IEnumerable<IShape> outlines, IEnumerable<IShape> holes)
|
||||
{
|
||||
Clipper clipper = new Clipper();
|
||||
|
||||
// add the outlines and the holes to clipper, scaling up from the float source to the int based system clipper uses
|
||||
this.AddPoints(clipper, outlines, PolyType.Subject);
|
||||
this.AddPoints(clipper, holes, PolyType.Clip);
|
||||
|
||||
PolyTree tree = clipper.Execute();
|
||||
|
||||
List<IShape> shapes = new List<IShape>();
|
||||
List<IPath> paths = new List<IPath>();
|
||||
|
||||
// convert the 'tree' back to paths
|
||||
this.ExtractOutlines(tree, shapes, paths);
|
||||
this.shapes = shapes.ToArray();
|
||||
this.paths = paths.ToArray();
|
||||
|
||||
int intersections = 0;
|
||||
foreach (IShape s in this.shapes)
|
||||
{
|
||||
intersections += s.MaxIntersections;
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
// <copyright file="Guard.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods to protect against invalid parameters.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
internal static class Guard
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies, that the method parameter with specified object value is not null
|
||||
/// and throws an exception if it is found to be so.
|
||||
/// </summary>
|
||||
/// <param name="target">The target object, which cannot be null.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <param name="message">The error message, if any to add to the exception.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception>
|
||||
public static void NotNull(object target, string parameterName, string message = "")
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
throw new ArgumentNullException(parameterName, message);
|
||||
}
|
||||
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies, that the string method parameter with specified object value and message
|
||||
/// is not null, not empty and does not contain only blanks and throws an exception
|
||||
/// if the object is null.
|
||||
/// </summary>
|
||||
/// <param name="target">The target string, which should be checked against being null or empty.</param>
|
||||
/// <param name="parameterName">Name of the parameter.</param>
|
||||
/// <param name="message">The error message, if any to add to the exception.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="target"/> is empty or contains only blanks.</exception>
|
||||
public static void NotNullOrEmpty(string target, string parameterName, string message = "")
|
||||
{
|
||||
NotNull(target, parameterName, message);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(target))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
throw new ArgumentException(message, parameterName);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Value cannot be null or empty and cannot contain only blanks.", parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies, that the enumeration is not null and not empty.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects in the <paramref name="target"/></typeparam>
|
||||
/// <param name="target">The target enumeration, which should be checked against being null or empty.</param>
|
||||
/// <param name="parameterName">Name of the parameter.</param>
|
||||
/// <param name="message">The error message, if any to add to the exception.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="target"/> is empty.</exception>
|
||||
public static void NotNullOrEmpty<T>(IEnumerable<T> target, string parameterName, string message = "")
|
||||
{
|
||||
NotNull(target, parameterName, message);
|
||||
|
||||
if (!target.Any())
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
throw new ArgumentException(message, parameterName);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Value cannot be empty.", parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the specified value is less than a maximum value
|
||||
/// and throws an exception if it is not.
|
||||
/// </summary>
|
||||
/// <param name="value">The target value, which should be validated.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is greater than the maximum value.
|
||||
/// </exception>
|
||||
public static void MustBeLessThan<TValue>(TValue value, TValue max, string parameterName)
|
||||
where TValue : IComparable<TValue>
|
||||
{
|
||||
if (value.CompareTo(max) >= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the specified value is less than or equal to a maximum value
|
||||
/// and throws an exception if it is not.
|
||||
/// </summary>
|
||||
/// <param name="value">The target value, which should be validated.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is greater than the maximum value.
|
||||
/// </exception>
|
||||
public static void MustBeLessThanOrEqualTo<TValue>(TValue value, TValue max, string parameterName)
|
||||
where TValue : IComparable<TValue>
|
||||
{
|
||||
if (value.CompareTo(max) > 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the specified value is greater than a minimum value
|
||||
/// and throws an exception if it is not.
|
||||
/// </summary>
|
||||
/// <param name="value">The target value, which should be validated.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is less than the minimum value.
|
||||
/// </exception>
|
||||
public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName)
|
||||
where TValue : IComparable<TValue>
|
||||
{
|
||||
if (value.CompareTo(min) <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
parameterName,
|
||||
$"Value must be greater than {min}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the specified value is greater than or equal to a minimum value
|
||||
/// and throws an exception if it is not.
|
||||
/// </summary>
|
||||
/// <param name="value">The target value, which should be validated.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is less than the minimum value.
|
||||
/// </exception>
|
||||
public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName)
|
||||
where TValue : IComparable<TValue>
|
||||
{
|
||||
if (value.CompareTo(min) < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the specified value is greater than or equal to a minimum value and less than
|
||||
/// or equal to a maximum value and throws an exception if it is not.
|
||||
/// </summary>
|
||||
/// <param name="value">The target value, which should be validated.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
|
||||
/// </exception>
|
||||
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
|
||||
where TValue : IComparable<TValue>
|
||||
{
|
||||
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min} and less than or equal to {max}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies, that the method parameter with specified target value is true
|
||||
/// and throws an exception if it is found to be so.
|
||||
/// </summary>
|
||||
/// <param name="target">
|
||||
/// The target value, which cannot be false.
|
||||
/// </param>
|
||||
/// <param name="parameterName">
|
||||
/// The name of the parameter that is to be checked.
|
||||
/// </param>
|
||||
/// <param name="message">
|
||||
/// The error message, if any to add to the exception.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="target"/> is false
|
||||
/// </exception>
|
||||
public static void IsTrue(bool target, string parameterName, string message)
|
||||
{
|
||||
if (!target)
|
||||
{
|
||||
throw new ArgumentException(message, parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies, that the method parameter with specified target value is false
|
||||
/// and throws an exception if it is found to be so.
|
||||
/// </summary>
|
||||
/// <param name="target">The target value, which cannot be true.</param>
|
||||
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
/// <param name="message">The error message, if any to add to the exception.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="target"/> is true
|
||||
/// </exception>
|
||||
public static void IsFalse(bool target, string parameterName, string message)
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
throw new ArgumentException(message, parameterName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// <copyright file="ILineSegment.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a simple path segment
|
||||
/// </summary>
|
||||
public interface ILineSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ILineSegment" /> into a simple linear path..
|
||||
/// </summary>
|
||||
/// <returns>Returns the current <see cref="ILineSegment" /> as simple linear path.</returns>
|
||||
ImmutableArray<Point> AsSimpleLinearPath(); // TODO move this over to ReadonlySpan<Vector2> once available
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// <copyright file="IPath.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a logic path that can be drawn
|
||||
/// </summary>
|
||||
public interface IPath : ILineSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the bounds enclosing the path
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
Rectangle Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is closed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsClosed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the path
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
float Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance along and away from the path for a specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point along the path.</param>
|
||||
/// <returns>
|
||||
/// Returns details about the point and its distance away from the path.
|
||||
/// </returns>
|
||||
PointInfo Distance(Point point);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// <copyright file="IShape.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a closed set of paths making up a single shape.
|
||||
/// </summary>
|
||||
public interface IShape
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
Rectangle Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths that make up this shape
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The paths.
|
||||
/// </value>
|
||||
IEnumerable<IPath> Paths { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum intersections.
|
||||
/// </value>
|
||||
int MaxIntersections { get; }
|
||||
|
||||
/// <summary>
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// Returns the distance from the shape to the point
|
||||
/// </returns>
|
||||
float Distance(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point of the line.</param>
|
||||
/// <param name="end">The end point of the line.</param>
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>
|
||||
/// The number of intersections populated into the buffer.
|
||||
/// </returns>
|
||||
int FindIntersections(Point start, Point end, Point[] buffer, int count, int offset);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,517 @@
|
|||
// <copyright file="InternalPath.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Internal logic for integrating linear paths.
|
||||
/// </summary>
|
||||
internal class InternalPath
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum vector
|
||||
/// </summary>
|
||||
private static readonly Vector2 MaxVector = new Vector2(float.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// The locker.
|
||||
/// </summary>
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// The points.
|
||||
/// </summary>
|
||||
private readonly ImmutableArray<Point> points;
|
||||
|
||||
/// <summary>
|
||||
/// The closed path.
|
||||
/// </summary>
|
||||
private readonly bool closedPath;
|
||||
|
||||
/// <summary>
|
||||
/// The total distance.
|
||||
/// </summary>
|
||||
private readonly Lazy<float> totalDistance;
|
||||
|
||||
/// <summary>
|
||||
/// The constant.
|
||||
/// </summary>
|
||||
private float[] constant;
|
||||
|
||||
/// <summary>
|
||||
/// The multiples.
|
||||
/// </summary>
|
||||
private float[] multiple;
|
||||
|
||||
/// <summary>
|
||||
/// The distances.
|
||||
/// </summary>
|
||||
private float[] distance;
|
||||
|
||||
/// <summary>
|
||||
/// The calculated.
|
||||
/// </summary>
|
||||
private bool calculated = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InternalPath"/> class.
|
||||
/// </summary>
|
||||
/// <param name="segments">The segments.</param>
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
||||
internal InternalPath(ILineSegment[] segments, bool isClosedPath)
|
||||
: this(Simplify(segments), isClosedPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InternalPath" /> class.
|
||||
/// </summary>
|
||||
/// <param name="segment">The segment.</param>
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
||||
internal InternalPath(ILineSegment segment, bool isClosedPath)
|
||||
: this(segment.AsSimpleLinearPath(), isClosedPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InternalPath" /> class.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
||||
internal InternalPath(ImmutableArray<Point> points, bool isClosedPath)
|
||||
{
|
||||
this.points = points;
|
||||
this.closedPath = isClosedPath;
|
||||
|
||||
float minX = this.points.Min(x => x.X);
|
||||
float maxX = this.points.Max(x => x.X);
|
||||
float minY = this.points.Min(x => x.Y);
|
||||
float maxY = this.points.Max(x => x.Y);
|
||||
|
||||
this.Bounds = new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
this.totalDistance = new Lazy<float>(this.CalculateLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounds.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
public Rectangle Bounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
public float Length => this.totalDistance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the points.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The points.
|
||||
/// </value>
|
||||
internal ImmutableArray<Point> Points => this.points;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance from the path.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>Returns the distance from the path</returns>
|
||||
public PointInfo DistanceFromPath(Vector2 point)
|
||||
{
|
||||
this.CalculateConstants();
|
||||
|
||||
PointInfoInternal internalInfo = default(PointInfoInternal);
|
||||
internalInfo.DistanceSquared = float.MaxValue; // Set it to max so that CalculateShorterDistance can reduce it back down
|
||||
|
||||
int polyCorners = this.points.Length;
|
||||
|
||||
if (!this.closedPath)
|
||||
{
|
||||
polyCorners -= 1;
|
||||
}
|
||||
|
||||
int closestPoint = 0;
|
||||
for (int i = 0; i < polyCorners; i++)
|
||||
{
|
||||
int next = i + 1;
|
||||
if (this.closedPath && next == polyCorners)
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
|
||||
if (this.CalculateShorterDistance(this.points[i], this.points[next], point, ref internalInfo))
|
||||
{
|
||||
closestPoint = i;
|
||||
}
|
||||
}
|
||||
|
||||
return new PointInfo
|
||||
{
|
||||
DistanceAlongPath = this.distance[closestPoint] + Vector2.Distance(this.points[closestPoint], point),
|
||||
DistanceFromPath = (float)Math.Sqrt(internalInfo.DistanceSquared),
|
||||
SearchPoint = point,
|
||||
ClosestPointOnPath = internalInfo.PointOnLine
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start" /> and <paramref name="end" />
|
||||
/// populate a buffer for all points on the path that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>number iof intersections hit</returns>
|
||||
public int FindIntersections(Vector2 start, Vector2 end, Point[] buffer, int count, int offset)
|
||||
{
|
||||
int polyCorners = this.points.Length;
|
||||
|
||||
if (!this.closedPath)
|
||||
{
|
||||
polyCorners -= 1;
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
for (int i = 0; i < polyCorners && count > 0; i++)
|
||||
{
|
||||
int next = i + 1;
|
||||
if (this.closedPath && next == polyCorners)
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
|
||||
Vector2 point = FindIntersection(this.points[i], this.points[next], start, end);
|
||||
if (point != MaxVector)
|
||||
{
|
||||
buffer[position + offset] = point;
|
||||
position++;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified point is inside or outside the path.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>Returns true if the point is inside the closed path.</returns>
|
||||
public bool PointInPolygon(Point point)
|
||||
{
|
||||
// You can only be inside a path if its "closed"
|
||||
if (!this.closedPath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.Bounds.Contains(point))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.CalculateConstants();
|
||||
|
||||
ImmutableArray<Point> poly = this.points;
|
||||
int polyCorners = poly.Length;
|
||||
|
||||
int j = polyCorners - 1;
|
||||
bool oddNodes = false;
|
||||
|
||||
for (int i = 0; i < polyCorners; i++)
|
||||
{
|
||||
if ((poly[i].Y < point.Y && poly[j].Y >= point.Y)
|
||||
|| (poly[j].Y < point.Y && poly[i].Y >= point.Y))
|
||||
{
|
||||
oddNodes ^= (point.Y * this.multiple[i]) + this.constant[i] < point.X;
|
||||
}
|
||||
|
||||
j = i;
|
||||
}
|
||||
|
||||
return oddNodes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determins if the bounding box for 2 lines
|
||||
/// described by <paramref name="line1Start" /> and <paramref name="line1End" />
|
||||
/// and <paramref name="line2Start" /> and <paramref name="line2End" /> overlap.
|
||||
/// </summary>
|
||||
/// <param name="line1Start">The line1 start.</param>
|
||||
/// <param name="line1End">The line1 end.</param>
|
||||
/// <param name="line2Start">The line2 start.</param>
|
||||
/// <param name="line2End">The line2 end.</param>
|
||||
/// <returns>Returns true it the bounding box of the 2 lines intersect</returns>
|
||||
private static bool BoundingBoxesIntersect(Vector2 line1Start, Vector2 line1End, Vector2 line2Start, Vector2 line2End)
|
||||
{
|
||||
Vector2 topLeft1 = Vector2.Min(line1Start, line1End);
|
||||
Vector2 bottomRight1 = Vector2.Max(line1Start, line1End);
|
||||
|
||||
Vector2 topLeft2 = Vector2.Min(line2Start, line2End);
|
||||
Vector2 bottomRight2 = Vector2.Max(line2Start, line2End);
|
||||
|
||||
float left1 = topLeft1.X;
|
||||
float right1 = bottomRight1.X;
|
||||
float top1 = topLeft1.Y;
|
||||
float bottom1 = bottomRight1.Y;
|
||||
|
||||
float left2 = topLeft2.X;
|
||||
float right2 = bottomRight2.X;
|
||||
float top2 = topLeft2.Y;
|
||||
float bottom2 = bottomRight2.Y;
|
||||
|
||||
return left1 <= right2 && right1 >= left2
|
||||
&&
|
||||
top1 <= bottom2 && bottom1 >= top2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the point on line described by <paramref name="line1Start" /> and <paramref name="line1End" />
|
||||
/// that intersects with line described by <paramref name="line2Start" /> and <paramref name="line2End" />
|
||||
/// </summary>
|
||||
/// <param name="line1Start">The line1 start.</param>
|
||||
/// <param name="line1End">The line1 end.</param>
|
||||
/// <param name="line2Start">The line2 start.</param>
|
||||
/// <param name="line2End">The line2 end.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Vector2"/> describing the point that the 2 lines cross or <see cref="MaxVector"/> if they do not.
|
||||
/// </returns>
|
||||
private static Vector2 FindIntersection(Vector2 line1Start, Vector2 line1End, Vector2 line2Start, Vector2 line2End)
|
||||
{
|
||||
// do bounding boxes overlap, if not then the lines can't and return fast.
|
||||
if (!BoundingBoxesIntersect(line1Start, line1End, line2Start, line2End))
|
||||
{
|
||||
return MaxVector;
|
||||
}
|
||||
|
||||
Vector2 line1Diff = line1End - line1Start;
|
||||
Vector2 line2Diff = line2End - line2Start;
|
||||
|
||||
Vector2 point;
|
||||
if (line1Diff.X == 0)
|
||||
{
|
||||
float slope = line2Diff.Y / line2Diff.X;
|
||||
float yinter = line2Start.Y - (slope * line2Start.X);
|
||||
float y = (line1Start.X * slope) + yinter;
|
||||
point = new Vector2(line1Start.X, y);
|
||||
|
||||
// horizontal and vertical lines
|
||||
}
|
||||
else if (line2Diff.X == 0)
|
||||
{
|
||||
float slope = line1Diff.Y / line1Diff.X;
|
||||
float yinter = line1Start.Y - (slope * line1Start.X);
|
||||
float y = (line2Start.X * slope) + yinter;
|
||||
point = new Vector2(line2Start.X, y);
|
||||
|
||||
// horizontal and vertical lines
|
||||
}
|
||||
else
|
||||
{
|
||||
float slope1 = line1Diff.Y / line1Diff.X;
|
||||
float slope2 = line2Diff.Y / line2Diff.X;
|
||||
|
||||
float yinter1 = line1Start.Y - (slope1 * line1Start.X);
|
||||
float yinter2 = line2Start.Y - (slope2 * line2Start.X);
|
||||
|
||||
if (slope1 == slope2 && yinter1 != yinter2)
|
||||
{
|
||||
return MaxVector;
|
||||
}
|
||||
|
||||
float x = (yinter2 - yinter1) / (slope1 - slope2);
|
||||
float y = (slope1 * x) + yinter1;
|
||||
|
||||
point = new Vector2(x, y);
|
||||
}
|
||||
|
||||
if (BoundingBoxesIntersect(line1Start, line1End, point, point))
|
||||
{
|
||||
return point;
|
||||
}
|
||||
else if (BoundingBoxesIntersect(line2Start, line2End, point, point))
|
||||
{
|
||||
return point;
|
||||
}
|
||||
|
||||
return MaxVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simplifies the collection of segments.
|
||||
/// </summary>
|
||||
/// <param name="segments">The segments.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="T:Vector2[]"/>.
|
||||
/// </returns>
|
||||
private static ImmutableArray<Point> Simplify(ILineSegment[] segments)
|
||||
{
|
||||
List<Point> simplified = new List<Point>();
|
||||
foreach (ILineSegment seg in segments)
|
||||
{
|
||||
simplified.AddRange(seg.AsSimpleLinearPath());
|
||||
}
|
||||
|
||||
return simplified.ToImmutableArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the path.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="float"/>.
|
||||
/// </returns>
|
||||
private float CalculateLength()
|
||||
{
|
||||
float length = 0;
|
||||
int polyCorners = this.points.Length;
|
||||
|
||||
if (!this.closedPath)
|
||||
{
|
||||
polyCorners -= 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < polyCorners; i++)
|
||||
{
|
||||
int next = i + 1;
|
||||
if (this.closedPath && next == polyCorners)
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
|
||||
length += Vector2.Distance(this.points[i], this.points[next]);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the constants.
|
||||
/// </summary>
|
||||
private void CalculateConstants()
|
||||
{
|
||||
// http://alienryderflex.com/polygon/ source for point in polygon logic
|
||||
if (this.calculated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
if (this.calculated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImmutableArray<Point> poly = this.points;
|
||||
int polyCorners = poly.Length;
|
||||
this.constant = new float[polyCorners];
|
||||
this.multiple = new float[polyCorners];
|
||||
this.distance = new float[polyCorners];
|
||||
int i, j = polyCorners - 1;
|
||||
|
||||
this.distance[0] = 0;
|
||||
|
||||
for (i = 0; i < polyCorners; i++)
|
||||
{
|
||||
this.distance[j] = this.distance[i] + Vector2.Distance(poly[i], poly[j]);
|
||||
if (poly[j].Y == poly[i].Y)
|
||||
{
|
||||
this.constant[i] = poly[i].X;
|
||||
this.multiple[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 subtracted = poly[j] - poly[i];
|
||||
this.constant[i] = (poly[i].X - ((poly[i].Y * poly[j].X) / subtracted.Y)) + ((poly[i].Y * poly[i].X) / subtracted.Y);
|
||||
this.multiple[i] = subtracted.X / subtracted.Y;
|
||||
}
|
||||
|
||||
j = i;
|
||||
}
|
||||
|
||||
this.calculated = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate any shorter distances along the path.
|
||||
/// </summary>
|
||||
/// <param name="start">The start position.</param>
|
||||
/// <param name="end">The end position.</param>
|
||||
/// <param name="point">The current point.</param>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="bool"/>.
|
||||
/// </returns>
|
||||
private bool CalculateShorterDistance(Vector2 start, Vector2 end, Vector2 point, ref PointInfoInternal info)
|
||||
{
|
||||
Vector2 diffEnds = end - start;
|
||||
|
||||
float lengthSquared = diffEnds.LengthSquared();
|
||||
Vector2 diff = point - start;
|
||||
|
||||
Vector2 multiplied = diff * diffEnds;
|
||||
float u = (multiplied.X + multiplied.Y) / lengthSquared;
|
||||
|
||||
if (u > 1)
|
||||
{
|
||||
u = 1;
|
||||
}
|
||||
else if (u < 0)
|
||||
{
|
||||
u = 0;
|
||||
}
|
||||
|
||||
Vector2 multipliedByU = diffEnds * u;
|
||||
|
||||
Vector2 pointOnLine = start + multipliedByU;
|
||||
|
||||
Vector2 d = pointOnLine - point;
|
||||
|
||||
float dist = d.LengthSquared();
|
||||
|
||||
if (info.DistanceSquared > dist)
|
||||
{
|
||||
info.DistanceSquared = dist;
|
||||
info.PointOnLine = pointOnLine;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains information about the current point.
|
||||
/// </summary>
|
||||
private struct PointInfoInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// The distance squared.
|
||||
/// </summary>
|
||||
public float DistanceSquared;
|
||||
|
||||
/// <summary>
|
||||
/// The point on the current line.
|
||||
/// </summary>
|
||||
public Point PointOnLine;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// <copyright file="LinearLineSegment.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a series of control points that will be joined by straight lines
|
||||
/// </summary>
|
||||
/// <seealso cref="ILineSegment" />
|
||||
public class LinearLineSegment : ILineSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection of points.
|
||||
/// </summary>
|
||||
private readonly ImmutableArray<Point> points;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinearLineSegment"/> class.
|
||||
/// </summary>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
public LinearLineSegment(Point start, Point end)
|
||||
: this(new[] { start, end })
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinearLineSegment"/> class.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
public LinearLineSegment(params Point[] points)
|
||||
{
|
||||
Guard.NotNull(points, nameof(points));
|
||||
Guard.MustBeGreaterThanOrEqualTo(points.Count(), 2, nameof(points));
|
||||
|
||||
this.points = ImmutableArray.Create(points);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ILineSegment" /> into a simple linear path..
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
||||
/// </returns>
|
||||
public ImmutableArray<Point> AsSimpleLinearPath()
|
||||
{
|
||||
return this.points;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// <copyright file="LinearPolygon.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a polygon made up exclusivly of a single Linear path.
|
||||
/// </summary>
|
||||
public sealed class LinearPolygon : IShape
|
||||
{
|
||||
private Polygon innerPolygon;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinearPolygon"/> class.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
public LinearPolygon(params Point[] points)
|
||||
{
|
||||
this.innerPolygon = new Polygon(new LinearLineSegment(points));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
public Rectangle Bounds => this.innerPolygon.Bounds;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum intersections.
|
||||
/// </value>
|
||||
public int MaxIntersections
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.innerPolygon.MaxIntersections;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths that make up this shape
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The paths.
|
||||
/// </value>
|
||||
public IEnumerable<IPath> Paths => this.innerPolygon.Paths;
|
||||
|
||||
/// <summary>
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// Returns the distance from the shape to the point
|
||||
/// </returns>
|
||||
public float Distance(Point point) => this.innerPolygon.Distance(point);
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point of the line.</param>
|
||||
/// <param name="end">The end point of the line.</param>
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>
|
||||
/// The number of intersections populated into the buffer.
|
||||
/// </returns>
|
||||
public int FindIntersections(Point start, Point end, Point[] buffer, int count, int offset)
|
||||
{
|
||||
return this.innerPolygon.FindIntersections(start, end, buffer, count, offset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// <copyright file="Path.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// A aggregate of <see cref="ILineSegment"/>s making a single logical path
|
||||
/// </summary>
|
||||
/// <seealso cref="IPath" />
|
||||
public class Path : IPath
|
||||
{
|
||||
/// <summary>
|
||||
/// The inner path.
|
||||
/// </summary>
|
||||
private readonly InternalPath innerPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Path"/> class.
|
||||
/// </summary>
|
||||
/// <param name="segment">The segment.</param>
|
||||
public Path(params ILineSegment[] segment)
|
||||
{
|
||||
this.innerPath = new InternalPath(segment, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Rectangle Bounds => this.innerPath.Bounds;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsClosed => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public float Length => this.innerPath.Length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ImmutableArray<Point> AsSimpleLinearPath()
|
||||
{
|
||||
return this.innerPath.Points;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PointInfo Distance(Point point)
|
||||
{
|
||||
return this.innerPath.DistanceFromPath(point);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
// <copyright file="Point.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
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);
|
||||
|
||||
private readonly Vector2 backingVector;
|
||||
|
||||
/// <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(float x, float y)
|
||||
: this(new Vector2(x, y))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Point"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="vector">
|
||||
/// The vector representing the width and height.
|
||||
/// </param>
|
||||
public Point(Vector2 vector)
|
||||
{
|
||||
this.backingVector = vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the x-coordinate of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public float X => this.backingVector.X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the y-coordinate of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public float Y => this.backingVector.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Point"/> is empty.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool IsEmpty => this.Equals(Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from <see cref="Point"/> to <see cref="Vector2"/>.
|
||||
/// </summary>
|
||||
/// <param name="d">The d.</param>
|
||||
/// <returns>
|
||||
/// The result of the conversion.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Vector2(Point d)
|
||||
{
|
||||
return d.backingVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from <see cref="Vector2"/> to <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="d">The d.</param>
|
||||
/// <returns>
|
||||
/// The result of the conversion.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Point(Vector2 d)
|
||||
{
|
||||
return new Point(d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the sum of adding two points.
|
||||
/// </summary>
|
||||
/// <param name="left">The point on the left hand of the operand.</param>
|
||||
/// <param name="right">The point on the right hand of the operand.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Point"/>
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Point operator +(Point left, Point right)
|
||||
{
|
||||
return new Point(left.backingVector + right.backingVector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the difference left by subtracting one point from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The point on the left hand of the operand.</param>
|
||||
/// <param name="right">The point on the right hand of the operand.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Point"/>
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Point operator -(Point left, Point right)
|
||||
{
|
||||
return new Point(left.backingVector - right.backingVector);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return left.backingVector == right.backingVector;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return left.backingVector != right.backingVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Vector2"/> representation for this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Vector2"/> representation for this object.</returns>
|
||||
public Vector2 ToVector2()
|
||||
{
|
||||
return this.backingVector;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <returns>A new point offset by the size</returns>
|
||||
public Point Offset(float dx, float dy)
|
||||
{
|
||||
return new Point(this.backingVector + new Vector2(dx, dy));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates this <see cref="Point" /> by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="p">The <see cref="Point" /> used offset this <see cref="Point" />.</param>
|
||||
/// <returns>A new point offset by the size</returns>
|
||||
public Point Offset(Size p)
|
||||
{
|
||||
return new Point(this.backingVector + new Vector2(p.Width, p.Height));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.backingVector.GetHashCode();
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (obj is Point)
|
||||
{
|
||||
return this.backingVector == ((Point)obj).backingVector;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current object is equal to another object of the same type.
|
||||
/// </summary>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
/// <returns>
|
||||
/// true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.
|
||||
/// </returns>
|
||||
public bool Equals(Point other)
|
||||
{
|
||||
return this.backingVector == other.backingVector;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// <copyright file="PointInfo.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Returns meta data about the nearest point on a path from a vector
|
||||
/// </summary>
|
||||
public struct PointInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The search point
|
||||
/// </summary>
|
||||
public Point SearchPoint;
|
||||
|
||||
/// <summary>
|
||||
/// The distance along path <see cref="ClosestPointOnPath"/> is away from the start of the path
|
||||
/// </summary>
|
||||
public float DistanceAlongPath;
|
||||
|
||||
/// <summary>
|
||||
/// The distance <see cref="SearchPoint"/> is away from <see cref="ClosestPointOnPath"/>.
|
||||
/// </summary>
|
||||
public float DistanceFromPath;
|
||||
|
||||
/// <summary>
|
||||
/// The closest point to <see cref="SearchPoint"/> that lies on the path.
|
||||
/// </summary>
|
||||
public Point ClosestPointOnPath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
// <copyright file="Polygon.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// A shape made up of a single path made up of one of more <see cref="ILineSegment"/>s
|
||||
/// </summary>
|
||||
public sealed class Polygon : IShape, IPath
|
||||
{
|
||||
private readonly InternalPath innerPath;
|
||||
private readonly IEnumerable<IPath> pathCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Polygon"/> class.
|
||||
/// </summary>
|
||||
/// <param name="segments">The segments.</param>
|
||||
public Polygon(params ILineSegment[] segments)
|
||||
{
|
||||
this.innerPath = new InternalPath(segments, true);
|
||||
this.pathCollection = new[] { this };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Polygon" /> class.
|
||||
/// </summary>
|
||||
/// <param name="segment">The segment.</param>
|
||||
public Polygon(ILineSegment segment)
|
||||
{
|
||||
this.innerPath = new InternalPath(segment, true);
|
||||
this.pathCollection = new[] { this };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
public Rectangle Bounds => this.innerPath.Bounds;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the path
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
public float Length => this.innerPath.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is closed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsClosed => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum intersections.
|
||||
/// </value>
|
||||
public int MaxIntersections => this.innerPath.Points.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths that make up this shape
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The paths.
|
||||
/// </value>
|
||||
public IEnumerable<IPath> Paths => this.pathCollection;
|
||||
|
||||
/// <summary>
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// The distance of the point away from the shape
|
||||
/// </returns>
|
||||
public float Distance(Point point)
|
||||
{
|
||||
bool isInside = this.innerPath.PointInPolygon(point);
|
||||
|
||||
float distance = this.innerPath.DistanceFromPath(point).DistanceFromPath;
|
||||
if (isInside)
|
||||
{
|
||||
return -distance;
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calcualtes the distance along and away from the path for a specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point along the path.</param>
|
||||
/// <returns>
|
||||
/// distance metadata about the point.
|
||||
/// </returns>
|
||||
PointInfo IPath.Distance(Point point)
|
||||
{
|
||||
return this.innerPath.DistanceFromPath(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current shape as a simple linear path.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
||||
/// </returns>
|
||||
public ImmutableArray<Point> AsSimpleLinearPath()
|
||||
{
|
||||
return this.innerPath.Points;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start" /> and <paramref name="end" />
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point of the line.</param>
|
||||
/// <param name="end">The end point of the line.</param>
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>
|
||||
/// The number of intersections populated into the buffer.
|
||||
/// </returns>
|
||||
public int FindIntersections(Point start, Point end, Point[] buffer, int count, int offset)
|
||||
{
|
||||
return this.innerPath.FindIntersections(start, end, buffer, count, offset);
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,29 @@
|
|||
// <copyright file="ClipperException.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Clipper Exception
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
internal class ClipperException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClipperException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="description">The description.</param>
|
||||
public ClipperException(string description)
|
||||
: base(description)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// <copyright file="Direction.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ???
|
||||
/// </summary>
|
||||
internal enum Direction
|
||||
{
|
||||
/// <summary>
|
||||
/// The right to left
|
||||
/// </summary>
|
||||
RightToLeft,
|
||||
|
||||
/// <summary>
|
||||
/// The left to right
|
||||
/// </summary>
|
||||
LeftToRight
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// <copyright file="EdgeSide.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ??
|
||||
/// </summary>
|
||||
internal enum EdgeSide
|
||||
{
|
||||
/// <summary>
|
||||
/// The left
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// The right
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="IntersectNode.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ??
|
||||
/// </summary>
|
||||
internal class IntersectNode
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The edge1
|
||||
/// </summary>
|
||||
internal TEdge Edge1;
|
||||
|
||||
/// <summary>
|
||||
/// The edge2
|
||||
/// </summary>
|
||||
internal TEdge Edge2;
|
||||
|
||||
/// <summary>
|
||||
/// The pt
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 Pt;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// <copyright file="IntersectNodeSort.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Compares <see cref="IntersectNode"/>s
|
||||
/// </summary>
|
||||
internal class IntersectNodeSort : IComparer<IntersectNode>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified node1.
|
||||
/// </summary>
|
||||
/// <param name="node1">The node1.</param>
|
||||
/// <param name="node2">The node2.</param>
|
||||
/// <returns>
|
||||
/// 1 if node2 %gt; node1
|
||||
/// -1 if node2 $lt; node1
|
||||
/// 0 if same
|
||||
/// </returns>
|
||||
public int Compare(IntersectNode node1, IntersectNode node2)
|
||||
{
|
||||
float i = node2.Pt.Y - node1.Pt.Y;
|
||||
if (i > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (i < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="Join.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ??
|
||||
/// </summary>
|
||||
internal class Join
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The out PT1
|
||||
/// </summary>
|
||||
internal OutPt OutPt1;
|
||||
|
||||
/// <summary>
|
||||
/// The out PT2
|
||||
/// </summary>
|
||||
internal OutPt OutPt2;
|
||||
|
||||
/// <summary>
|
||||
/// The off pt
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 OffPt;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// <copyright file="LocalMinima.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ??
|
||||
/// </summary>
|
||||
internal class LocalMinima
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The y
|
||||
/// </summary>
|
||||
internal float Y;
|
||||
|
||||
/// <summary>
|
||||
/// The left bound
|
||||
/// </summary>
|
||||
internal TEdge LeftBound;
|
||||
|
||||
/// <summary>
|
||||
/// The right bound
|
||||
/// </summary>
|
||||
internal TEdge RightBound;
|
||||
|
||||
/// <summary>
|
||||
/// The next
|
||||
/// </summary>
|
||||
internal LocalMinima Next;
|
||||
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="Maxima.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ??
|
||||
/// </summary>
|
||||
internal class Maxima
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The x
|
||||
/// </summary>
|
||||
internal float X;
|
||||
|
||||
/// <summary>
|
||||
/// The next
|
||||
/// </summary>
|
||||
internal Maxima Next;
|
||||
|
||||
/// <summary>
|
||||
/// The previous
|
||||
/// </summary>
|
||||
internal Maxima Prev;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// <copyright file="OutPt.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// ??
|
||||
/// </summary>
|
||||
internal class OutPt
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The index
|
||||
/// </summary>
|
||||
internal int Idx;
|
||||
|
||||
/// <summary>
|
||||
/// The pt
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 Pt;
|
||||
|
||||
/// <summary>
|
||||
/// The next
|
||||
/// </summary>
|
||||
internal OutPt Next;
|
||||
|
||||
/// <summary>
|
||||
/// The previous
|
||||
/// </summary>
|
||||
internal OutPt Prev;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// <copyright file="OutRec.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// OutRec: contains a path in the clipping solution. Edges in the AEL will
|
||||
/// carry a pointer to an OutRec when they are part of the clipping solution.
|
||||
/// </summary>
|
||||
internal class OutRec
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The source path
|
||||
/// </summary>
|
||||
internal IPath SourcePath;
|
||||
|
||||
/// <summary>
|
||||
/// The index
|
||||
/// </summary>
|
||||
internal int Idx;
|
||||
|
||||
/// <summary>
|
||||
/// The is hole
|
||||
/// </summary>
|
||||
internal bool IsHole;
|
||||
|
||||
/// <summary>
|
||||
/// The is open
|
||||
/// </summary>
|
||||
internal bool IsOpen;
|
||||
|
||||
/// <summary>
|
||||
/// The first left
|
||||
/// </summary>
|
||||
internal OutRec FirstLeft;
|
||||
|
||||
/// <summary>
|
||||
/// The PTS
|
||||
/// </summary>
|
||||
internal OutPt Pts;
|
||||
|
||||
/// <summary>
|
||||
/// The bottom pt
|
||||
/// </summary>
|
||||
internal OutPt BottomPt;
|
||||
|
||||
/// <summary>
|
||||
/// The poly node
|
||||
/// </summary>
|
||||
internal PolyNode PolyNode;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
// <copyright file="PolyNode.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Poly Node
|
||||
/// </summary>
|
||||
internal class PolyNode
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The polygon
|
||||
/// </summary>
|
||||
internal List<Vector2> Polygon = new List<Vector2>();
|
||||
|
||||
/// <summary>
|
||||
/// The index
|
||||
/// </summary>
|
||||
internal int Index;
|
||||
|
||||
/// <summary>
|
||||
/// The childs
|
||||
/// </summary>
|
||||
protected List<PolyNode> children = new List<PolyNode>();
|
||||
|
||||
private PolyNode parent;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child count.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The child count.
|
||||
/// </value>
|
||||
public int ChildCount
|
||||
{
|
||||
get { return this.children.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contour.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The contour.
|
||||
/// </value>
|
||||
public List<Vector2> Contour
|
||||
{
|
||||
get { return this.Polygon; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the childs.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The childs.
|
||||
/// </value>
|
||||
public List<PolyNode> Children
|
||||
{
|
||||
get { return this.children; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The parent.
|
||||
/// </value>
|
||||
public PolyNode Parent
|
||||
{
|
||||
get { return this.parent; }
|
||||
internal set { this.parent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is hole.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is hole; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsHole
|
||||
{
|
||||
get { return this.IsHoleNode(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is open.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is open; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsOpen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source path.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The source path.
|
||||
/// </value>
|
||||
public IPath SourcePath { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next.
|
||||
/// </summary>
|
||||
/// <returns>The next node</returns>
|
||||
public PolyNode GetNext()
|
||||
{
|
||||
if (this.children.Count > 0)
|
||||
{
|
||||
return this.children[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.GetNextSiblingUp();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the child.
|
||||
/// </summary>
|
||||
/// <param name="child">The child.</param>
|
||||
internal void AddChild(PolyNode child)
|
||||
{
|
||||
int cnt = this.children.Count;
|
||||
this.children.Add(child);
|
||||
child.parent = this;
|
||||
child.Index = cnt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next sibling up.
|
||||
/// </summary>
|
||||
/// <returns>The next sibling up</returns>
|
||||
internal PolyNode GetNextSiblingUp()
|
||||
{
|
||||
if (this.parent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (this.Index == this.parent.children.Count - 1)
|
||||
{
|
||||
return this.parent.GetNextSiblingUp();
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.parent.Children[this.Index + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is hole node].
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [is hole node]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
private bool IsHoleNode()
|
||||
{
|
||||
bool result = true;
|
||||
PolyNode node = this.parent;
|
||||
while (node != null)
|
||||
{
|
||||
result = !result;
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// <copyright file="PolyTree.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Poly Tree
|
||||
/// </summary>
|
||||
/// <seealso cref="Shaper2D.PolygonClipper.PolyNode" />
|
||||
internal class PolyTree : PolyNode
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// All polys
|
||||
/// </summary>
|
||||
internal List<PolyNode> AllPolys = new List<PolyNode>();
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The total.
|
||||
/// </value>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = this.AllPolys.Count;
|
||||
|
||||
// with negative offsets, ignore the hidden outer polygon ...
|
||||
if (result > 0 && this.Children[0] != this.AllPolys[0])
|
||||
{
|
||||
result--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears this instance.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
for (int i = 0; i < this.AllPolys.Count; i++)
|
||||
{
|
||||
this.AllPolys[i] = null;
|
||||
}
|
||||
|
||||
this.AllPolys.Clear();
|
||||
this.Children.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first.
|
||||
/// </summary>
|
||||
/// <returns>the first node</returns>
|
||||
public PolyNode GetFirst()
|
||||
{
|
||||
if (this.Children.Count > 0)
|
||||
{
|
||||
return this.Children[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// <copyright file="PolyType.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Poly Type
|
||||
/// </summary>
|
||||
internal enum PolyType
|
||||
{
|
||||
/// <summary>
|
||||
/// The subject
|
||||
/// </summary>
|
||||
Subject,
|
||||
|
||||
/// <summary>
|
||||
/// The clip
|
||||
/// </summary>
|
||||
Clip
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
# Clipper
|
||||
|
||||
License details for code in this folder, this is code original written by **Angus Johnson**
|
||||
|
||||
The license header onthe original file which has now be split across multiple files in this folder.
|
||||
|
||||
```
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Author : Angus Johnson *
|
||||
* Version : 6.4.0 *
|
||||
* Date : 2 July 2015 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2015 *
|
||||
* *
|
||||
* License: *
|
||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||
* http://www.boost.org/LICENSE_1_0.txt *
|
||||
* *
|
||||
* Attributions: *
|
||||
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
|
||||
* "A generic solution to polygon clipping" *
|
||||
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
|
||||
* http://portal.acm.org/citation.cfm?id=129906 *
|
||||
* *
|
||||
* Computer graphics and geometric modeling: implementation and algorithms *
|
||||
* By Max K. Agoston *
|
||||
* Springer; 1 edition (January 4, 2005) *
|
||||
* http://books.google.com/books?q=vatti+clipping+agoston *
|
||||
* *
|
||||
* See also: *
|
||||
* "Polygon Offsetting by Computing Winding Numbers" *
|
||||
* Paper no. DETC2005-85513 pp. 565-575 *
|
||||
* ASME 2005 International Design Engineering Technical Conferences *
|
||||
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
|
||||
* September 24-28, 2005 , Long Beach, California, USA *
|
||||
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
```
|
|
@ -0,0 +1,31 @@
|
|||
// <copyright file="Scanbeam.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Scanbeam
|
||||
/// </summary>
|
||||
internal class Scanbeam // would this work as a struct?
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The y
|
||||
/// </summary>
|
||||
internal float Y;
|
||||
|
||||
/// <summary>
|
||||
/// The next
|
||||
/// </summary>
|
||||
internal Scanbeam Next;
|
||||
#pragma warning restore SA1401 // Field must be private
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// <copyright file="TEdge.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D.PolygonClipper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// TEdge
|
||||
/// </summary>
|
||||
internal class TEdge
|
||||
{
|
||||
#pragma warning disable SA1401 // Field must be private
|
||||
/// <summary>
|
||||
/// The source path, see if we can link this back later
|
||||
/// </summary>
|
||||
internal IPath SourcePath;
|
||||
|
||||
/// <summary>
|
||||
/// The bot
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 Bot;
|
||||
|
||||
/// <summary>
|
||||
/// The current (updated for every new scanbeam)
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 Curr;
|
||||
|
||||
/// <summary>
|
||||
/// The top
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 Top;
|
||||
|
||||
/// <summary>
|
||||
/// The delta
|
||||
/// </summary>
|
||||
internal System.Numerics.Vector2 Delta;
|
||||
|
||||
/// <summary>
|
||||
/// The dx
|
||||
/// </summary>
|
||||
internal double Dx;
|
||||
|
||||
/// <summary>
|
||||
/// The poly type
|
||||
/// </summary>
|
||||
internal PolyType PolyTyp;
|
||||
|
||||
/// <summary>
|
||||
/// Side only refers to current side of solution poly
|
||||
/// </summary>
|
||||
internal EdgeSide Side;
|
||||
|
||||
/// <summary>
|
||||
/// 1 or -1 depending on winding direction
|
||||
/// </summary>
|
||||
internal int WindDelta;
|
||||
|
||||
/// <summary>
|
||||
/// The winding count
|
||||
/// </summary>
|
||||
internal int WindCnt;
|
||||
|
||||
/// <summary>
|
||||
/// The winding count of the opposite polytype
|
||||
/// </summary>
|
||||
internal int WindCnt2;
|
||||
|
||||
/// <summary>
|
||||
/// The out index
|
||||
/// </summary>
|
||||
internal int OutIdx;
|
||||
|
||||
/// <summary>
|
||||
/// The next
|
||||
/// </summary>
|
||||
internal TEdge Next;
|
||||
|
||||
/// <summary>
|
||||
/// The previous
|
||||
/// </summary>
|
||||
internal TEdge Prev;
|
||||
|
||||
/// <summary>
|
||||
/// The next in LML
|
||||
/// </summary>
|
||||
internal TEdge NextInLML;
|
||||
|
||||
/// <summary>
|
||||
/// The next in ael
|
||||
/// </summary>
|
||||
internal TEdge NextInAEL;
|
||||
|
||||
/// <summary>
|
||||
/// The previous in ael
|
||||
/// </summary>
|
||||
internal TEdge PrevInAEL;
|
||||
|
||||
/// <summary>
|
||||
/// The next in sel
|
||||
/// </summary>
|
||||
internal TEdge NextInSEL;
|
||||
|
||||
/// <summary>
|
||||
/// The previous in sel
|
||||
/// </summary>
|
||||
internal TEdge PrevInSEL;
|
||||
#pragma warning restore SA1401 // Field must be
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// <copyright file="AssemblyInfo.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
// Common values read from `AssemblyInfo.Common.cs`
|
|
@ -0,0 +1,345 @@
|
|||
// <copyright file="Rectangle.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// A way of optermising drawing rectangles.
|
||||
/// </summary>
|
||||
/// <seealso cref="Shaper2D.IShape" />
|
||||
public class Rectangle : IShape, IPath
|
||||
{
|
||||
private readonly Vector2 topLeft;
|
||||
private readonly Vector2 bottomRight;
|
||||
private readonly ImmutableArray<Point> points;
|
||||
private readonly IEnumerable<IPath> pathCollection;
|
||||
private readonly float halfLength;
|
||||
private readonly float length;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle" /> class.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public Rectangle(float x, float y, float width, float height)
|
||||
: this(new Point(x, y), new Size(width, height))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="location">The location.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
public Rectangle(Point location, Size size)
|
||||
{
|
||||
this.Location = location;
|
||||
this.topLeft = location;
|
||||
this.bottomRight = location.Offset(size);
|
||||
this.Size = size;
|
||||
|
||||
this.points = ImmutableArray.Create(new Point[4]
|
||||
{
|
||||
this.topLeft,
|
||||
new Vector2(this.bottomRight.X, this.topLeft.Y),
|
||||
this.bottomRight,
|
||||
new Vector2(this.topLeft.X, this.bottomRight.Y)
|
||||
});
|
||||
|
||||
this.halfLength = size.Width + size.Height;
|
||||
this.length = this.halfLength * 2;
|
||||
this.pathCollection = new[] { this };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The location.
|
||||
/// </value>
|
||||
public Point Location { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The left.
|
||||
/// </value>
|
||||
public float Left => this.topLeft.X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The right.
|
||||
/// </value>
|
||||
public float Right => this.bottomRight.X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The top.
|
||||
/// </value>
|
||||
public float Top => this.topLeft.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bottom.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bottom.
|
||||
/// </value>
|
||||
public float Bottom => this.bottomRight.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
Rectangle IShape.Bounds => this;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of this shape.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
Rectangle IPath.Bounds => this;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths that make up this shape
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The paths.
|
||||
/// </value>
|
||||
IEnumerable<IPath> IShape.Paths => this.pathCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is closed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IPath.IsClosed => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the path
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
float IPath.Length => this.length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum intersections.
|
||||
/// </value>
|
||||
int IShape.MaxIntersections => 4;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The size.
|
||||
/// </value>
|
||||
public Size Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specfied 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>
|
||||
public bool Contains(Point point)
|
||||
{
|
||||
var v = point.ToVector2();
|
||||
return Vector2.Clamp(v, this.topLeft, this.bottomRight) == v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance along and away from the path for a specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point along the path.</param>
|
||||
/// <returns>
|
||||
/// Returns details about the point and its distance away from the path.
|
||||
/// </returns>
|
||||
PointInfo IPath.Distance(Point point)
|
||||
{
|
||||
bool inside; // dont care about inside/outside for paths just distance
|
||||
return this.Distance(point, false, out inside);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// Returns the distance from the shape to the point
|
||||
/// </returns>
|
||||
public float Distance(Point point)
|
||||
{
|
||||
bool insidePoly;
|
||||
PointInfo result = this.Distance(point, true, out insidePoly);
|
||||
|
||||
// invert the distance from path when inside
|
||||
return insidePoly ? -result.DistanceFromPath : result.DistanceFromPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
||||
/// populate a buffer for all points on the edges of the <see cref="Rectangle"/>
|
||||
/// that the line intersects.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point of the line.</param>
|
||||
/// <param name="end">The end point of the line.</param>
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>
|
||||
/// The number of intersections populated into the buffer.
|
||||
/// </returns>
|
||||
public int FindIntersections(Point start, Point end, Point[] buffer, int count, int offset)
|
||||
{
|
||||
int discovered = 0;
|
||||
Vector2 startPoint = Vector2.Clamp(start, this.topLeft, this.bottomRight);
|
||||
Vector2 endPoint = Vector2.Clamp(end, this.topLeft, this.bottomRight);
|
||||
|
||||
if (startPoint == Vector2.Clamp(startPoint, start, end))
|
||||
{
|
||||
// if start closest is within line then its a valid point
|
||||
discovered++;
|
||||
buffer[offset++] = startPoint;
|
||||
}
|
||||
|
||||
if (endPoint == Vector2.Clamp(endPoint, start, end))
|
||||
{
|
||||
// if start closest is within line then its a valid point
|
||||
discovered++;
|
||||
buffer[offset++] = endPoint;
|
||||
}
|
||||
|
||||
return discovered;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ILineSegment" /> into a simple linear path..
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
||||
/// </returns>
|
||||
ImmutableArray<Point> ILineSegment.AsSimpleLinearPath()
|
||||
{
|
||||
return this.points;
|
||||
}
|
||||
|
||||
private PointInfo Distance(Vector2 point, bool getDistanceAwayOnly, out bool isInside)
|
||||
{
|
||||
// point in rectangle
|
||||
// if after its clamped by the extreams its still the same then it must be inside :)
|
||||
Vector2 clamped = Vector2.Clamp(point, this.topLeft, this.bottomRight);
|
||||
isInside = clamped == point;
|
||||
|
||||
float distanceFromEdge = float.MaxValue;
|
||||
float distanceAlongEdge = 0f;
|
||||
|
||||
if (isInside)
|
||||
{
|
||||
// get the absolute distances from the extreams
|
||||
Vector2 topLeftDist = Vector2.Abs(point - this.topLeft);
|
||||
Vector2 bottomRightDist = Vector2.Abs(point - this.bottomRight);
|
||||
|
||||
// get the min components
|
||||
Vector2 minDists = Vector2.Min(topLeftDist, bottomRightDist);
|
||||
|
||||
// and then the single smallest (dont have to worry about direction)
|
||||
distanceFromEdge = Math.Min(minDists.X, minDists.Y);
|
||||
|
||||
if (!getDistanceAwayOnly)
|
||||
{
|
||||
// we need to make clamped the closest point
|
||||
if (this.topLeft.X + distanceFromEdge == point.X)
|
||||
{
|
||||
// closer to lhf
|
||||
clamped.X = this.topLeft.X; // y is already the same
|
||||
|
||||
// distance along edge is length minus the amout down we are from the top of the rect
|
||||
distanceAlongEdge = this.length - (clamped.Y - this.topLeft.Y);
|
||||
}
|
||||
else if (this.topLeft.Y + distanceFromEdge == point.Y)
|
||||
{
|
||||
// closer to top
|
||||
clamped.Y = this.topLeft.Y; // x is already the same
|
||||
|
||||
distanceAlongEdge = clamped.X - this.topLeft.X;
|
||||
}
|
||||
else if (this.bottomRight.Y - distanceFromEdge == point.Y)
|
||||
{
|
||||
// closer to bottom
|
||||
clamped.Y = this.bottomRight.Y; // x is already the same
|
||||
|
||||
distanceAlongEdge = (this.bottomRight.X - clamped.X) + this.halfLength;
|
||||
}
|
||||
else if (this.bottomRight.X - distanceFromEdge == point.X)
|
||||
{
|
||||
// closer to rhs
|
||||
clamped.X = this.bottomRight.X; // x is already the same
|
||||
|
||||
distanceAlongEdge = (this.bottomRight.Y - clamped.Y) + this.Size.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// clamped is the point on the path thats closest no matter what
|
||||
distanceFromEdge = (clamped - point).Length();
|
||||
|
||||
if (!getDistanceAwayOnly)
|
||||
{
|
||||
// we need to figure out whats the cloests edge now and thus what distance/poitn is closest
|
||||
if (this.topLeft.X == clamped.X)
|
||||
{
|
||||
// distance along edge is length minus the amout down we are from the top of the rect
|
||||
distanceAlongEdge = this.length - (clamped.Y - this.topLeft.Y);
|
||||
}
|
||||
else if (this.topLeft.Y == clamped.Y)
|
||||
{
|
||||
distanceAlongEdge = clamped.X - this.topLeft.X;
|
||||
}
|
||||
else if (this.bottomRight.Y == clamped.Y)
|
||||
{
|
||||
distanceAlongEdge = (this.bottomRight.X - clamped.X) + this.halfLength;
|
||||
}
|
||||
else if (this.bottomRight.X == clamped.X)
|
||||
{
|
||||
distanceAlongEdge = (this.bottomRight.Y - clamped.Y) + this.Size.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new PointInfo
|
||||
{
|
||||
SearchPoint = point,
|
||||
DistanceFromPath = distanceFromEdge,
|
||||
ClosestPointOnPath = clamped,
|
||||
DistanceAlongPath = distanceAlongEdge
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>2e33181e-6e28-4662-a801-e2e7dc206029</ProjectGuid>
|
||||
<RootNamespace>Shaper2D</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
|
@ -0,0 +1,163 @@
|
|||
// <copyright file="Size.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
|
||||
namespace Shaper2D
|
||||
{
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Numerics;
|
||||
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);
|
||||
|
||||
private readonly Vector2 backingVector;
|
||||
|
||||
/// <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(float width, float height)
|
||||
: this(new Vector2(width, height))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Size"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector.</param>
|
||||
public Size(Vector2 vector)
|
||||
{
|
||||
this.backingVector = vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of this <see cref="Size"/>.
|
||||
/// </summary>
|
||||
public float Width => this.backingVector.X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of this <see cref="Size"/>.
|
||||
/// </summary>
|
||||
public float Height => this.backingVector.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Size"/> is empty.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool IsEmpty => this.Equals(Empty);
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return new Size(left.backingVector + right.backingVector);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return new Size(left.backingVector - right.backingVector);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return left.backingVector == right.backingVector;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return left.backingVector != right.backingVector;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.backingVector.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.IsEmpty)
|
||||
{
|
||||
return "Size [ Empty ]";
|
||||
}
|
||||
|
||||
return $"Size [ Width={this.Width}, Height={this.Height} ]";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Size)
|
||||
{
|
||||
return this.Equals((Size)obj);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(Size other)
|
||||
{
|
||||
return this.backingVector == other.backingVector;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"version": "0.0.1-alpha1-*",
|
||||
"title": "Shaper2D",
|
||||
"description": "Polygon manipulation on mergin library",
|
||||
"authors": [
|
||||
"Scott Williams and contributors"
|
||||
],
|
||||
"packOptions": {
|
||||
"owners": [
|
||||
"Scott Williams and contributors"
|
||||
],
|
||||
"projectUrl": "https://github.com/tocsoft/Shaper2D",
|
||||
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
|
||||
"iconUrl": "https://raw.githubusercontent.com/tocsoft/Shaper2D/master/build/icons/shaper2d-logo-128.png",
|
||||
"requireLicenseAcceptance": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tocsoft/Shaper2D"
|
||||
},
|
||||
"tags": [
|
||||
"polygon",
|
||||
"rectangle",
|
||||
"point in polygon",
|
||||
"complex polygons",
|
||||
"shape",
|
||||
"2D"
|
||||
]
|
||||
},
|
||||
"buildOptions": {
|
||||
"allowUnsafe": true,
|
||||
"xmlDoc": true,
|
||||
"additionalArguments": [ "/additionalfile:../Shared/stylecop.json", "/ruleset:../../Shaper2D.ruleset" ],
|
||||
"compile": [
|
||||
"../Shared/*.cs"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"Release": {
|
||||
"buildOptions": {
|
||||
"warningsAsErrors": true,
|
||||
"optimize": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"StyleCop.Analyzers": {
|
||||
"version": "1.0.0",
|
||||
"type": "build"
|
||||
},
|
||||
"System.Collections.Immutable": "1.2.0",
|
||||
"System.Numerics.Vectors": "4.1.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"netstandard1.1": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.0.11",
|
||||
"System.Diagnostics.Debug": "4.0.11",
|
||||
"System.Diagnostics.Tools": "4.0.1",
|
||||
"System.Linq": "4.1.0",
|
||||
"System.ObjectModel": "4.0.12",
|
||||
"System.Resources.ResourceManager": "4.0.1",
|
||||
"System.Runtime.Extensions": "4.1.0",
|
||||
"System.Runtime.InteropServices": "4.1.0",
|
||||
"System.Runtime.Numerics": "4.0.1"
|
||||
}
|
||||
},
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// <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("ImageSharp")]
|
||||
[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("Shaper2D.Tests")]
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
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("ImageSharp.Tests")]
|
||||
[assembly: AssemblyDescription("A cross-platform library for processing of image files written in C#")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ImageSharp.Tests")]
|
||||
[assembly: AssemblyCopyright("Copyright © Scott Williams and contributors.")]
|
||||
[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("f836e8e6-b4d9-4208-8346-140c74678b91")]
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>f836e8e6-b4d9-4208-8346-140c74678b91</ProjectGuid>
|
||||
<RootNamespace>ImageSharp.Tests</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"version": "0.0.0-*",
|
||||
"configurations": {
|
||||
"Release": {
|
||||
"buildOptions": {
|
||||
"warningsAsErrors": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"Shaper2D": {
|
||||
"target": "project",
|
||||
"version": "0.0.1-*"
|
||||
},
|
||||
"xunit": "2.2.0-*",
|
||||
"dotnet-test-xunit": "2.2.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"type": "platform",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"Microsoft.CodeCoverage": "1.0.2"
|
||||
}
|
||||
},
|
||||
"net451": {
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
},
|
||||
"testRunner": "xunit"
|
||||
}
|
Загрузка…
Ссылка в новой задаче