namespace UglyToad.PdfPig.Functions
{
using System.Linq;
using UglyToad.PdfPig.Core;
using UglyToad.PdfPig.Tokens;
///
/// This class represents a function in a PDF document.
///
public abstract class PdfFunction
{
///
/// The function dictionary.
///
public DictionaryToken? FunctionDictionary { get; }
///
/// The function stream.
///
public StreamToken? FunctionStream { get; }
private int numberOfInputValues = -1;
private int numberOfOutputValues = -1;
///
/// This class represents a function in a PDF document.
///
public PdfFunction(DictionaryToken function, ArrayToken domain, ArrayToken? range)
{
FunctionDictionary = function;
DomainValues = domain;
RangeValues = range;
}
///
/// This class represents a function in a PDF document.
///
public PdfFunction(StreamToken function, ArrayToken domain, ArrayToken? range)
{
FunctionStream = function;
DomainValues = domain;
RangeValues = range;
}
///
/// Returns the function type. Possible values are:
///
/// - 0Sampled function
/// - 2Exponential interpolation function
/// - 3Stitching function
/// - 4PostScript calculator function
///
///
/// the function type.
public abstract FunctionTypes FunctionType { get; }
///
/// Returns the function's dictionary. If is defined, it will be returned.
/// If not, the 's StreamDictionary will be returned.
///
public DictionaryToken? GetDictionary()
{
if (FunctionStream != null)
{
return FunctionStream.StreamDictionary;
}
else
{
return FunctionDictionary;
}
}
///
/// This will get the number of output parameters that
/// have a range specified. A range for output parameters
/// is optional so this may return zero for a function
/// that does have output parameters, this will simply return the
/// number that have the range specified.
///
/// The number of output parameters that have a range specified.
public int NumberOfOutputParameters
{
get
{
if (numberOfOutputValues == -1)
{
if (RangeValues is null)
{
numberOfOutputValues = 0;
}
else
{
numberOfOutputValues = RangeValues.Length / 2;
}
}
return numberOfOutputValues;
}
}
///
/// This will get the range for a certain output parameters. This is will never
/// return null. If it is not present then the range 0 to 0 will
/// be returned.
///
/// The output parameter number to get the range for.
/// The range for this component.
public PdfRange GetRangeForOutput(int n)
{
return new PdfRange(RangeValues!.Data.OfType().Select(t => t.Double), n);
}
///
/// This will get the number of input parameters that
/// have a domain specified.
///
/// The number of input parameters that have a domain specified.
public int NumberOfInputParameters
{
get
{
if (numberOfInputValues == -1)
{
ArrayToken array = DomainValues;
numberOfInputValues = array.Length / 2;
}
return numberOfInputValues;
}
}
///
/// This will get the range for a certain input parameter. This is will never
/// return null. If it is not present then the range 0 to 0 will
/// be returned.
///
/// The parameter number to get the domain for.
/// The domain range for this component.
public PdfRange GetDomainForInput(int n)
{
ArrayToken domainValues = DomainValues;
return new PdfRange(domainValues.Data.OfType().Select(t => t.Double), n);
}
///
/// Evaluates the function at the given input.
/// ReturnValue = f(input)
///
/// The array of input values for the function.
/// In many cases will be an array of a single value, but not always.
/// The of outputs the function returns based on those inputs.
/// In many cases will be an array of a single value, but not always.
public abstract double[] Eval(params double[] input);
///
/// Returns all ranges for the output values as . Required for type 0 and type 4 functions.
///
/// the ranges array.
protected ArrayToken? RangeValues { get; }
///
/// Returns all domains for the input values as . Required for all function types.
///
/// the domains array.
private ArrayToken DomainValues { get; }
///
/// Clip the given input values to the ranges.
///
/// inputValues the input values
/// the clipped values
protected double[] ClipToRange(double[] inputValues)
{
ArrayToken rangesArray = RangeValues!;
double[] result;
if (rangesArray != null && rangesArray.Length > 0)
{
double[] rangeValues = rangesArray.Data.OfType().Select(t => t.Double).ToArray();
int numberOfRanges = rangeValues.Length / 2;
result = new double[numberOfRanges];
for (int i = 0; i < numberOfRanges; i++)
{
int index = i << 1;
result[i] = ClipToRange(inputValues[i], rangeValues[index], rangeValues[index + 1]);
}
}
else
{
result = inputValues;
}
return result;
}
///
/// Clip the given input value to the given range.
///
/// x the input value
/// the min value of the range
/// the max value of the range
/// the clipped value
public static double ClipToRange(double x, double rangeMin, double rangeMax)
{
if (x < rangeMin)
{
return rangeMin;
}
else if (x > rangeMax)
{
return rangeMax;
}
return x;
}
///
/// For a given value of x, interpolate calculates the y value
/// on the line defined by the two points (xRangeMin, xRangeMax)
/// and (yRangeMin, yRangeMax).
///
/// the value to be interpolated value.
/// the min value of the x range
/// the max value of the x range
/// the min value of the y range
/// the max value of the y range
/// the interpolated y value
protected static double Interpolate(double x, double xRangeMin, double xRangeMax, double yRangeMin, double yRangeMax)
{
return yRangeMin + ((x - xRangeMin) * (yRangeMax - yRangeMin) / (xRangeMax - xRangeMin));
}
}
///
/// Pdf function types.
///
public enum FunctionTypes : byte
{
///
/// Sampled function.
///
Sampled = 0,
///
/// Exponential interpolation function.
///
Exponential = 2,
///
/// Stitching function.
///
Stitching = 3,
///
/// PostScript calculator function.
///
PostScript = 4
}
}