📄 uvectorgraphics.pas
字号:
begin
Assert(not Files.IsEmpty);
inherited Create; //create the object
FFiles := Files; //save the parameters
FFormatHandler := FormatHandler;
FURLCallBack := URLCallBack;
end;
{Frees the drawer of diagrams and the drawing object. }
destructor TVectorFileDrawer.Destroy;
begin
FFormatHandler.Free; //free the drawing object
inherited Destroy; //free this object
end;
{Calculates the size of all the boxes of files in the diagram. This is done by
ascertaining the biggest size of the file names and adding a margin.
~result the needed size for the boxes }
function TVectorFileDrawer.CalculateBoxSize: TSize;
var i :Integer; //counter through all files
TextSize :TSize; //size of the names of the files
begin
//let the format handler prepare itself for measuring the text sizes
FFormatHandler.PrepareTextMeasuring;
try
Result.cx := 0; //maximum size of all names of files;
Result.cy := 0; //empty so far
for i := 0 to FFiles.Count - 1 do //for each file
begin //get size of its name
TextSize := FFormatHandler.GetTextExtent(FFiles[i].InternalFileName);
if TextSize.cx > Result.cx then //and get the maximum
Result.cx := TextSize.cx;
if TextSize.cy > Result.cy then
Result.cy := TextSize.cy;
end;
Inc(Result.cx, 10); //add a margin of 5 pixels
Inc(Result.cy, 10); //on each side
finally
FFormatHandler.EndTextMeasuring; //shut down the text measuring again
end;
end;
{Draws all files on the image. }
procedure TVectorFileDrawer.DrawFiles;
//Draws the arrows showing the usage between the file and other files.
procedure DrawUse(SPos: TPoint; List: TStrings; BoldArrows: Boolean;
OrgFile: TPascalFile); forward;
//Calculates the position of all used files.
procedure CalcUnits(List: TStrings); forward;
//Sets the position of the next file to be drawn.
procedure CalcFilePos(Index: Cardinal; CountPerColumn: Integer = 0); forward;
var Files :PPascalFile; //list of the files
Positions :PPoint; //positions of the files
{Draws the arrows showing the usage between the file and other files.
~param SPos position of the file to draw arrows from
~param List a list of used files
~param BoldArrows whether bold arrows should be used to indicate usage in the
implementation part
~param OrgFile the file to print the usage of (to recognize self-usage) }
procedure DrawUse(SPos: TPoint; List: TStrings; BoldArrows: Boolean;
OrgFile: TPascalFile);
{Draws an arrow from the source point of the file to this destination,
modifying the positions for better clarity.
~param Dest the destination point to draw the arrow to }
procedure DrawArrowTo(Dest: TPoint);
var Src :TPoint; //the modified source point of the arrow
Part :Extended; //which part of the arrow is its arrowhead
Arr1, Arr2 :TPoint; //points of the arrowhead
Tmp :Integer; //helper for calculating Arr1 and Arr2
begin
Src := SPos; //use original source point
if Src.y < Dest.y then //if files not in same column
Inc(Src.y, FBoxSize.cy) //draw from one side to the other
else
if Src.y > Dest.y then
Inc(Dest.y, FBoxSize.cy)
else
begin
Inc(Src.y, FBoxSize.cy div 3); //else displace arrows a bit
Inc(Dest.y, FBoxSize.cy * 2 div 3);
end;
if Src.x < Dest.x then //same for horizontal position
Inc(Src.x, FBoxSize.cx)
else
if Src.x > Dest.x then
Inc(Dest.X, FBoxSize.cx)
else
begin
Inc(Src.x, FBoxSize.cx div 3);
Inc(Dest.x, FBoxSize.cx * 2 div 3);
end;
if (Src.x <> Dest.x) or (Src.y <> Dest.y) then //arrow not empty?
begin
Part := (FBoxSize.cy * 3 div 4) / //calculate portion of arrowhead
Sqrt(Sqr(Src.x - Dest.x) + Sqr(Src.y - Dest.y));
//calculate position on arrow
Arr1.x := Round(Part * Src.x + (1 - Part) * Dest.x);
Arr1.y := Round(Part * Src.y + (1 - Part) * Dest.y);
Arr2 := Arr1;
Tmp := Arr1.x; //now move both arrowhead points
Dec(Arr1.x, Arr1.y - Dest.y); //orthographic/upright
Inc(Arr1.y, Tmp - Dest.x);
Inc(Arr2.x, Arr2.y - Dest.y);
Dec(Arr2.y, Tmp - Dest.x);
//draw the arrow
FFormatHandler.DrawArrow(Src, Dest, Arr1, Arr2, BoldArrows);
end;
end;
var i :Integer; //counter through the files
Item :TPascalFile; //each file
FRunner :PPascalFile; //runner through list of files
DPos :PPoint; //position to draw the file
begin
for i := 0 to List.Count - 1 do //for each file
begin
Item := TPascalFile(List.Objects[i]); //get it
if Assigned(Item) and (Item <> OrgFile) then //if not used by itself
begin
FRunner := Files;
DPos := Positions;
while (FRunner^ <> Item) do //search file to get its
begin //position
Inc(DPos);
Inc(FRunner);
end;
DrawArrowTo(DPos^); //draw arrow to its position
end;
end;
end;
{Calculates the position of all used files.
~param List the list of files to position }
procedure CalcUnits(List: TStrings);
var i :Integer; //counter through the files
Item :TPascalFile; //each file in the list
FRunner :PPascalFile; //runner through list of all files
begin
for i := 0 to List.Count - 1 do //for each file
begin
Item := TPascalFile(List.Objects[i]); //get it
if Assigned(Item) then //if assigned
begin
FRunner := Files;
while (FRunner^ <> Item) do //search in the list
Inc(FRunner);
//draw file by its index in the list
CalcFilePos((Cardinal(FRunner) - Cardinal(Files)) div SizeOf(FRunner^));
end;
end;
end;
var CanvasSize :TSize; //size of used canvas for the figure
NextUnit :TPoint; //position of the next file to draw
{Sets the position of the next file to be drawn.
~param Index index of the file in the list
~param CountPerColumn number of files in a column; 0: as many as fit }
procedure CalcFilePos(Index: Cardinal; CountPerColumn: Integer = 0);
var PositionP :PPoint; //position of the file
begin
PositionP := Positions;
Inc(PositionP, Index); //get position
if PositionP.x = -1 then //file not yet positioned?
begin
//center file if number given
if (CountPerColumn <> 0) and (NextUnit.y = 0) then
NextUnit.y := (CanvasSize.cy + FBoxSize.cy) div CountPerColumn div 2;
PositionP^ := NextUnit; //save position of file
if CountPerColumn = 0 then //normal list(/block) of files?
begin
Inc(NextUnit.y, FBoxSize.cy * 4); //next position
if NextUnit.y > CanvasSize.cy then //end of page reached?
begin
NextUnit.y := 0; //start next column
Inc(NextUnit.x, FBoxSize.cx * 2);
end;
end
else
Inc(NextUnit.y, FBoxSize.cy * 4); //go to next "centered" position
end;
end;
var FilesPerColumn :Cardinal; //number of files fitting in a column
Count :Cardinal; //number of files in the list
FRunner :PPascalFile; //runner through the list of files
i :Cardinal; //general counter through the files
C :Cardinal; //number of project files
CPC :Cardinal; //number of project files in a column
PRunner :PPoint; //runner through the positions
begin
Count := FFiles.Count; //get number of files
//create buffer of positions of files
GetMem(Positions, SizeOf(Positions^) * Count);
try
FillChar(Positions^, SizeOf(Positions^) * Count, $FF); //set positions to -1
//create buffer of files
GetMem(Files, SizeOf(Files^) * Count);
try
FRunner := Files;
for i := 0 to Count - 1 do //fill buffer with files
begin
FRunner^ := FFiles.GetFile(i);
Inc(FRunner);
end;
NextUnit.x := 0; //start printing of files at top left
NextUnit.y := 0;
C := 0; //count all project files
FRunner := Files;
for i := 0 to Count - 1 do //for each file
begin
if FRunner^.FileType <> sftUnit then //if not a unit
Inc(C); //add number
Inc(FRunner);
end;
//get number of files per column, creating figure approximately with the
//same height as the width
FilesPerColumn := Round(Sqrt(Count - C) + 0.3);
if C > FilesPerColumn then
FilesPerColumn := C;
//set height of the figure
CanvasSize.cy := (FilesPerColumn - 1) * (Cardinal(FBoxSize.cy) * 4);
if C <> 0 then //project files parsed?
begin
if C < FilesPerColumn then //fit in one column?
CPC := C //use this column completely
else
CPC := 0; //center all files
FRunner := Files;
for i := 0 to Count - 1 do //for each (project) file
begin
if FRunner^.FileType <> sftUnit then //is a project file?
begin
CalcFilePos(i, CPC); //calculate its position
if NextUnit.y = 0 then //column ended?
begin
//subtract processed files
if C > FilesPerColumn then
Dec(C, FilesPerColumn)
else
C := 0;
//center rest of files if they fit in one column
if C < FilesPerColumn then
CPC := C
else
CPC := 0;
end;
end;
Inc(FRunner); //next file
end;
if NextUnit.y <> 0 then //not in a new column?
begin
NextUnit.y := 0; //start new column
Inc(NextUnit.x, FBoxSize.cx * 2);
end;
FRunner := Files;
for i := 0 to Count - 1 do //for each (project) file
begin
if FRunner^.FileType <> sftUnit then //is a project file?
begin
//calculate position of all used files in interface and
//implementation, i.e. all units of the project
CalcUnits(FRunner^.UsedUnitList[fpInterface]);
CalcUnits(FRunner^.UsedUnitList[fpMain]);
end;
Inc(FRunner); //next file
end;
if NextUnit.y <> 0 then //not a new column?
begin
NextUnit.y := 0; //start a new column
Inc(NextUnit.x, FBoxSize.cx * 2);
end;
end;
for i := 0 to Count - 1 do //for any other file
CalcFilePos(i); //calculate its position
CanvasSize.cx := NextUnit.x; //set width of figure
if NextUnit.y = 0 then //if new column has been started
Dec(CanvasSize.cx, FBoxSize.cx * 2); //subtract it
//prepare the drawer object to draw the diagram
FFormatHandler.InitImageObjects(CanvasSize.cx + FBoxSize.cx + 1,
CanvasSize.cy + FBoxSize.cy,
'Figure of the Interdependency of the Files');
try
FRunner := Files;
PRunner := Positions;
for i := 0 to Count - 1 do //for each file
begin
//draw arrows showing usage of other units in the interface and
DrawUse(PRunner^, FRunner^.UsedUnitList[fpInterface],
FRunner^.FileType <> sftUnit, FRunner^);
DrawUse(PRunner^, FRunner^.UsedUnitList[fpMain], //in implementation
True, FRunner^);
Inc(FRunner); //next file
Inc(PRunner); //and next position
end;
FRunner := Files;
PRunner := Positions;
for i := 0 to Count - 1 do //for each file
begin
//if links should be included
if Assigned(FURLCallBack) then
//get the link target and set it
FFormatHandler.SetNextTextBoxesLinkTarget(FURLCallBack(FRunner^));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -