mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-18 20:39:14 +02:00
cody: lvl graph: scale nodes
git-svn-id: trunk@40166 -
This commit is contained in:
parent
8679c001a4
commit
26de43d572
@ -31,7 +31,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
types, math, contnrs, Classes, SysUtils, FPCanvas, FPimage,
|
types, math, contnrs, Classes, SysUtils, FPCanvas, FPimage,
|
||||||
LazLogger, AvgLvlTree, ComCtrls, Controls, Graphics, LCLType;
|
LazLogger, AvgLvlTree, ComCtrls, Controls, Graphics, LCLType, Forms;
|
||||||
|
|
||||||
type
|
type
|
||||||
TCodyCtrlPalette = array of TFPColor;
|
TCodyCtrlPalette = array of TFPColor;
|
||||||
@ -335,8 +335,7 @@ type
|
|||||||
property Nodes[Index: integer]: TLvlGraphNode read GetNodes; default;
|
property Nodes[Index: integer]: TLvlGraphNode read GetNodes; default;
|
||||||
function IndexOf(Node: TLvlGraphNode): integer;
|
function IndexOf(Node: TLvlGraphNode): integer;
|
||||||
function Count: integer;
|
function Count: integer;
|
||||||
function GetTotalInWeight: single;
|
function GetTotalInOutWeights: single; // sum of all nodes Max(InWeight,OutWeight)
|
||||||
function GetTotalOutWeight: single;
|
|
||||||
property Index: integer read FIndex;
|
property Index: integer read FIndex;
|
||||||
property Graph: TLvlGraph read FGraph;
|
property Graph: TLvlGraph read FGraph;
|
||||||
property DrawPosition: integer read FDrawPosition write SetDrawPosition;
|
property DrawPosition: integer read FDrawPosition write SetDrawPosition;
|
||||||
@ -391,7 +390,8 @@ type
|
|||||||
property LevelClass: TLvlGraphLevelClass read FLevelClass;
|
property LevelClass: TLvlGraphLevelClass read FLevelClass;
|
||||||
|
|
||||||
procedure CreateTopologicalLevels; // create levels from edges
|
procedure CreateTopologicalLevels; // create levels from edges
|
||||||
procedure SetAllNodeDrawSizes(PixelPerWeight: single = 1.0);
|
procedure ScaleNodeDrawSizes(NodeGap, HardMaxTotal, HardMinOneNode, SoftMaxTotal, SoftMinOneNode: integer);
|
||||||
|
procedure SetAllNodeDrawSizes(PixelPerWeight: single = 1.0; MinWeight: single = 0.0);
|
||||||
procedure MarkBackEdges;
|
procedure MarkBackEdges;
|
||||||
procedure MinimizeCrossings; // set all Node.Position to minimize crossings
|
procedure MinimizeCrossings; // set all Node.Position to minimize crossings
|
||||||
procedure MinimizeOverlappings(Gap: integer = 1; aLevel: integer = -1); // set all Node.Position to minimize overlappings
|
procedure MinimizeOverlappings(Gap: integer = 1; aLevel: integer = -1); // set all Node.Position to minimize overlappings
|
||||||
@ -430,8 +430,7 @@ type
|
|||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure EraseBackground({%H-}DC: HDC); override;
|
procedure EraseBackground({%H-}DC: HDC); override;
|
||||||
property Graph: TLvlGraph read FGraph;
|
property Graph: TLvlGraph read FGraph;
|
||||||
procedure AutoLayout(RndColors: boolean = true; MinPixelPerWeight: single = 1.0;
|
procedure AutoLayout(RndColors: boolean = true; NodeGap: integer = 1); virtual;
|
||||||
MaxPixelPerWeight: single = 30.0; NodeGap: integer = 1); virtual;
|
|
||||||
procedure Invalidate; override;
|
procedure Invalidate; override;
|
||||||
procedure BeginUpdate;
|
procedure BeginUpdate;
|
||||||
procedure EndUpdate;
|
procedure EndUpdate;
|
||||||
@ -692,22 +691,16 @@ begin
|
|||||||
Result:=fNodes.Count;
|
Result:=fNodes.Count;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLvlGraphLevel.GetTotalInWeight: single;
|
function TLvlGraphLevel.GetTotalInOutWeights: single;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
Node: TLvlGraphNode;
|
||||||
begin
|
begin
|
||||||
Result:=0;
|
Result:=0;
|
||||||
for i:=0 to Count-1 do
|
for i:=0 to Count-1 do begin
|
||||||
Result+=Nodes[i].InWeight;
|
Node:=Nodes[i];
|
||||||
|
Result+=Max(Node.InWeight,Node.OutWeight);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLvlGraphLevel.GetTotalOutWeight: single;
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
Result:=0;
|
|
||||||
for i:=0 to Count-1 do
|
|
||||||
Result+=Nodes[i].OutWeight;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TCustomLvlGraphControl }
|
{ TCustomLvlGraphControl }
|
||||||
@ -787,7 +780,7 @@ begin
|
|||||||
Level:=Graph.Levels[i];
|
Level:=Graph.Levels[i];
|
||||||
for j:=0 to Level.Count-1 do begin
|
for j:=0 to Level.Count-1 do begin
|
||||||
Node:=Level.Nodes[j];
|
Node:=Level.Nodes[j];
|
||||||
debugln(['TCustomLvlGraphControl.Paint ',Node.Caption,' ',dbgs(FPColorToTColor(Node.Color)),' Level.DrawPosition=',Level.DrawPosition,' Node.DrawPosition=',Node.DrawPosition]);
|
debugln(['TCustomLvlGraphControl.Paint ',Node.Caption,' ',dbgs(FPColorToTColor(Node.Color)),' Level.DrawPosition=',Level.DrawPosition,' Node.DrawPosition=',Node.DrawPosition,' ',Node.DrawPositionEnd]);
|
||||||
Canvas.Brush.Color:=FPColorToTColor(Node.Color);
|
Canvas.Brush.Color:=FPColorToTColor(Node.Color);
|
||||||
Canvas.Rectangle(Level.DrawPosition,Node.DrawPosition,
|
Canvas.Rectangle(Level.DrawPosition,Node.DrawPosition,
|
||||||
Level.DrawPosition+NodeWidth,Node.DrawPositionEnd);
|
Level.DrawPosition+NodeWidth,Node.DrawPositionEnd);
|
||||||
@ -816,26 +809,28 @@ begin
|
|||||||
// Paint paints all, no need to erase background
|
// Paint paints all, no need to erase background
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomLvlGraphControl.AutoLayout(RndColors: boolean;
|
procedure TCustomLvlGraphControl.AutoLayout(RndColors: boolean; NodeGap: integer
|
||||||
MinPixelPerWeight: single; MaxPixelPerWeight: single; NodeGap: integer);
|
);
|
||||||
{ Min/MaxPixelPerWeight: used to scale Node.DrawSize depending on weight of
|
{ Min/MaxPixelPerWeight: used to scale Node.DrawSize depending on weight of
|
||||||
incoming and outgoing edges
|
incoming and outgoing edges
|
||||||
NodeGap: space between nodes
|
NodeGap: space between nodes
|
||||||
}
|
}
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
Level: TLvlGraphLevel;
|
|
||||||
LvlWeight: Single;
|
|
||||||
HeaderHeight: integer;
|
HeaderHeight: integer;
|
||||||
DrawHeight: Integer;
|
|
||||||
Palette: TCodyCtrlPalette;
|
Palette: TCodyCtrlPalette;
|
||||||
|
TxtH: LongInt;
|
||||||
begin
|
begin
|
||||||
debugln(['TCustomLvlGraphControl.AutoLayout ',DbgSName(Self),' ClientRect=',dbgs(ClientRect)]);
|
debugln(['TCustomLvlGraphControl.AutoLayout ',DbgSName(Self),' ClientRect=',dbgs(ClientRect)]);
|
||||||
Exclude(FFlags,lgcNeedAutoLayout);
|
Exclude(FFlags,lgcNeedAutoLayout);
|
||||||
BeginUpdate;
|
BeginUpdate;
|
||||||
try
|
try
|
||||||
|
if HandleAllocated then
|
||||||
|
TxtH:=Canvas.TextHeight('M')
|
||||||
|
else
|
||||||
|
TxtH:=Max(10,abs(Font.Height));
|
||||||
if Caption<>'' then begin
|
if Caption<>'' then begin
|
||||||
HeaderHeight:=round(1.5*abs(Font.Height));
|
HeaderHeight:=round(1.5*TxtH);
|
||||||
end else
|
end else
|
||||||
HeaderHeight:=0;
|
HeaderHeight:=0;
|
||||||
|
|
||||||
@ -843,27 +838,15 @@ begin
|
|||||||
Graph.CreateTopologicalLevels;
|
Graph.CreateTopologicalLevels;
|
||||||
|
|
||||||
// Level DrawPosition
|
// Level DrawPosition
|
||||||
for i:=0 to Graph.LevelCount-1 do begin
|
for i:=0 to Graph.LevelCount-1 do
|
||||||
Level:=Graph.Levels[i];
|
Graph.Levels[i].DrawPosition:=i*(ClientWidth div Graph.LevelCount)+NodeGap;
|
||||||
Level.DrawPosition:=i*(ClientWidth div Graph.LevelCount)+NodeGap;
|
|
||||||
debugln(['TCustomLvlGraphControl.AutoLayout ',i,' ',ClientWidth div Graph.LevelCount]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// set Nodes.DrawSize
|
// scale Nodes.DrawSize
|
||||||
// Use for each Node the maximum of InSize and OutSize, which is the weight
|
// Preferably the smallest node should be the size of the text
|
||||||
// of incoming and outgoing edges.
|
// Preferably the largest level should fit without needing a scrollbar
|
||||||
// Consider NodeGap
|
Graph.ScaleNodeDrawSizes(NodeGap,Screen.Height*2,1,ClientHeight-HeaderHeight,TxtH);
|
||||||
DrawHeight:=ClientHeight-HeaderHeight;
|
|
||||||
for i:=0 to Graph.LevelCount-1 do begin
|
|
||||||
Level:=Graph.Levels[i];
|
|
||||||
LvlWeight:=Max(Level.GetTotalInWeight,Level.GetTotalOutWeight);
|
|
||||||
if LvlWeight<0.001 then LvlWeight:=0.001;
|
|
||||||
MaxPixelPerWeight:=Min(MaxPixelPerWeight,
|
|
||||||
single(DrawHeight-(Level.Count-1)*NodeGap)/LvlWeight);
|
|
||||||
MaxPixelPerWeight:=Max(MinPixelPerWeight,MaxPixelPerWeight);
|
|
||||||
end;
|
|
||||||
Graph.SetAllNodeDrawSizes(MaxPixelPerWeight);
|
|
||||||
|
|
||||||
|
// sort nodes within levels to avoid crossings
|
||||||
Graph.MinimizeCrossings;
|
Graph.MinimizeCrossings;
|
||||||
|
|
||||||
// position nodes without overlapping
|
// position nodes without overlapping
|
||||||
@ -1206,14 +1189,99 @@ begin
|
|||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TLvlGraph.SetAllNodeDrawSizes(PixelPerWeight: single);
|
procedure TLvlGraph.ScaleNodeDrawSizes(NodeGap, HardMaxTotal, HardMinOneNode,
|
||||||
|
SoftMaxTotal, SoftMinOneNode: integer);
|
||||||
|
{ NodeGap: minimum space between nodes
|
||||||
|
HardMaxTotal: maximum size of largest level
|
||||||
|
HardMinOneNode: minimum size of a node
|
||||||
|
SoftMaxTotal: preferred maximum size of the largest level, total can be bigger
|
||||||
|
to achieve HardMinOneNode
|
||||||
|
SoftMinOneNode: preferred minimum size of a node, can be smaller to achieve
|
||||||
|
SoftMaxTotal
|
||||||
|
Order of precedence: HardMinOneNode, SoftMaxTotal, SoftMinOneNode
|
||||||
|
}
|
||||||
|
var
|
||||||
|
SmallestWeight: Single;
|
||||||
|
i: Integer;
|
||||||
|
Node: TLvlGraphNode;
|
||||||
|
j: Integer;
|
||||||
|
Edge: TLvlGraphEdge;
|
||||||
|
Level: TLvlGraphLevel;
|
||||||
|
LvlWeight: Single;
|
||||||
|
MinPixelPerWeight, PrefMinPixelPerWeight: single;
|
||||||
|
DrawHeight: integer;
|
||||||
|
PixelPerWeight, MaxPixelPerWeight, PrefMaxPixelPerWeight: single;
|
||||||
|
begin
|
||||||
|
debugln(['TLvlGraph.ScaleNodeDrawSizes NodeGap=',NodeGap,
|
||||||
|
' HardMaxTotal=',HardMaxTotal,' HardMinOneNode=',HardMinOneNode,
|
||||||
|
' SoftMaxTotal=',SoftMaxTotal,' SoftMinOneNode=',SoftMinOneNode]);
|
||||||
|
// sanitize input
|
||||||
|
HardMinOneNode:=Max(0,HardMinOneNode);
|
||||||
|
SoftMinOneNode:=Max(SoftMinOneNode,HardMinOneNode);
|
||||||
|
HardMaxTotal:=Max(1,HardMaxTotal);
|
||||||
|
SoftMaxTotal:=Min(Max(1,SoftMaxTotal),HardMaxTotal);
|
||||||
|
|
||||||
|
SmallestWeight:=-1.0;
|
||||||
|
for i:=0 to NodeCount-1 do begin
|
||||||
|
Node:=Nodes[i];
|
||||||
|
for j:=0 to Node.OutEdgeCount-1 do begin
|
||||||
|
Edge:=Node.OutEdges[j];
|
||||||
|
if Edge.Weight<=0.0 then continue;
|
||||||
|
if (SmallestWeight<0) or (SmallestWeight>Edge.Weight) then
|
||||||
|
SmallestWeight:=Edge.Weight;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if SmallestWeight<0 then SmallestWeight:=1.0;
|
||||||
|
if SmallestWeight>0 then begin
|
||||||
|
MinPixelPerWeight:=single(HardMinOneNode)/SmallestWeight;
|
||||||
|
PrefMinPixelPerWeight:=single(SoftMinOneNode)/SmallestWeight;
|
||||||
|
end else begin
|
||||||
|
MinPixelPerWeight:=single(HardMinOneNode);
|
||||||
|
PrefMinPixelPerWeight:=single(SoftMinOneNode);
|
||||||
|
end;
|
||||||
|
debugln(['TLvlGraph.ScaleNodeDrawSizes SmallestWeight=',SmallestWeight,
|
||||||
|
' MinPixelPerWeight=',MinPixelPerWeight,
|
||||||
|
' PrefMinPixelPerWeight=',PrefMinPixelPerWeight]);
|
||||||
|
|
||||||
|
MaxPixelPerWeight:=0.0;
|
||||||
|
PrefMaxPixelPerWeight:=0.0;
|
||||||
|
for i:=0 to LevelCount-1 do begin
|
||||||
|
Level:=Levels[i];
|
||||||
|
// LvlWeight = how much weight to draw
|
||||||
|
LvlWeight:=Level.GetTotalInOutWeights;
|
||||||
|
if LvlWeight=0.0 then continue;
|
||||||
|
// DrawHeight - how much pixel left to draw the weight
|
||||||
|
DrawHeight:=Max(1,HardMaxTotal-(Level.Count-1)*NodeGap);
|
||||||
|
PixelPerWeight:=single(DrawHeight)/LvlWeight;
|
||||||
|
if (MaxPixelPerWeight=0.0) or (MaxPixelPerWeight>PixelPerWeight) then
|
||||||
|
MaxPixelPerWeight:=PixelPerWeight;
|
||||||
|
DrawHeight:=Max(1,SoftMaxTotal-(Level.Count-1)*NodeGap);
|
||||||
|
PixelPerWeight:=single(DrawHeight)/LvlWeight;
|
||||||
|
if (PrefMaxPixelPerWeight=0.0) or (PrefMaxPixelPerWeight>PixelPerWeight) then
|
||||||
|
PrefMaxPixelPerWeight:=PixelPerWeight;
|
||||||
|
end;
|
||||||
|
debugln(['TLvlGraph.ScaleNodeDrawSizes MaxPixelPerWeight=',MaxPixelPerWeight,' PrefMaxPixelPerWeight=',PrefMaxPixelPerWeight]);
|
||||||
|
|
||||||
|
PixelPerWeight:=PrefMinPixelPerWeight;
|
||||||
|
if PrefMaxPixelPerWeight>0.0 then
|
||||||
|
PixelPerWeight:=Min(PixelPerWeight,PrefMaxPixelPerWeight);
|
||||||
|
PixelPerWeight:=Max(PixelPerWeight,MinPixelPerWeight);
|
||||||
|
if MaxPixelPerWeight>0.0 then
|
||||||
|
PixelPerWeight:=Min(PixelPerWeight,MaxPixelPerWeight);
|
||||||
|
|
||||||
|
debugln(['TLvlGraph.ScaleNodeDrawSizes PixelPerWeight=',PixelPerWeight]);
|
||||||
|
SetAllNodeDrawSizes(PixelPerWeight,SmallestWeight);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TLvlGraph.SetAllNodeDrawSizes(PixelPerWeight: single;
|
||||||
|
MinWeight: single);
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
Node: TLvlGraphNode;
|
Node: TLvlGraphNode;
|
||||||
begin
|
begin
|
||||||
for i:=0 to NodeCount-1 do begin
|
for i:=0 to NodeCount-1 do begin
|
||||||
Node:=Nodes[i];
|
Node:=Nodes[i];
|
||||||
Node.DrawSize:=round(Max(Node.InWeight,Node.OutWeight)*PixelPerWeight+0.5);
|
Node.DrawSize:=round(Max(MinWeight,Max(Node.InWeight,Node.OutWeight))*PixelPerWeight+0.5);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user