Реализация
Сайт: | Информатикс |
Курс: | Геометрия |
Книга: | Реализация |
Напечатано:: | Гость |
Дата: | Суббота, 28 Июнь 2025, 01:14 |
{ Модуль для работы с геометрическими объектами }
{ Copyright (c) Антон Лапунов, 1998-2001 }
{ Версия для сайта ОНЗИ http://onzi.narod.ru }
Unit Geometry; {$N+,G+}
Interface
Type Real = Extended; { Вещественное число }
Point = Record x,y : Real End; { Точка }
Line = Record A,B,C : Real End; { Прямая }
Circle = Record O : Point; R : Real End; { Окружность }
PClass = (_On,_Inside,_Outside,_Negative,_Positive);
Const Eps : Real = 1e-10; { "Почти" ноль }
Inf = 1e+10; { "Бесконечность" }
PInf : Point = (x:Inf; y:Inf); { "Бесконечная" точка }
PClassStr : Array [PClass] Of String =
('On','Inside','Outside','Negative','Positive');
Function Eq(Const a,b : Real) : Boolean;
Function Ne(Const a,b : Real) : Boolean;
Function Le(Const a,b : Real) : Boolean;
Function Lt(Const a,b : Real) : Boolean;
Function Ge(Const a,b : Real) : Boolean;
Function Gt(Const a,b : Real) : Boolean;
Function Btw(Const x,a,b : Real) : Boolean;
Function BtwS(Const x,a,b : Real) : Boolean;
Function Det(Const a,b,c,d : Real) : Real;
Function ArcTan2(Const y,x : Real) : Real;
Function PolarAngle(Const x,y : Real) : Real;
Function ADiff(Const a,b : Real) : Real;
Procedure PSet(Var P : Point; Const x,y : Real);
Procedure LSet(Var L : Line; Const A,B,C : Real);
Procedure CSet(Var C : Circle; Const x,y,R : Real);
Function PStr(Const P : Point; w,d : Integer) : String;
Function PBtw(Const P,P1,P2 : Point) : Boolean;
Function PBtwS(Const P,P1,P2 : Point) : Boolean;
Procedure PMoveTo(Const P1,P2 : Point; Const k : Real; Var P : Point);
Procedure PLProject(Const P : Point; Const L : Line; Var P1 : Point);
Procedure PLine(Const A,B : Real; Const P : Point; Var L : Line);
Procedure P2Line(Const P1,P2 : Point; Var L : Line);
Procedure PLPerp(Const P : Point; Const L : Line; Var L1 : Line);
Procedure SPerp(Const P1,P2 : Point; Var L : Line);
Procedure P3Circle(Const P1,P2,P3 : Point; Var C : Circle);
Function L2Point(Const L1,L2 : Line; Var P : Point) : Boolean;
Function CPPoints(Const C: Circle; Const P: Point; Var P1,P2: Point) : Byte;
Function CLPoints(Const C: Circle; Const L: Line; Var P1,P2: Point) : Byte;
Function C2Points(Const C1,C2 : Circle; Var P1,P2 : Point) : Byte;
Function PLClass(Const P : Point; Const L : Line) : PClass;
Function PCClass(Const P : Point; Const C : Circle) : PClass;
Function PSClass(Const P,P1,P2 : Point) : PClass;
Function PPolyClass(Const P : Point; N : Word; Const A : Array Of Point) : PClass;
Function P2Dist(Const P1,P2 : Point) : Real;
Function P2SqrDist(Const P1,P2 : Point) : Real;
Function SSquare(Const P1,P2 : Point) : Real;
Function P3Square(Const P1,P2,P3 : Point) : Real;
Function PolySquare(N : Word; Const A : Array Of Point) : Real;
Function PAngle(Const P : Point) : Real;
Function PPAngle(Const P1,P2 : Point) : Real;
Function PSAngle(Const P,P1,P2 : Point) : Real;
Procedure ConvexHull(N : Word; Const A : Array Of Point;
Var M : Word; Var Ind : Array Of Word);
Implementation
{ Проверяет два числа на равенство с точностью до Eps }
Function Eq(Const a,b : Real) : Boolean;
Begin
Eq:=Abs(a-b)<=Eps;
End;
{ Проверяет два числа на неравенство с точностью до Eps }
Function Ne(Const a,b : Real) : Boolean;
Begin
Ne:=Abs(a-b)>Eps;
End;
{ Проверяет два числа на "меньше или равно" с точностью до Eps }
Function Le(Const a,b : Real) : Boolean;
Begin
Le:=(a<=b) Or Eq(a,b);
End;
{ Проверяет два числа на "меньше" с точностью до Eps }
Function Lt(Const a,b : Real) : Boolean;
Begin
Lt:=(a<b) And Ne(a,b);
End;
{ Проверяет два числа на "больше или равно" с точностью до Eps }
Function Ge(Const a,b : Real) : Boolean;
Begin
Ge:=(a>=b) Or Eq(a,b);
End;
{ Проверяет два числа на "больше" с точностью до Eps }
Function Gt(Const a,b : Real) : Boolean;
Begin
Gt:=(a>b) And Ne(a,b);
End;
{ Проверяет принадлежность числа заданному диапазону с точностью до Eps }
Function Btw(Const x,a,b : Real) : Boolean;
Begin
Btw:=Le(a,x) And Le(x,b) Or Ge(a,x) And Ge(x,b);
End;
{ Аналогично предыдущей функции, но проверка строгая }
Function BtwS(Const x,a,b : Real) : Boolean;
Begin
BtwS:=(a<=x) And (x<=b) Or (a>=x) And (x>=b);
End;
{ Считает определитель 2x2 }
Function Det(Const a,b,c,d : Real) : Real;
Begin
Det:=a*d-b*c;
End;
{ Вычисляет арктангенс — число из диапазона (-Pi,Pi] }
Function ArcTan2(Const y,x : Real) : Real; Assembler;
Asm
fld y
fld x
fpatan
fwait
End;
{ Находит полярный угол точки с координатами (x,y) }
{ Возвращает число из диапазона [0,2*Pi) }
{ ASSERT: хотя бы одно из чисел x и y отлично от нуля! }
Function PolarAngle(Const x,y : Real) : Real;
Var p : Real;
Begin
p:=ArcTan2(y,x);
If p<0 Then p:=p+2*Pi;
PolarAngle:=p;
End;
{ Находит ориентированный угол от a до b }
{ Возвращает число из диапазона [0,2*Pi) }
Function ADiff(Const a,b : Real) : Real;
Begin
If b>=a Then ADiff:=b-a Else ADiff:=b-a+2*Pi;
End;
{====================================================================}
{ }
{====================================================================}
{ Инициализирует объект типа Point }
Procedure PSet(Var P : Point; Const x,y : Real);
Begin
P.x:=x; P.y:=y;
End;
{ Инициализирует объект типа Line }
Procedure LSet(Var L : Line; Const A,B,C : Real);
Begin
L.A:=A; L.B:=B; L.C:=C;
End;
{ Инициализирует объект типа Circle }
Procedure CSet(Var C : Circle; Const x,y,R : Real);
Begin
PSet(C.O,x,y);
C.R:=R;
End;
{ Возвращает строковое представление точки }
Function PStr(Const P : Point; w,d : Integer) : String;
Var sx,sy : String;
Begin
Str(P.x:w:d,sx);
Str(P.y:w:d,sy);
PStr:='('+sx+','+sy+')';
End;
{ Проверяет принадлежность точки замкнутому прямоугольнику, }
{ образованному двумя заданными точками, с точностью до Eps }
Function PBtw(Const P,P1,P2 : Point) : Boolean;
Begin
PBtw:=Btw(P.x,P1.x,P2.x) And Btw(P.y,P1.y,P2.y);
End;
{ Аналогично предыдущей функции, но проверка строгая }
Function PBtwS(Const P,P1,P2 : Point) : Boolean;
Begin
PBtwS:=BtwS(P.x,P1.x,P2.x) And BtwS(P.y,P1.y,P2.y);
End;
{ Проходит от точки P1 заданную часть отрезка [P1,P2] }
Procedure PMoveTo(Const P1,P2 : Point; Const k : Real; Var P : Point);
Begin
P.x:=P1.x+(P2.x-P1.x)*k;
P.y:=P1.y+(P2.y-P1.y)*k;
End;
{ Проектирует точку на прямую }
Procedure PLProject(Const P : Point; Const L : Line; Var P1 : Point);
Var k : Real;
Begin
With P,L Do
Begin
k:=(A*x+B*y+C)/(Sqr(A)+Sqr(B));
P1.x:=x-A*k;
P1.y:=y-B*k;
End;
End;
{ Проводит прямую с заданными коэффициентами A и B через точку P }
Procedure PLine(Const A,B : Real; Const P : Point; Var L : Line);
Begin
L.A:=A; L.B:=B;
L.C:=-A*P.x-B*P.y;
End;
{ Проводит прямую через две точки }
{ ASSERT: точки P1 и P2 различны! }
Procedure P2Line(Const P1,P2 : Point; Var L : Line);
Begin
PLine(P1.y-P2.y,P2.x-P1.x,P1,L);
End;
{ Строит прямую, перпендикулярную заданной прямой и проходящую }
{ через указанную точку }
Procedure PLPerp(Const P : Point; Const L : Line; Var L1 : Line);
Begin
PLine(L.B,-L.A,P,L1);
End;
{ Строит серединный перпендикуляр к отрезку }
{ ASSERT: точки P1 и P2 различны! }
Procedure SPerp(Const P1,P2 : Point; Var L : Line);
Var L1 : Line;
P : Point;
Begin
P2Line(P1,P2,L1);
PMoveTo(P1,P2,1/2,P);
PLPerp(P,L1,L);
End;
{ Проводит окружность через три точки }
{ ASSERT: точки P1, P2 и P3 различны! }
Procedure P3Circle(Const P1,P2,P3 : Point; Var C : Circle);
Var L1,L2 : Line;
Begin
SPerp(P1,P2,L1);
SPerp(P2,P3,L2);
L2Point(L1,L2,C.O);
C.R:=P2Dist(C.O,P1);
End;
{====================================================================}
{ Пересечения }
{====================================================================}
{ Пересекает две прямые }
Function L2Point(Const L1,L2 : Line; Var P : Point) : Boolean;
Var D : Real;
Begin
L2Point:=False;
D:=Det(L1.A,L2.A,L1.B,L2.B);
If Eq(D,0) Then P:=PInf
Else Begin
P.x:=-Det(L1.C,L2.C,L1.B,L2.B)/D;
P.y:=-Det(L1.A,L2.A,L1.C,L2.C)/D;
L2Point:=True;
End;
End;
{ Пересекает окружность с прямой, проходящей через заданную точку }
{ и перпендикулярной отрезку, соединяющему ее с центром окружности }
Function CPPoints(Const C : Circle; Const P: Point; Var P1,P2: Point) : Byte;
Var d,t,xx,yy : Real;
Begin
P1:=PInf; P2:=PInf;
With C Do
Begin
d:=P2Dist(O,P); CPPoints:=0;
If Eq(d,R) Then CPPoints:=1
Else If d<R Then CPPoints:=2
Else Exit;
t:=Sqr(R)-Sqr(d);
If t<0 Then t:=0 Else t:=Sqrt(t)/d;
xx:=(P.x-O.x)*t; yy:=(P.y-O.y)*t;
P1.x:=P.x-yy; P1.y:=P.y+xx;
P2.x:=P.x+yy; P2.y:=P.y-xx;
End;
End;
{ Пересекает окружность и прямую }
Function CLPoints(Const C : Circle; Const L: Line; Var P1,P2: Point) : Byte;
Var P : Point;
Begin
PLProject(C.O,L,P);
CLPoints:=CPPoints(C,P,P1,P2);
End;
{ Пересекает две окружности }
Function C2Points(Const C1,C2 : Circle; Var P1,P2 : Point) : Byte;
Var P : Point;
d2,t : Real;
Begin
d2:=P2SqrDist(C1.O,C2.O);
t:=(Sqr(C1.R)-Sqr(C2.R)+d2)/(2*d2);
PMoveTo(C1.O,C2.O,t,P);
C2Points:=CPPoints(C1,P,P1,P2);
End;
{====================================================================}
{ Классификация точек }
{====================================================================}
{ Определяет расположение точки относительно прямой }
Function PLClass(Const P : Point; Const L : Line) : PClass;
Var d : Real;
Begin
With P,L Do d:=A*x+B*y+C;
If Eq(d,0) Then PLClass:=_On
Else If d<0 Then PLClass:=_Negative
Else PLClass:=_Positive;
End;
{ Определяет расположение точки относительно окружности }
Function PCClass(Const P : Point; Const C : Circle) : PClass;
Var d : Real;
Begin
d:=P2Dist(C.O,P);
If Eq(d,C.R) Then PCClass:=_On
Else If d<C.R Then PCClass:=_Inside
Else PCClass:=_Outside;
End;
{ Определяет расположение точки относительно отрезка }
{ ASSERT: точки P1 и P2 различны! }
Function PSClass(Const P,P1,P2 : Point) : PClass;
Var L: Line;
Begin
P2Line(P1,P2,L);
If (PLClass(P,L)=_On) And PBtw(P,P1,P2) Then PSClass:=_On
Else PSClass:=_Outside;
End;
{ Определяет принадлежность точки многоугольнику, }
{ заданному массивом вершин A, N - число вершин }
Function PPolyClass(Const P: Point; N: Word; Const A: Array Of Point): PClass;
Var i : Word;
S : Real;
Begin
PPolyClass:=_On;
If PSClass(P,A[N-1],A[0])=_On Then Exit;
S:=PSAngle(P,A[N-1],A[0]);
For i:=0 To N-2 Do
Begin
If PSClass(P,A[i],A[i+1])=_On Then Exit;
S:=S+PSAngle(P,A[i],A[i+1]);
End;
If Abs(S)<Pi Then PPolyClass:=_Outside { Точка P лежит снаружи }
Else PPolyClass:=_Inside; { Точка P лежит внутри }
End;
{====================================================================}
{ Расстояния }
{====================================================================}
{ Считает расстояние между двумя точками }
Function P2Dist(Const P1,P2 : Point) : Real;
Begin
P2Dist:=Sqrt(Sqr(P1.x-P2.x)+Sqr(P1.y-P2.y));
End;
{ Считает квадрат расстояния между двумя точками }
Function P2SqrDist(Const P1,P2 : Point) : Real;
Begin
P2SqrDist:=Sqr(P1.x-P2.x)+Sqr(P1.y-P2.y);
End;
{====================================================================}
{ Периметры и площади }
{====================================================================}
{ Вычисляет ориентированную площадь треугольника, }
{ натянутого на отрезок [P1,P2] и начало координат }
Function SSquare(Const P1,P2 : Point) : Real;
Begin
SSquare:=Det(P1.x,P2.x,P1.y,P2.y)/2;
End;
{ Вычисляет ориентированную площадь треугольника, }
{ натянутого на точки P1, P2 и P3 }
Function P3Square(Const P1,P2,P3 : Point) : Real;
Begin
P3Square:=Det(P1.x-P3.x,P2.x-P3.x,P1.y-P3.y,P2.y-P3.y)/2;
End;
{ Вычисляет ориентированную площадь многоугольника, }
{ заданного массивом вершин A, N - число вершин }
Function PolySquare(N : Word; Const A : Array Of Point) : Real;
Var S : Real;
i : Word;
Begin
S:=SSquare(A[N-1],A[0]);
For i:=0 To N-2 Do S:=S+SSquare(A[i],A[i+1]);
PolySquare:=S;
End;
{====================================================================}
{ Углы }
{====================================================================}
{ Определяет полярный угол точки }
Function PAngle(Const P : Point) : Real;
Begin
PAngle:=PolarAngle(P.x,P.y);
End;
{ Определяет полярный угол точки P2 относительно точки P1 }
Function PPAngle(Const P1,P2 : Point) : Real;
Begin
PPAngle:=PolarAngle(P2.x-P1.x,P2.y-P1.y);
End;
{ Определяет ориентированный угол, под которым виден отрезок }
{ из заданной точки }
Function PSAngle(Const P,P1,P2 : Point) : Real;
Var a : Real;
Begin
a:=ADiff(PPAngle(P,P1),PPAngle(P,P2));
If a<=Pi Then PSAngle:=a Else PSAngle:=a-2*Pi;
End;
{====================================================================}
{ Выпуклая оболочка }
{====================================================================}
{ Строит выпуклую оболочку множества из N точек, заданного массивом A, }
{ занося в массив Ind номера точек из A, являющихся вершинами выпуклой }
{ оболочки, в порядке обхода против часовой стрелки, а в M - их количество }
Procedure ConvexHull(N : Word; Const A : Array Of Point;
Var M : Word; Var Ind : Array Of Word);
Var First,Cur,Min,i : Word;
z : Real;
Begin
{ Метод Джарвиса }
{ Находим самую нижнюю из самых левых точек с минимальным номером }
First:=0;
For i:=1 To N-1 Do
If (A[i].x<A[First].x) Or (A[i].x=A[First].x) And (A[i].y<A[First].y)
Then First:=i;
{ "Заворачиваем подарок" }
M:=0; Cur:=First;
Repeat
Ind[M]:=Cur; Inc(M); Min:=Cur;
{ Цикл в противоположном направлении и строгие неравенства! }
For i:=N-1 DownTo 0 Do
Begin
If (A[i].x=A[Cur].x) And (A[i].y=A[Cur].y) Then Continue;
z:=P3Square(A[Cur],A[i],A[Min]);
If (Min=Cur) Or (z>0) Or (z=0) And PBtwS(A[Min],A[Cur],A[i]) Then Min:=i;
End;
Cur:=Min;
Until Cur=First;
End;
End.