2005. március 12., szombat
Find the intersection of two polylines
Problem/Question/Abstract:
How to find the intersection of two polylines
Answer:
Solve 1:
You have to intersect each polygon segment set which has a collision of their overlapping rectangles defined by the start and end point of each segment except neigboring segments. That means m-1 * n-1 segments are possible. To make a fast overlapping (collision) set I use a xy hash tree based on quadtree decomposition of the segments. Here is the code for the line intersection:
{XYIntersect Container Intersection for 2 dimensional segments
XII/2001 TriplexWare; Written by A.Weidauer
Abstract: Representation for 2-dimensional segment intersections
Author: Alexander Weidauer (alex.weidauer@huckfinn.de)
Created: December 2001
Lastmod: December 2001
The Unit delivers a 2-dimensional segment intersection for several objects
represented by basic data types for I/O}
unit UXYIntersect;
interface
uses
UConst; {Basic datatype definitions. See further down the page.)
{The function checks a possible segmentation of two segments. The first is defined by
the coordinate set S1( P1(x1, y1): P2(x2, y2)) and the second is
defined S2( P3(x3, y3): P4(x4, y4)) where P1, P2, P3, P4 be the points.
OutX and OutY represent the intersection coordinates and are only valid if
the function turns back the value TRUE. If the segments are paralell the flag
is set to TRUE and if the the segmets are paralell and overlapping eachother
then OutX, OutY keeping the heavy point of the 4 coordinates sets.
In this case you have to check the intervall borders.
The solution of the intersection is NOT a point, it is a SEGMENT again.}
function Isec(x1, y1, x2, y2, x3, y3, x4, y4: TDouble; var OutX, OutY: TDouble;
var ParallelFlag: TBoolean): TBoolean;
implementation
function Isec(x1, y1, x2, y2, x3, y3, x4, y4: TDouble; var OutX, OutY: TDouble;
var ParallelFlag: TBoolean): TBoolean;
var
delta, rmu, l1, l2, l3: TDouble;
begin
OutY := 0;
OutX := 0;
ParallelFlag := False;
x2 := x2 - x1;
y2 := y2 - y1;
x4 := x4 - x3;
y4 := y4 - y3;
delta := x2 * y4 - y2 * x4;
{First case segments are paralell !}
if abs(delta) < 1 E - 8 then
begin
ParallelFlag := False;
x2 := x2 + x1;
x4 := x4 + x3;
y2 := y2 + y1;
y4 := y4 + y3;
l1 := sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
l2 := sqrt((x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1));
l3 := sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2));
if (l1 = l2 - l3) or (l1 = l2 + l3) then
begin
Result := true;
Parallelflag := true;
OutX := (x1 + x2 + x3 + x4) / 4;
OutY := (y1 + y2 + y3 + y4) / 4;
end
else
Result := False;
Exit;
end;
{End of parallel case}
rmu := ((x3 - x1) * y4 - (y3 - y1) * x4) / delta;
if (rmu > 1) or (rmu < 0) then
begin
isec := False;
Exit;
end;
OutX := x1 + RMU * x2;
Outy := y1 + RMU * y2;
x2 := x2 + x1;
x4 := x4 + x3;
y2 := y2 + y1;
y4 := y4 + y3;
if x1 > x2 then
SwapDouble(x1, x2);
if y1 > y2 then
SwapDouble(y1, y2);
if x3 > x4 then
SwapDouble(x3, x4);
if y3 > y4 then
SwapDouble(y3, y4);
{Rangecheck of the solution}
if (outx > x2) or (outx < x1) or (outx > x4) or (outx < x3) or (outy > y2)
or (outy < y1) or (outy > y4) or (outy < y3) then
begin
Result := False;
exit;
end;
Result := True;
end;
end.
{Basic Datatype and file extention definitions
XII/2001TriplaxWare; Written by A.Weidauer
Abstract: Basic Datatype and file extention definitions
Author: Alexander Weidauer (alex.weidauer@huckfinn.de)
Created: December 2001
Lastmod: December 2001
The Unit delivers basic data types for I/O and their file extentions.}
unit UConst;
interface
const
{registry destination set}
cStorageName = 'Software\tsWB';
{Maximal read buffer size for blocked bufferd reading}
cMaxReadBuffer = 32000;
{Maximal write buffer size for blocked bufferd writing}
cMaxWriteBuffer = 32000;
type
{Type encapsulation Boolean}
TBoolean = Boolean;
{Type encapsulation String}
TString = string;
{Type encapsulation Byte 8 Bit}
TByte = Byte;
{Type encapsulation Word 16 Bit}
TWord = Word;
{Type encapsulation LongWord 32 Bit}
TLongWord = LongWord;
{Type encapsulation SmallInt signed 8 Bit}
TInt08 = SmallInt;
{Type encapsulation Integer signed 16 Bit}
TInt16 = ShortInt;
{Type encapsulation Integer signed 32 Bit}
TInt32 = LongInt;
{Type encapsulation Integer signed 32 Bit as common integer}
TInteger = TInt32;
{Type encapsulation Integer signed 64 Bit}
TInt64 = Int64;
{Type encapsulation Single 4 Byte}
TSingle = Single;
{Type encapsulation Real 6 Byte}
TReal = Real48;
{Type encapsulation Double 8 Byte}
TDouble = Double;
{Type encapsulation Double 10 Byte}
TExtended = Extended;
{Swap data if a > b for String}
procedure SwapString(var a, b: TString);
{Swap data if a > b for TByte}
procedure SwapByte(var a, b: TByte);
{Swap data if a > b for TWord}
procedure SwapWord(var a, b: TWord);
{Swap data if a > b for TLongWord}
procedure SwapLongWord(var a, b: TLongWord);
{Swap data if a > b for TInt08}
procedure SwapInt08(var a, b: TInt08);
{Swap data if a > b for TInt16}
procedure SwapInt16(var a, b: TInt16);
{Swap data if a > b for TInt32}
procedure SwapInt32(var a, b: TInt32);
{Swap data if a > b for TInt64}
procedure SwapInt64(var a, b: TInt64);
{Swap data if a > b for single}
procedure SwapSingle(var a, b: TSingle);
{Swap data if a > b for double}
procedure SwapDouble(var a, b: TDouble);
{Swap data if a > b for Extended}
procedure SwapExtended(var a, b: TExtended);
implementation
procedure SwapString(var a, b: TString);
var
r: TString;
begin
r := a;
a := b;
b := r;
end;
procedure SwapByte(var a, b: TByte);
var
r: TByte;
begin
r := a;
a := b;
b := r;
end;
procedure SwapWord(var a, b: TWord);
var
r: TWord;
begin
r := a;
a := b;
b := r;
end;
procedure SwapLongWord(var a, b: TLongWord);
var
r: TLongWord;
begin
r := a;
a := b;
b := r;
end;
procedure SwapInt08(var a, b: TInt08);
var
r: TInt08;
begin
r := a;
a := b;
b := r;
end;
procedure SwapInt16(var a, b: TInt16);
var
r: TInt16;
begin
r := a;
a := b;
b := r;
end;
procedure SwapInt32(var a, b: TInt32);
var
r: TInt32;
begin
r := a;
a := b;
b := r;
end;
procedure SwapInt64(var a, b: TInt64);
var
r: TInt64;
begin
r := a;
a := b;
b := r;
end;
procedure SwapSingle(var a, b: TSingle);
var
r: TSingle;
begin
r := a;
a := b;
b := r;
end;
procedure SwapDouble(var a, b: TDouble);
var
r: TDouble;
begin
r := a;
a := b;
b := r;
end;
procedure SwapExtended(var a, b: TExtended);
var
r: TExtended;
begin
r := a;
a := b;
b := r;
end;
end.
Solve 2:
This function will return the list of points found on a line from (x1,y1) to (x2,y2).
The procedure will calculate the points in the direction the line is drawn. For the line (x1,y1)------ --(x2,y2) or the line (x2,y2)-------(x1,y1) the first point in the list is always (x1,y1) and the last point in the list is always (x2, y2).
Points are calculated along the axis with the most change so that as many points as possible are created for the line.
// The point object
TPointFill = class
X: Integer;
Y: Integer;
end;
// ----------------------------------------------------------------------------
// GetLinePoints
// ----------------------------------------------------------------------------
function GetLinePoints(X1, Y1, X2, Y2: Integer): TList;
var
ChangeInX, ChangeInY, i, MinX, MinY, MaxX, MaxY, LineLength: Integer;
ChangingX: Boolean;
Point: TPointFill;
ReturnList, ReversedList: TList;
begin
ReturnList := TList.Create;
ReversedList := TList.Create;
// Get the change in the X axis and the Max & Min X values
if X1 > X2 then
begin
ChangeInX := X1 - X2;
MaxX := X1;
MinX := X2;
end
else
begin
ChangeInX := X2 - X1;
MaxX := X2;
MinX := X1;
end;
// Get the change in the Y axis and the Max & Min Y values
if Y1 > Y2 then
begin
ChangeInY := Y1 - Y2;
MaxY := Y1;
MinY := Y2;
end
else
begin
ChangeInY := Y2 - Y1;
MaxY := Y2;
MinY := Y1;
end;
// Find out which axis has the greatest change
if ChangeInX > ChangeInY then
begin
LineLength := ChangeInX;
ChangingX := True;
end
else
begin
LineLength := ChangeInY;
ChangingX := false;
end;
// If the x's match then the line changes only on the Y axis
if X1 = X2 then
begin
// Loop thru the points on the list, lowest to highest.
for i := MinY to MaxY do
begin
Point := TPointFill.Create;
Point.X := X1;
Point.Y := i;
ReturnList.Add(Point);
end;
// If the point was started on the right and went to the left then
reverse the list.
if Y1 > Y2 then
begin
ReversedList := ReversePointOrder(ReturnList);
ReturnList := ReversedList;
end;
end
// If the x's match then the line changes only on the Y axis
else if Y1 = Y2 then
begin
// Loop thru the points on the list, lowest to highest.
for i := MinX to MaxX do
begin
Point := TPointFill.Create;
Point.X := i;
Point.Y := Y1;
ReturnList.Add(Point);
end;
// If the point was started on the bottom and went to the top then reverse the list.
if X1 > X2 then
begin
ReversedList := ReversePointOrder(ReturnList);
ReturnList := ReversedList;
end;
end
// The line is on an angle
else
begin
// Add the first point to the list.
Point := TPointFill.Create;
Point.X := X1;
Point.Y := Y1;
ReturnList.Add(Point);
// Loop thru the longest axis
for i := 1 to (LineLength - 1) do
begin
Point := TPointFill.Create;
// If we are moving on the x axis then get the related Y point.
if ChangingX then
begin
Point.y := Round((ChangeInY * i) / ChangeInX);
Point.x := i;
end
// otherwise we are moving on the y axis so get the related X point.
else
begin
Point.y := i;
Point.x := Round((ChangeInX * i) / ChangeInY);
end;
// if y1 is smaller than y2 then we are moving in a Top to Bottom direction.
// we need to add y1 to get the next y value.
if Y1 < Y2 then
Point.y := Point.Y + Y1
// otherwise we are moving in a Bottom to Top direction.
// we need to subtract y1 to get the next y value.
else
Point.Y := Y1 - Point.Y;
// if X1 is smaller than X2 then we are moving in a Left to Right direction
// we need to add x1 to get the next x value
if X1 < X2 then
Point.X := Point.X + X1
// otherwise we are moving in a Right to Left direction
// we need to subtract x1 to get the next x value.
else
Point.X := X1 - Point.X;
ReturnList.Add(Point);
end;
// Add the second point to the list.
Point := TPointFill.Create;
Point.X := X2;
Point.Y := Y2;
ReturnList.Add(Point);
end;
Result := ReturnList;
end;
// ----------------------------------------------------------------------------
// ReversePointOrder
// ----------------------------------------------------------------------------
function ReversePointOrder(LinePointList: TList): TList;
var
i: integer;
NewPointList: TList;
CurrentPointFill: TPointFill;
begin
NewPointList := TList.Create;
i := LinePointList.Count - 1;
while i > -1 do
begin
CurrentPointFill := TPointFill(LinePointList.Items[i]);
NewPointList.Add(CurrentPointFill);
dec(i);
end;
Result := NewPointList;
end;
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése