limits line segment to the boundry of rectangle
I use the following code to extend line segment to the boundry of rectangle, it work well if points within the rectangle, but if there's a point out of the rectangle boundry it fail
static void extend(Rectangle bounds, ref PointF start, ref PointF end)
{
if (start != end) // this to avoid small changes in orientation
开发者_开发技巧 {
float slope = (end.Y - start.Y) / (end.X - start.X);
if (Math.Round(start.Y, 2) == Math.Round(end.Y, 2) || Math.Abs(slope) <= 0.01d) // 0.01 is offset to check if the slope is very small
{
start.X = bounds.X;
start.Y = start.Y;
end.X = bounds.X + bounds.Width;
end.Y = end.Y;
return;
}
if (Math.Round(start.X, 2) == Math.Round(end.X, 2) || Math.Abs(slope) <= 0.01d)
{
start.X = start.X;
start.Y = bounds.Y;
end.X = end.X;
end.Y = bounds.Y + bounds.Height;
return;
}
// based on (y - y1) / (x - x1) == (y2 - y1) / (x2 - x1)
// => (y - y1) * (x2 - x1) == (y2 - y1) * (x - x1)
// y_for_xmin = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1)
float y_for_xmin = start.Y + ((end.Y - start.Y) * (bounds.X - start.X) / (end.X - start.X));
// y_for_xmax = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1)
float y_for_xmax = start.Y + ((end.Y - start.Y) * (bounds.X + bounds.Width - start.X) / (end.X - start.X));
// x_for_ymin = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1)
float x_for_ymin = start.X + ((end.X - start.X) * (bounds.Y - start.Y) / (end.Y - start.Y));
//x_for_ymax = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1)
float x_for_ymax = start.X + ((end.X - start.X) * (bounds.Y + bounds.Height - start.Y) / (end.Y - start.Y));
if ((bounds.Y <= y_for_xmin) && (y_for_xmin <= bounds.Y + bounds.Height))
{
if ((bounds.X <= x_for_ymax) && (bounds.X <= bounds.X + bounds.Width))
{
start.X = bounds.X;
start.Y = y_for_xmin;
end.X = x_for_ymax;
end.Y = bounds.Y + bounds.Height;
return;
}
if ((bounds.X <= x_for_ymin && x_for_ymin <= bounds.X + bounds.Width))
{
start.X = bounds.X;
start.Y = y_for_xmin;
end.X = x_for_ymin;
end.Y = bounds.Y;
return;
}
}
if ((bounds.Y <= y_for_xmax) && (bounds.Y <= bounds.Y + bounds.Height))
{
if ((bounds.X <= x_for_ymin) && (x_for_ymin <= bounds.X + bounds.Width))
{
start.X = x_for_ymin;
start.Y = bounds.Y;
end.X = bounds.X + bounds.Width;
end.Y = y_for_xmax;
return;
}
if ((bounds.X <= x_for_ymax) && (x_for_ymax <= bounds.X + bounds.Width))
{
start.X = x_for_ymax;
start.Y = bounds.Y + bounds.Height;
end.X = bounds.X + bounds.Width;
end.Y = y_for_xmax;
return;
}
}
}
}
any idea how to solve the case of points of line out of the rectangle
static bool intersection(PointF a1, PointF a2, PointF b1, PointF b2, ref PointF ans)
{
float x = ((a1.X*a2.Y - a1.Y*a2.X)*(b1.X - b2.X) - (a1.X - a2.X)*(b1.X*b2.Y - b1.Y*b2.X)) / ((a1.X - a2.X)*(b1.Y - b2.Y) - (a1.Y - a2.Y)*(b1.X - b2.X));
float y = ((a1.X*a2.Y - a1.Y*a2.X)*(b1.Y - b2.Y) - (a1.Y - a2.Y)*(b1.X*b2.Y - b1.Y*b2.X)) / ((a1.X - a2.X)*(b1.Y - b2.Y) - (a1.Y - a2.Y)*(b1.X - b2.X));
if(x == float.NaN || x == float.PositiveInfinity || x == float.NegativeInfinity || y == float.NaN || y == float.PositiveInfinity || y == float.NegativeInfinity)
{ // the lines are equal or never intersect
return false;
}
ans.X = x;
ans.Y = y;
return true;
}
static void extend(Rectangle bounds, ref PointF start, ref PointF end)
{
List<PointF> ansFinal = new List<PointF>();
PointF ansLeft = new PointF();
bool hitLeft = intersection(start, end, new PointF(bounds.X, bounds.Y), new PointF(bounds.X, bounds.Y + bounds.Height), ansLeft);
if(hitLeft && (ansLeft.Y < bounds.Y || ansLeft.Y > bounds.Y + bounds.Height)) hitLeft = false;
if(hitLeft) ansFinal.Add(ansLeft);
PointF ansTop = new PointF();
bool hitTop = intersection(start, end, new PointF(bounds.X, bounds.Y), new PointF(bounds.X + bounds.Width, bounds.Y), ansTop);
if(hitTop && (ansTop.X < bounds.X || ansTop.X > bounds.X + bounds.Width)) hitTop = false;
if(hitTop) ansFinal.Add(ansTop);
PointF ansRight = new PointF();
bool hitRight = intersection(start, end, new PointF(bounds.X + bounds.Width, bounds.Y), new PointF(bounds.X + bounds.Width, bounds.Y + bounds.Height), ansRight);
if(hitRight && (ansRight.Y < bounds.Y || ansRight.Y > bounds.Y + bounds.Height)) hitRight = false;
if(hitRight) ansFinal.Add(ansRight);
PointF ansBottom = new PointF();
bool hitBottom = intersection(start, end, new PointF(bounds.X, bounds.Y + bounds.Height), new PointF(bounds.X + bounds.Height, bounds.Y + bounds.Height), ansBottom);
if(hitBottom && (ansBottom.X < bounds.X || ansBottom.X > bounds.X + bounds.Width)) hitBottom = false;
if(hitBottom) ansFinal.Add(ansBottom);
if(!hitLeft && !hitTop && !hitRight && !hitBottom)
{
throw new Exception("No interections");
}
/*
// IF YOU HAD LINQ
PointF[] ans = ansFinal.Distinct().ToArray();
if(ans.Length < 2)
{
throw new Exception("Corner case *wink*");
}
start.X = ans[0].X; start.Y = ans[0].Y;
end.X = ans[1].X; end.Y = ans[1].Y;
*/
// the following is sufficient to cull out corner to corner, one corner
for(int x=ansFinal.Count-1; x>=1; x--)
if(ansFinal[x] == ansFinal[x-1])
ansFinal.RemoveAt(x);
if(ansFinal.Count < 2)
{
throw new Exception("Corner case *wink*");
}
start.X = ansFinal[0].X; start.Y = ansFinal[0].Y;
end.X = ansFinal[1].X; end.Y = ansFinal[1].Y;
}
EDIT I wrote this inside the browser so there may be a few syntax errors...
The concept is you test using Line to Line intersection with each side of the rectangle. If an intersection exists you make sure it's within the bounds of the rectangles side (line segment).
精彩评论