using System;
using System.Collections.Generic;
using System.Linq;
using UglyToad.PdfPig.Geometry;
namespace UglyToad.PdfPig.DocumentLayoutAnalysis
{
///
/// Contains helpful tools for distance measures.
///
public static class Distances
{
///
/// The Euclidean distance is the "ordinary" straight-line distance between two points.
///
/// The first point.
/// The second point.
public static double Euclidean(PdfPoint point1, PdfPoint point2)
{
double dx = point1.X - point2.X;
double dy = point1.Y - point2.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
///
/// The weighted Euclidean distance.
///
/// The first point.
/// The second point.
/// The weight of the X coordinates. Default is 1.
/// The weight of the Y coordinates. Default is 1.
public static double WeightedEuclidean(PdfPoint point1, PdfPoint point2, double wX = 1.0, double wY = 1.0)
{
double dx = point1.X - point2.X;
double dy = point1.Y - point2.Y;
return Math.Sqrt(wX * dx * dx + wY * dy * dy);
}
///
/// The Manhattan distance between two points is the sum of the absolute differences of their Cartesian coordinates.
/// Also known as rectilinear distance, L1 distance, L1 norm, snake distance, city block distance, taxicab metric.
///
/// The first point.
/// The second point.
public static double Manhattan(PdfPoint point1, PdfPoint point2)
{
return Math.Abs(point1.X - point2.X) + Math.Abs(point1.Y - point2.Y);
}
///
/// The angle in degrees between the horizontal axis and the line between two points.
///
/// The first point.
/// The second point.
///
public static double Angle(PdfPoint point1, PdfPoint point2)
{
return Math.Atan2(point2.Y - point1.Y, point2.X - point1.X) * 180.0 / Math.PI;
}
///
/// The absolute distance between the Y coordinates of two points.
///
/// The first point.
/// The second point.
///
public static double Vertical(PdfPoint point1, PdfPoint point2)
{
return Math.Abs(point2.Y - point1.Y);
}
///
/// The absolute distance between the X coordinates of two points.
///
/// The first point.
/// The second point.
///
public static double Horizontal(PdfPoint point1, PdfPoint point2)
{
return Math.Abs(point2.X - point1.X);
}
///
/// Find the index of the nearest point, excluding itself.
///
///
/// The reference point, for which to find the nearest neighbour.
/// The list of neighbours candidates.
///
///
/// The distance measure to use.
/// The distance between reference point, and its nearest neighbour.
///
internal static int FindIndexNearest(this T element, IReadOnlyList candidates,
Func candidatesPoint, Func pivotPoint,
Func distanceMeasure, out double distance)
{
if (candidates == null || candidates.Count == 0)
{
throw new ArgumentException("Distances.FindIndexNearest(): The list of neighbours candidates is either null or empty.", "points");
}
if (distanceMeasure == null)
{
throw new ArgumentException("Distances.FindIndexNearest(): The distance measure must not be null.", "distanceMeasure");
}
distance = double.MaxValue;
int closestPointIndex = -1;
var candidatesPoints = candidates.Select(candidatesPoint).ToList();
var pivot = pivotPoint(element);
for (var i = 0; i < candidates.Count; i++)
{
double currentDistance = distanceMeasure(candidatesPoints[i], pivot);
if (currentDistance < distance && !candidates[i].Equals(element))
{
distance = currentDistance;
closestPointIndex = i;
}
}
return closestPointIndex;
}
///
/// Find the index of the nearest line, excluding itself.
///
///
/// The reference line, for which to find the nearest neighbour.
/// The list of neighbours candidates.
///
///
/// The distance measure between two lines to use.
/// The distance between reference line, and its nearest neighbour.
internal static int FindIndexNearest(this T element, IReadOnlyList candidates,
Func candidatesLine, Func pivotLine,
Func distanceMeasure, out double distance)
{
if (candidates == null || candidates.Count == 0)
{
throw new ArgumentException("Distances.FindIndexNearest(): The list of neighbours candidates is either null or empty.", "lines");
}
if (distanceMeasure == null)
{
throw new ArgumentException("Distances.FindIndexNearest(): The distance measure must not be null.", "distanceMeasure");
}
distance = double.MaxValue;
int closestLineIndex = -1;
var candidatesLines = candidates.Select(candidatesLine).ToList();
var pivot = pivotLine(element);
for (var i = 0; i < candidates.Count; i++)
{
double currentDistance = distanceMeasure(candidatesLines[i], pivot);
if (currentDistance < distance && !candidates[i].Equals(element))
{
distance = currentDistance;
closestLineIndex = i;
}
}
return closestLineIndex;
}
}
}