По мотивам - http://programmingmindstream.blogspot.ru/2016/09/1268-bounding-rect.html
Пример использования:
unit msmLineF; interface (*uses System.Types, FMX.DUnit.msLog ;*) uses Types , l3Units ; type Pixel = Single; TPointF = object public X : Pixel; Y : Pixel; constructor Create(aX: Pixel; aY: Pixel); end;//TPointF TmsPointF = object public P : TPointF; constructor Create(const aPoint: TPointF); overload; constructor Create(aX: Pixel; aY: Pixel); overload; (* procedure ToLog(aLog: TmsLog); class function Normalize(const aPt: TmsPointF): TmsPointF; static;*) function N: TmsPointF; function ToString: String; property X: Pixel read P.X write P.X; property Y: Pixel read P.Y write P.Y; end;//TmsPointF TmsLineF = object public A : TmsPointF; B : TmsPointF; (* procedure ToLog(aLog: TmsLog);*) function ToString: String; function Cross(const anOther: TmsLineF; out theCross: TmsPointF): Boolean; // - пересечение линий function SegmentsCross(const anOther: TmsLineF; out theCross: TmsPointF): Boolean; // - пересечение отрезков constructor Create(const aA: TmsPointF; const aB: TmsPointF); overload; constructor Create(const aA: TPointF; const aB: TPointF); overload; constructor Create(aAX, aAY: Pixel; aBX, aBY: Pixel); overload; function dX: Pixel; function dY: Pixel; function Length: Pixel; function ScalarMul(const anOther: TmsLineF): Pixel; function CosA(const anOther: TmsLineF): Single; end;//TmsLineF TmsLineFPair = object public L1 : TmsLineF; L2 : TmsLineF; constructor Create(const aL1: TmsLineF; const aL2: TmsLineF); (* procedure ToLog(aLog: TmsLog);*) function ToString: String; function Cross(out theCross: TmsPointF): Boolean; // - пересечение линий function SegmentsCross(out theCross: TmsPointF): Boolean; // - пересечение отрезков end;//TmsLineFPair TmsLineFPairs = array of TmsLineFPair; TRectF = Tl3SRect; TmsRectF = object public R : TRectF; constructor Create(const aR: TRectF); function Cross(const anOther: TmsLineF; out theCross: TmsPointF): Boolean; overload; function Cross(const anOther: TmsLineF; var theCross: Tl3SPoint): Boolean; overload; end;//TmsRectF function TmsPointF_Create(aX: Pixel; aY: Pixel): TmsPointF; function TmsLineF_Create(const aA: TPointF; const aB: TPointF): TmsLineF; overload; function TmsLineF_Create(const aA: Tl3SPoint; const aB: Tl3SPoint): TmsLineF; overload; function TPointF_Create(aX: Pixel; aY: Pixel): TPointF; function TmsRectF_Create(const aR: TRectF): TmsRectF; implementation uses SysUtils, Math ; (*uses System.SysUtils, Math, FMX.DUnit.msAppLog ;*) // TmsPointF constructor TmsPointF.Create(const aPoint: TPointF); begin P := aPoint; end; constructor TmsPointF.Create(aX: Pixel; aY: Pixel); begin Create(TPointF_Create(aX, aY)); end; (*procedure TmsPointF.ToLog(aLog: TmsLog); var l_N : TmsPointF; begin l_N := Self.N; aLog.ToLog('X:'); aLog.ToLog(FloatToStr(l_N.P.X)); aLog.ToLog('Y:'); aLog.ToLog(FloatToStr(l_N.P.Y)); end; class function TmsPointF.Normalize(const aPt: TmsPointF): TmsPointF; begin Result := aPt; end;*) function TmsPointF.N: TmsPointF; begin //Result := Normalize(Self); Result := Self; end; function TmsPointF.ToString: String; var l_P : TmsPointF; begin l_P := Self.N; Result := FloatToStr(l_P.X) + '_' + FloatToStr(l_P.Y); end; // TmsLineF (*procedure TmsLineF.ToLog(aLog: TmsLog); begin aLog.ToLog('dump line:'); aLog.ToLog('A:'); A.ToLog(aLog); aLog.ToLog('B:'); B.ToLog(aLog); end;*) function TmsLineF.ToString: String; begin Result := A.ToString + '_' + B.ToString; end; function DoCross(const Self: TmsLineF; const anOther: TmsLineF; out theCross: TmsPointF): Boolean; var dXdY : Pixel; dYdX : Pixel; dYdY : Pixel; begin//DoCross Result := false; Assert(not IsZero(Self.dY)); // - теперь тут можно будет вставить ЛЮБОЙ ДРУГОЙ алгоритм и посмотреть - "что будет" dXdY := Self.dX * anOther.dY; dYdX := Self.dY * anOther.dX; dYdY := Self.dY * anOther.dY; // - тут тот самый "определитель матрицы" считается и "векторное произведение" if IsZero(dXdY - dYdX) then // - условие параллельности прямых begin // Self.ToLog(TmsAppLog.Instance); // anOther.ToLog(TmsAppLog.Instance); theCross.X := -1; theCross.Y := -1; Exit; end;//IsZero(dXdY - dYdX) Assert(not IsZero(dXdY - dYdX)); theCross.Y := ( dXdY * Self.A.Y - dYdX * anOther.A.Y + dYdY * (anOther.A.X - Self.A.X) ) / (dXdY - dYdX); theCross.X := Self.A.X + Self.dX * (theCross.Y - Self.A.Y) / Self.dY; Result := true; end;//DoCross function TmsLineF.Cross(const anOther: TmsLineF; out theCross: TmsPointF): Boolean; (*var l_Angle : Single;*) begin Result := false; theCross.Create(High(Integer), High(Integer)); if IsZero(Self.Length) then begin theCross.X := 0; if IsZero(anOther.Length) then theCross.Y := 0; Exit; end//IsZero(Self.Length) else if IsZero(anOther.Length) then begin theCross.Y := 0; Exit; end;//IsZero(anOther.Length) // Дальше нужно проверить параллельность прямых (* l_Angle := ArcCos(Self.CosA(anOther)); if IsZero(l_Angle) OR SameValue(l_Angle, pi) OR SameValue(l_Angle, 2 * pi) then begin theCross.X := -1; theCross.Y := -1; Exit; end;//IsZero(ArcCos(Self.CosA(anOther)))*) if IsZero(Self.dY) then begin if IsZero(anOther.dX) then begin Result := true; theCross.X := anOther.A.X; theCross.Y := Self.A.Y; Exit; end//IsZero(anOther.dX) else begin if IsZero(anOther.dY) then begin Result := false; Exit; end//IsZero(anOther.dY) else begin Result := DoCross(anOther, Self, theCross); Exit; end;//IsZero(anOther.dY) end;//IsZero(anOther.dX) end//IsZero(Self.dY) else if IsZero(anOther.dY) then begin if IsZero(Self.dX) then begin Result := true; theCross.X := Self.A.X; theCross.Y := anOther.A.Y; Exit; end;//IsZero(Self.dX) end;//IsZero(anOther.dY) // Дальше можно по идее применять Мишин алгоритм: Result := DoCross(Self, anOther, theCross); end; function TmsLineF.SegmentsCross(const anOther: TmsLineF; out theCross: TmsPointF): Boolean; function Btwn(aValue, aB1, aB2: Pixel): Boolean; var l_Min : Pixel; l_Max : Pixel; begin//Btwn l_Min := Min(aB1, aB2); l_Max := Max(aB1, aB2); Result := ((aValue >= l_Min) OR SameValue(aValue, l_Min)) and ((aValue <= l_Max) OR SameValue(aValue, l_Max)); end;//Btwn begin Result := Cross(anOther, theCross); if Result then begin Result := Btwn(theCross.X, Self.A.X, Self.B.X) and Btwn(theCross.X, anOther.A.X, anOther.B.X) and Btwn(theCross.Y, Self.A.Y, Self.B.Y) and Btwn(theCross.Y, anOther.A.Y, anOther.B.Y); end;//Result end; constructor TmsLineF.Create(const aA: TmsPointF; const aB: TmsPointF); begin A := aA; B := aB; end; constructor TmsLineF.Create(const aA: TPointF; const aB: TPointF); begin Create(TmsPointF_Create(aA.X, aA.Y), TmsPointF_Create(aB.X, aB.Y)); end; constructor TmsLineF.Create(aAX, aAY: Pixel; aBX, aBY: Pixel); begin Create(TmsPointF_Create(aAX, aAY), TmsPointF_Create(aBX, aBY)); end; function TmsLineF.dX: Pixel; begin Result := (B.X - A.X); end; function TmsLineF.dY: Pixel; begin Result := (B.Y - A.Y); end; function TmsLineF.Length: Pixel; begin Result := Sqrt(dX * dX + dY * dY); end; function TmsLineF.ScalarMul(const anOther: TmsLineF): Pixel; begin Result := Self.dX * anOther.dX + Self.dY * anOther.dY; end; function TmsLineF.CosA(const anOther: TmsLineF): Single; begin Result := Self.ScalarMul(anOther) / (Self.Length * anOther.Length); end; // TmsLineFPair constructor TmsLineFPair.Create(const aL1: TmsLineF; const aL2: TmsLineF); begin L1 := aL1; L2 := aL2; end; (*procedure TmsLineFPair.ToLog(aLog: TmsLog); begin aLog.ToLog('L1:'); L1.ToLog(aLog); aLog.ToLog('L2:'); L2.ToLog(aLog); end;*) function TmsLineFPair.ToString: String; begin Result := L1.ToString + '_' + L2.ToString; end; function TmsLineFPair.Cross(out theCross: TmsPointF): Boolean; begin Result := L1.Cross(L2, theCross); end; function TmsLineFPair.SegmentsCross(out theCross: TmsPointF): Boolean; begin Result := L1.SegmentsCross(L2, theCross); end; // TmsRectF constructor TmsRectF.Create(const aR: TRectF); begin R := aR; end; function TmsRectF.Cross(const anOther: TmsLineF; out theCross: TmsPointF): Boolean; var l_R : array [0..3] of TmsLineF; l_Index : Integer; l_L : TmsLineF; begin Result := true; l_R[0] := TmsLineF_Create(TPointF_Create(R.Left, R.Top), TPointF_Create(R.Right, R.Top)); l_R[1] := TmsLineF_Create(TPointF_Create(R.Right, R.Top), TPointF_Create(R.Right, R.Bottom)); l_R[2] := TmsLineF_Create(TPointF_Create(R.Left, R.Bottom), TPointF_Create(R.Right, R.Bottom)); l_R[3] := TmsLineF_Create(TPointF_Create(R.Left, R.Top), TPointF_Create(R.Left, R.Bottom)); for l_Index := Low(l_R) to High(l_R) do begin l_L := l_R[l_Index]; if l_L.SegmentsCross(anOther, theCross) then Exit; end;//for l_Index theCross := anOther.A; Result := false; end; function TmsRectF.Cross(const anOther: TmsLineF; var theCross: Tl3SPoint): Boolean; var l_Cross: TmsPointF; begin Result := Cross(anOther, l_Cross); if Result then theCross := l3SPoint(Round(l_Cross.X), Round(l_Cross.Y)); end; constructor TPointF.Create(aX: Pixel; aY: Pixel); begin X := aX; Y := aY; end; function TmsPointF_Create(aX: Pixel; aY: Pixel): TmsPointF; begin Result.Create(aX, aY); end; function TmsLineF_Create(const aA: TPointF; const aB: TPointF): TmsLineF; begin Result.Create(aA, aB); end; function TmsLineF_Create(const aA: Tl3SPoint; const aB: Tl3SPoint): TmsLineF; begin Result := TmsLineF_Create(TPointF_Create(aA.X, aA.Y), TPointF_Create(aB.X, aB.Y)); end; function TPointF_Create(aX: Pixel; aY: Pixel): TPointF; begin Result.Create(aX, aY); end; function TmsRectF_Create(const aR: TRectF): TmsRectF; begin Result.Create(aR); end; end.
Пример использования:
function TmsmDrawingView.ElementRect(const anElement: ImsmModelElement): Tl3SRect; var l_X : Integer; l_Y : Integer; l_W : Integer; l_H : Integer; l_From : ImsmModelElement; l_To : ImsmModelElement; l_ToR : Tl3SRect; l_FromR : Tl3SRect; l_FromP : Tl3SPoint; l_ToP : Tl3SPoint; l_Line : TmsLineF; begin if anElement.IsView then begin l_X := anElement.IntProp['msm:View:X']; l_Y := anElement.IntProp['msm:View:Y']; l_W := anElement.IntProp['msm:View:Width']; l_H := anElement.IntProp['msm:View:Height']; Result := l3SBounds(l_X, l_Y, l_W, l_H); Result.Inflate1(-10); Result := Result.SubPt(f_Origin); end//anElement.IsView else if anElement.IsViewLink then begin l_From := anElement.ElementProp['msm:View:From']; l_To := anElement.ElementProp['msm:View:To']; if (l_From <> nil) AND (l_To <> nil) then begin l_FromR := ElementRect(l_From); l_ToR := ElementRect(l_To); l_FromP.X := (l_FromR.Left + l_FromR.Right) div 2; l_FromP.Y := (l_FromR.Top + l_FromR.Bottom) div 2; l_ToP.X := (l_ToR.Left + l_ToR.Right) div 2; l_ToP.Y := (l_ToR.Top + l_ToR.Bottom) div 2; l_Line := TmsLineF_Create(l_FromP, l_ToP); TmsRectF_Create(l_FromR).Cross(l_Line, l_FromP); TmsRectF_Create(l_ToR).Cross(l_Line, l_ToP); Result := l3SRect(l_FromP, l_ToP); end//(l_From <> nil) AND (l_To <> nil) else Result := l3SRect(0, 0, 0, 0); end;//anElement.IsView end;
Комментариев нет:
Отправить комментарий