mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-19 10:47:56 +08:00
Merge branch 'master' of https://github.com/UglyToad/Pdf
This commit is contained in:
173
src/UglyToad.PdfPig.Tests/Geometry/PdfLineTests.cs
Normal file
173
src/UglyToad.PdfPig.Tests/Geometry/PdfLineTests.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using UglyToad.PdfPig.Geometry;
|
||||
using Xunit;
|
||||
|
||||
namespace UglyToad.PdfPig.Tests.Geometry
|
||||
{
|
||||
public class PdfLineTests
|
||||
{
|
||||
[Fact]
|
||||
public void OriginIsZero()
|
||||
{
|
||||
var origin = new PdfLine();
|
||||
|
||||
Assert.Equal(0, origin.Point1.X);
|
||||
Assert.Equal(0, origin.Point1.Y);
|
||||
Assert.Equal(0, origin.Point2.X);
|
||||
Assert.Equal(0, origin.Point2.Y);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Length()
|
||||
{
|
||||
var line = new PdfLine(2, 1, 6, 4);
|
||||
Assert.Equal(5m, line.Length);
|
||||
|
||||
var line2 = new PdfLine(-2, 8, -7, -5);
|
||||
Assert.Equal(13.93m, Math.Round(line2.Length, 2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Contains()
|
||||
{
|
||||
var line = new PdfLine(10, 7.5m, 26.3m, 12);
|
||||
Assert.False(line.Contains(new PdfPoint(5, 2)));
|
||||
Assert.False(line.Contains(new PdfPoint(5, 6.11963190184049m)));
|
||||
Assert.False(line.Contains(new PdfPoint(27, 12.1932515337423m)));
|
||||
Assert.False(line.Contains(new PdfPoint(12, 15)));
|
||||
Assert.False(line.Contains(new PdfPoint(10, 12)));
|
||||
Assert.True(line.Contains(new PdfPoint(20, 10.260736196319m)));
|
||||
Assert.True(line.Contains(new PdfPoint(10, 7.5m)));
|
||||
|
||||
var verticalLine = new PdfLine(10, 7.5m, 10, 15);
|
||||
Assert.False(verticalLine.Contains(new PdfPoint(5, 2)));
|
||||
Assert.False(verticalLine.Contains(new PdfPoint(12, 15)));
|
||||
Assert.False(verticalLine.Contains(new PdfPoint(10, 16)));
|
||||
Assert.False(verticalLine.Contains(new PdfPoint(10, 7)));
|
||||
Assert.True(verticalLine.Contains(new PdfPoint(10, 12)));
|
||||
Assert.True(verticalLine.Contains(new PdfPoint(10, 7.5m)));
|
||||
|
||||
var horizontalLine = new PdfLine(10, 7.5m, 26.3m, 7.5m);
|
||||
Assert.False(horizontalLine.Contains(new PdfPoint(5, 2)));
|
||||
Assert.False(horizontalLine.Contains(new PdfPoint(5, 7.5)));
|
||||
Assert.False(horizontalLine.Contains(new PdfPoint(27, 7.5)));
|
||||
Assert.False(horizontalLine.Contains(new PdfPoint(10, 12)));
|
||||
Assert.True(horizontalLine.Contains(new PdfPoint(20, 7.5)));
|
||||
Assert.True(horizontalLine.Contains(new PdfPoint(26.3m, 7.5m)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParallelTo()
|
||||
{
|
||||
var verticalLine1 = new PdfLine(10, 7.5m, 10, 15);
|
||||
var verticalLine2 = new PdfLine(200, 0, 200, 551.5467m);
|
||||
var horizontalLine1 = new PdfLine(10, 7.5m, 26.3m, 7.5m);
|
||||
var horizontalLine2 = new PdfLine(27, 57, 200.9999872m, 57);
|
||||
var obliqueLine1 = new PdfLine(10, 7.5m, 26.3m, 12);
|
||||
var obliqueLine2 = new PdfLine(60, 28.8036809815951m, 40, 23.2822085889571m);
|
||||
|
||||
Assert.True(verticalLine1.ParallelTo(verticalLine2));
|
||||
Assert.True(verticalLine2.ParallelTo(verticalLine1));
|
||||
|
||||
Assert.False(obliqueLine1.ParallelTo(verticalLine2));
|
||||
Assert.False(verticalLine2.ParallelTo(obliqueLine1));
|
||||
|
||||
Assert.False(obliqueLine1.ParallelTo(verticalLine1));
|
||||
Assert.False(verticalLine1.ParallelTo(obliqueLine1));
|
||||
|
||||
Assert.True(horizontalLine1.ParallelTo(horizontalLine2));
|
||||
Assert.True(horizontalLine2.ParallelTo(horizontalLine1));
|
||||
|
||||
Assert.False(obliqueLine1.ParallelTo(horizontalLine1));
|
||||
Assert.False(horizontalLine1.ParallelTo(obliqueLine1));
|
||||
|
||||
Assert.False(obliqueLine1.ParallelTo(horizontalLine2));
|
||||
Assert.False(horizontalLine2.ParallelTo(obliqueLine1));
|
||||
|
||||
Assert.False(verticalLine1.ParallelTo(horizontalLine2));
|
||||
Assert.False(horizontalLine2.ParallelTo(verticalLine1));
|
||||
|
||||
Assert.False(verticalLine2.ParallelTo(horizontalLine2));
|
||||
Assert.False(horizontalLine2.ParallelTo(verticalLine2));
|
||||
|
||||
Assert.True(obliqueLine1.ParallelTo(obliqueLine2));
|
||||
Assert.True(obliqueLine2.ParallelTo(obliqueLine1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IntersectsWithLine()
|
||||
{
|
||||
var verticalLine1 = new PdfLine(10, 7.5m, 10, 15);
|
||||
var verticalLine2 = new PdfLine(200, 0, 200, 551.5467m);
|
||||
var horizontalLine1 = new PdfLine(10, 7.5m, 26.3m, 7.5m);
|
||||
var horizontalLine2 = new PdfLine(27, 57, 200.9999872m, 57);
|
||||
var horizontalLine3 = new PdfLine(27, 57, 250, 57);
|
||||
var obliqueLine1 = new PdfLine(10, 7.5m, 26.3m, 12);
|
||||
var obliqueLine2 = new PdfLine(60, 28.8036809815951m, 40, 23.2822085889571m);
|
||||
var obliqueLine3 = new PdfLine(20, 7.5m, 10, 15);
|
||||
|
||||
Assert.False(verticalLine1.IntersectsWith(verticalLine2));
|
||||
Assert.False(verticalLine2.IntersectsWith(verticalLine1));
|
||||
Assert.False(horizontalLine1.IntersectsWith(horizontalLine2));
|
||||
Assert.False(horizontalLine2.IntersectsWith(horizontalLine1));
|
||||
Assert.False(obliqueLine1.IntersectsWith(obliqueLine2));
|
||||
Assert.False(obliqueLine2.IntersectsWith(obliqueLine1));
|
||||
Assert.False(obliqueLine1.IntersectsWith(obliqueLine1));
|
||||
Assert.False(obliqueLine1.IntersectsWith(verticalLine2));
|
||||
Assert.False(verticalLine2.IntersectsWith(obliqueLine1));
|
||||
Assert.False(obliqueLine1.IntersectsWith(horizontalLine2));
|
||||
Assert.False(horizontalLine2.IntersectsWith(obliqueLine1));
|
||||
Assert.False(verticalLine1.IntersectsWith(horizontalLine2));
|
||||
Assert.False(horizontalLine2.IntersectsWith(verticalLine1));
|
||||
|
||||
Assert.True(obliqueLine1.IntersectsWith(horizontalLine1));
|
||||
Assert.True(horizontalLine1.IntersectsWith(obliqueLine1));
|
||||
Assert.True(obliqueLine1.IntersectsWith(verticalLine1));
|
||||
Assert.True(verticalLine1.IntersectsWith(obliqueLine1));
|
||||
Assert.True(verticalLine2.IntersectsWith(horizontalLine2));
|
||||
Assert.True(horizontalLine2.IntersectsWith(verticalLine2));
|
||||
Assert.True(verticalLine2.IntersectsWith(horizontalLine3));
|
||||
Assert.True(horizontalLine3.IntersectsWith(verticalLine2));
|
||||
Assert.True(obliqueLine1.IntersectsWith(obliqueLine3));
|
||||
Assert.True(obliqueLine3.IntersectsWith(obliqueLine1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IntersectLine()
|
||||
{
|
||||
var verticalLine1 = new PdfLine(10, 7.5m, 10, 15);
|
||||
var verticalLine2 = new PdfLine(200, 0, 200, 551.5467m);
|
||||
var horizontalLine1 = new PdfLine(10, 7.5m, 26.3m, 7.5m);
|
||||
var horizontalLine2 = new PdfLine(27, 57, 200.9999872m, 57);
|
||||
var horizontalLine3 = new PdfLine(27, 57, 250, 57);
|
||||
var obliqueLine1 = new PdfLine(10, 7.5m, 26.3m, 12);
|
||||
var obliqueLine2 = new PdfLine(60, 28.8036809815951m, 40, 23.2822085889571m);
|
||||
var obliqueLine3 = new PdfLine(20, 7.5m, 10, 15);
|
||||
|
||||
Assert.Null(verticalLine1.Intersect(verticalLine2));
|
||||
Assert.Null(verticalLine2.Intersect(verticalLine1));
|
||||
Assert.Null(horizontalLine1.Intersect(horizontalLine2));
|
||||
Assert.Null(horizontalLine2.Intersect(horizontalLine1));
|
||||
Assert.Null(obliqueLine1.Intersect(obliqueLine2));
|
||||
Assert.Null(obliqueLine2.Intersect(obliqueLine1));
|
||||
Assert.Null(obliqueLine1.Intersect(obliqueLine1));
|
||||
Assert.Null(obliqueLine1.Intersect(verticalLine2));
|
||||
Assert.Null(verticalLine2.Intersect(obliqueLine1));
|
||||
Assert.Null(obliqueLine1.Intersect(horizontalLine2));
|
||||
Assert.Null(horizontalLine2.Intersect(obliqueLine1));
|
||||
Assert.Null(verticalLine1.Intersect(horizontalLine2));
|
||||
Assert.Null(horizontalLine2.Intersect(verticalLine1));
|
||||
|
||||
Assert.Equal(new PdfPoint(10, 7.5m), obliqueLine1.Intersect(horizontalLine1));
|
||||
Assert.Equal(new PdfPoint(10, 7.5m), horizontalLine1.Intersect(obliqueLine1));
|
||||
Assert.Equal(new PdfPoint(10, 7.5m), obliqueLine1.Intersect(verticalLine1));
|
||||
Assert.Equal(new PdfPoint(10, 7.5m), verticalLine1.Intersect(obliqueLine1));
|
||||
Assert.Equal(new PdfPoint(200, 57), verticalLine2.Intersect(horizontalLine2));
|
||||
Assert.Equal(new PdfPoint(200, 57), horizontalLine2.Intersect(verticalLine2));
|
||||
Assert.Equal(new PdfPoint(200, 57), verticalLine2.Intersect(horizontalLine3));
|
||||
Assert.Equal(new PdfPoint(200, 57), horizontalLine3.Intersect(verticalLine2));
|
||||
Assert.Equal(new PdfPoint(17.3094170403587m, 9.51793721973094m), obliqueLine1.Intersect(obliqueLine3));
|
||||
Assert.Equal(new PdfPoint(17.3094170403587m, 9.51793721973094m), obliqueLine3.Intersect(obliqueLine1));
|
||||
}
|
||||
}
|
||||
}
|
66
src/UglyToad.PdfPig.Tests/Geometry/PdfRectangleTests.cs
Normal file
66
src/UglyToad.PdfPig.Tests/Geometry/PdfRectangleTests.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using UglyToad.PdfPig.Geometry;
|
||||
using Xunit;
|
||||
|
||||
namespace UglyToad.PdfPig.Tests.Geometry
|
||||
{
|
||||
public class PdfRectangleTests
|
||||
{
|
||||
public void Area()
|
||||
{
|
||||
PdfRectangle rectangle = new PdfRectangle(10, 10, 20, 20);
|
||||
Assert.Equal(100m, rectangle.Area);
|
||||
|
||||
PdfRectangle rectangle1 = new PdfRectangle(149.95376m, 687.13456m, 451.73539m, 1478.4997m);
|
||||
Assert.Equal(238819.4618743782m, rectangle1.Area);
|
||||
}
|
||||
|
||||
public void Centroid()
|
||||
{
|
||||
PdfRectangle rectangle = new PdfRectangle(10, 10, 20, 20);
|
||||
Assert.Equal(new PdfPoint(15, 15), rectangle.Centroid);
|
||||
|
||||
PdfRectangle rectangle1 = new PdfRectangle(149.95376m, 687.13456m, 451.73539m, 1478.4997m);
|
||||
Assert.Equal(new PdfPoint(300.844575m, 1082.81713m), rectangle1.Centroid);
|
||||
}
|
||||
|
||||
public void Intersect()
|
||||
{
|
||||
PdfRectangle rectangle = new PdfRectangle(10, 10, 20, 20);
|
||||
PdfRectangle rectangle1 = new PdfRectangle(149.95376m, 687.13456m, 451.73539m, 1478.4997m);
|
||||
Assert.Null(rectangle.Intersect(rectangle1));
|
||||
Assert.Equal(rectangle1, rectangle1.Intersect(rectangle1));
|
||||
|
||||
PdfRectangle rectangle2 = new PdfRectangle(50, 687.13456m, 350, 1478.4997m);
|
||||
Assert.Equal(new PdfRectangle(149.95376m, 687.13456m, 350, 1478.4997m), rectangle1.Intersect(rectangle2));
|
||||
|
||||
PdfRectangle rectangle3 = new PdfRectangle(200, 800, 350, 1200);
|
||||
Assert.Equal(rectangle3, rectangle1.Intersect(rectangle3));
|
||||
}
|
||||
|
||||
public void IntersectsWith()
|
||||
{
|
||||
PdfRectangle rectangle = new PdfRectangle(10, 10, 20, 20);
|
||||
PdfRectangle rectangle1 = new PdfRectangle(149.95376m, 687.13456m, 451.73539m, 1478.4997m);
|
||||
Assert.False(rectangle.IntersectsWith(rectangle1));
|
||||
Assert.True(rectangle1.IntersectsWith(rectangle1));
|
||||
|
||||
PdfRectangle rectangle2 = new PdfRectangle(50, 687.13456m, 350, 1478.4997m);
|
||||
Assert.True(rectangle1.IntersectsWith(rectangle2));
|
||||
|
||||
PdfRectangle rectangle3 = new PdfRectangle(200, 800, 350, 1200);
|
||||
Assert.True(rectangle1.IntersectsWith(rectangle3));
|
||||
|
||||
PdfRectangle rectangle4 = new PdfRectangle(5, 7, 10, 25);
|
||||
Assert.False(rectangle1.IntersectsWith(rectangle4)); // special case where they share one border
|
||||
}
|
||||
|
||||
public void Contains()
|
||||
{
|
||||
PdfRectangle rectangle = new PdfRectangle(10, 10, 20, 20);
|
||||
Assert.True(rectangle.Contains(new PdfPoint(15, 15)));
|
||||
Assert.False(rectangle.Contains(new PdfPoint(10, 15)));
|
||||
Assert.True(rectangle.Contains(new PdfPoint(10, 15), true));
|
||||
Assert.False(rectangle.Contains(new PdfPoint(100, 100), true));
|
||||
}
|
||||
}
|
||||
}
|
@@ -103,6 +103,7 @@
|
||||
"UglyToad.PdfPig.Fonts.FontDescriptorFlags",
|
||||
"UglyToad.PdfPig.Fonts.FontStretch",
|
||||
"UglyToad.PdfPig.Fonts.Standard14Font",
|
||||
"UglyToad.PdfPig.Geometry.GeometryExtensions",
|
||||
"UglyToad.PdfPig.Geometry.PdfPath",
|
||||
"UglyToad.PdfPig.Geometry.PdfPoint",
|
||||
"UglyToad.PdfPig.Geometry.PdfLine",
|
||||
|
@@ -9502,75 +9502,111 @@ namespace UglyToad.PdfPig.Export
|
||||
public enum PageXmlTextSimpleType
|
||||
{
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Paragraph
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("paragraph")]
|
||||
Paragraph,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Heading
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("heading")]
|
||||
Heading,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Caption
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("caption")]
|
||||
Caption,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Header
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("header")]
|
||||
Header,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Footer
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("footer")]
|
||||
Footer,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Page number
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("page-number")]
|
||||
PageNumber,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Drop Capital, a letter a the beginning of a word that is bigger than the usual character size. Usually to start a chapter.
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("drop-capital")]
|
||||
DropCapital,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Credit
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("credit")]
|
||||
Credit,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Floating
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("floating")]
|
||||
Floating,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Signature mark
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("signature-mark")]
|
||||
SignatureMark,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Catch word
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("catch-word")]
|
||||
CatchWord,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Marginalia
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("marginalia")]
|
||||
Marginalia,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Foot note
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("footnote")]
|
||||
FootNote,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Foot note - continued
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("footnote-continued")]
|
||||
FootNoteContinued,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// End note
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("endnote")]
|
||||
EndNote,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Table of content
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("TOC-entry")]
|
||||
TocEntry,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// List
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("list-label")]
|
||||
LisLabel,
|
||||
|
||||
/// <remarks/>
|
||||
/// <summary>
|
||||
/// Other
|
||||
/// </summary>
|
||||
[XmlEnumAttribute("other")]
|
||||
Other,
|
||||
}
|
||||
|
518
src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs
Normal file
518
src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs
Normal file
@@ -0,0 +1,518 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static UglyToad.PdfPig.Geometry.PdfPath;
|
||||
|
||||
namespace UglyToad.PdfPig.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension class to Geometry.
|
||||
/// </summary>
|
||||
public static class GeometryExtensions
|
||||
{
|
||||
#region PdfRectangle
|
||||
/// <summary>
|
||||
/// Whether the rectangle contains the point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle that should contain the point.</param>
|
||||
/// <param name="point">The point that should be contained within the rectangle.</param>
|
||||
/// <param name="includeBorder">If set to false, will return false if the point belongs to the border.</param>
|
||||
public static bool Contains(this PdfRectangle rectangle, PdfPoint point, bool includeBorder = false)
|
||||
{
|
||||
if (includeBorder)
|
||||
{
|
||||
return point.X >= rectangle.Left &&
|
||||
point.X <= rectangle.Right &&
|
||||
point.Y >= rectangle.Bottom &&
|
||||
point.Y <= rectangle.Top;
|
||||
}
|
||||
|
||||
return point.X > rectangle.Left &&
|
||||
point.X < rectangle.Right &&
|
||||
point.Y > rectangle.Bottom &&
|
||||
point.Y < rectangle.Top;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether two rectangles overlap.
|
||||
/// <para>Returns false if the two rectangles only share a border.</para>
|
||||
/// </summary>
|
||||
public static bool IntersectsWith(this PdfRectangle rectangle, PdfRectangle other)
|
||||
{
|
||||
if (rectangle.Left > other.Right || other.Left > rectangle.Right)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rectangle.Top < other.Bottom || other.Top < rectangle.Bottom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="PdfRectangle"/> that is the intersection of two rectangles.
|
||||
/// </summary>
|
||||
public static PdfRectangle? Intersect(this PdfRectangle rectangle, PdfRectangle other)
|
||||
{
|
||||
if (!rectangle.IntersectsWith(other)) return null;
|
||||
return new PdfRectangle(Math.Max(rectangle.BottomLeft.X, other.BottomLeft.X),
|
||||
Math.Max(rectangle.BottomLeft.Y, other.BottomLeft.Y),
|
||||
Math.Min(rectangle.TopRight.X, other.TopRight.X),
|
||||
Math.Min(rectangle.TopRight.Y, other.TopRight.Y));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PdfLine
|
||||
/// <summary>
|
||||
/// Whether the line segment contains the point.
|
||||
/// </summary>
|
||||
public static bool Contains(this PdfLine line, PdfPoint point)
|
||||
{
|
||||
if (line.Point2.X == line.Point1.X)
|
||||
{
|
||||
if (point.X == line.Point2.X)
|
||||
{
|
||||
return Math.Sign(point.Y - line.Point2.Y) != Math.Sign(point.Y - line.Point1.Y);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (line.Point2.Y == line.Point1.Y)
|
||||
{
|
||||
if (point.Y == line.Point2.Y)
|
||||
{
|
||||
return Math.Sign(point.X - line.Point2.X) != Math.Sign(point.X - line.Point1.X);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var tx = (point.X - line.Point1.X) / (line.Point2.X - line.Point1.X);
|
||||
var ty = (point.Y - line.Point1.Y) / (line.Point2.Y - line.Point1.Y);
|
||||
if (Math.Round(tx - ty, 5) != 0) return false;
|
||||
return (tx >= 0 && tx <= 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether two lines intersect.
|
||||
/// </summary>
|
||||
public static bool IntersectsWith(this PdfLine line, PdfLine other)
|
||||
{
|
||||
return Intersect(line, other) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="PdfPoint"/> that is the intersection of two lines.
|
||||
/// </summary>
|
||||
public static PdfPoint? Intersect(this PdfLine line, PdfLine other)
|
||||
{
|
||||
// if the bounding boxes do not intersect, the lines cannot intersect
|
||||
if (!line.GetBoundingRectangle().IntersectsWith(other.GetBoundingRectangle()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var eq1 = GetSlopeIntercept(line.Point1, line.Point2);
|
||||
var eq2 = GetSlopeIntercept(other.Point1, other.Point2);
|
||||
|
||||
if (double.IsNaN(eq1.Slope) && double.IsNaN(eq2.Slope)) return null; // both lines are vertical (hence parallel)
|
||||
if (eq1.Slope == eq2.Slope) return null; // both lines are parallel
|
||||
|
||||
var intersection = new PdfPoint();
|
||||
|
||||
if (double.IsNaN(eq1.Slope))
|
||||
{
|
||||
var x = eq1.Intercept;
|
||||
var y = eq2.Slope * x + eq2.Intercept;
|
||||
intersection = new PdfPoint(x, y);
|
||||
}
|
||||
else if (double.IsNaN(eq2.Slope))
|
||||
{
|
||||
var x = eq2.Intercept;
|
||||
var y = eq1.Slope * x + eq1.Intercept;
|
||||
intersection = new PdfPoint(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
var x = (eq2.Intercept - eq1.Intercept) / (eq1.Slope - eq2.Slope);
|
||||
var y = eq1.Slope * x + eq1.Intercept;
|
||||
intersection = new PdfPoint(x, y);
|
||||
}
|
||||
|
||||
// check if the intersection point belongs to both segments
|
||||
// (for the moment we only know it belongs to both lines)
|
||||
if (!line.Contains(intersection)) return null;
|
||||
if (!other.Contains(intersection)) return null;
|
||||
return intersection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if both lines are parallel.
|
||||
/// </summary>
|
||||
public static bool ParallelTo(this PdfLine line, PdfLine other)
|
||||
{
|
||||
var val1 = (line.Point2.Y - line.Point1.Y) * (other.Point2.X - other.Point1.X);
|
||||
var val2 = (other.Point2.Y - other.Point1.Y) * (line.Point2.X - line.Point1.X);
|
||||
return Math.Round(val1 - val2, 5) == 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Path Line
|
||||
/// <summary>
|
||||
/// Whether the line segment contains the point.
|
||||
/// </summary>
|
||||
public static bool Contains(this Line line, PdfPoint point)
|
||||
{
|
||||
if (line.To.X == line.From.X)
|
||||
{
|
||||
if (point.X == line.To.X)
|
||||
{
|
||||
return Math.Sign(point.Y - line.To.Y) != Math.Sign(point.Y - line.From.Y);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (line.To.Y == line.From.Y)
|
||||
{
|
||||
if (point.Y == line.To.Y)
|
||||
{
|
||||
return Math.Sign(point.X - line.To.X) != Math.Sign(point.X - line.From.X);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var tx = (point.X - line.From.X) / (line.To.X - line.From.X);
|
||||
var ty = (point.Y - line.From.Y) / (line.To.Y - line.From.Y);
|
||||
if (Math.Round(tx - ty, 5) != 0) return false;
|
||||
return (tx >= 0 && tx <= 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether two lines intersect.
|
||||
/// </summary>
|
||||
public static bool IntersectsWith(this Line line, Line other)
|
||||
{
|
||||
return Intersect(line, other) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="PdfPoint"/> that is the intersection of two lines.
|
||||
/// </summary>
|
||||
public static PdfPoint? Intersect(this Line line, Line other)
|
||||
{
|
||||
// if the bounding boxes do not intersect, the lines cannot intersect
|
||||
var thisLineBbox = line.GetBoundingRectangle();
|
||||
if (!thisLineBbox.HasValue) return null;
|
||||
|
||||
var lineBbox = other.GetBoundingRectangle();
|
||||
if (!lineBbox.HasValue) return null;
|
||||
|
||||
if (!thisLineBbox.Value.IntersectsWith(lineBbox.Value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var eq1 = GetSlopeIntercept(line.From, line.To);
|
||||
var eq2 = GetSlopeIntercept(other.From, other.To);
|
||||
|
||||
if (double.IsNaN(eq1.Slope) && double.IsNaN(eq2.Slope)) return null; // both lines are vertical (hence parallel)
|
||||
if (eq1.Slope == eq2.Slope) return null; // both lines are parallel
|
||||
|
||||
var intersection = new PdfPoint();
|
||||
|
||||
if (double.IsNaN(eq1.Slope))
|
||||
{
|
||||
var x = eq1.Intercept;
|
||||
var y = eq2.Slope * x + eq2.Intercept;
|
||||
intersection = new PdfPoint(x, y);
|
||||
}
|
||||
else if (double.IsNaN(eq2.Slope))
|
||||
{
|
||||
var x = eq2.Intercept;
|
||||
var y = eq1.Slope * x + eq1.Intercept;
|
||||
intersection = new PdfPoint(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
var x = (eq2.Intercept - eq1.Intercept) / (eq1.Slope - eq2.Slope);
|
||||
var y = eq1.Slope * x + eq1.Intercept;
|
||||
intersection = new PdfPoint(x, y);
|
||||
}
|
||||
|
||||
// check if the intersection point belongs to both segments
|
||||
// (for the moment we only know it belongs to both lines)
|
||||
if (!line.Contains(intersection)) return null;
|
||||
if (!other.Contains(intersection)) return null;
|
||||
return intersection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if both lines are parallel.
|
||||
/// </summary>
|
||||
public static bool ParallelTo(this Line line, Line other)
|
||||
{
|
||||
var val1 = (line.To.Y - line.From.Y) * (other.To.X - other.From.X);
|
||||
var val2 = (other.To.Y - other.From.Y) * (line.To.X - line.From.X);
|
||||
return Math.Round(val1 - val2, 5) == 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Path Bezier Curve
|
||||
/// <summary>
|
||||
/// Split a bezier curve into 2 bezier curves, at tau.
|
||||
/// </summary>
|
||||
/// <param name="bezierCurve">The original bezier curve.</param>
|
||||
/// <param name="tau">The t value were to split the curve, usually between 0 and 1, but not necessary.</param>
|
||||
private static (BezierCurve, BezierCurve) Split(this BezierCurve bezierCurve, decimal tau)
|
||||
{
|
||||
// De Casteljau Algorithm
|
||||
PdfPoint[][] points = new PdfPoint[4][];
|
||||
|
||||
points[0] = new PdfPoint[]
|
||||
{
|
||||
bezierCurve.StartPoint,
|
||||
bezierCurve.FirstControlPoint,
|
||||
bezierCurve.SecondControlPoint,
|
||||
bezierCurve.EndPoint
|
||||
};
|
||||
points[1] = new PdfPoint[3];
|
||||
points[2] = new PdfPoint[2];
|
||||
points[3] = new PdfPoint[1];
|
||||
|
||||
for (int j = 1; j <= 3; j++)
|
||||
{
|
||||
for (int i = 0; i <= 3 - j; i++)
|
||||
{
|
||||
var x = (1 - tau) * points[j - 1][i].X + tau * points[j - 1][i + 1].X;
|
||||
var y = (1 - tau) * points[j - 1][i].Y + tau * points[j - 1][i + 1].Y;
|
||||
points[j][i] = new PdfPoint(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
return (new BezierCurve(points[0][0], points[1][0], points[2][0], points[3][0]),
|
||||
new BezierCurve(points[3][0], points[2][1], points[1][2], points[0][3]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="PdfPoint"/>s that are the intersections of the line and the curve.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static PdfPoint[] Intersect(this BezierCurve bezierCurve, PdfLine line)
|
||||
{
|
||||
var ts = FindIntersectionT(bezierCurve, line);
|
||||
if (ts.Count() == 0) return null;
|
||||
|
||||
List<PdfPoint> points = new List<PdfPoint>();
|
||||
foreach (var t in ts)
|
||||
{
|
||||
PdfPoint point = new PdfPoint(
|
||||
BezierCurve.ValueWithT((double)bezierCurve.StartPoint.X,
|
||||
(double)bezierCurve.FirstControlPoint.X,
|
||||
(double)bezierCurve.SecondControlPoint.X,
|
||||
(double)bezierCurve.EndPoint.X,
|
||||
t),
|
||||
BezierCurve.ValueWithT((double)bezierCurve.StartPoint.Y,
|
||||
(double)bezierCurve.FirstControlPoint.Y,
|
||||
(double)bezierCurve.SecondControlPoint.Y,
|
||||
(double)bezierCurve.EndPoint.Y,
|
||||
t));
|
||||
points.Add(point);
|
||||
}
|
||||
return points.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="PdfPoint"/>s that are the intersections of the line and the curve.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static PdfPoint[] Intersect(this BezierCurve bezierCurve, Line line)
|
||||
{
|
||||
var ts = FindIntersectionT(bezierCurve, line);
|
||||
if (ts.Count() == 0) return null;
|
||||
|
||||
List<PdfPoint> points = new List<PdfPoint>();
|
||||
foreach (var t in ts)
|
||||
{
|
||||
PdfPoint point = new PdfPoint(
|
||||
BezierCurve.ValueWithT((double)bezierCurve.StartPoint.X,
|
||||
(double)bezierCurve.FirstControlPoint.X,
|
||||
(double)bezierCurve.SecondControlPoint.X,
|
||||
(double)bezierCurve.EndPoint.X,
|
||||
t),
|
||||
BezierCurve.ValueWithT((double)bezierCurve.StartPoint.Y,
|
||||
(double)bezierCurve.FirstControlPoint.Y,
|
||||
(double)bezierCurve.SecondControlPoint.Y,
|
||||
(double)bezierCurve.EndPoint.Y,
|
||||
t)
|
||||
);
|
||||
points.Add(point);
|
||||
}
|
||||
return points.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the t values that are the intersections of the line and the curve.
|
||||
/// </summary>
|
||||
/// <returns>List of t values where the <see cref="BezierCurve"/> and the <see cref="PdfLine"/> intersect.</returns>
|
||||
public static double[] FindIntersectionT(this BezierCurve bezierCurve, PdfLine line)
|
||||
{
|
||||
// if the bounding boxes do not intersect, they cannot intersect
|
||||
var bezierBbox = bezierCurve.GetBoundingRectangle();
|
||||
if (!bezierBbox.HasValue) return null;
|
||||
var lineBbox = line.GetBoundingRectangle();
|
||||
|
||||
if (!bezierBbox.Value.IntersectsWith(lineBbox))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
double x1 = (double)line.Point1.X;
|
||||
double y1 = (double)line.Point1.Y;
|
||||
double x2 = (double)line.Point2.X;
|
||||
double y2 = (double)line.Point2.Y;
|
||||
return FindIntersectionT(bezierCurve, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the t values that are the intersections of the line and the curve.
|
||||
/// </summary>
|
||||
/// <returns>List of t values where the <see cref="BezierCurve"/> and the <see cref="Line"/> intersect.</returns>
|
||||
public static double[] FindIntersectionT(this BezierCurve bezierCurve, Line line)
|
||||
{
|
||||
// if the bounding boxes do not intersect, they cannot intersect
|
||||
var bezierBbox = bezierCurve.GetBoundingRectangle();
|
||||
if (!bezierBbox.HasValue) return null;
|
||||
var lineBbox = line.GetBoundingRectangle();
|
||||
if (!lineBbox.HasValue) return null;
|
||||
|
||||
if (!bezierBbox.Value.IntersectsWith(lineBbox.Value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
double x1 = (double)line.From.X;
|
||||
double y1 = (double)line.From.Y;
|
||||
double x2 = (double)line.To.X;
|
||||
double y2 = (double)line.To.Y;
|
||||
return FindIntersectionT(bezierCurve, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
private static double[] FindIntersectionT(BezierCurve bezierCurve, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
double A = (y2 - y1);
|
||||
double B = (x1 - x2);
|
||||
double C = x1 * (y1 - y2) + y1 * (x2 - x1);
|
||||
|
||||
double alpha = (double)bezierCurve.StartPoint.X * A + (double)bezierCurve.StartPoint.Y * B;
|
||||
double beta = 3.0 * ((double)bezierCurve.FirstControlPoint.X * A + (double)bezierCurve.FirstControlPoint.Y * B);
|
||||
double gamma = 3.0 * ((double)bezierCurve.SecondControlPoint.X * A + (double)bezierCurve.SecondControlPoint.Y * B);
|
||||
double delta = (double)bezierCurve.EndPoint.X * A + (double)bezierCurve.EndPoint.Y * B;
|
||||
|
||||
double a = (-alpha + beta - gamma + delta);
|
||||
double b = (3 * alpha - 2 * beta + gamma);
|
||||
double c = -3 * alpha + beta;
|
||||
double d = alpha + C;
|
||||
|
||||
var solution = SolveCubicEquation(a, b, c, d);
|
||||
|
||||
return solution.Where(s => !double.IsNaN(s)).Where(s => s >= -double.Epsilon && s <= 1.0).OrderBy(s => s).ToArray();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static readonly double oneThird = 0.333333333333333333333;
|
||||
|
||||
private static (double Slope, double Intercept) GetSlopeIntercept(PdfPoint point1, PdfPoint point2)
|
||||
{
|
||||
if ((point1.X - point2.X) != 0) // vertical line special case
|
||||
{
|
||||
var slope = (double)((point2.Y - point1.Y) / (point2.X - point1.X));
|
||||
var intercept = (double)point2.Y - slope * (double)point2.X;
|
||||
return (slope, intercept);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (double.NaN, (double)point1.X);
|
||||
}
|
||||
}
|
||||
|
||||
private static double CubicRoot(double d)
|
||||
{
|
||||
if (d < 0.0) return -Math.Pow(-d, oneThird);
|
||||
return Math.Pow(d, oneThird);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the real roots of a Cubic (or Quadratic, a=0) equation.
|
||||
/// <para>ax^3 + bx^2 + cx + d = 0</para>
|
||||
/// </summary>
|
||||
/// <param name="a">ax^3</param>
|
||||
/// <param name="b">bx^2</param>
|
||||
/// <param name="c">cx</param>
|
||||
/// <param name="d">d</param>
|
||||
private static double[] SolveCubicEquation(double a, double b, double c, double d)
|
||||
{
|
||||
if (Math.Abs(a) <= double.Epsilon)
|
||||
{
|
||||
// handle Quadratic equation (a=0)
|
||||
double detQ = c * c - 4 * b * d;
|
||||
if (detQ >= 0)
|
||||
{
|
||||
double x = (-c + Math.Sqrt(detQ)) / (2.0 * b);
|
||||
double x0 = (-c - Math.Sqrt(detQ)) / (2.0 * b);
|
||||
return new double[] { x, x0 };
|
||||
}
|
||||
return new double[0]; // no real roots
|
||||
}
|
||||
|
||||
double aSquared = a * a;
|
||||
double aCubed = aSquared * a;
|
||||
double bCubed = b * b * b;
|
||||
double abc = a * b * c;
|
||||
double bOver3a = b / (3.0 * a);
|
||||
|
||||
double Q = (3.0 * a * c - b * b) / (9.0 * aSquared);
|
||||
double R = (9.0 * abc - 27.0 * aSquared * d - 2.0 * bCubed) / (54.0 * aCubed);
|
||||
|
||||
double det = Q * Q * Q + R * R; // same sign as determinant because: 4p^3 + 27q^2 = (4 * 27) * (Q^3 + R^2)
|
||||
double x1 = double.NaN;
|
||||
double x2 = double.NaN;
|
||||
double x3 = double.NaN;
|
||||
|
||||
if (det >= 0) // Cardano's Formula
|
||||
{
|
||||
double sqrtDet = Math.Sqrt(det);
|
||||
|
||||
double S = CubicRoot(R + sqrtDet);
|
||||
double T = CubicRoot(R - sqrtDet);
|
||||
double SPlusT = S + T;
|
||||
|
||||
x1 = SPlusT - bOver3a; // real root
|
||||
|
||||
// Complex roots
|
||||
double complexPart = Math.Sqrt(3) / 2.0 * (S - T); // complex part of complex root
|
||||
if (Math.Abs(complexPart) <= double.Epsilon) // if complex part == 0
|
||||
{
|
||||
// complex roots only have real part
|
||||
// the real part is the same for both roots
|
||||
x2 = -SPlusT / 2 - bOver3a;
|
||||
}
|
||||
}
|
||||
else // Casus irreducibilis
|
||||
{
|
||||
// François Viète's formula
|
||||
Func<double, double, double, double> vietTrigonometricSolution = (p_, q_, k) => 2.0 * Math.Sqrt(-p_ / 3.0)
|
||||
* Math.Cos(oneThird * Math.Acos((3.0 * q_) / (2.0 * p_) * Math.Sqrt(-3.0 / p_)) - (2.0 * Math.PI * k) / 3.0);
|
||||
|
||||
double p = Q * 3.0; // (3.0 * a * c - b * b) / (3.0 * aSquared);
|
||||
double q = -R * 2.0; // (2.0 * bCubed - 9.0 * abc + 27.0 * aSquared * d) / (27.0 * aCubed);
|
||||
x1 = vietTrigonometricSolution(p, q, 0) - bOver3a;
|
||||
x2 = vietTrigonometricSolution(p, q, 1) - bOver3a;
|
||||
x3 = vietTrigonometricSolution(p, q, 2) - bOver3a;
|
||||
}
|
||||
|
||||
return new double[] { x1, x2, x3 };
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Geometry
|
||||
using System;
|
||||
|
||||
namespace UglyToad.PdfPig.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// A line in a PDF file.
|
||||
@@ -53,6 +55,18 @@
|
||||
Point2 = point2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rectangle completely containing the <see cref="PdfLine"/>.
|
||||
/// </summary>
|
||||
public PdfRectangle GetBoundingRectangle()
|
||||
{
|
||||
return new PdfRectangle(
|
||||
Math.Min(this.Point1.X, this.Point2.X),
|
||||
Math.Min(this.Point1.Y, this.Point2.Y),
|
||||
Math.Max(this.Point1.X, this.Point2.X),
|
||||
Math.Max(this.Point1.Y, this.Point2.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this <see cref="PdfLine"/> is equal to a specified <see cref="PdfLine"/> .
|
||||
/// </summary>
|
||||
|
@@ -18,6 +18,11 @@ namespace UglyToad.PdfPig.Geometry
|
||||
/// </summary>
|
||||
public IReadOnlyList<IPathCommand> Commands => commands;
|
||||
|
||||
/// <summary>
|
||||
/// True if the <see cref="PdfPath"/> was originaly draw as a rectangle.
|
||||
/// </summary>
|
||||
public bool IsDrawnAsRectangle { get; internal set; }
|
||||
|
||||
private PdfPoint? currentPosition;
|
||||
|
||||
private double shoeLaceSum;
|
||||
@@ -118,8 +123,8 @@ namespace UglyToad.PdfPig.Geometry
|
||||
/// <summary>
|
||||
/// Simplify this <see cref="PdfPath"/> by converting everything to <see cref="PdfLine"/>s.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal PdfPath Simplify()
|
||||
/// <param name="n">Number of lines required (minimum is 1).</param>
|
||||
internal PdfPath Simplify(int n = 4)
|
||||
{
|
||||
PdfPath simplifiedPath = new PdfPath();
|
||||
var startPoint = GetStartPoint(Commands.First());
|
||||
@@ -133,7 +138,7 @@ namespace UglyToad.PdfPig.Geometry
|
||||
}
|
||||
else if (command is BezierCurve curve)
|
||||
{
|
||||
foreach (var lineB in curve.ToLines(4))
|
||||
foreach (var lineB in curve.ToLines(n))
|
||||
{
|
||||
simplifiedPath.LineTo(lineB.To.X, lineB.To.Y);
|
||||
}
|
||||
@@ -351,6 +356,26 @@ namespace UglyToad.PdfPig.Geometry
|
||||
{
|
||||
builder.Append("Z ");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Close);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -385,6 +410,22 @@ namespace UglyToad.PdfPig.Geometry
|
||||
{
|
||||
builder.Append("M ").Append(Location.X).Append(' ').Append(Location.Y).Append(' ');
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Move move)
|
||||
{
|
||||
return this.Location.Equals(move.Location);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (this.Location).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -422,6 +463,22 @@ namespace UglyToad.PdfPig.Geometry
|
||||
{
|
||||
builder.AppendFormat("L {0} {1} ", To.X, To.Y);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Line line)
|
||||
{
|
||||
return this.From.Equals(line.From) && this.To.Equals(line.To);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (this.From, this.To).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -592,7 +649,7 @@ namespace UglyToad.PdfPig.Geometry
|
||||
return true;
|
||||
}
|
||||
|
||||
private static double ValueWithT(double p1, double p2, double p3, double p4, double t)
|
||||
internal static double ValueWithT(double p1, double p2, double p3, double p4, double t)
|
||||
{
|
||||
// P = (1−t)^3*P_1 + 3(1−t)^2*t*P_2 + 3(1−t)*t^2*P_3 + t^3*P_4
|
||||
var oneMinusT = 1 - t;
|
||||
@@ -629,6 +686,25 @@ namespace UglyToad.PdfPig.Geometry
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is BezierCurve curve)
|
||||
{
|
||||
return this.StartPoint.Equals(curve.StartPoint) &&
|
||||
this.FirstControlPoint.Equals(curve.FirstControlPoint) &&
|
||||
this.SecondControlPoint.Equals(curve.SecondControlPoint) &&
|
||||
this.EndPoint.Equals(curve.EndPoint);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (this.StartPoint, this.FirstControlPoint, this.SecondControlPoint, this.EndPoint).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Rectangle(decimal x, decimal y, decimal width, decimal height)
|
||||
@@ -638,6 +714,38 @@ namespace UglyToad.PdfPig.Geometry
|
||||
LineTo(x + width, y + height);
|
||||
LineTo(x, y + height);
|
||||
LineTo(x, y);
|
||||
IsDrawnAsRectangle = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="PdfPath"/>s for equality. Paths will only be considered equal if the commands which construct the paths are in the same order.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is PdfPath path)
|
||||
{
|
||||
if (this.Commands.Count != path.Commands.Count) return false;
|
||||
|
||||
for (int i = 0; i < this.Commands.Count; i++)
|
||||
{
|
||||
if (!this.Commands[i].Equals(path.Commands[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the hash code. Paths will only have the same hash code if the commands which construct the paths are in the same order.
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = this.Commands.Count + 1;
|
||||
for (int i = 0; i < this.Commands.Count; i++)
|
||||
{
|
||||
hash = hash * (i + 1) * 17 + this.Commands[i].GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -77,6 +77,17 @@
|
||||
return new PdfPoint(X, Y + dy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="PdfPoint"/> which is the current point moved in the x and y directions relative to its current position by a value.
|
||||
/// </summary>
|
||||
/// <param name="dx">The distance to move the point in the x direction relative to its current location.</param>
|
||||
/// <param name="dy">The distance to move the point in the y direction relative to its current location.</param>
|
||||
/// <returns>A new point shifted on the y axis by the given delta value.</returns>
|
||||
public PdfPoint Translate(decimal dx, decimal dy)
|
||||
{
|
||||
return new PdfPoint(X + dx, Y + dy);
|
||||
}
|
||||
|
||||
internal PdfVector ToVector()
|
||||
{
|
||||
return new PdfVector(X, Y);
|
||||
|
@@ -126,23 +126,15 @@
|
||||
BottomRight = bottomRight;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Whether two rectangles overlap.
|
||||
/// Creates a new <see cref="PdfRectangle"/> which is the current rectangle moved in the x and y directions relative to its current position by a value.
|
||||
/// </summary>
|
||||
public bool IntersectsWith(PdfRectangle rectangle)
|
||||
/// <param name="dx">The distance to move the rectangle in the x direction relative to its current location.</param>
|
||||
/// <param name="dy">The distance to move the rectangle in the y direction relative to its current location.</param>
|
||||
/// <returns>A new rectangle shifted on the y axis by the given delta value.</returns>
|
||||
public PdfRectangle Translate(decimal dx, decimal dy)
|
||||
{
|
||||
if (Left > rectangle.Right || rectangle.Left > Right)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Top < rectangle.Bottom || rectangle.Top < Bottom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return new PdfRectangle(this.BottomLeft.Translate(dx, dy), this.TopRight.Translate(dx, dy));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Reference in New Issue
Block a user