I have a following problem and I will try to explain it.
I have a huge, political world map. I want to get a shape of selected country (in example Ghana, Nepal or Poland). How can I do that?
It depends what other information you are able to provide in advance. From your question and comments it sounds like you plan to predefine your shapes so the problem isn't too difficult. If you define each region by a set of points it's just a matter of checking if the selection (presumably a mouse click or similar) is within a polygon. There are a number of ways to do this. I think I've used one of the answers to the following question:
C# Point in polygon
Something like:
public static bool IsInPolygon(Point[] poly, Point clickedPoint)
{
if (poly.Length < 3)
{
return false;
}
Point p1, p2;
bool inside = false;
Point oldPoint = new Point(poly[poly.Length - 1].X, poly[poly.Length - 1].Y);
for (int i = 0; i < poly.Length; i++)
{
Point newPoint = new Point(poly[i].X, poly[i].Y);
if (newPoint.X > oldPoint.X)
{
p1 = oldPoint;
p2 = newPoint;
}
else
{
p1 = newPoint;
p2 = oldPoint;
}
if ((newPoint.X < clickedPoint.X) == (clickedPoint.X <= oldPoint.X)
&& (clickedPoint.Y - (long)p1.Y) * (p2.X - p1.X) < (p2.Y - (long)p1.Y) *(clickedPoint.X - p1.X))
{
inside = !inside;
}
oldPoint = newPoint;
}
return inside;
}
I haven't tested the above code so I'd make sure to test it properly if you use it.
If you can't predefine the shapes then you'll probably have to use some sort of analysis method to pick out the shapes. If you use a clean map with solid lines it won't be too difficult. Any kind of flood fill algorithm should be able to pick out individual countries (obviously you'd have to deal with special cases like when a country is made of two distinct regions). From there getting a set of points from each shape can be done using a simple marching squares algorithm. If desired, you can then reduce the number of points depending on the level of accuracy you need.
Related
I'm trying to get the corners of the following shape:
By corners I mean this (red dots):
The minimum quantity of points that can define this shape.
And I have implemented the following:
public Shape Optimize()
{
// If the vertices are null or empty this can't be executed
if (vertices.IsNullOrEmpty())
return this; // In this case, return the same instance.
if (!edges.IsNullOrEmpty())
edges = null; //Reset edges, because a recalculation was requested
// The corners available on each iteration
var corners = new Point[] { Point.upperLeft, Point.upperRight, Point.downLeft, Point.downRight };
//The idea is to know if any of the following or previous vertice is inside of the the array from upside, if it is true then we can add it.
Point[] vs = vertices.ToArray();
for (int i = 0; i < vertices.Count - 1; ++i)
{
Point backPos = i > 0 ? vs[i - 1] : vs[vertices.Count - 1],
curPos = vs[i], //Punto actual
nextPos = i < vertices.Count - 1 ? vs[i + 1] : vs[0];
// We get the difference point between the actual point and the back & next point
Point backDiff = backPos - curPos,
nextDiff = nextPos - curPos,
totalDiff = nextPos - backPos;
if (corners.Contains(backDiff) || corners.Contains(nextDiff) || corners.Contains(totalDiff))
AddEdge(curPos, center); // If any of the two points are defined in the corners of the point of before or after it means that the actual vertice is a edge/corner
}
return this;
}
This works rectangled shapes, but rotated shapes are very sharp, so, this code doesn't work well:
Blue pixels (in this photo and the following) are the vertices variable processed on Optimize method.
Green pixels are the detected corners/edges (on both photos).
But sharpness in a shape only defines the side inclination, so what can I do to improve this?
Also, I have tested Accord.NET BaseCornersDetector inherited classes, but the best result is obtained with HarrisCornersDetector, but:
Many edges/corners are innecesary, and they aren't in the needed place (see first photo).
Well, after hours of research I found a library called Simplify.NET that internally runs the Ramer–Douglas–Peucker algorithm.
Also, you maybe interested on the Bresenham algorithm, with this algorithm you can draw a line using two Points.
With this algorithm, you can check if your tolerance is too high, comparing the actual points and the points that this algorithm outputs and making some kind of percentage calculator of similarity.
Finally, is interesting to mention Concave Hull and Convex Hull algorithms.
All this is related to Unity3D.
My outputs:
And my implementation.
It's very important to say, that points needs to be sorted forcing them to be connected. If the shape is concave as you can see on the second photo maybe you need to iterate walls of the shape.
You can see an example of implementation here. Thanks to #Bunny83.
is there a way to get the rectangles of the stackcolumn chart bar?
this code snippet is how it can be works but it's very ugly:
var points = new List<Point>();
for (int x = 0; x < chart.Size.Width; x++)
{
for (int y = 0; y < chart.Size.Height; y++)
{
var hp = chart.HitTest(x, y, false, ChartElementType.DataPoint);
var result = hp.Where(h => h.Series?.Name == "Cats");
if (result.Count() > 0)
{
points.Add(new Point(x, y));
}
}
}
var bottomright = points.First();
var topleft = points.Last();
I will try to describe my purpose:
I would like to create a chart from various testresults and make this available as a HTML file. This generated Chart is inserted as an image file in the HTML document. Now, I would like to link each part of a Bar area from the Chart to an external document. Since the graphics is static, I have only the possibility to use the "MAP Area" element to make any area as a link from HTML. The "map" element requires a "rectangle", or these coordinates. That's the reason why I need the coordinator of each part of a Bar.
I have to mention that I am not really familiar with the Chart control yet.
The graphics is generated testweise.
[SOLVED]
i got the solution:
var stackedColumns = new List<Tuple<string,string,Rectangle>>();
for (int p = 0; p < chart.Series.Select(sm => sm.Points.Count).Max(); p++)
{
var totalPoints = 0;
foreach (var series in chart.Series)
{
var width = int.Parse(series.GetCustomProperty("PixelPointWidth"));
var x = (int)area.AxisX.ValueToPixelPosition(p + 1) - (width / 2);
int y = (int)area.AxisY.ValueToPixelPosition(totalPoints);
totalPoints += series.Points.Count > p ? (int)series.Points[p].YValues[0] : 0;
int y_total = (int)area.AxisY.ValueToPixelPosition(totalPoints);
var rect = new Rectangle(x, y_total, width, Math.Abs(y - y_total));
stackedColumns.Add(new Tuple<string, string, Rectangle>(series.Name, series.Points.ElementAtOrDefault(p)?.AxisLabel, rect));
}
}
this workaround works for stackedcolumn and points starts at x-axis=0.
just the PixelPointWidth property has to be set manualy to get the right width. i have not yet found a way to get the default bar width..
This is extremely tricky and I really wish I knew how to get the bounds from some chart functionionality!
You code snippet is actulally a good start for a workaround. I agree though that it has issues:
It is ugly
It doesn't always work
It has terrible performance
Let's tackle these issues one by one:
Yes it is ugly, but then that's the way of workarounds. My solution is even uglier ;-)
There are two things I found don't work:
You can't call a HitTest during a Pre/PostPaint event or terrible things will happen, like some Series go missing, SO exceptions or other crashes..
The result for the widths of the last Series are off by 1-2 pixels.
The performance of testing each pixel in the chart will be terrible even for small charts, but gets worse and worse when you enlarge the chart. This is relatively easy to prevent, though..:
What we are searching are bounding rectangles for each DataPoint of each Series.
A rectangle is defined by left and right or width plus top and bottom or height.
We can get precise values for top and bottom by using the axis function ValueToPixelPosition feeding in the y-value and 0 for each point. This is simple and cheap.
With that out of the way we still need to find the left and right edges of the points. To do so all we need to do it test along the zero-line. (All points will either start or end there!)
This greatly reduces the number of tests.
I have decided to do the testing for each series separately, restaring at 0 each time. For even better performance one could do it all in one go.
Here is a function that returns a List<Rectangle> for a given Series:
List<Rectangle> GetColumnSeriesRectangles(Series s, Chart chart, ChartArea ca)
{
ca.RecalculateAxesScale();
List<Rectangle> rex = new List<Rectangle>();
int loff = s == chart.Series.Last() ? 2 : 0; ;
int y0 = (int)ca.AxisY.ValueToPixelPosition(0);
int left = -1;
int right = -1;
foreach (var dp in s.Points)
{
left = -1;
int delta = 0;
int off = dp.YValues[0] > 0 ? delta : -delta;
for (int x = 0; x < chart.Width; x++)
{
var hitt = chart.HitTest(x, y0 +off );
if (hitt.ChartElementType == ChartElementType.DataPoint &&
((DataPoint)hitt.Object) == dp)
{
if (left < 0) left = x;
right = x;
}
else if (left > 0 && right > left) break;
}
int y = (int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]);
rex.Add(new Rectangle(left, Math.Min(y0, y),
right - left + 1 - loff, Math.Abs(y - y0)));
left = -1;
}
return rex;
}
A few notes:
I start by doing a RecalculateAxesScale because we can't Hittest before the current layout has been calculated.
I use a helper variable loff to hold the offset for the width in the last Series.
I start searching at the last x coordinate as the points should all lie in sequence. If they don't because you have used funny x-values or inserted points you may need to start at 0 instead..
I use y0 as the baseline of the zero values for both the hittesting y and also the points' base.
I use a little Math to get the bounds right for both positive and negative y-values.
Here is a structure to hold those rectangles for all Series and code to collect them:
Dictionary<string, List<Rectangle>> ChartColumnRectangles = null;
Dictionary<string, List<Rectangle>> GetChartColumnRectangles(Chart chart, ChartArea ca)
{
Dictionary<string, List<Rectangle>> allrex = new Dictionary<string, List<Rectangle>>();
foreach (var s in chart.Series)
{
allrex.Add(s.Name, GetColumnSeriesRectangles(s, chart, ca));
}
return allrex;
}
We need to re-calculate the rectangles whenever we add points or resize the chart; also whenever the axis view changes. The common code for AxisViewChanged, ClientSizeChanged, Resize and any spot you add or remove points could look like this:
Chart chart= sender as Chart;
GetChartColumnRectangles(chart, chart.ChartAreas[0]);
Let's test the result with a Paint event:
private void chart1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
chart1.ApplyPaletteColors();
foreach (var kv in ChartColumnRectangles)
{
{
foreach (var r in kv.Value)
g.DrawRectangle(Pens.Black, r);
}
}
}
Here it is in action:
Well, I've been down this path and the BIG issue for me is that the custom property of 'PixelPointWidth' is just that - it is custom. You cannot retrieve it unless you've set it. I needed the width of the item - had to scwag/calculate it myself. Keep in mind that many charts can be panned/zoomed, so once you go down this path, then you need to recalculate it and set it for the chart prepaint events.
Here is a crude little function I made (is more verbose than needed - for educational purposes and has no error handling :)):
private int CalculateChartPixelPointWidth(Chart chart, ChartArea chartArea, Series series)
{
// Get right side - takes some goofy stuff - as the pixel location isn't available
var areaRightX = Math.Round(GetChartAreaRightPositionX(chart, chartArea));
var xValue = series.Points[0].XValue;
var xPixelValue = chartArea.AxisX.ValueToPixelPosition(xValue);
var seriesLeftX = chart.Location.X + xPixelValue;
var viewPointWidth = Math.Round((areaRightX - seriesLeftX - (series.Points.Count * 2)) / series.Points.Count, 0);
return Convert.ToInt32(viewPointWidth);
}
And this as well:
private double GetChartAreaRightPositionX(Chart chart, ChartArea area)
{
var xLoc = chart.Location.X;
return xLoc + (area.Position.Width + area.Position.X) / 100 * chart.Size.Width;
}
The reason I'm calculating this is because I need to draw some graphical overlays on top of the normal chart item objects (my own rendering for my own purposes).
In the 'prepaint' event for the chart, I need to calculate the 'PixelPointWidth' that matches the current chart view (might be panned/zoomed). I then use that value to SET the chart custom property to match . . . such that the normal chart entities and MINE are correctly aligned/scaled (ensures we're in exactly the right 'x' axis position):
In my prepaint event, I do the following - just prior to drawing my graphical entities:
// Pretty close scwag . . .
var viewPointWidth = CalculateChartPixelPointWidth(e.Chart, e.Chart.ChartAreas[0], e.Chart.Series[0]);
// Set the custom property and use the same point width for my own entities . .
chart1.Series[0].SetCustomProperty("PixelPointWidth", viewPointWidth.ToString("D"));
// . . . now draw my entities below . . .
We are currently using the following algorithm to detect if a geographic point is inside a complex polygon or not. This works fine, except when the polygon crosses the 180° longitude line.
For example the point (-170, 60) is not detected in polygon 160,65,0 160,15,0 -160,15,0 -160,65,0 160,65,0
Look at the following image:
[Img]http://tinypic.com/r/14x2xl1[/img]
I want everything in the red box. Not the yellow box!
public static bool IsCoordinateInPolygon(IList<KMLCoordinate> polygon, KMLCoordinate testPoint)
{
bool result = false;
int j = polygon.Count - 1;
for (int i = 0; i < polygon.Count; i++)
{
if (polygon[i].Latitude < testPoint.Latitude && polygon[j].Latitude >= testPoint.Latitude || polygon[j].Latitude < testPoint.Latitude && polygon[i].Latitude >= testPoint.Latitude)
{
if (polygon[i].Longitude + (testPoint.Latitude - polygon[i].Latitude) / (polygon[j].Latitude - polygon[i].Latitude) * (polygon[j].Longitude - polygon[i].Longitude) < testPoint.Longitude)
{
result = !result;
}
}
j = i;
}
return result;
}
Does anybody have a better algorithm?
Spheric coordinate system has it quirks
To avoid them use 3D orthogonal/orthonormal cartesian coordinate system instead
convert your polygon vertexes and geolocation
so (long,lat,alt) -> (x,y,z). here you find how to do it. You do not need to apply local transform just the first spheric to 3D cartesian transformation (bullet #1.)
use any inside polygon test ...
I usually count the number of intersections between line cast from your geolocation to any direction and polygon boundary lines.
if it is odd then point is inside
if it is even then the point is outside
if point lies on any line of polygon then it is inside
if your casted line hits any vertex then either take in mind (do not count multiple hits at this vertex) or change the direction a bit and try again
[Notes]
Do not forget to handle all as 3D vectors not 2D !!!
I have a canvas where there are several polygons, what I want to do is try detect whether the polygons are overlapping. I'v looked around on various websites and most of what i'v found is to do with object collision - this for example, my polygons aren't moving so that's not going to be an issue.
I was wondering if someone could point me in the right direction on how to detect if they are overlapping. Is there a method that can calculate the space that's used on screen? or the region of the Polygon to compare the two?
So for example like the mock up here, the red shape overlaps the green one.
essentially all i want is to say yes they are overlapping or no they are not.
http://peterfleming.net84.net/Slice%201.png
Thanks in advance.
Pete
This library here (free and open source) will show polygon clipping: http://www.angusj.com/delphi/clipper.php
That said, if by polygons overlapping you mean at least one point of one is inside the other, you can test each polygon's point against the others by either looking at the point in point polygon problem or checking each polygons lines to see if it cuts across another polygon.
These methods will all work with different efficiency, try and see what's best for your situation.
However, your diagram seems to suggest you want to see if these polygons are 'side by side' or something similar. It would help to get clarification on this. Overlapping generally needs some coordinate plan to determine overlap against.
Assuming that each polygon is a Shape (either Path or Polygon) you could use the FillContainsWithDetail method of their RenderedGeometry to pairwise check interscetion.
I was having the same problem too and I used this implementation (which is heavenly inspired by this: C# Point in polygon ):
bool DoesPolygonsOverlap(IList<Point> firstPolygon, IList<Point> secondPolygon)
{
foreach (var item in firstPolygon)
{
if (IsPointInPolygon(secondPolygon, item))
{
return true;
}
}
foreach (var item in secondPolygon)
{
if (IsPointInPolygon(firstPolygon, item))
{
return true;
}
}
return false;
}
bool IsPointInPolygon(IList<Point> polygon, Point testPoint)
{
bool result = false;
int j = polygon.Count() - 1;
for (int i = 0; i < polygon.Count(); i++)
{
if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
{
if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
{
result = !result;
}
}
j = i;
}
return result;
}
Attention: The function was not very much tested and has a big potential for improvement. Please tell me if you find a bug/problem.
I've simplified things down to cubes/a single cube colliding with an infinite-mass rectangle and the following code:
The problem is, the boxes tend to spin too much and get stuck together spinning and, if the binary search is included, just hit and spin a lot.
Thanks for any and all help.
/// <summary>
/// Projects an abstract 1D line "perpendicular" to the axis,
/// stretching across the width of the model,
/// measured from that axis.
/// </summary>
/// <param name="Axis"></param>
/// <param name="Min"></param>
/// <param name="Max"></param>
protected virtual void ProjectToAxis(Vector2 Axis, IMotionData motionData, out double Min, out double Max)
{
Double DotP = Axis.Dot(motionData.PositionGS + (this.Vertices[0].Position * this.Model.Scale).Rotate(motionData.RotationGS));
Min = Max = DotP;
for (int t = 1; t < this.Vertices.Count(); ++t)
{
DotP = Axis.Dot(motionData.PositionGS + (this.Vertices[t].Position * this.Model.Scale).Rotate(motionData.RotationGS));
Min = Math.Min(DotP, Min);
Max = Math.Max(DotP, Max);
}
}
/// <summary>
/// Projects two imaginary lines even with each edge,
/// equal to the width of each object while looking at
/// that edge, then checks to see if they intersect.
/// </summary>
/// <param name="B1"></param>
/// <param name="B2"></param>
/// <returns></returns>
public static bool DetectCollision(Body B1, Body B2, Double elapsedSeconds)
{
CollisionData collisionInfo = new CollisionData();
double lowestDistance = double.MaxValue;
double distance;
Vector2 normalB1ToB2 = (B2.MotionHandler.PositionGS - B1.MotionHandler.PositionGS).Normalized;
foreach (Edge edge in B1.Edges)
{
if (edge.Normal.RelativePosition.Dot(normalB1ToB2) >= 0.0)
{
double minA, minB, maxA, maxB;
B1.ProjectToAxis(edge.Normal.RelativePosition, B1.MotionHandler.MotionDataGet, out minA, out maxA);
B2.ProjectToAxis(edge.Normal.RelativePosition, B2.MotionHandler.MotionDataGet, out minB, out maxB);
if (minA < minB)
distance = minB - maxA;
else
distance = minA - maxB;
if (distance > 0.0f)
return false;
else if (Math.Abs(distance) < lowestDistance)
{
lowestDistance = Math.Abs(distance);
collisionInfo.Normal = edge.Normal.RelativePosition;
collisionInfo.Edge = edge;
}
}
}
Vector2 normalB2ToB1 = -normalB1ToB2;
foreach (Edge edge in B2.Edges)
{
if (edge.Normal.RelativePosition.Dot(normalB2ToB1) >= 0.0)
{
double minA, minB, maxA, maxB;
B1.ProjectToAxis(edge.Normal.RelativePosition, B1.MotionHandler.MotionDataGet, out minA, out maxA);
B2.ProjectToAxis(edge.Normal.RelativePosition, B2.MotionHandler.MotionDataGet, out minB, out maxB);
if (minA < minB)
distance = minB - maxA;
else
distance = minA - maxB;
if (distance > 0.0f)
return false;
else if (Math.Abs(distance) < lowestDistance)
{
lowestDistance = Math.Abs(distance);
collisionInfo.Normal = edge.Normal.RelativePosition;
collisionInfo.Edge = edge;
}
}
}
collisionInfo.Depth = lowestDistance;
/* Double lowHighSeconds = elapsedSeconds;
Double highLowSeconds = 0.0;
Double seconds;
IMotionData md1;
IMotionData md2;
bool collision;
do
{
md1 = B1.MotionHandler.MotionDataLastGet.Copy;
md2 = B2.MotionHandler.MotionDataLastGet.Copy;
collision = true;
lowestDistance = Double.MaxValue;
seconds = MathExtensions.MathExt.Lerp(highLowSeconds, lowHighSeconds, 0.5);
B1.MotionHandler.Simulate(seconds, ref md1);
B2.MotionHandler.Simulate(seconds, ref md2);
normalB1ToB2 = (md2.PositionGS - md1.PositionGS).Normalized;
foreach (Edge edge in B1.Edges)
{
if ((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS).Dot(normalB1ToB2) >= 0.0)
{
double minA, minB, maxA, maxB;
B1.ProjectToAxis((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS), md1, out minA, out maxA);
B2.ProjectToAxis((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS), md2, out minB, out maxB);
if (minA < minB)
distance = minB - maxA;
else
distance = minA - maxB;
if (distance > 0.0f)
collision = false;
else if (Math.Abs(distance) < lowestDistance)
{
lowestDistance = Math.Abs(distance);
collisionInfo.Normal = (edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS);
collisionInfo.Edge = edge;
}
}
}
normalB2ToB1 = -normalB1ToB2;
foreach (Edge edge in B2.Edges)
{
if ((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS).Dot(normalB2ToB1) >= 0.0)
{
double minA, minB, maxA, maxB;
B2.ProjectToAxis((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS), md2, out minA, out maxA);
B1.ProjectToAxis((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS), md1, out minB, out maxB);
if (minA < minB)
distance = minB - maxA;
else
distance = minA - maxB;
if (distance > 0.0f)
collision = false;
else if (Math.Abs(distance) < lowestDistance)
{
lowestDistance = Math.Abs(distance);
collisionInfo.Normal = (edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS);
collisionInfo.Edge = edge;
}
}
}
collisionInfo.Depth = lowestDistance;
if (!collision)
{
lowHighSeconds = seconds;
}
else
{
highLowSeconds = seconds;
}
} while (Math.Abs(highLowSeconds - lowHighSeconds) > 0.0001);
B1.MotionHandler.MotionDataSet = md1;
B2.MotionHandler.MotionDataSet = md2; */
// bool flip = false;
if (collisionInfo.Edge.Parent != B2.Model)
{
Body temp = B1;
B1 = B2;
B2 = temp;
}
//This is needed to make sure that the collision normal is pointing at B1
int Sign = Math.Sign(
collisionInfo.Normal.Dot(
B1.MotionHandler.MotionDataGet.PositionGS + (B1.Center * B1.Model.Scale).Rotate(B1.MotionHandler.MotionDataGet.RotationGS) -
B2.MotionHandler.MotionDataGet.PositionGS + (B2.Center * B2.Model.Scale).Rotate(B2.MotionHandler.MotionDataGet.RotationGS)
)
);
//Remember that the line equation is N*( R - R0 ). We choose B2->Center
//as R0; the normal N is given by the collision normal
if (Sign != 1)
collisionInfo.Normal = -collisionInfo.Normal; //Revert the collision normal if it points away from B1
double SmallestD = double.MaxValue; //Initialize the smallest distance to a high value
//Measure the distance of the vertex from the line using the line equation
for (int t = 0; t < B1.Vertices.Count(); ++t)
{
double Distance = collisionInfo.Normal.Dot(B1.Vertices[t].WorldPosition - B2.Center);
// If the measured distance is smaller than the smallest distance reported
// so far, set the smallest distance and the collision vertex
if (Distance < SmallestD)
{
SmallestD = Distance;
collisionInfo.Vertex = B1.Vertices[t];
}
}
if ((Body.CollisionType & CollisionType.Velocity) > 0)
{
Vector2 vab1 = B1.MotionHandler.VelocityGS - B2.MotionHandler.VelocityGS;
Vector2 rap = (B1.MotionHandler.PositionGS - collisionInfo.Normal);
Vector2 rbp = (B2.MotionHandler.PositionGS - collisionInfo.Normal);
Double rap2 = (rap.Cross(collisionInfo.Normal));
Double rbp2 = (rbp.Cross(collisionInfo.Normal));
Vector2 one = (collisionInfo.Vertex.WorldPosition - B1.MotionHandler.PositionGS).GetPerpendicular;
Vector2 two = (collisionInfo.Vertex.WorldPosition - B2.MotionHandler.PositionGS).GetPerpendicular;
Double j = (-(1 + 0.0) * vab1.Dot(collisionInfo.Normal)) /
((collisionInfo.Normal.Dot(collisionInfo.Normal) * (B1.MotionHandler.InverseMassGS + B2.MotionHandler.InverseMassGS)) +
(one.Dot(one) * B1.MotionHandler.InverseInertiaGS) + (two.Dot(two) * B2.MotionHandler.InverseInertiaGS));
B1.MotionHandler.AddImpulse = new Force(
collisionInfo.Normal,
j /* ,
one */
);
B2.MotionHandler.AddImpulse = new Force(
collisionInfo.Normal,
-(j) /* ,
two */
);
NewtonianMotionData data1 = (NewtonianMotionData)B1.MotionHandler.MotionDataGet;
NewtonianMotionData data2 = (NewtonianMotionData)B2.MotionHandler.MotionDataGet;
data1.AngularVelocity += (one.Dot(j * collisionInfo.Normal)) * data1.inverseInertia;
data2.AngularVelocity += (two.Dot(-j * collisionInfo.Normal)) * data2.inverseInertia;
B1.MotionHandler.MotionDataSet = data1;
B2.MotionHandler.MotionDataSet = data2;
}
return true;
}
You've got two problems.
1) There's something wrong with the code. You need to fix that.
2) You don't know how to figure out what "something" is.
Solving the first problem is gated on your solving the second problem. You need to learn how to debug a program you just wrote.
You've already tested it and gotten a result which you've identified as nonsensical. That's a good first step. Now break it down even farther. Pick a simple problem in this domain that you can solve yourself with pencil and paper; do so, and then watch your algorithm solve the same problem in the debugger, checking every step along the way. Listen to quiet nagging doubts. When there is anything that looks slightly off or unexpected, stop what you're doing and investigate the issue until you understand whether things are working correctly or not. Eventually you'll find a step where things aren't as they should be, and that's where the bug is.
Yes, this is tedious. When you've found the bug and fixed it, pause and reflect upon what caused you to write the bug in the first place, and figure out how to not write that kind of bug ever again.
UPDATE:
Re: your recent comments.
Apology accepted. Now calm down. You're never going to find this bug if you're this worked up. Your brain will not let you. Humans who are in a panicky, worked-up state lose the ability to reason. That's why fire doors open outwards; humans fleeing a burning building literally will not stop to think "I'm pushing on this door and its not opening, maybe I should try pulling". They just push harder. I suspect you are pushing harder.
Debugging requires rationality and careful attention to small details. If you're all worked up about this problem then that's going to go out the window and its just going to get worse. Take it from someone who has been there. We've all been there. It's a deeply frustrating thing to have caused a bug in your own program that you then cannot find.
The reason no one is helping you is because... well, let me list the set of preconditions that have to be met for me to help you with more than vague platitudes and suggestions of how to focus your debugging efforts:
1) I have to know something about simulation of 3d physics. I had a pretty decent grasp of the differential equations of simple of Newtonian mechanics in 1992, but I haven't used it since. And the equation of a damped driven spring is rather different than the equations of rigid body collisions. If I spent a couple weeks reviewing my notes I could get the math back, but that's not realistic. You need someone who is deeply conversant right now with 3d collision physics simulations.
2) I have to be able to read and understand your code, code which is hundreds of lines long, written by someone other than me, to solve a problem I'm not familiar with. Worse, a hundred lines of that code is commented out. Why? Is it relevant? Is the bug in there? Moreover, I need to be able to read and understand the code without running it in a debugger. Heck, I can't even compile that code. It depends on libraries that I don't have.
And even worse still, one of those libraries might contain the bug. For all I know, the bug is a typo in some code that calculates a normal somewhere that you haven't shown us. The code shown could be perfect.
3) I need to have the free time to work on someone else's hard problem; a problem that the person who wrote the code and understands the physics is making no headway on.
All of these are requirements; if any one of them is missing, the reader cannot effectively help you. You're asking people you don't know to help you find a black cat in a dark warehouse at midnight without a flashlight -- a cat that might not even be there. It's not surprising you're getting few takers. Of the 74 stack overflow users who have read your question, how many of them meet all three requirements? I meet none of them.
If you want help on this site then post an easier problem. Narrow the problem down to a problem that requires less special knowledge of physics and simulation algorithms and has only the relevant code, preferably code that can be compiled and run.
This may not be good news, but I have a couple of things to add to Eric Lippert's analysis, and a suggestion.
Your comments are misleading. I know that if you're not familiar with math and physics it's hard to be precise, but take a look at "ProjectToAxis":
/// Projects an abstract 1D line "perpendicular" to the axis,
/// stretching across the width of the model,
/// measured from that axis.
Forgive me if this sounds harsh, but
"abstract 1d line" is kind of meaningless, it should just say "line".
It's not really projecting a line.
It's measuring extent parallel to the axis, not perpendicular to it.
It's not "across the width", exactly, it's just the greatest extent.
"measured from that axis" is either meaningless or wrong, I can't tell which.
Believe me, I'm not trying to pick nits, it's just that I'm trying to figure out what this code is supposed to do, and a bad comment is worse than none. I can see what this function does (assuming that functions like "Dot" work as advertised), but I still don't know whether it does what you want it to do.
Now I take a look at DetectCollision (which does more than just detect a collision):
/// Projects two imaginary lines even with each edge,
/// equal to the width of each object while looking at
/// that edge, then checks to see if they intersect.
What? All I can do is ignore this and look at the code... There are parts of it that don't make much sense (e.g. why the heck do you project a body onto every one of its edges?), so reverse-engineering is going to be very difficult.
If I knew the algorithm you were trying for, I could try to find the bug. If the code worked, I could try to deduce the algorithm. But if the code doesn't work and (as I suspect) you don't really know the algorithm yourself, we're kind of stuck.
Here's an approach that might work: This function is too long, it does a lot, and you don't know which parts it does correctly. Ergo you should break it down into several functions and test them individually. (I can't do that myself, for the reasons Eric Lippert spelled out.) You could start by breaking into two functions, one that calculates CollisionInfo (leaving the bodies constant) and another that adjusts the motion of the bodies (leaving CollisionInfo constant).