Skip to content

Commit

Permalink
Properly implement Quadtratic and Cubic bezier-curves and use Quadtra…
Browse files Browse the repository at this point in the history
…tic in CalculatePath to fix #625
  • Loading branch information
BobLd committed Apr 5, 2024
1 parent f62929e commit 2d6cb1a
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 194 deletions.
334 changes: 254 additions & 80 deletions src/UglyToad.PdfPig.Core/PdfSubpath.cs

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions src/UglyToad.PdfPig.Core/TransformationMatrix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,19 @@ public PdfSubpath Transform(PdfSubpath subpath)
var to = Transform(line.To);
trSubpath.LineTo(to.X, to.Y);
}
else if (c is BezierCurve curve)
else if (c is CubicBezierCurve cubic)
{
var first = Transform(curve.FirstControlPoint);
var second = Transform(curve.SecondControlPoint);
var end = Transform(curve.EndPoint);
var first = Transform(cubic.FirstControlPoint);
var second = Transform(cubic.SecondControlPoint);
var end = Transform(cubic.EndPoint);
trSubpath.BezierCurveTo(first.X, first.Y, second.X, second.Y, end.X, end.Y);
}
else if (c is QuadraticBezierCurve quadratic)
{
var control = Transform(quadratic.ControlPoint);
var end = Transform(quadratic.EndPoint);
trSubpath.BezierCurveTo(control.X, control.Y, end.X, end.Y);
}
else if (c is Close)
{
trSubpath.CloseSubpath();
Expand Down
6 changes: 2 additions & 4 deletions src/UglyToad.PdfPig.Fonts/TrueType/Glyphs/Glyph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,14 @@ private static IReadOnlyList<PdfSubpath> CalculatePath(GlyphPoint[] points)
}
else if (contour[j + 1].IsOnCurve)
{
var pPrevious = contour[j - 1];
var pNext = contour[j + 1];
subpath.BezierCurveTo(pPrevious.X, pPrevious.Y, pNow.X, pNow.Y, pNext.X, pNext.Y);
subpath.BezierCurveTo(pNow.X, pNow.Y, pNext.X, pNext.Y);
++j;
}
else
{
var pPrevious = contour[j - 1];
var pmid = midValue(pNow, contour[j + 1]);
subpath.BezierCurveTo(pPrevious.X, pPrevious.Y, pNow.X, pNow.Y, pmid.X, pmid.Y);
subpath.BezierCurveTo(pNow.X, pNow.Y, pmid.X, pmid.Y);
}
}
subpath.CloseSubpath();
Expand Down
12 changes: 6 additions & 6 deletions src/UglyToad.PdfPig.Tests/Fonts/CharacterPathTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ public class CharacterPathTests
[Fact]
public void BezierCurveGeneratesCorrectBoundingBox()
{
var curve = new PdfSubpath.BezierCurve(new PdfPoint(60, 105),
new PdfPoint(75, 30),
new PdfPoint(215, 115),
var curve = new PdfSubpath.CubicBezierCurve(new PdfPoint(60, 105),
new PdfPoint(75, 30),
new PdfPoint(215, 115),
new PdfPoint(140, 160));

var result = curve.GetBoundingRectangle();
Assert.NotNull(result);
Assert.Equal(160, result.Value.Top);
Expand All @@ -26,7 +26,7 @@ public void BezierCurveGeneratesCorrectBoundingBox()
[Fact]
public void LoopBezierCurveGeneratesCorrectBoundingBox()
{
var curve = new PdfSubpath.BezierCurve(new PdfPoint(166, 142),
var curve = new PdfSubpath.CubicBezierCurve(new PdfPoint(166, 142),
new PdfPoint(75, 30),
new PdfPoint(215, 115),
new PdfPoint(140, 160));
Expand All @@ -45,7 +45,7 @@ public void LoopBezierCurveGeneratesCorrectBoundingBox()
[Fact]
public void BezierCurveAddsCorrectSvgCommand()
{
var curve = new PdfSubpath.BezierCurve(new PdfPoint(60, 105),
var curve = new PdfSubpath.CubicBezierCurve(new PdfPoint(60, 105),
new PdfPoint(75, 30),
new PdfPoint(215, 115),
new PdfPoint(140, 160));
Expand Down
134 changes: 67 additions & 67 deletions src/UglyToad.PdfPig.Tests/Geometry/BezierCurveTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class GenerateLetterGlyphImages

private const string OutputPath = "ImagesGlyphs";

private const float Scale = 2.5f;
private const float Scale = 10f;

private static readonly SKMatrix ScaleMatrix = SKMatrix.CreateScale(Scale, Scale);

Expand Down Expand Up @@ -125,6 +125,12 @@ public void veraPDF_Issue1010_3()
Run("FontMatrix-raw");
}

[Fact]
public void JudgementDocument()
{
Run("Judgement Document");
}

[Fact]
public void veraPDF_Issue1010_4()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ public static SKPath ToSKPath(this IReadOnlyList<PdfSubpath> path)
{
skPath.LineTo((float)line.To.X, (float)line.To.Y);
}
else if (c is PdfSubpath.BezierCurve curve)
else if (c is PdfSubpath.CubicBezierCurve curve)
{
if (curve.StartPoint.Equals(curve.FirstControlPoint)) // TODO - This needs to be fixed in PdfPig
{
// Quad curve
skPath.QuadTo((float)curve.SecondControlPoint.X, (float)curve.SecondControlPoint.Y,
(float)curve.EndPoint.X, (float)curve.EndPoint.Y);
}
else
{
// Cubic curve
skPath.CubicTo((float)curve.FirstControlPoint.X, (float)curve.FirstControlPoint.Y,
(float)curve.SecondControlPoint.X, (float)curve.SecondControlPoint.Y,
(float)curve.EndPoint.X, (float)curve.EndPoint.Y);
}
skPath.CubicTo((float)curve.FirstControlPoint.X,
(float)curve.FirstControlPoint.Y,
(float)curve.SecondControlPoint.X,
(float)curve.SecondControlPoint.Y,
(float)curve.EndPoint.X,
(float)curve.EndPoint.Y);
}
else if (c is PdfSubpath.QuadraticBezierCurve quadratic)
{
skPath.QuadTo((float)quadratic.ControlPoint.X,
(float)quadratic.ControlPoint.Y,
(float)quadratic.EndPoint.X,
(float)quadratic.EndPoint.Y);
}
else if (c is PdfSubpath.Close)
{
Expand Down
36 changes: 18 additions & 18 deletions src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -804,11 +804,11 @@ private static bool ParallelTo(PdfPoint p11, PdfPoint p12, PdfPoint p21, PdfPoin

#region Path Bezier Curve
/// <summary>
/// Split a bezier curve into 2 bezier curves, at tau.
/// Split a cubic bezier-curve into 2 bezier-curves, at tau.
/// </summary>
/// <param name="bezierCurve">The original bezier curve.</param>
/// <param name="bezierCurve">The original cubic bezier-curve.</param>
/// <param name="tau">The t value were to split the curve, usually between 0 and 1, but not necessary.</param>
public static (BezierCurve, BezierCurve) Split(this BezierCurve bezierCurve, double tau)
public static (CubicBezierCurve, CubicBezierCurve) Split(this CubicBezierCurve bezierCurve, double tau)
{
// De Casteljau Algorithm
PdfPoint[][] points = new PdfPoint[4][];
Expand All @@ -835,50 +835,50 @@ public static (BezierCurve, BezierCurve) Split(this BezierCurve bezierCurve, dou
}
}

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]));
return (new CubicBezierCurve(points[0][0], points[1][0], points[2][0], points[3][0]),
new CubicBezierCurve(points[3][0], points[2][1], points[1][2], points[0][3]));
}

/// <summary>
/// Checks if the curve and the line are intersecting.
/// <para>Avoid using this method as it is not optimised. Use <see cref="Intersect(BezierCurve, PdfLine)"/> instead.</para>
/// <para>Avoid using this method as it is not optimised. Use <see cref="Intersect(CubicBezierCurve, PdfLine)"/> instead.</para>
/// </summary>
public static bool IntersectsWith(this BezierCurve bezierCurve, PdfLine line)
public static bool IntersectsWith(this CubicBezierCurve bezierCurve, PdfLine line)
{
return IntersectsWith(bezierCurve, line.Point1, line.Point2);
}

/// <summary>
/// Checks if the curve and the line are intersecting.
/// <para>Avoid using this method as it is not optimised. Use <see cref="Intersect(BezierCurve, Line)"/> instead.</para>
/// <para>Avoid using this method as it is not optimised. Use <see cref="Intersect(CubicBezierCurve, Line)"/> instead.</para>
/// </summary>
public static bool IntersectsWith(this BezierCurve bezierCurve, Line line)
public static bool IntersectsWith(this CubicBezierCurve bezierCurve, Line line)
{
return IntersectsWith(bezierCurve, line.From, line.To);
}

private static bool IntersectsWith(BezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
private static bool IntersectsWith(CubicBezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
{
return Intersect(bezierCurve, p1, p2).Length > 0;
}

/// <summary>
/// Get the <see cref="PdfPoint"/>s that are the intersections of the line and the curve.
/// </summary>
public static PdfPoint[] Intersect(this BezierCurve bezierCurve, PdfLine line)
public static PdfPoint[] Intersect(this CubicBezierCurve bezierCurve, PdfLine line)
{
return Intersect(bezierCurve, line.Point1, line.Point2);
}

/// <summary>
/// Get the <see cref="PdfPoint"/>s that are the intersections of the line and the curve.
/// </summary>
public static PdfPoint[] Intersect(this BezierCurve bezierCurve, Line line)
public static PdfPoint[] Intersect(this CubicBezierCurve bezierCurve, Line line)
{
return Intersect(bezierCurve, line.From, line.To);
}

private static PdfPoint[] Intersect(BezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
private static PdfPoint[] Intersect(CubicBezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
{
var ts = IntersectT(bezierCurve, p1, p2);
if (ts is null || ts.Length == 0) return [];
Expand All @@ -905,22 +905,22 @@ private static PdfPoint[] Intersect(BezierCurve bezierCurve, PdfPoint p1, PdfPoi
/// <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[]? IntersectT(this BezierCurve bezierCurve, PdfLine line)
/// <returns>List of t values where the <see cref="CubicBezierCurve"/> and the <see cref="PdfLine"/> intersect.</returns>
public static double[]? IntersectT(this CubicBezierCurve bezierCurve, PdfLine line)
{
return IntersectT(bezierCurve, line.Point1, line.Point2);
}

/// <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[]? IntersectT(this BezierCurve bezierCurve, Line line)
/// <returns>List of t values where the <see cref="CubicBezierCurve"/> and the <see cref="Line"/> intersect.</returns>
public static double[]? IntersectT(this CubicBezierCurve bezierCurve, Line line)
{
return IntersectT(bezierCurve, line.From, line.To);
}

private static double[]? IntersectT(BezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
private static double[]? IntersectT(CubicBezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
{
// if the bounding boxes do not intersect, they cannot intersect
var bezierBbox = bezierCurve.GetBoundingRectangle();
Expand Down

0 comments on commit 2d6cb1a

Please sign in to comment.