I want to know when four Vector 3 positions intersect, as in the image, from point to point a1 to a2 and b1 to b2 if they intersect each other from each of their positions. Does anyone have any idea how to do this?
The Unity community wiki has a page of math functions that includes a very helpful procedure for this. Copied (and edited slightly) below:
public static bool LineLineIntersection(out Vector3 intersection, Vector3 linePoint1,
Vector3 lineVec1, Vector3 linePoint2, Vector3 lineVec2){
Vector3 lineVec3 = linePoint2 - linePoint1;
Vector3 crossVec1and2 = Vector3.Cross(lineVec1, lineVec2);
Vector3 crossVec3and2 = Vector3.Cross(lineVec3, lineVec2);
float planarFactor = Vector3.Dot(lineVec3, crossVec1and2);
//is coplanar, and not parallel
if( Mathf.Abs(planarFactor) < 0.0001f
&& crossVec1and2.sqrMagnitude > 0.0001f)
{
float s = Vector3.Dot(crossVec3and2, crossVec1and2)
/ crossVec1and2.sqrMagnitude;
intersection = linePoint1 + (lineVec1 * s);
return true;
}
else
{
intersection = Vector3.zero;
return false;
}
}
So, in your situation, you could use that, then check if the intersection point is between a1 and a2 and b1 and b2:
Vector3 intersection;
Vector3 aDiff = a2-a1;
Vector3 bDiff = b2-b1;
if (LineLineIntersection(out intersection, a1, aDiff, b1, bDiff))
{
float aSqrMagnitude = aDiff.sqrMagnitude;
float bSqrMagnitude = bDiff.sqrMagnitude;
if ( (intersection - a1).sqrMagnitude <= aSqrMagnitude
&& (intersection - a2).sqrMagnitude <= aSqrMagnitude
&& (intersection - b1).sqrMagnitude <= bSqrMagnitude
&& (intersection - b2).sqrMagnitude <= bSqrMagnitude)
{
// there is an intersection between the two segments and
// it is at intersection
}
}
You could also use this option if you want to dispense with the UnityEngine library:
We define a 2d vector as:
public class V2
{
public static V2 zero = new V2(0, 0);
public readonly float x;
public readonly float y;
[JsonConstructor]
public V2(float x, float y)
{
if (
physicsLogic.debugOn &&
(float.IsNaN(x) || float.IsNaN(y) || float.IsInfinity(y) || float.IsInfinity(x)))
{
throw new Exception($"Dodgy V2 ({x},{y})");
}
this.x = x; this.y = y;
}
public FieldPoint asFlooredFieldPoint()
{
return new FieldPoint(x,0,y);
}
public static V2 operator +(V2 b, V2 a) => new V2(b.x + a.x, b.y + a.y);
public static V2 operator -(V2 b, V2 a) => new V2(b.x - a.x, b.y - a.y);
public static V2 operator /(V2 a, double b) => new V2((float)(a.x / b), (float)(a.y / b));
public static V2 operator *(V2 a, double b) => new V2((float)(a.x * b), (float)(a.y * b));
public static V2 operator *(double b, V2 a) => new V2((float)(a.x * b), (float)(a.y * b));
[JsonIgnore]
float mag = -1;
[JsonIgnore]
public float magnitude
{
get
{
if (mag < 0)
{
mag = (float)Math.Sqrt(sqrMagnitude);
}
return mag;
}
}
[JsonIgnore]
public V2 normalized
{
get
{
var mag = magnitude;
if (mag == 0)
{
return V2.zero;
}
return new V2(x / mag, y / mag);
}
}
[JsonIgnore]
float _sqrMagnitude = -1;
[JsonIgnore]
public float sqrMagnitude
{
get
{
if (_sqrMagnitude < 0)
{
_sqrMagnitude = (float)(Math.Pow(x, 2) + Math.Pow(y, 2));
}
return _sqrMagnitude;
}
}
public override string ToString()
{
return $"({x.ToString("F1")},{y.ToString("F1")})";
}
public override bool Equals(Object obj)
{
//Check for null and compare run-time types.
if ((obj == null) || !this.GetType().Equals(obj.GetType()))
{
return false;
}
else
{
V2 p = (V2)obj;
return (MathUtil.closeEnough(x, p.x, .001)) && (MathUtil.closeEnough(y, p.y, .001));
}
}
We look for the intersection between the two lines, if it exists:
public static V2 intersectSegment(V2 goalStart, V2 goalEnd, V2 ballStart, V2 ballEnd)
{
/* Equations of straight lines 2D: Qx + Ry + S = 0
*
* The objective is to determine the equations of the straight line of each segment,
* on the one hand the goal line and on the other hand the ball line.
*
* Then I determine if the two lines intersect at a point, or on the contrary
* if they never intersect, that is, they are parallel.
*
* If the two lines intersect at a point, I determine the value of that point (P)
*
* Finally, it is checked if this point is contained in each of the segments. *
*
*
* r1: Point A (x_A, y_A); Point B (x_B, y_B) --> AB = (x_B - x_A, y_B - y_A)
* r2: Point C (x_C, y_C), Point D (x_D, y_D) --> CD = (x_D - x_C, y_D - y_C)
*
* r1: (x - x_A)/x_AB = (y - y_A)/y_AB --> r1: Q1x + R1y + S1 = 0
* r2: (x - x_C)/x_CD = (y - y_C)/y_CD --> r2: Q2x + R2y + S2 = 0
*
* ** Q1 = y_AB ; R1 = -x_AB ; S1 = x_AB*y_A - y_AB*x_A
* ** Q2 = y_CD ; R2 = -x_CD ; S2 = x_CD*y_C - y_CD*x_C
*
* | Q1 R1 |
* determinant = | Q2 R2 | = Q1*R2 - Q2*R1
*
* ** if determinant == 0 -> is parallell
* ** if determinant != 0 -> is secant line
*
* Cut-off point (P):
*
* Q2*S1 - Q1*S2
* y_P = -------------
* Q1*R2 - Q2*R1
*
* S1*(Q2*R1 - Q1*R2) + R1*Q2*(Q1 - S1)
* x_P = ------------------------------------
* Q1^2*R2 - Q1*Q2*R1
*
*
* ** if P c in AB or CD -> Intersection true
*
*/
var goalVector = goalEnd - goalStart;
var ballVector = ballEnd - ballStart;
var Q1 = goalVector.y;
var Q2 = ballVector.y;
var R1 = goalVector.x;
var R2 = ballVector.x;
var S1 = goalVector.x * goalStart.y - goalVector.y * goalStart.x;
var S2 = ballVector.x * ballStart.y - ballVector.y * ballStart.x;
var determinant = Q1 * R2 - Q2 * R1;
if (determinant != 0)
{
var x_P = (S2 * R1 - R2 * S1) / (R2 * Q1 - Q2 * R1);
var y_P = (S2 * Q1 - Q2 * S1) / (R2 * Q1 - Q2 * R1);
var intersectPoint = new V2(x_P, y_P);
if (MathUtil.PointContentInAB(goalStart, goalEnd, intersectPoint ) &&
MathUtil.PointContentInAB(ballStart, ballEnd, intersectPoint))
{
return intersectPoint;
}
}
return null;
}
Then, We return true or false if there is such an intersection between the two lines:
public static bool findIfIntersectsInSegment(V2 goalStart, V2 goalEnd, V2 ballStart, V2 ballEnd)
{
return intersectSegment(goalStart, goalEnd, ballStart, ballEnd)!=null;
}
Finally, if the intersection exists, we look for the intersection point of the two lines:
public static bool PointContentInAB(V2 A, V2 B, V2 P)
{
/* Equations of straight lines 2D: Qx + Ry + S = 0
*
* Point A (x_A, y_A); Point B (x_B, y_B) --> AB = (x_B - x_A, y_B - y_A)
*
* r1: (x - x_A)/x_AB = (y - y_A)/y_AB --> r1: Q1x + R1y + S1 = 0
* Q1 = y_AB ; R1 = -x_AB ; S1 = x_AB*y_A - y_AB*x_A
*
* ** if P.x <= B.x && P.x >= A.x --> Point content in AB
*/
var AB = B - A;
var P_content_in_r1 = AB.y * P.x - AB.x * P.y + (AB.x * A.y - AB.y * A.x);
if (nearzero(P_content_in_r1))
// Point content in r1
{
if (AB.x > 0f)
{
if (P.x <= B.x && P.x >= A.x)
// Point content in r1 and AB
{
if (AB.y > 0f)
{
if (P.y <= B.y && P.y >= A.y)
{
return true;
}
}
else
{
if(P.y >= B.y && P.y <= A.y)
{
return true;
}
}
}
}
else
{
if (P.x >= B.x && P.x <= A.x)
{
// Point content in r1 and AB
if (AB.y > 0f)
{
if (P.y <= B.y && P.y >= A.y)
{
return true;
}
}
else
{
if (P.y >= B.y && P.y <= A.y)
{
return true;
}
}
}
}
}
return false;
}
I hope you find it useful, best regards.
Related
This question already has answers here:
Return multiple values to a method caller
(28 answers)
Closed 3 years ago.
I'm working on a C# program that getting some values from textboxes and set them to some variables in 2 class that their name is:"Tfluid" and "Twell"
then I use them in another class the name is "Tcalc" through a method with name "fluidcal" and make some calculations with them,
I have 2 crossed while that means it starts with a "ql" and then match a final P to each "ql". now I want to use these (ql, P) to plot a graph in another form,
so what is the best way of saving them
(i don't know what should I return with the fluidal method)
and how can I add them to the chart?
this is my calculation process
public double Fluidcal(TWell well, TFluid fluid)
{
double froudnumber, noslipholdup, vm, vsl, vsg, liquidvelocitynumber, l1, l2, l3, l4, fluidregim, hozhold, C, psy, liqhold, liqholdseg, liqholdinter, fn, nren, densityn, viscosityn, y, S, ftp, dpdzel, dpdzf, dpperdzt, rhos, Ek,ql;
ql = 1;
while (ql<=fluid.maxoilflowrate)
{
vsl = (ql) / Math.PI * Math.Pow(well.tubingdiameter / 2, 2); //superficial velocities
vsg = (ql * fluid.gor) / Math.PI * Math.Pow(well.tubingdiameter / 2, 2);
vm = vsl + vsg;
double nowlength = 0;
double P = well.wellheadpressure;
while (nowlength <= well.welldepth)
{
froudnumber = Math.Pow(vm, 2) / well.tubingdiameter * 32.174; //froud number
noslipholdup = vsl / vm; //no slip holdup
double newoildensity = fluid.oildensity / (1 + 0.00056 * (0.01515 * nowlength));
liquidvelocitynumber = vsl * Math.Pow((newoildensity / 32.174 * fluid.gasliquidsurfacetension), 0.25); //liquid velocity number
//correlating parametrs
l1 = 316 * Math.Pow(noslipholdup, 0.302);
l2 = 0.0009252 * Math.Pow(noslipholdup, -2.4684);
l3 = 0.1 * Math.Pow(noslipholdup, -1.4516);
l4 = 0.5 * Math.Pow(noslipholdup, -6.738);
fluidregim = 0;
C = 0;
liqhold = 0;
if ((noslipholdup < 0.01 && froudnumber < l1) || (noslipholdup >= 0.01 && froudnumber < l2))
{
segregated(froudnumber, noslipholdup, liquidvelocitynumber, out fluidregim, out hozhold, out C, out psy, out liqhold);
}
else if ((noslipholdup >= 0.01 && l2 < froudnumber && froudnumber <= l3))
{
transition(froudnumber, noslipholdup, liquidvelocitynumber, l2, l3, out fluidregim, out hozhold, out C, out psy, out liqhold, out liqholdseg, out liqholdinter);
}
else if ((noslipholdup >= 0.01 && noslipholdup < 0.4 && froudnumber > l3 && froudnumber <= l1)
|| (noslipholdup >= 0.4 && l3 < froudnumber && froudnumber <= 4))
{
intermittent(froudnumber, noslipholdup, liquidvelocitynumber, out fluidregim, out hozhold, out C, out psy, out liqhold);
}
else if ((noslipholdup < 0.4 && froudnumber >= l1) || (noslipholdup >= 0.4 && froudnumber > l4))
{
disturbuted(froudnumber, noslipholdup, out fluidregim, out hozhold, out C, out psy, out liqhold);
}
// else
// {
// System.Windows.Forms.MessageBox.Show("Undefined Flow Regim");
//}
y = noslipholdup / Math.Pow(liqhold, 2);
if (y > 1 && y < 1.2)
{
S = Math.Log(2.2 * y - 1.2);
}
else
{
S = (Math.Log(y)) / (-0.0523 + 3.182 * Math.Log(y) - 0.8725 * Math.Pow(Math.Log(y), 2) + 0.01853 * Math.Pow(Math.Log(y), 4));
}
viscosityn = fluid.oilviscosity * noslipholdup + fluid.gasviscosity * (1 - noslipholdup);
densityn = fluid.oildensity * noslipholdup + (1 - noslipholdup) * fluid.gasdensity;
nren = densityn * vm * (well.tubingdiameter) / viscosityn;
fn = 1 / Math.Pow((2 * Math.Log(nren / (4.5223 * Math.Log(nren) - 3.8215))), 2);
ftp = fn * Math.Exp(S);
rhos = fluid.oildensity * liqhold + fluid.gasdensity * (1 - liqhold);
Ek = rhos * vm * vsg / 32.2 * P;
dpdzel = (1) * rhos;
dpdzf = ftp * densityn * Math.Pow(vm, 2) / 2 * 32.174 * well.tubingdiameter;
dpperdzt = (dpdzel + dpdzf) / 1 - Ek;
P = P + 5 * dpperdzt;
yax.Add(P);
nowlength = +5;
vsl = vm * liqhold;
vsg = vsl * fluid.gor;
}
ql = +20;
}return; //what should I write here?I want pairs of(ql,P)
There are a few ways you could do this. I would suggest encapsulating the lists in an object and returning that object from your method. For example:
public class MyLists
{
public List<double> ListOne { get; set; }
public List<double> ListTwo { get; set; }
}
Of course, you could use a Tuple. If I understand your question correctly...
I am in need of a C# solution to some code i have in Javascript, which takes a array of latitude and longitude positions which forms an area or zone. and also an object X and Y position and returns the X and Y of the closest point from the object. This code works perfect in Javascript, however as i am now re-writing my work in C#, i am unable to find a workable solution which works, and does what this function does.
The Javascript function is below, which takes 2 arguments pXy, which is the X and Y position of your location and aXys, which is an array of X and Y positions forming the polylines of an area.
var getClosestPointOnLines = function(pXy, aXys) {
var minDist;
var fTo;
var fFrom;
var x;
var y;
var i;
var dist;
if (aXys.length > 1) {
for (var n = 1 ; n < aXys.length ; n++) {
if (aXys[n].x != aXys[n - 1].x) {
var a = (aXys[n].y - aXys[n - 1].y) / (aXys[n].x - aXys[n - 1].x);
var b = aXys[n].y - a * aXys[n].x;
dist = Math.abs(a * pXy.x + b - pXy.y) / Math.sqrt(a * a + 1);
}
else
dist = Math.abs(pXy.x - aXys[n].x)
// length^2 of line segment
var rl2 = Math.pow(aXys[n].y - aXys[n - 1].y, 2) + Math.pow(aXys[n].x - aXys[n - 1].x, 2);
// distance^2 of pt to end line segment
var ln2 = Math.pow(aXys[n].y - pXy.y, 2) + Math.pow(aXys[n].x - pXy.x, 2);
// distance^2 of pt to begin line segment
var lnm12 = Math.pow(aXys[n - 1].y - pXy.y, 2) + Math.pow(aXys[n - 1].x - pXy.x, 2);
// minimum distance^2 of pt to infinite line
var dist2 = Math.pow(dist, 2);
// calculated length^2 of line segment
var calcrl2 = ln2 - dist2 + lnm12 - dist2;
// redefine minimum distance to line segment (not infinite line) if necessary
if (calcrl2 > rl2)
dist = Math.sqrt(Math.min(ln2, lnm12));
if ((minDist == null) || (minDist > dist)) {
if (calcrl2 > rl2) {
if (lnm12 < ln2) {
fTo = 0;//nearer to previous point
fFrom = 1;
}
else {
fFrom = 0;//nearer to current point
fTo = 1;
}
}
else {
// perpendicular from point intersects line segment
fTo = ((Math.sqrt(lnm12 - dist2)) / Math.sqrt(rl2));
fFrom = ((Math.sqrt(ln2 - dist2)) / Math.sqrt(rl2));
}
minDist = dist;
i = n;
}
}
var dx = aXys[i - 1].x - aXys[i].x;
var dy = aXys[i - 1].y - aXys[i].y;
x = aXys[i - 1].x - (dx * fTo);
y = aXys[i - 1].y - (dy * fTo);
}
return { 'x': x, 'y': y, 'i': i, 'fTo': fTo, 'fFrom': fFrom };
}
How can i replicate the above in C#?
Thanks xdtTransform. I was not using Visual Studio so did not realize the Ctrl+Space. After installing it, i have managed to convert the code myself. I have included it here for anyone who will benefit from it in the future.
I started with a class to hold the Latitude and Longitude details of the points.
namespace Classes
{
public class AppGeoPoint
{
public double X { get; set; }
public double Y { get; set; }
}
}
And the function converted
public static Classes.AppGeoPoint getClosestPointOnLines(Classes.AppGeoPoint pXy, Classes.AppGeoPoint[] aXys)
{
double? minDist = null;
double fTo = 0.0;
double fFrom;
double x = 0.0;
double y = 0.0;
int i = 0;
double dist;
if (aXys.Length > 1)
{
for (var n = 1; n < aXys.Length; n++)
{
if (aXys[n].X != aXys[n - 1].X)
{
var a = (aXys[n].Y - aXys[n - 1].Y) / (aXys[n].X - aXys[n - 1].X);
var b = aXys[n].Y - a * aXys[n].X;
dist = Math.Abs(a * pXy.X + b - pXy.Y) / Math.Sqrt(a * a + 1);
}
else
dist = Math.Abs(pXy.X - aXys[n].X);
// length^2 of line segment
double rl2 = Math.Pow(aXys[n].Y - aXys[n - 1].Y, 2) + Math.Pow(aXys[n].X - aXys[n - 1].X, 2);
// distance^2 of pt to end line segment
double ln2 = Math.Pow(aXys[n].Y - pXy.Y, 2) + Math.Pow(aXys[n].X - pXy.X, 2);
// distance^2 of pt to begin line segment
double lnm12 = Math.Pow(aXys[n - 1].Y - pXy.Y, 2) + Math.Pow(aXys[n - 1].X - pXy.X, 2);
// minimum distance^2 of pt to infinite line
double dist2 = Math.Pow(dist, 2);
// calculated length^2 of line segment
double calcrl2 = ln2 - dist2 + lnm12 - dist2;
// redefine minimum distance to line segment (not infinite line) if necessary
if (calcrl2 > rl2)
dist = Math.Sqrt(Math.Min(ln2, lnm12));
if ((minDist == null) || (minDist > dist))
{
if (calcrl2 > rl2)
{
if (lnm12 < ln2)
{
fTo = 0;//nearer to previous point
fFrom = 1;
}
else
{
fFrom = 0;//nearer to current point
fTo = 1;
}
}
else
{
// perpendicular from point intersects line segment
fTo = ((Math.Sqrt(lnm12 - dist2)) / Math.Sqrt(rl2));
fFrom = ((Math.Sqrt(ln2 - dist2)) / Math.Sqrt(rl2));
}
minDist = dist;
i = n;
}
}
var dx = aXys[i - 1].X - aXys[i].X;
var dy = aXys[i - 1].Y - aXys[i].Y;
x = aXys[i - 1].X - (dx * fTo);
y = aXys[i - 1].Y - (dy * fTo);
}
return new Classes.AppGeoPoint { X = x, Y = y };
}
Then finally, to build an array list and check for the closest point
Classes.AppGeoPoint YourLocation= new Classes.AppGeoPoint { X = 50.83737, Y = -1.07428 };
Classes.AppGeoPoint[] AreaCheck = new[] {
new Classes.AppGeoPoint { X = 50.847550000000005, Y = -1.0863200000000002 },
new Classes.AppGeoPoint { X = 50.83975, Y = -1.0859800000000002 },
new Classes.AppGeoPoint { X = 50.83845, Y = -1.06487 },
new Classes.AppGeoPoint { X = 50.84723, Y = -1.0645200000000001 }
};
Classes.AppGeoPoint ReturnVal = getClosestPointOnLines(YourLocation, AreaCheck);
Console.WriteLine("X " + ReturnVal.X);
Console.WriteLine("Y " + ReturnVal.Y);
ReturnVal.X and ReturnVal.Y will return the closest points latitude and longitude.
Hopefully, this may come in helpful to others.
I have 2 lines. Both lines containing their 2 points of X and Y. This means they both have length.
I see 2 formulas, one using determinants and one using normal algebra. Which would be the most efficient to calculate and what does the formula looks like?
I'm having a hard time using matrices in code.
This is what I have so far, can it be more efficient?
public static Vector3 Intersect(Vector3 line1V1, Vector3 line1V2, Vector3 line2V1, Vector3 line2V2)
{
//Line1
float A1 = line1V2.Y - line1V1.Y;
float B1 = line1V1.X - line1V2.X;
float C1 = A1*line1V1.X + B1*line1V1.Y;
//Line2
float A2 = line2V2.Y - line2V1.Y;
float B2 = line2V1.X - line2V2.X;
float C2 = A2 * line2V1.X + B2 * line2V1.Y;
float det = A1*B2 - A2*B1;
if (det == 0)
{
return null;//parallel lines
}
else
{
float x = (B2*C1 - B1*C2)/det;
float y = (A1 * C2 - A2 * C1) / det;
return new Vector3(x,y,0);
}
}
Assuming you have two lines of the form Ax + By = C, you can find it pretty easily:
float delta = A1 * B2 - A2 * B1;
if (delta == 0)
throw new ArgumentException("Lines are parallel");
float x = (B2 * C1 - B1 * C2) / delta;
float y = (A1 * C2 - A2 * C1) / delta;
Pulled from here
I recently went back on paper to find a solution to this problem using basic algebra. We just need to solve the equations formed by the two lines and if a valid solution exist then there is an intersection.
You can check my Github repository for extended implementation handling potential precision issue with double and tests.
public struct Line
{
public double x1 { get; set; }
public double y1 { get; set; }
public double x2 { get; set; }
public double y2 { get; set; }
}
public struct Point
{
public double x { get; set; }
public double y { get; set; }
}
public class LineIntersection
{
// Returns Point of intersection if do intersect otherwise default Point (null)
public static Point FindIntersection(Line lineA, Line lineB, double tolerance = 0.001)
{
double x1 = lineA.x1, y1 = lineA.y1;
double x2 = lineA.x2, y2 = lineA.y2;
double x3 = lineB.x1, y3 = lineB.y1;
double x4 = lineB.x2, y4 = lineB.y2;
// equations of the form x=c (two vertical lines) with overlapping
if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance && Math.Abs(x1 - x3) < tolerance)
{
throw new Exception("Both lines overlap vertically, ambiguous intersection points.");
}
//equations of the form y=c (two horizontal lines) with overlapping
if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance && Math.Abs(y1 - y3) < tolerance)
{
throw new Exception("Both lines overlap horizontally, ambiguous intersection points.");
}
//equations of the form x=c (two vertical parallel lines)
if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance)
{
//return default (no intersection)
return default(Point);
}
//equations of the form y=c (two horizontal parallel lines)
if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance)
{
//return default (no intersection)
return default(Point);
}
//general equation of line is y = mx + c where m is the slope
//assume equation of line 1 as y1 = m1x1 + c1
//=> -m1x1 + y1 = c1 ----(1)
//assume equation of line 2 as y2 = m2x2 + c2
//=> -m2x2 + y2 = c2 -----(2)
//if line 1 and 2 intersect then x1=x2=x & y1=y2=y where (x,y) is the intersection point
//so we will get below two equations
//-m1x + y = c1 --------(3)
//-m2x + y = c2 --------(4)
double x, y;
//lineA is vertical x1 = x2
//slope will be infinity
//so lets derive another solution
if (Math.Abs(x1 - x2) < tolerance)
{
//compute slope of line 2 (m2) and c2
double m2 = (y4 - y3) / (x4 - x3);
double c2 = -m2 * x3 + y3;
//equation of vertical line is x = c
//if line 1 and 2 intersect then x1=c1=x
//subsitute x=x1 in (4) => -m2x1 + y = c2
// => y = c2 + m2x1
x = x1;
y = c2 + m2 * x1;
}
//lineB is vertical x3 = x4
//slope will be infinity
//so lets derive another solution
else if (Math.Abs(x3 - x4) < tolerance)
{
//compute slope of line 1 (m1) and c2
double m1 = (y2 - y1) / (x2 - x1);
double c1 = -m1 * x1 + y1;
//equation of vertical line is x = c
//if line 1 and 2 intersect then x3=c3=x
//subsitute x=x3 in (3) => -m1x3 + y = c1
// => y = c1 + m1x3
x = x3;
y = c1 + m1 * x3;
}
//lineA & lineB are not vertical
//(could be horizontal we can handle it with slope = 0)
else
{
//compute slope of line 1 (m1) and c2
double m1 = (y2 - y1) / (x2 - x1);
double c1 = -m1 * x1 + y1;
//compute slope of line 2 (m2) and c2
double m2 = (y4 - y3) / (x4 - x3);
double c2 = -m2 * x3 + y3;
//solving equations (3) & (4) => x = (c1-c2)/(m2-m1)
//plugging x value in equation (4) => y = c2 + m2 * x
x = (c1 - c2) / (m2 - m1);
y = c2 + m2 * x;
//verify by plugging intersection point (x, y)
//in orginal equations (1) & (2) to see if they intersect
//otherwise x,y values will not be finite and will fail this check
if (!(Math.Abs(-m1 * x + y - c1) < tolerance
&& Math.Abs(-m2 * x + y - c2) < tolerance))
{
//return default (no intersection)
return default(Point);
}
}
//x,y can intersect outside the line segment since line is infinitely long
//so finally check if x, y is within both the line segments
if (IsInsideLine(lineA, x, y) &&
IsInsideLine(lineB, x, y))
{
return new Point { x = x, y = y };
}
//return default (no intersection)
return default(Point);
}
// Returns true if given point(x,y) is inside the given line segment
private static bool IsInsideLine(Line line, double x, double y)
{
return (x >= line.x1 && x <= line.x2
|| x >= line.x2 && x <= line.x1)
&& (y >= line.y1 && y <= line.y2
|| y >= line.y2 && y <= line.y1);
}
}
How to find intersection of two lines/segments/ray with rectangle
public class LineEquation{
public LineEquation(Point start, Point end){
Start = start;
End = end;
IsVertical = Math.Abs(End.X - start.X) < 0.00001f;
M = (End.Y - Start.Y)/(End.X - Start.X);
A = -M;
B = 1;
C = Start.Y - M*Start.X;
}
public bool IsVertical { get; private set; }
public double M { get; private set; }
public Point Start { get; private set; }
public Point End { get; private set; }
public double A { get; private set; }
public double B { get; private set; }
public double C { get; private set; }
public bool IntersectsWithLine(LineEquation otherLine, out Point intersectionPoint){
intersectionPoint = new Point(0, 0);
if (IsVertical && otherLine.IsVertical)
return false;
if (IsVertical || otherLine.IsVertical){
intersectionPoint = GetIntersectionPointIfOneIsVertical(otherLine, this);
return true;
}
double delta = A*otherLine.B - otherLine.A*B;
bool hasIntersection = Math.Abs(delta - 0) > 0.0001f;
if (hasIntersection){
double x = (otherLine.B*C - B*otherLine.C)/delta;
double y = (A*otherLine.C - otherLine.A*C)/delta;
intersectionPoint = new Point(x, y);
}
return hasIntersection;
}
private static Point GetIntersectionPointIfOneIsVertical(LineEquation line1, LineEquation line2){
LineEquation verticalLine = line2.IsVertical ? line2 : line1;
LineEquation nonVerticalLine = line2.IsVertical ? line1 : line2;
double y = (verticalLine.Start.X - nonVerticalLine.Start.X)*
(nonVerticalLine.End.Y - nonVerticalLine.Start.Y)/
((nonVerticalLine.End.X - nonVerticalLine.Start.X)) +
nonVerticalLine.Start.Y;
double x = line1.IsVertical ? line1.Start.X : line2.Start.X;
return new Point(x, y);
}
public bool IntersectWithSegementOfLine(LineEquation otherLine, out Point intersectionPoint){
bool hasIntersection = IntersectsWithLine(otherLine, out intersectionPoint);
if (hasIntersection)
return intersectionPoint.IsBetweenTwoPoints(otherLine.Start, otherLine.End);
return false;
}
public bool GetIntersectionLineForRay(Rect rectangle, out LineEquation intersectionLine){
if (Start == End){
intersectionLine = null;
return false;
}
IEnumerable<LineEquation> lines = rectangle.GetLinesForRectangle();
intersectionLine = new LineEquation(new Point(0, 0), new Point(0, 0));
var intersections = new Dictionary<LineEquation, Point>();
foreach (LineEquation equation in lines){
Point point;
if (IntersectWithSegementOfLine(equation, out point))
intersections[equation] = point;
}
if (!intersections.Any())
return false;
var intersectionPoints = new SortedDictionary<double, Point>();
foreach (var intersection in intersections){
if (End.IsBetweenTwoPoints(Start, intersection.Value) ||
intersection.Value.IsBetweenTwoPoints(Start, End)){
double distanceToPoint = Start.DistanceToPoint(intersection.Value);
intersectionPoints[distanceToPoint] = intersection.Value;
}
}
if (intersectionPoints.Count == 1){
Point endPoint = intersectionPoints.First().Value;
intersectionLine = new LineEquation(Start, endPoint);
return true;
}
if (intersectionPoints.Count == 2){
Point start = intersectionPoints.First().Value;
Point end = intersectionPoints.Last().Value;
intersectionLine = new LineEquation(start, end);
return true;
}
return false;
}
public override string ToString(){
return "[" + Start + "], [" + End + "]";
}
}
full sample is described [here][1]
I am trying to implement the algorithm described in the following http://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf
I am using the following class libraries. Loyc libs come from http://core.loyc.net/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Device.Location;
using Loyc.Collections;
using Loyc.Geometry;
using Loyc;
Here is the basic class
public class Hulls
{
private static List<Point<double>> KNearestNeighbors(List<Point<double>> points, Point<double> currentPoint, int k, out int kk)
{
kk = Math.Min(k, points.Count - 1);
var ret = points
.OrderBy(x => PointMath.Length(new Vector<double>(currentPoint.X - x.X, currentPoint.Y - x.Y)))
.Take(k)
.ToList();
return ret;
}
private static double Angle(Point<double> a, Point<double> b)
{
var ret = -Math.Atan2(b.Y - a.Y, b.X - a.X);
return NormaliseAngle(ret);
}
private static double NormaliseAngle(double a)
{
//while (a < b - Math.PI) a += Math.PI * 2;
//while (b < a - Math.PI) b += Math.PI * 2;
if (a < 0.0) { return a + Math.PI + Math.PI; }
return a;
}
private static List<Point<double>> SortByAngle(List<Point<double>> kNearest, Point<double> currentPoint, double angle)
{
//kNearest
// .Sort((v1, v2) => AngleDifference(angle, Angle(currentPoint, v1)).CompareTo(AngleDifference(angle, Angle(currentPoint, v2))));
//return kNearest.ToList();
kNearest = kNearest.OrderByDescending(x => NormaliseAngle(Angle(currentPoint, x) - angle)).ToList();
return kNearest;
}
private static bool CCW(Point<double> p1, Point<double> p2, Point<double> p3)
{
var cw = ((p3.Y - p1.Y) * (p2.X - p1.X)) - ((p2.Y - p1.Y) * (p3.X - p1.X));
return cw > 0 ? true : cw < 0 ? false : true; // colinear
}
private static bool _Intersect(LineSegment<double> seg1, LineSegment<double> seg2)
{
return CCW(seg1.A, seg2.A, seg2.B) != CCW(seg1.B, seg2.A, seg2.B) && CCW(seg1.A, seg1.B, seg2.A) != CCW(seg1.A, seg1.B, seg2.B);
}
private static bool Intersect(LineSegment<double> seg1, LineSegment<double> seg2)
{
if ((seg1.A.X == seg2.A.X && seg1.A.Y == seg2.A.Y)
|| (seg1.B.X == seg2.B.X && seg1.B.Y == seg2.B.Y))
{
return false;
}
if (_Intersect(seg1, seg2))
{
return true;
}
return false;
}
public IListSource<Point<double>> KNearestConcaveHull(List<Point<double>> points, int k)
{
points.Sort((a, b) => a.Y == b.Y ? (a.X > b.X ? 1 : -1) : (a.Y >= b.Y ? 1 : -1));
Console.WriteLine("Starting with size {0}", k.ToString());
DList<Point<double>> hull = new DList<Point<double>>();
var len = points.Count;
if (len < 3) { return null; }
if (len == 3) { return hull; }
var kk = Math.Min(Math.Max(k, 3), len);
var dataset = new List<Point<double>>();
dataset.AddRange(points.Distinct());
var firstPoint = dataset[0];
hull.PushFirst(firstPoint);
var currentPoint = firstPoint;
dataset.RemoveAt(0);
double previousAngle = 0;
int step = 2;
int i;
while ((currentPoint != firstPoint || step == 2) && dataset.Count > 0)
{
if (step == 5) { dataset.Add(firstPoint); }
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out kk);
var cPoints = SortByAngle(kNearest, currentPoint, previousAngle);
var its = true;
i = 0;
while (its == true && i < cPoints.Count)
{
i++;
int lastPoint = 0;
if (cPoints[i - 1] == firstPoint)
{
lastPoint = 1;
}
int j = 2;
its = false;
while (its == false && j < hull.Count - lastPoint)
{
LineSegment<double> line1 = new LineSegment<double>(hull[step - 2], cPoints[i - 1]);
LineSegment<double> line2 = new LineSegment<double>(hull[step - 2 - j], hull[step - 1 - j]);
//its = LineMath.ComputeIntersection(line1, line2, out pfrac, LineType.Segment);
its = Intersect(line1, line2);
j++;
}
}
if (its == true)
{
return KNearestConcaveHull(points, kk + 1);
}
currentPoint = cPoints[i - 1];
hull.PushLast(currentPoint);
previousAngle = Angle(hull[step - 1], hull[step - 2]);
dataset.Remove(currentPoint);
step++;
}
bool allInside = true;
i = dataset.Count;
while (allInside == true && i > 0)
{
allInside = PolygonMath.IsPointInPolygon(hull, dataset[i - 1]);
i--;
}
if (allInside == false) { return KNearestConcaveHull(points, kk + 1); }
return hull;
}
}
The above is supposed to pick a new edge for the boundary based on the furthest right-hand turn from the previous edge going around the point set counterclockwise. The code seems to pick the correct first edge from the initial vertex which has the lowest y-value, but then does not pick the next edge correctly when the offset angle is nonzero. I think the issue is the SortByAngle or Angle. -atan2 would return the clockwise turn, correct? Possibly I should be adding the offset angle?
EDIT (SOLUTION): Found the issue after following Eric's helpful advice provided in the first comment to the question. It was SortByAngle and Angle:
private static double Angle(Point<double> a, Point<double> b)
{
var ret = Math.Atan2(b.Y - a.Y, b.X - a.X);
return NormaliseAngle(ret);
}
private static double NormaliseAngle(double a)
{
if (a < 0.0) { return a + Math.PI + Math.PI; }
return a;
}
private static List<Point<double>> SortByAngle(List<Point<double>> kNearest, Point<double> currentPoint, double angle)
{
//kNearest = kNearest.OrderByDescending(x => NormaliseAngle(Angle(currentPoint, x) - angle)).ToList();
kNearest.Sort((a, b) => NormaliseAngle(Angle(currentPoint, a) - angle) > NormaliseAngle(Angle(currentPoint, b) - angle) ? 1 : -1);
return kNearest;
}
You have some bug:
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out kk);
Change the kk to just some var. You override the incrementation of that "kk" value, and then you're getting StackOverflow exceptions.
Change your code to the following:
int someVal;
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out someVal);
similarly to the question: Inverted beta in MySQL I need to use the BetaInv function inside a SQL Server stored procedure.
function is described here: Excel's BETAINV
is anybody aware of anything similar in TSQL or would you wrap it in a CLR .NET managed SQL user defined function?
I really need to use it within the stored procedure and not as post executing code in the C# side after data has been retrieved with the stored procedure because I should keep all logic on the db server for better reuse.
can I assume that a .NET managed udf running in the SQL Server would perform as fast as a normal native TSQL function?
Thanks!
I've in the end implemented the whole function myself, here the source code in case somebody needs it:
public static class UDFs
{
private const int MAXIT = 100;
private const double EPS = 0.0000003;
private const double FPMIN = 1.0E-30;
[SqlFunction(Name = "BetaInv", DataAccess = DataAccessKind.Read)]
public static SqlDouble BetaInv(SqlDouble p, SqlDouble alpha, SqlDouble beta, SqlDouble A, SqlDouble B)
{
return InverseBeta(p.Value, alpha.Value, beta.Value, A.Value, B.Value);
}
private static double InverseBeta(double p, double alpha, double beta, double A, double B)
{
double x = 0;
double a = 0;
double b = 1;
double precision = Math.Pow(10, -6); // converge until there is 6 decimal places precision
while ((b - a) > precision)
{
x = (a + b) / 2;
if (IncompleteBetaFunction(x, alpha, beta) > p)
{
b = x;
}
else
{
a = x;
}
}
if ((B > 0) && (A > 0))
{
x = x * (B - A) + A;
}
return x;
}
private static double IncompleteBetaFunction(double x, double a, double b)
{
double bt = 0;
if (x <= 0.0)
{
return 0;
}
if (x >= 1)
{
return 1;
}
bt = System.Math.Exp(Gammln(a + b) - Gammln(a) - Gammln(b) + a * System.Math.Log(x) + b * System.Math.Log(1.0 - x));
if (x < ((a + 1.0) / (a + b + 2.0)))
{
// Use continued fraction directly.
return (bt * betacf(a, b, x) / a);
}
else
{
// Use continued fraction after making the symmetry transformation.
return (1.0 - bt * betacf(b, a, 1.0 - x) / b);
}
}
private static double betacf(double a, double b, double x)
{
int m, m2;
double aa, c, d, del, h, qab, qam, qap;
qab = a + b; // These q’s will be used in factors that occur in the coe.cients (6.4.6).
qap = a + 1.0;
qam = a - 1.0;
c = 1.0; // First step of Lentz’s method.
d = 1.0 - qab * x / qap;
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
d = 1.0 / d;
h = d;
for (m = 1; m <= MAXIT; ++m)
{
m2 = 2 * m;
aa = m * (b - m) * x / ((qam + m2) * (a + m2));
d = 1.0 + aa * d; //One step (the even one) of the recurrence.
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
h *= d * c;
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
d = 1.0 + aa * d; // Next step of the recurrence (the odd one).
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
del = d * c;
h *= del;
if (System.Math.Abs(del - 1.0) < EPS)
{
// Are we done?
break;
}
}
if (m > MAXIT)
{
return 0;
}
else
{
return h;
}
}
public static double Gammln(double xx)
{
double x, y, tmp, ser;
double[] cof = new double[] { 76.180091729471457, -86.505320329416776, 24.014098240830911, -1.231739572450155, 0.001208650973866179, -0.000005395239384953 };
y = xx;
x = xx;
tmp = x + 5.5;
tmp -= (x + 0.5) * System.Math.Log(tmp);
ser = 1.0000000001900149;
for (int j = 0; j <= 5; ++j)
{
y += 1;
ser += cof[j] / y;
}
return -tmp + System.Math.Log(2.5066282746310007 * ser / x);
}
}
}
as you can see in the code, the SqlFunction is calling the InverseBeta private method which does the job using couple of other methods.
results are the same of Excel.BetaInv up to 5 or 6 digits after comma.