mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-14 19:05:01 +08:00
Improve bounding box for word
This commit is contained in:
@@ -360,6 +360,30 @@
|
||||
E * scalar, F * scalar, row3 * scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TransformationMatrix Inverse()
|
||||
{
|
||||
var a = (D * row3 - row2 * F);
|
||||
var c = -(C * row3 - row2 * E);
|
||||
var e = (C * F - D * E);
|
||||
|
||||
var b = -(B * row3 - row1 * F);
|
||||
var d = (A * row3 - row1 * E);
|
||||
var f = -(A * F - B * E);
|
||||
|
||||
var r1 = (B * row2 - row1 * D);
|
||||
var r2 = -(A * row2 - row1 * C);
|
||||
var r3 = (A * D - B * C);
|
||||
var det = A * a + B * c + row1 * e;
|
||||
|
||||
return new TransformationMatrix(a, b, r1,
|
||||
c, d, r2,
|
||||
e, f, r3).Multiply(1.0 / det);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the X scaling component of the current matrix.
|
||||
/// </summary>
|
||||
|
@@ -272,55 +272,87 @@
|
||||
builder.Append(letters[i].Value);
|
||||
}
|
||||
|
||||
var points = letters.SelectMany(r => new[]
|
||||
var baseLinePoints = letters.SelectMany(r => new[]
|
||||
{
|
||||
r.StartBaseLine,
|
||||
r.EndBaseLine,
|
||||
}).ToList();
|
||||
|
||||
// Fitting a line through the base lines points
|
||||
// to find the orientation (slope)
|
||||
double x0 = baseLinePoints.Average(p => p.X);
|
||||
double y0 = baseLinePoints.Average(p => p.Y);
|
||||
double sumProduct = 0;
|
||||
double sumDiffSquaredX = 0;
|
||||
|
||||
for (int i = 0; i < baseLinePoints.Count; i++)
|
||||
{
|
||||
var x_diff = baseLinePoints[i].X - x0;
|
||||
var y_diff = baseLinePoints[i].Y - y0;
|
||||
sumProduct += x_diff * y_diff;
|
||||
sumDiffSquaredX += x_diff * x_diff;
|
||||
}
|
||||
|
||||
var slope = sumProduct / sumDiffSquaredX;
|
||||
|
||||
// Rotate the points to build the axis-aligned bounding box (AABB)
|
||||
var angleRad = Math.Atan(slope);
|
||||
var cos = Math.Cos(angleRad);
|
||||
var sin = Math.Sin(angleRad);
|
||||
|
||||
var transformation = new TransformationMatrix(
|
||||
cos, -sin, 0,
|
||||
sin, cos, 0,
|
||||
(1 - cos) * x0 - sin * y0, sin * x0 - (1 - cos) * y0, 1);
|
||||
|
||||
var transformedPoints = letters.SelectMany(r => new[]
|
||||
{
|
||||
r.StartBaseLine,
|
||||
r.EndBaseLine,
|
||||
r.GlyphRectangle.TopLeft,
|
||||
r.GlyphRectangle.TopRight
|
||||
}).Distinct();
|
||||
var convexHull = GeometryExtensions.GrahamScan(points).ToList();
|
||||
}).Distinct().Select(p => transformation.Transform(p));
|
||||
var aabb = new PdfRectangle(transformedPoints.Min(p => p.X),
|
||||
transformedPoints.Min(p => p.Y),
|
||||
transformedPoints.Max(p => p.X),
|
||||
transformedPoints.Max(p => p.Y));
|
||||
|
||||
// Rotate back the AABB to obtain to oriented bounding box (OBB)
|
||||
// Candidates bounding boxes
|
||||
var mbr = GeometryExtensions.ParametricPerpendicularProjection(convexHull);
|
||||
var mbr1 = new PdfRectangle(mbr.BottomLeft, mbr.TopLeft, mbr.BottomRight, mbr.TopRight);
|
||||
var mbr2 = new PdfRectangle(mbr.TopRight, mbr.BottomRight, mbr.TopLeft, mbr.BottomLeft);
|
||||
var mbr3 = new PdfRectangle(mbr.BottomRight, mbr.BottomLeft, mbr.TopRight, mbr.TopLeft);
|
||||
var obb = transformation.Inverse().Transform(aabb);
|
||||
var obb1 = new PdfRectangle(obb.BottomLeft, obb.TopLeft, obb.BottomRight, obb.TopRight);
|
||||
var obb2 = new PdfRectangle(obb.TopRight, obb.BottomRight, obb.TopLeft, obb.BottomLeft);
|
||||
var obb3 = new PdfRectangle(obb.BottomRight, obb.BottomLeft, obb.TopRight, obb.TopLeft);
|
||||
|
||||
// Find the orientation of the minimum bounding box, using the baseline angle.
|
||||
// Find the orientation of the OBB, using the baseline angle
|
||||
var firstLetter = letters[0];
|
||||
var lastLetter = letters[letters.Count - 1];
|
||||
var baseLineAngle = Math.Atan2(
|
||||
lastLetter.EndBaseLine.Y - firstLetter.StartBaseLine.Y,
|
||||
lastLetter.EndBaseLine.X - firstLetter.StartBaseLine.X);
|
||||
lastLetter.EndBaseLine.X - firstLetter.StartBaseLine.X) * 180 / Math.PI;
|
||||
|
||||
var bbox = mbr;
|
||||
var deltaAngle = Math.Abs(baseLineAngle - Math.Atan2(mbr.BottomRight.Y - mbr.BottomLeft.Y,
|
||||
mbr.BottomRight.X - mbr.BottomLeft.X));
|
||||
var bbox = obb;
|
||||
var deltaAngle = Math.Abs(baseLineAngle - angleRad);
|
||||
|
||||
double deltaAngle1 = Math.Abs(baseLineAngle - Math.Atan2(mbr1.BottomRight.Y - mbr1.BottomLeft.Y,
|
||||
mbr1.BottomRight.X - mbr1.BottomLeft.X));
|
||||
double deltaAngle1 = Math.Abs(baseLineAngle - obb1.Rotation);
|
||||
if (deltaAngle1 < deltaAngle)
|
||||
{
|
||||
deltaAngle = deltaAngle1;
|
||||
bbox = mbr1;
|
||||
bbox = obb1;
|
||||
}
|
||||
|
||||
double deltaAngle2 = Math.Abs(baseLineAngle - Math.Atan2(mbr2.BottomRight.Y - mbr2.BottomLeft.Y,
|
||||
mbr2.BottomRight.X - mbr2.BottomLeft.X));
|
||||
double deltaAngle2 = Math.Abs(baseLineAngle - obb2.Rotation);
|
||||
if (deltaAngle2 < deltaAngle)
|
||||
{
|
||||
deltaAngle = deltaAngle2;
|
||||
bbox = mbr2;
|
||||
bbox = obb2;
|
||||
}
|
||||
|
||||
double deltaAngle3 = Math.Abs(baseLineAngle - Math.Atan2(mbr3.BottomRight.Y - mbr3.BottomLeft.Y,
|
||||
mbr3.BottomRight.X - mbr3.BottomLeft.X));
|
||||
double deltaAngle3 = Math.Abs(baseLineAngle - obb3.Rotation);
|
||||
if (deltaAngle3 < deltaAngle)
|
||||
{
|
||||
bbox = mbr3;
|
||||
bbox = obb3;
|
||||
}
|
||||
|
||||
return new Tuple<string, PdfRectangle>(builder.ToString(), bbox);
|
||||
}
|
||||
#endregion
|
||||
|
@@ -130,6 +130,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (l == -1)
|
||||
{
|
||||
// All points are colinear - rectangle has no area (need more tests)
|
||||
var bottomLeft = polygon.OrderBy(p => p.X).ThenBy(p => p.Y).First();
|
||||
var topRight = polygon.OrderByDescending(p => p.Y).OrderByDescending(p => p.X).First();
|
||||
return new PdfRectangle(bottomLeft, topRight, bottomLeft, topRight);
|
||||
}
|
||||
|
||||
PdfPoint PlMinusQ = polygon[l].Subtract(Q);
|
||||
PdfPoint R2 = R1.Add(PlMinusQ);
|
||||
PdfPoint R3 = R0.Add(PlMinusQ);
|
||||
@@ -153,6 +161,54 @@
|
||||
return new PdfRectangle(MBR[2], MBR[3], MBR[1], MBR[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Algorithm to find the oriented bounding box (OBB) by first fitting a line through the points to get the slope,
|
||||
/// then rotating the points to obtain the axis-aligned bounding box (AABB), and then rotating back the AABB.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
/// <returns></returns>
|
||||
public static PdfRectangle OrientedBoundingBox(IReadOnlyList<PdfPoint> points)
|
||||
{
|
||||
if (points == null || points.Count < 2)
|
||||
{
|
||||
throw new ArgumentException("OrientedBoundingBox(): points cannot be null and must contain at least two points.");
|
||||
}
|
||||
|
||||
double x0 = points.Average(p => p.X);
|
||||
double y0 = points.Average(p => p.Y);
|
||||
double sum_prod = 0;
|
||||
double sum_x_diff_squared = 0;
|
||||
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
var x_diff = points[i].X - x0;
|
||||
var y_diff = points[i].Y - y0;
|
||||
|
||||
sum_prod += x_diff * y_diff;
|
||||
sum_x_diff_squared += x_diff * x_diff;
|
||||
}
|
||||
|
||||
var slope = sum_prod / sum_x_diff_squared;
|
||||
var rad_angle = Math.Atan(slope);
|
||||
|
||||
var cos = Math.Cos(rad_angle);
|
||||
var sin = Math.Sin(rad_angle);
|
||||
|
||||
var transformation = new TransformationMatrix(
|
||||
cos, -sin, 0,
|
||||
sin, cos, 0,
|
||||
(1.0 - cos) * x0 - sin * y0, sin * x0 - (1.0 - cos) * y0, 1);
|
||||
|
||||
var transformedPoints = points.Select(p => transformation.Transform(p)).ToArray();
|
||||
var aabb = new PdfRectangle(transformedPoints.Min(p => p.X),
|
||||
transformedPoints.Min(p => p.Y),
|
||||
transformedPoints.Max(p => p.X),
|
||||
transformedPoints.Max(p => p.Y));
|
||||
|
||||
var obb = transformation.Inverse().Transform(aabb);
|
||||
return obb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Algorithm to find the convex hull of the set of points with time complexity O(n log n).
|
||||
/// </summary>
|
||||
|
Reference in New Issue
Block a user