(*--▽---------------------------▼-- RectとPointを処理するユニット 更新履歴 2006/07/10(月) ・RectWidth/HeightをGetRectWidth/Heightに名前変更 ・RectSetWidth/HeightをSetRectWidth/Heightに名前変更 2011/04/12(火) ・ PopupRectの左上指定が範囲外ならやはり右下にポップアップするようにした。 ・ PopupRectのRect指定機能を追加した。 2012/08/21(火) ・ PopupRectのRect指定機能にオプションで右から左へのポップアップも追加した。 //--▲---------------------------△--*) unit RectPointUnit; interface uses Types; function PointEqual(const A, B: TPoint): Boolean; function RectEqual(const A, B: TRect): Boolean; function NormalizeRect(R: TRect): TRect; function PointInRect(Point1: TPoint; Rect1: TRect): Boolean; function PointPlus(Point1, Point2: TPoint): TPoint; function PointMinus(Point1, Point2: TPoint): TPoint; function GetRectWidth(Rect: TRect): Integer; procedure SetRectWidth(var Rect: TRect; Width: Integer); function GetRectHeight(Rect: TRect): Integer; procedure SetRectHeight(var Rect: TRect; Height: Integer); function GetRectChangeWidth(Original: TRect; Width: Integer): TRect; function GetRectChangeWidthFixRight(Original: TRect; Width: Integer): TRect; function GetRectChangeHeight(Original: TRect; Height: Integer): TRect; function GetRectChangeHeightFixBottom(Original: TRect; Height: Integer): TRect; function RectCenter(Rect: TRect): TPoint; function RectMove(const Rect: TRect; X, Y: Integer): TRect; function RectMovePoint(const Rect: TRect; MovePoint: TPoint): TRect; function RectMoveCenter(const MoveRect: TRect; BaseRectCenter: TPoint): TRect; function RectSquare(Left, Top, Width, Hight: Integer): TRect; function PointMove(const Point: TPoint; X, Y: Integer): TPoint; function PopupRect(BaseRect, RectValue: TRect; PopupPoint: TPoint): TRect; overload; type TPopupDirect = (pdTopLeftToBottomRight, pdTopRightToBottomLeft); function PopupRect(ScreenRect, PopupRect: TRect; PopupButtonRect: TRect; Direct: TPopupDirect = pdTopLeftToBottomRight): TRect; overload; function OutsideRectMoveInside(BaseRect, TargetRect: TRect; TopFit: Boolean = True; BottomFit: Boolean = True; LeftFit: Boolean = True; RightFit: Boolean = True): TRect; function OutsidePointMoveInside(BaseRect: TRect; Point: TPoint): TPoint; function RectSizePlus(const Rect: TRect; LeftValue, TopValue, RightValue, BottomValue: Integer): TRect; function GetTopLeft(const Rect: TRect): TPoint; function GetTopRight(const Rect: TRect): TPoint; function GetBottomLeft(const Rect: TRect): TPoint; function GetBottomRight(const Rect: TRect): TPoint; type { ----------------------------------- //Rectのサイズを表す構造体 戻り値: TRectSize 履歴: 2001/03/22 //----------------------------------- } TRectSize = record Width: Cardinal; Height:Cardinal; end; function RectSize(Width, Height: Cardinal): TRectSize; overload; function RectSize(const Rect: TRect): TRectSize; overload; function RectSizeEqual(const A, B: TRectSize): Boolean; function RectDirectionRatio(Source: TRectSize; Percent: Cardinal): TRectSize; function FitRectSize(Original, FitSize: TRectSize): TRectSize; function RectInRect(InCheckRect, BaseRect: TRect): Boolean; function RectOutRect(OutCheckRect, BaseRect: TRect): Boolean; implementation {------------------------------- // Point/Rectの一致を調べます。 処理: 単に中身を比較するだけ。 備考: Delphi6での話しだが、同機能の関数が それぞれ PointsEqual EqualRect という VCL標準関数で実装されているが Classesというusesすると勝手にバイナリサイズが増加する ユニットに含まれているので (EqualRectはTypesにも実装されているようだが) 独自実装した方がましだと判断。関数名もあえて変えている 履歴: 2002/11/18 //--▼----------------------▽--} function PointEqual(const A, B: TPoint): Boolean; begin Result := (A.X = B.X) and (A.Y = B.Y); end; function RectEqual(const A, B: TRect): Boolean; begin Result := (A.Left = B.Left) and (A.Top = B.Top) and (A.Right = B.Right) and (A.Bottom = B.Bottom) end; function RectSizeEqual(const A, B: TRectSize): Boolean; begin Result := (A.Width = B.Width) and (A.Height = B.Height); end; //--△----------------------▲-- {--------------------------------------- Rectを正規化する 機能: LeftとRight、TopとBottomが正しくなるように変換する 備考: varで引数を変更するほうが若干早いかもしれないが 書き方が便利なので R := NormalizeRect(R); という今の形にする 履歴: 2011/09/02(金) ・ 作成 }//(*----------------------------------- function NormalizeRect(R: TRect): TRect; begin if R.Left < R.Right then begin Result.Left := R.Left; Result.Right := R.Right; end else begin Result.Left := R.Right; Result.Right := R.Left; end; if R.Top < R.Bottom then begin Result.Top := R.Top; Result.Bottom := R.Bottom; end else begin Result.Top := R.Bottom; Result.Bottom := R.Top; end; end; //------------------------------------*) {------------------------------- // PointがRectの中に含まれるかどうかを調べる 機能: RectのLeft〜Right/Top〜Bottomの間に Pointgaがあるかどうかを調べている。 備考: 似たような関数にTypes.PtInRectがありますが 独自実装した方が安心そうなのでVCL標準を無視 PtInRectはHELPの記述もなんか怪しい 履歴: 2002/11/18 2011/09/02(金) ・ NormilizeRectを追加して修正 //--▼----------------------▽--} function PointInRect(Point1: TPoint; Rect1: TRect): Boolean; begin Rect1 := NormalizeRect(Rect1); Result := (Rect1.Left <= Point1.X) and (Point1.X <= Rect1.Right) and (Rect1.Top <= Point1.Y) and (Point1.Y <= Rect1.Bottom); end; //--△----------------------▲-- {------------------------------- // Pointを演算します。 PointPlus/PointMinus 機能: Point.XとPoint.Yをそれぞれ足し引きします 備考: 履歴: 2002/11/25 //--▼----------------------▽--} function PointPlus(Point1, Point2: TPoint): TPoint; begin Result := Types.Point(Point1.X + Point2.X , Point1.Y + Point2.Y); end; function PointMinus(Point1, Point2: TPoint): TPoint; begin Result := Types.Point(Point1.X - Point2.X , Point1.Y - Point2.Y); end; //--△----------------------▲-- (*------------------------------- //Rectの幅 機能: Rect.Right - Rect.Left 履歴: 2000/11/16 -------------------------------*) function GetRectWidth(Rect: TRect): Integer; begin Result := Rect.Right - Rect.Left; end; procedure SetRectWidth(var Rect: TRect; Width: Integer); begin Rect.Right := Rect.Left + Width; end; function GetRectChangeWidth(Original: TRect; Width: Integer): TRect; begin Result := Original; SetRectWidth(Result, Width); end; function GetRectChangeWidthFixRight(Original: TRect; Width: Integer): TRect; begin Result := Original; Result := RectSizePlus(Original, -(GetRectWidth(Original)-Width), 0, 0, 0); end; (*------------------------------- //Rectの高さ 機能: Rect.Bottom - Rect.Top 履歴: 2000/11/16 -------------------------------*) function GetRectHeight(Rect: TRect): Integer; begin Result := Rect.Bottom - Rect.Top; end; procedure SetRectHeight(var Rect: TRect; Height: Integer); begin Rect.Bottom := Rect.Top + Height; end; function GetRectChangeHeight(Original: TRect; Height: Integer): TRect; begin Result := Original; SetRectHeight(Result, Height); end; function GetRectChangeHeightFixBottom(Original: TRect; Height: Integer): TRect; begin Result := Original; Result := RectSizePlus(Original, 0, -(GetRectHeight(Original)-Height), 0, 0); end; {------------------------------- // Rectの中央位置 備考: Types.CenterPointと同じだと思う 履歴: 2000/11/16 ・作成 2002/11/18 ・TypesにVCL標準で同じ機能のCenterPoint があることに気がついた。 //------------------------------} function RectCenter(Rect: TRect): TPoint; begin Result.x := (GetRectWidth(Rect) div 2) + Rect.Left; Result.y := (GetRectHeight(Rect) div 2) + Rect.Top; end; //------------------------------ {------------------------------- //Rectを上下左右に移動 機能: RectMove:指定Pixelだけ上下に移動させる RectMovePoint:指定Point位置をTopLeftする移動 履歴: 2000/11/16 //--▼----------------------▽--} function RectMove(const Rect: TRect; X, Y: Integer): TRect; overload; begin Result := Types.Rect(Rect.Left+X, Rect.Top+Y, Rect.Right+X, Rect.Bottom+Y); end; function RectMovePoint(const Rect: TRect; MovePoint: TPoint): TRect; overload; begin Result := Types.Rect(MovePoint.X, MovePoint.Y, MovePoint.X + GetRectWidth(Rect), MovePoint.Y + GetRectHeight(Rect)); end; //--△----------------------▲-- {------------------------------- // Rectを中央に移動 機能: BaseRectCenterを中心として MoveRectが移動した場合のRectを返す関数 備考: 履歴: 2006/05/24(水) 00:07 //--▼----------------------▽--} function RectMoveCenter(const MoveRect: TRect; BaseRectCenter: TPoint): TRect; var MoveRectCenter: TPoint; begin MoveRectCenter := RectCenter(MoveRect); Result := RectMove(MoveRect, BaseRectCenter.X-MoveRectCenter.X, BaseRectCenter.Y-MoveRectCenter.Y); end; //--△----------------------▲-- //Pointを移動する関数 function PointMove(const Point: TPoint; X, Y: Integer): TPoint; begin Result.X := Point.X + X; Result.Y := Point.Y + Y; end; //------------------------------- //TRectSizeを作成する関数 function RectSize(Width, Height: Cardinal): TRectSize; overload; begin Result.Width := Width; Result.Height := Height; end; function RectSize(const Rect: TRect): TRectSize; overload; begin Result.Width := GetRectWidth(Rect); Result.Height:= GetRectHeight(Rect); end; {------------------------------- // Rectを%で拡大縮小する 備考: 履歴: //--▼----------------------▽--} function RectDirectionRatio(Source: TRectSize; Percent: Cardinal): TRectSize; begin Result := RectSize(0, 0); if (Percent<=0) or (Source.Width<=0) or (Source.Height<=0) then Exit; Result.Width := Round(Source.Width * Percent /100.0); Result.Height:= Round(Source.Height* Percent /100.0); end; //--△----------------------▲-- {------------------------------- // 特定サイズにFitさせて(枠内に収まるように)戻す関数 機能: FitSizeに640x480を指定しておくと 640x480の枠内に収まるように Originalサイズを縦横比を保持して変更したサイズを戻す関数 縦か横かがサイズをはみ出さないように拡大/縮小するように なっている。 処理: ・Source/Target: TRectを用意 ・Target.Widht / Source.Width →Targetに対して何%かがわかる ・Source.Width * X% = Target.Width ・if Source.Height * X% <= Target.Height then いいんだけど ・elseなら ・Source.Height * Y% = Target.Height ・if Source.Widht * Y% <= Target.Widht then になるはず ・elseになったらエラーだな。 備考: 履歴: 2000/00/00 //--▼----------------------▽--} function FitRectSize(Original, FitSize: TRectSize): TRectSize; var XPercent, YPercent: Real; begin XPercent := (FitSize.Width / Original.Width *100.0); YPercent := (FitSize.Height/ Original.Height*100.0); if (Original.Height * YPercent/100.0) <= FitSize.Height then begin Result.Width := Round(Original.Width * YPercent/100.0); Result.Height := Round(Original.Height * YPercent/100.0); end else begin Result.Width := Round(Original.Width * XPercent/100.0); Result.Height := Round(Original.Height * XPercent/100.0); end; (* 以下のようなやり方をすると誤差で適切なサイズにならない場合がある のでやっちゃだめです if (Original.Height * YPercent/100.0) <= FitSize.Height then begin Result.Width := Round(Original.Width * YPercent/100.0); Result.Height := Round(Original.Height * YPercent/100.0); end else if (Original.Width * XPercent/100.0) <= FitSize.Width then begin Result.Width := Round(Original.Width * XPercent/100.0); Result.Height := Round(Original.Height * XPercent/100.0); end else begin raise Exception.Create('サイズFitにてエラーです'); Result := RectSize(0,0); end; *) //--△----------------------▲-- end; (*------------------------------- //Rectがちゃんと内部に入っているかどうか確認する関数 機能: 引数説明: InCheckRect: 中に入っていると想定するRect BaseRect: 外を囲っていると想定するRect 戻り値: true:内部にある false:はみ出ているか完全に外部にある 備考: 境界線上で位置が重なっても 内部と判断している 履歴: 2000/10/19 2011/09/02(金) ・ NormalizeRectを追加して修正 -------------------------------*) function RectInRect(InCheckRect, BaseRect: TRect): Boolean; begin Result := false; InCheckRect := NormalizeRect(InCheckRect); BaseRect := NormalizeRect(BaseRect); if (BaseRect.Left <= InCheckRect.Left) and (InCheckRect.Right <= BaseRect.Right) and (BaseRect.Top <= InCheckRect.Top) and (InCheckRect.Bottom <= BaseRect.Bottom) then begin Result := true; end; end; (*------------------------------- //Rectがちゃんと外部になっているかどうか確認する関数 機能: 引数説明: OutCheckRect: 外に出ていると想定するRect BaseRect: このRectに対してCheckRectの関係を確認してる 戻り値: true:外部にある false:内部にあるか一部重なっている 備考: 境界線上で位置が重なっても 内部と判断している 履歴: 2000/10/19 -------------------------------*) function RectOutRect(OutCheckRect, BaseRect: TRect): Boolean; begin Result := false; if (OutCheckRect.Bottom < BaseRect.Top) or (BaseRect.Bottom < OutCheckRect.Top) or (OutCheckRect.Right < BaseRect.Left) or (BaseRect.Right < OutCheckRect.Left) then begin Result := true; end; end; {------------------------------- // RectをWidthとHightで生成します 備考: 標準関数ではLeft,Top,Right,Bottomで Rectを指定するので、 Width,Hightで指定する関数を作ってみた 履歴: 2005/07/09 //--▼----------------------▽--} function RectSquare(Left, Top, Width, Hight: Integer): TRect; begin Result := Types.Rect( Left, Top, Left+Width-1, Top+Hight-1); end; //--△----------------------▲-- {------------------------------- // ポップアップする場所を返します 機能: 通常右下、はみ出したら上や左にポップアップする関数 Pointに対してと、Rectに対してポップアップできる 引数説明: BaseRect:ポップアップする領域を指定します デスクトップのWorkAreaなど指定してください。 RectValue:ポップアップする窓のRectを指定します PopupPoint:ポップアップ開始位置を指定します。 マウスカーソル位置などを指定してください。 戻り値: ポップアップするべき位置を返します 備考: 履歴: 2005/07/09 2008/03/06 BaseRectからはみ出した位置から ポップアップさせようとする時にも OutsidePointMoveInsideを作成して対応した。 2011/04/12(火) ・ PopupRectポイントの左/上ポップアップ処理が さらにはみ出るときは右下ポップのままにした ・ PopupRectレクトを作成した。 //--▼----------------------▽--} //Rectの外のポイントを指定した場合、Rect内に移動する関数 //function OutsidePointMoveInside(BaseRect: TRect; Point: TPoint): TPoint; // // function Min(A, B: Integer): Integer; // begin // if A <= B then Result := A // else Result := B; // end; // function Max(A, B: Integer): Integer; // begin // if A <= B then Result := B // else Result := A; // end; // //begin // BaseRect := Rect(Min(BaseRect.Left, BaseRect.Right), // Min(BaseRect.Top, BaseRect.Bottom), // Max(BaseRect.Left, BaseRect.Right), // Max(BaseRect.Top, BaseRect.Bottom) ); // {↑Rectが正しく} // if Point.X < BaseRect.Left then Point.X := BaseRect.Left; // if BaseRect.Right < Point.X then Point.X := BaseRect.Right; // if Point.Y < BaseRect.Top then Point.Y := BaseRect.Top; // if BaseRect.Bottom < Point.Y then Point.Y := BaseRect.Bottom; // Result := Point; //end; function OutsidePointMoveInside(BaseRect: TRect; Point: TPoint): TPoint; begin BaseRect := NormalizeRect(BaseRect); if Point.X < BaseRect.Left then Point.X := BaseRect.Left; if BaseRect.Right < Point.X then Point.X := BaseRect.Right; if Point.Y < BaseRect.Top then Point.Y := BaseRect.Top; if BaseRect.Bottom < Point.Y then Point.Y := BaseRect.Bottom; Result := Point; end; function PopupRect(BaseRect, RectValue: TRect; PopupPoint: TPoint): TRect; overload; begin RectValue := RectMovePoint(RectValue, PopupPoint); //ポップアップ開始位置がRect内にない場合に移動する PopupPoint := OutsidePointMoveInside(BaseRect, PopupPoint); //ポップアップ位置が右にはみ出している場合は //左にポップアップすること if BaseRect.Right <= (PopupPoint.X + GetRectWidth(RectValue)) then begin if BaseRect.Left <= (PopupPoint.X - GetRectWidth(RectValue)) then RectValue := RectMove(RectValue, -GetRectWidth(RectValue), 0); end; //ポップアップ位置が下にはみ出している場合は //左にポップアップすること if BaseRect.Bottom <= (PopupPoint.Y + GetRectHeight(RectValue)) then begin if BaseRect.Top <= (PopupPoint.Y - GetRectHeight(RectValue)) then RectValue := RectMove(RectValue, 0, -GetRectHeight(RectValue)); end; Result := RectValue end; //function PopupRect(BaseRect, RectValue: TRect; TargetRect: TRect): TRect; overload; //var // PopupPoint: TPoint; //begin // PopupPoint := GetBottomLeft(TargetRect); // RectValue := RectMovePoint(RectValue, PopupPoint); // // //ポップアップ開始位置がRect内にない場合に移動する // PopupPoint := OutsidePointMoveInside(BaseRect, PopupPoint); // // if BaseRect.Right <= (PopupPoint.X + GetRectWidth(RectValue)) then // begin // if BaseRect.Left <= (TargetRect.Right - GetRectWidth(RectValue)) then // RectValue := RectMove(RectValue, -GetRectWidth(RectValue)+GetRectWidth(TargetRect), 0); // end; // // if BaseRect.Bottom <= (PopupPoint.Y + GetRectHeight(RectValue)) then // begin // if BaseRect.Top <= (TargetRect.Top - GetRectHeight(RectValue)) then // RectValue := RectMove(RectValue, 0, -GetRectHeight(RectValue)-GetRectHeight(TargetRect)); // end; // Result := RectValue //end; function PopupRect(ScreenRect, PopupRect, PopupButtonRect: TRect; Direct: TPopupDirect = pdTopLeftToBottomRight): TRect; overload; var PopupPoint: TPoint; MovePoint: TPoint; begin case Direct of pdTopLeftToBottomRight: begin PopupPoint := GetBottomLeft(OutsideRectMoveInside(ScreenRect, PopupButtonRect)); PopupRect := RectMovePoint(PopupRect, PopupPoint); if ScreenRect.Right <= PopupRect.Right then begin MovePoint := Point(PopupButtonRect.Right - GetRectWidth(PopupRect), PopupRect.Top); if ScreenRect.Left <= MovePoint.X then PopupRect := RectMovePoint(PopupRect, MovePoint); end; if ScreenRect.Bottom <= PopupRect.Bottom then begin MovePoint := Point(PopupRect.Left, PopupButtonRect.Top - GetRectHeight(PopupRect)); if ScreenRect.Top <= MovePoint.Y then PopupRect := RectMovePoint(PopupRect,MovePoint); end; end; pdTopRightToBottomLeft: begin PopupPoint := GetBottomRight(OutsideRectMoveInside(ScreenRect, PopupButtonRect)); PopupPoint := PointMove(PopupPoint, - GetRectWidth(PopupRect), 0); PopupRect := RectMovePoint(PopupRect, PopupPoint); if PopupRect.Left <= ScreenRect.Left then begin MovePoint := Point(PopupButtonRect.Left, PopupRect.Top); if (MovePoint.X + GetRectWidth(PopupRect)) <= ScreenRect.Right then PopupRect := RectMovePoint(PopupRect, MovePoint); end; if ScreenRect.Bottom <= PopupRect.Bottom then begin MovePoint := Point(PopupRect.Left, PopupButtonRect.Top - GetRectHeight(PopupRect)); if ScreenRect.Top <= MovePoint.Y then PopupRect := RectMovePoint(PopupRect,MovePoint); end; Result := PopupRect end; end; Result := PopupRect end; //--△----------------------▲-- {------------------------------- // 指定したRectからはみ出たRectを指定したRectに収まるように動かす関数 機能: BaseRect:基準となる大きいRect TargetRect:動かすRect 備考: BaseRectには次のような値を取得する場合が多い NoneTaskBarDesktopRect タスクバーを含まないDesktop領域 GetDesktopRect マルチディスプレイ環境でもデスクトップ領域全体を示す 履歴: 2006/12/23(土) 12:17 //--▼----------------------▽--} function OutsideRectMoveInside(BaseRect, TargetRect: TRect; TopFit: Boolean = True; BottomFit: Boolean = True; LeftFit: Boolean = True; RightFit: Boolean = True): TRect; begin {↓WindowのRectを取得} // GetWindowRect(WndHandle, WinRect); {↓WindowがDesktop領域より大きければサイズ修正} if GetRectWidth(BaseRect) < GetRectWidth(TargetRect) then TargetRect.Right := GetRectWidth(BaseRect) + TargetRect.Left; if GetRectHeight(BaseRect) < GetRectHeight(TargetRect) then TargetRect.Bottom := GetRectHeight(BaseRect) + TargetRect.Top; {↓上の位置が限界より更に上なら} if TopFit and (TargetRect.Top < BaseRect.Top) then begin {↓Topを上端に移動} TargetRect := RectMove(TargetRect, 0, BaseRect.Top - TargetRect.Top); end; {↓下の位置が限界より更に下なら} if BottomFit and (BaseRect.Bottom < TargetRect.Bottom) then begin {↓Bottomを下端に移動} TargetRect := RectMove(TargetRect, 0, -(TargetRect.Bottom - BaseRect.Bottom)); end; {↓左の位置が限界より更に左なら} if LeftFit and (TargetRect.Left < BaseRect.Left) then begin {↓Leftを左端に移動} TargetRect := RectMove(TargetRect, BaseRect.Left - TargetRect.Left, 0); end; {↓右の位置が限界より更に右なら} if RightFit and (BaseRect.Right < TargetRect.Right) then begin {↓Bottomを右端に移動} TargetRect := RectMove(TargetRect, -(TargetRect.Right - BaseRect.Right), 0); end; Result := TargetRect; end; //--△----------------------▲-- {------------------------------- // Rectサイズをそれぞれの位置に対して増減させます 機能: 枠を大きくしたり小さくしたりできます 1,1,1,1と指定すると、枠が1ピクセル分大きくなります 備考: 履歴: 2007/05/25(金) 11:45 //--▼----------------------▽--} function RectSizePlus(const Rect: TRect; LeftValue, TopValue, RightValue, BottomValue: Integer): TRect; begin Result := Types.Rect( Rect.Left - LeftValue, Rect.Top - TopValue, Rect.Right + RightValue, Rect.Bottom + BottomValue); end; //--△----------------------▲-- {---------------------------------------- // RectのBottomLeft/TopRightをTPointで取得する関数 機能: BoundRect.TopLeft/BottomRightでは取得できない TopRight/BottomLeftを取得する関数 備考: 履歴: 2010/04/16(金) //----------------------------------------} function GetBottomLeft(const Rect: TRect): TPoint; begin Result := Point(Rect.Left, Rect.Bottom); end; function GetTopRight(const Rect: TRect): TPoint; begin Result := Point(Rect.Right, Rect.Top); end; function GetBottomRight(const Rect: TRect): TPoint; begin Result := Rect.BottomRight; end; function GetTopLeft(const Rect: TRect): TPoint; begin Result := Rect.TopLeft; end; //---------------------------------------- end.