I am trying to implement Bicubic image interpolation using cubic splines and the generalized formula for interpolation.
I was able to implement this by using all 16 surrounding pixels and calculate the interpolation coefficients from these 16 function values. But as interpolation is separable by definition, I tried to implement a version which uses two 1-D interpolations in order to achieve bicubic interpolation.
For the generalized interpolation formula I use e.g. http://bigwww.epfl.ch/publications/thevenaz9901.pdf chapter 3 and for the 1-D interpolation I use the idea behind this: http://www.vision-systems.com/articles/print/volume-12/issue-10/departments/wilsons-websites/understanding-image-interpolation-techniques.html
My coefficients are calculated by invertind the 4x4 matrix like in https://en.wikipedia.org/wiki/Spline_interpolation#Example
The interpolation consists of three functions.
The interpolation function itself (phi):
private float interpolate(float p_fValue)
{
p_fValue = Math.Abs(p_fValue);
if (p_fValue >= 1 && p_fValue < 2)
{
return 1.0f / 6.0f * (2.0f - p_fValue) * (2.0f - p_fValue) * (2.0f - p_fValue);//-(p_fValue * p_fValue * p_fValue) / 6.0f + p_fValue * p_fValue - 2.0f * p_fValue + 4.0f / 3.0f;
}
else if (p_fValue < 1)
{
return 2.0f / 3.0f - p_fValue * p_fValue + 0.5f * (p_fValue * p_fValue * p_fValue);
}
else
{
return 0.0f;
}
}
Calculating 4 coefficients from 4 function values:
private void calculateCoefficients(float p_f1, float p_f2, float p_f3, float p_f4, out float p_c1, out float p_c2, out float p_c3, out float p_c4)
{
p_c1 = 6.0f / 209.0f * (56.0f * p_f1 - 15.0f * p_f2 + 4.0f * p_f3 - p_f4);
p_c2 = 6.0f / 209.0f * (-15.0f * p_f1 + 60.0f * p_f2 - 16.0f * p_f3 + 4.0f * p_f4);
p_c3 = 6.0f / 209.0f * (4.0f * p_f1 - 16.0f * p_f2 + 60.0f * p_f3 - 15.0f * p_f4);
p_c4 = 6.0f / 209.0f * (-p_f1 + 4.0f * p_f2 - 15.0f * p_f3 + 56.0f * p_f4);
}
And the interpolation of a whole image from a smaller source image:
// p_siImage = original (smaller) image
// p_iImageWidth, p_iImageHeight = Image size (pixel count) of the new image
// Interpolation with standard formula u(x) = sum_{k to N} c_k * phi(x-k); For N function values
public SampledImage InterpolateImage(SampledImage p_siImage, int p_iImageWidth, int p_iImageHeight)
{
// Create interpolated image
SampledImage resultImage = new SampledImage((int)p_siImage.GetWidth(), (int)p_siImage.GetHeight(), p_iImageWidth, p_iImageHeight, p_siImage.GetPixelWidth(), p_siImage.GetPixelHeight());
for (int iX = 0; iX < p_iImageWidth; iX++)
{
for (int iY = 0; iY < p_iImageHeight; iY++)
{
// Calculate pixel position "in space"
float[] pixelSpace = resultImage.GetPixelInSpace(iX, iY);
// Calculate the index in smaller image, as real number (pixel index between pixels)
float[] pixelRealIndex = p_siImage.GetPixelRealFromSpace(pixelSpace[0], pixelSpace[1]);
// Calculate X values of surrounding pixels
int x_2 = (int)Math.Floor(pixelRealIndex[0]) - 1;
int x_1 = (int)Math.Floor(pixelRealIndex[0]);
int x1 = (int)Math.Floor(pixelRealIndex[0]) + 1;
int x2 = (int)Math.Floor(pixelRealIndex[0]) + 2;
// Calculate Y value of nearest pixel
int y = (int)Math.Floor(pixelRealIndex[1]);
// Arrays for "function values" (= color values)
float[] red = new float[4];
float[] green = new float[4];
float[] blue = new float[4];
// Create polynom for each column
for (int iiX = -1; iiX < 3; iiX++)
{
// Get column X-index
int x = (int)Math.Floor(pixelRealIndex[0]) + iiX;
// Used Y-Indices for polynom
int y_2 = (int)Math.Floor(pixelRealIndex[1]) - 1;
int y_1 = (int)Math.Floor(pixelRealIndex[1]);
int y1 = (int)Math.Floor(pixelRealIndex[1]) + 1;
int y2 = (int)Math.Floor(pixelRealIndex[1]) + 2;
// Get "function values" for each pixel in the column
Color fxy_2 = p_siImage.GetValueMirrored(x, y_2);
Color fxy_1 = p_siImage.GetValueMirrored(x, y_1);
Color fxy1 = p_siImage.GetValueMirrored(x, y1);
Color fxy2 = p_siImage.GetValueMirrored(x, y2);
// Coefficients c_k
float redC_2, redC_1, redC1, redC2;
float greenC_2, greenC_1, greenC1, greenC2;
float blueC_2, blueC_1, blueC1, blueC2;
// Calculate the coefficients for the column polynom
calculateCoefficients(fxy_2.R, fxy_1.R, fxy1.R, fxy2.R, out redC_2, out redC_1, out redC1, out redC2);
calculateCoefficients(fxy_2.G, fxy_1.G, fxy1.G, fxy2.G, out greenC_2, out greenC_1, out greenC1, out greenC2);
calculateCoefficients(fxy_2.B, fxy_1.B, fxy1.B, fxy2.B, out blueC_2, out blueC_1, out blueC1, out blueC2);
// Interpolate in each column at the same Y-Index as the actual interpolation position
red[iiX+1] = redC_2 * interpolate(pixelRealIndex[1] - (float)y_2) + redC_1 * interpolate(pixelRealIndex[1] - (float)y_1) +
redC1 * interpolate(pixelRealIndex[1] - (float)y1) + redC2 * interpolate(pixelRealIndex[1] - (float)y2);
green[iiX+1] = greenC_2 * interpolate(pixelRealIndex[1] - (float)y_2) + greenC_1 * interpolate(pixelRealIndex[1] - (float)y_1) +
greenC1 * interpolate(pixelRealIndex[1] - (float)y1) + greenC2 * interpolate(pixelRealIndex[1] - (float)y2);
blue[iiX+1] = blueC_2 * interpolate(pixelRealIndex[1] - (float)y_2) + blueC_1 * interpolate(pixelRealIndex[1] - (float)y_1) +
blueC1 * interpolate(pixelRealIndex[1] - (float)y1) + blueC2 * interpolate(pixelRealIndex[1] - (float)y2);
}
//Now: interpolate the actual value
// Get "function values" for each pixel in the row
Color fx_2y = p_siImage.GetValueMirrored(x_2, y);
Color fx_1y = p_siImage.GetValueMirrored(x_1, y);
Color fx1y = p_siImage.GetValueMirrored(x1, y);
Color fx2y = p_siImage.GetValueMirrored(x2, y);
// Coefficients c_k
float redCX_2, redCX_1, redCX1, redCX2;
float greenCX_2, greenCX_1, greenCX1, greenCX2;
float blueCX_2, blueCX_1, blueCX1, blueCX2;
// Calculate coefficients by using the interpolated values of each column
calculateCoefficients(red[0], red[1], red[2], red[3], out redCX_2, out redCX_1, out redCX1, out redCX2);
calculateCoefficients(green[0], green[1], green[2], green[3], out greenCX_2, out greenCX_1, out greenCX1, out greenCX2);
calculateCoefficients(blue[0], blue[1], blue[2], blue[3], out blueCX_2, out blueCX_1, out blueCX1, out blueCX2);
// Interpolate finally for the actual value
float redResult = redCX_2 * interpolate(pixelRealIndex[0] - (float)x_2) + redCX_1 * interpolate(pixelRealIndex[0] - (float)x_1) +
redCX1 * interpolate(pixelRealIndex[0] - (float)x1) + redCX2 * interpolate(pixelRealIndex[0] - (float)x2);
float greenResult = greenCX_2 * interpolate(pixelRealIndex[0] - (float)x_2) + greenCX_1 * interpolate(pixelRealIndex[0] - (float)x_1) +
greenCX1 * interpolate(pixelRealIndex[0] - (float)x1) + greenCX2 * interpolate(pixelRealIndex[0] - (float)x2);
float blueResult = blueCX_2 * interpolate(pixelRealIndex[0] - (float)x_2) + blueCX_1 * interpolate(pixelRealIndex[0] - (float)x_1) +
blueCX1 * interpolate(pixelRealIndex[0] - (float)x1) + blueCX2 * interpolate(pixelRealIndex[0] - (float)x2);
// Make sure to care for under/overshoots
redResult = Math.Max(0, Math.Min(redResult, 255));
greenResult = Math.Max(0, Math.Min(greenResult, 255));
blueResult = Math.Max(0, Math.Min(blueResult, 255));
// Set the pixel to the calculated value
Color resultColor = Color.FromArgb((int)redResult, (int)greenResult, (int)blueResult);
resultImage.SetValue(iX, iY, resultColor);
}
}
return resultImage;
}
For an image like this (15x15px):
The result looks like this (scaled to 80x80px):
In contrast, this is how the result looks if I calculate all 16 coefficients at once(80x80px):
My question is now:
How is the separation done correctly? Or am I missing something completely and I only can separate the interpolation function (phi) but not the calculation of the coefficients?
Related
I'm creating a script that blends 4 textures together based on a mask the code I have so far is this:
public Color GetBlend(Color32 mask, int x, int y,Color baseColor, Color redChannel, Color greenChannel, Color blueChannel) {
float val = 1f / (mask.r + mask.g + mask.b);
float rWeight = mask.r * val;
float gWeight = mask.g * val;
float bWeight = mask.b * val;
// I can't find what follows this though.
}
It is here that I am stuck though. I can't find any information on google about how this blending is supposed to work.
// EDIT //
After fiddling around with it for a bit away from my computer I came up with this idea:
public Color GetBlend(Color32 mask, int x, int y,Color baseColor, Color redChannel, Color greenChannel, Color blueChannel) {
int total = mask.r + mask.g + mask.b;
float alpha = Mathf.Max(mask.r, mask.g, mask.b) / 255f; // not sure this will work
float val = (total == 0) ? 0 : 1f / total;
float rWeight = mask.r * val;
float gWeight = mask.g * val;
float bWeight = mask.b * val;
Color c = (redChannel * rWeight) + (greenChannel * gWeight) + (blueChannel * blueWeight);
c.a = baseColor.a;
return Color.Lerp(baseColor, c, alpha);
}
More fiddling resulted in this idea:
public Color GetBlend(Color32 mask,Color baseColor, Color redChannel, Color greenChannel, Color blueChannel) {
int total = mask.r + mask.g + mask.b;
float alpha = Mathf.Max(mask.r, mask.g, mask.b) / 255f;
float r1 = mask.r == 0 ? baseColor.r : ((redChannel.r * mask.r) + (greenChannel.r * mask.r) + (blueChannel.r * mask.r)) / (mask.r * 3);
float g1 = mask.g == 0 ? baseColor.g : ((redChannel.g * mask.g) + (greenChannel.g * mask.g) + (blueChannel.g * mask.g)) / (mask.g * 3);
float b1 = mask.b == 0 ? baseColor.b : ((redChannel.b * mask.b) + (greenChannel.b * mask.b) + (blueChannel.b * mask.b)) / (mask.b * 3);
float a1 = baseColor.a;
Color blend = new Color(r1, g1, b1);
return Color.Lerp(baseColor, blend, alpha);
}
I've written the following code to calculate the length of a cubic bezier curve. I got the idea from Calculate the arclength, curve length of a cubic bezier curve. Why is not working?. The problem is it always produces a length of zero.
public Vector2 SegmentAtPoint(int segmentIndex, float t)
{
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
oneMinusT * oneMinusT * oneMinusT * points[segmentIndex * 3] +
3f * oneMinusT * oneMinusT * t * points[segmentIndex * 3 + 1] +
3f * oneMinusT * t * t * points[segmentIndex * 3 + 2] +
t * t * t * points[segmentIndex * 3 + 3];
}
public float SegmentLength(int segmentIndex) {
var steps = 10;
var t = 1 / steps;
var sumArc = 0.0f;
var j = 0.0f;
var a = new Vector2(0.0f, 0.0f);
var b = points[segmentIndex * 3];
var dX = 0.0f;
var dY = 0.0f;
var dS = 0.0f;
for (int i = 0; i < steps; j = j + t)
{
a = SegmentAtPoint(segmentIndex, j);
dX = a.x - b.x;
dY = a.y - b.y;
dS = Mathf.Sqrt((dX * dX) + (dY * dY));
sumArc = sumArc + dS;
b.x = a.x;
b.y = a.y;
i++;
}
return sumArc;
}
Code var t = 1 / steps; makes integer division, so result t is zero
Also note that j = j + t is executed after every loop, so at the first iteration j==0
This flaw causes such problem: both b and a are equal at the first iteration because j still remains =0. So you calculate segment lengths on intervals: 0-0, 0-0.1, 0.1-0.2...0.7-0.8,0.8-0.9 - ignoring 0.9-1.0 interval
Currently, I have the player select 2 positions in the map, it's a 3d world but my problem is only relevant in the first 2 dimensions. I want to create a floor between the 2 selected points, and rotate them to the angle between those 2 points. http://i.imgur.com/hCjtEzB.png (excuse my paint skills)
My code works properly in some cases, but in other cases the crates are spawned at the wrong angle. I think it has something to do with vec1.x > vec2.x, or something similar. But I can't figure it out.
What am I missing?
const int modelLength = 55;
const int modelWidth = 30;
void createFloor(Vector vec1, Vector vec2)
{
float height = vec1.Z;
float length = Math.Abs(vec1.X - vec2.X);
float width = Math.Abs(vec1.Y - vec2.Y);
int crateWidth = (int)Math.Ceiling(width / modelWidth);
int crateLength = (int)Math.Ceiling(length / modelLength);
int adjustedWidth = crateWidth * modelWidth;
int adjustedLength = crateLength * modelLength;
double angleRad = Math.Atan2(adjustedWidth, adjustedLength);
double angleDeg = Util.RadianToDegree(angleRad);
Vector angles = new Vector(0, (float)angleDeg, 0);
float mX = (vec1.X < vec2.X) ? adjustedLength / 2 + vec1.X : vec1.X - (adjustedLength / 2);
float mY = (vec1.Y < vec2.Y) ? adjustedWidth / 2 + vec1.Y : vec1.Y - (adjustedWidth / 2);
Vector middle = new Vector(mX, mY, vec1.Z);
for (int i = 0; i < crateLength; i++)
{
for (int j = 0; j < crateWidth; j++)
{
float x = (vec1.X < vec2.X) ? vec1.X + i * modelLength : vec1.X - i * modelLength;
float y = (vec1.Y < vec2.Y) ? vec1.Y + j * modelWidth : vec1.Y - j * modelWidth;
Vector v = new Vector(x, y, height);
v = Util.RotateAround(v, middle, angleDeg);
spawnCrate(v, angles);
}
}
}
public static Vector RotateAround(Vector vectorToRotate, Vector center, double angleDeg)
{
double angleRad = DegreeToRadian(angleDeg);
double cosTheta = Math.Cos(angleRad);
double sinTheta = Math.Sin(angleRad);
return new Vector
{
X = (int)(cosTheta * (vectorToRotate.X - center.X) - sinTheta * (vectorToRotate.Y - center.Y) + center.X),
Y = (int)(sinTheta * (vectorToRotate.X - center.X) + cosTheta * (vectorToRotate.Y - center.Y) + center.Y),
Z = vectorToRotate.Z
};
}
Thanks.
This question already has answers here:
Rotating a point about another point (2D)
(6 answers)
Closed 9 years ago.
I have list of points containing x and y locations of a page. I want to apply rotation on all these points relative to any pivot point of page (currently lets assume its center).
var points = new List<Point>();
points.Add(1,1);
points.Add(15,18);
points.Add(25,2);
points.Add(160,175);
points.Add(150,97);
const int pageHeight = 300;
const int pageWidth = 400;
var pivotPoint = new Point(200, 150); //Center
var angle = 45; // its in degree.
// Apply rotation.
Do I need some formula here?
public static Point Rotate(Point point, Point pivot, double angleDegree)
{
double angle = angleDegree * Math.PI / 180;
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
int dx = point.X - pivot.X;
int dy = point.Y - pivot.Y;
double x = cos * dx - sin * dy + pivot.X;
double y = sin * dx + cos * dy + pivot.X;
Point rotated = new Point((int)Math.Round(x), (int)Math.Round(y));
return rotated;
}
static void Main(string[] args)
{
Console.WriteLine(Rotate(new Point(1, 1), new Point(0, 0), 45));
}
If you have a large number of points to rotate, you might want to precompute the rotation matrix…
[C -S U]
[S C V]
[0 0 1]
…where…
C = cos(θ)
S = sin(θ)
U = (1 - C) * pivot.x + S * pivot.y
V = (1 - C) * pivot.y - S * pivot.x
You then rotate each point as follows:
rotated.x = C * original.x - S * original.y + U;
rotated.x = S * original.x + C * original.y + V;
The above formula is the result of combining three transforms…
rotated = translate(pivot) * rotate(θ) * translate(-pivot) * original
…where…
translate([x y]) = [1 0 x]
[0 1 y]
[0 0 1]
rotate(θ) = [cos(θ) -sin(θ) 0]
[sin(θ) cos(θ) 0]
[ 0 0 1]
if u rotate a point(x,y) around point (x1,y1) by an angle some a then you need a formula...
x2 = cos(a) * (x-x1) - sin(a) * (y-y1) + x1
y2 = sin(a) * (x-x1) + cos(a) * (y-y1) + y1
Point newRotatedPoint = new Point(x2,y2)
I need to find a point where a line (its origin is ellipse' center) intersects an ellipse in 2D... I can easily find a point on a circle, because I know an angle F and the circle' radius (R):
x = x0 + R * cosF
y = y0 + R * sinF
However I just can't figure how am I supposed to deal with an ellipse... I know it's dimensions (A & B), but what is the way of finding parameter T?!
x = x0 + A * cosT
y = y0 + B * sinT
From what I understand the parameter T (T angle) is not far from the F angle (approximately +-15 degrees in some cases), but I just can't figure how to calculate it!!!
If there is a kind hearted soul, please help me with this problem...
The standard equation of an ellipse, stationed at 0,0, is:
1 = (x)^2 / (a) + (y)^2 / (b)
Where a is 1/2 the diameter on the horizontal axis, and b is 1/2 the diameter on the vertical axis.
you have a line, assuming an equation:
y = (m)(x - x0) + y0
So, let us plug-and-play!
1 = (x)^2 / (a) + (m(x - x0) + y0)^2 / (b)
1 = x^2 / a + (mx + (y0 - mx0))^2 / b
1 = x^2 / a + (m^2 * x^2 + 2mx*(y0 - mx0) + (y0 - mx0)^2) / b
1 = x^2 / a + (m^2 x^2) / b + (2mx*(y0 - mx0) + (y0^2 - 2y0mx0 + m^2*x0^2)) / b
1 = ((x^2 * b) / (a * b)) + ((m^2 * x^2 * a) / (a * b)) + (2mxy0 - 2m^2xx0)/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
1 = ((bx^2 + am^2x^2)/(ab)) + (x*(2my0 - 2m^2x0))/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
0 = x^2*((b + a*m^2)/(ab)) + x*((2my0 - 2m^2x0)/b) + (((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
That last equation follows the form of a standard quadratic equation.
So just use the quadratic formula, with:
((b + a*m^2)/(ab))
((2my0 - 2m^2x0)/b)
and
(((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
to get the X values at the intersections; Then, plug in those values into your original line equation to get the Y values.
Good luck!
Don't do it this way. Instead check the equation that forms an ellipse and that forming a line and solve the set:
The ellipse: (x/a)^2 + (y/b)^2 = 1
Your line: y = cx
You know a, b and c, so finding a solution is going to be easy. You'll find two solutions, because the line crosses the ellipse twice.
EDIT: Note I moved your ellipse's center to (0,0). It makes everything easier. Just add (x0,y0) to the solution.
public Hits<float2> EllipseLineIntersection ( float rx , float ry , float2 p1 , float2 p2 )
{
Hits<float2> hits = default(Hits<float2>);
float2 p3, p4;
Rect rect = default(Rect);
{
rect.xMin = math.min(p1.x,p2.x);
rect.xMax = math.max(p1.x,p2.x);
rect.yMin = math.min(p1.y,p2.y);
rect.yMax = math.max(p1.y,p2.y);
}
float s = ( p2.y - p1.y )/( p2.x - p1.x );
float si = p2.y - ( s * p2.x );
float a = ( ry*ry )+( rx*rx * s*s );
float b = 2f * rx*rx * si * s;
float c = rx*rx * si*si - rx*rx * ry*ry;
float radicand_sqrt = math.sqrt( ( b*b )-( 4f * a * c) );
p3.x = ( -b - radicand_sqrt )/( 2f*a );
p4.x = ( -b + radicand_sqrt )/( 2f*a );
p3.y = s*p3.x + si;
p4.y = s*p4.x + si;
if( rect.Contains(p3) ) hits.Push( p3 );
if( rect.Contains(p4) ) hits.Push( p4 );
return hits;
}
public struct Hits<T>
{
public byte count;
public T point0, point1;
public void Push ( T val )
{
if( count==0 ) { point0 = val; count ++; }
else if( count==1 ) { point1 = val; count ++; }
else print("This structure can only fit 2 values");
}
}
I wrote a C# code for your problem and I hope you can find it helpful. the distance function inside this code calculates euclidean distance between two points in space.
wX denotes horizontal radios of ellipse and wY denotes vertical radios.
private PointF LineIntersectEllipse(PointF A, PointF B, float wX, float wY)
{
double dx = B.X - A.X;
double dy = B.Y - A.Y;
double theta = Math.Atan2(dy, dx);
double r = distance(A, B) - ((wX * wY) / Math.Sqrt(Math.Pow(wY * Math.Cos(theta), 2) + Math.Pow(wX * Math.Sin(theta), 2)));
return PointF((float)(A.X + r * Math.Cos(theta)), (float)(A.Y + r * Math.Sin(theta)));
}
Andrew Łukasik posted a good and useful answer, however it is not using regular C# types. As I wrote in the comments, I converted the code using System.Drawing objects PointF and RectangleF. I found out that if the points given as parameters are aligned as a vertical or horizontal line, then "rect" will have a width or a height equal to 0. Then, rect.Contains(point) will return false even if the point is on this line.
I also modified the "Hits" structure to check if the point pushed is not already existing, which is the case if the line is perfectly tangent, then p3 and p4 will have same coordinates, as the exact tangent point is the only crossing point.
Here is the new code taking care of all the cases :
public static Hits<PointF> EllipseLineIntersection0(float rx, float ry, PointF p1, PointF p2)
{
Hits<PointF> hits = default(Hits<PointF>);
PointF p3 = new PointF();
PointF p4 = new PointF();
var rect = default(RectangleF);
rect.X = Math.Min(p1.X, p2.X);
rect.Width = Math.Max(p1.X, p2.X) - rect.X;
rect.Y = Math.Min(p1.Y, p2.Y);
rect.Height = Math.Max(p1.Y, p2.Y) - rect.Y;
float s = (p2.Y - p1.Y) / (p2.X - p1.X);
float si = p2.Y - (s * p2.X);
float a = (ry * ry) + (rx * rx * s * s);
float b = 2f * rx * rx * si * s;
float c = rx * rx * si * si - rx * rx * ry * ry;
float radicand_sqrt = (float)Math.Sqrt((b * b) - (4f * a * c));
p3.X = (-b - radicand_sqrt) / (2f * a);
p4.X = (-b + radicand_sqrt) / (2f * a);
p3.Y = s * p3.X + si;
p4.Y = s * p4.X + si;
if (rect.Width == 0)
{
if (p3.Y >= rect.Y && p3.Y <= rect.Y + rect.Height) hits.Push(p3);
if (p4.Y >= rect.Y && p4.Y <= rect.Y + rect.Height) hits.Push(p4);
}
else if (rect.Height == 0)
{
if (p3.X >= rect.X && p3.X <= rect.X + rect.Width) hits.Push(p3);
if (p4.X >= rect.X && p4.X <= rect.X + rect.Width) hits.Push(p4);
}
else
{
if (rect.Contains(p3)) hits.Push(p3);
if (rect.Contains(p4)) hits.Push(p4);
}
return hits;
}
public struct Hits<T>
{
public byte Count;
public T P0, P1;
public void Push(T val)
{
if (Count == 0) { P0 = val; Count++; }
else if (Count == 1) { if (!P0.Equals(val)) { P1 = val; Count++; } }
else throw new OverflowException("Structure Hits can only fit 2 values.");
}
}