V. Propriétés de TCircleChart▲
Nous définirons cinq propriétés dans TCircleChart :
Nom | Type | Description |
---|---|---|
Spoke | integer | Rayon du disque |
Brush | TBrush | Brush du fond du disque (sous les quartiers) |
Pen | TPen | Pen utilisé pour les traits (de circonférence et de séparation entre deux quartiers) |
Quarters | TChartQuarters | Collection des différents quartiers |
BaseAngle | integer | Angle (en degrés) à partir duquel dessiner les quartiers (0 = à droite ; 90 = en haut) |
Ces cinq propriétés doivent avoir une méthode d'accès en écriture puisqu'un changement implique qu'il faille redessiner le contrôle. Comme tout à l'heure, nous ajoutons une méthode GraphicsChange pour les modifications de Brush et de Pen.
private
FSpoke : integer
;
FBrush : TBrush;
FPen : TPen;
FQuarters : TChartQuarters;
FBaseAngle : integer
;
procedure
SetSpoke(New : integer
);
procedure
SetBrush(New : TBrush);
procedure
SetPen(New : TPen);
procedure
SetQuarters(New : TChartQuarters);
procedure
SetBaseAngle(New : integer
);
procedure
GraphicsChange(Sender : TObject);
Voici les déclarations publiées de ces cinq propriétés :
property
Spoke : integer
read
FSpoke write
SetSpoke default
100
;
property
Brush : TBrush read
FBrush write
SetBrush;
property
Pen : TPen read
FPen write
SetPen;
property
Quarters : TChartQuarters read
FQuarters write
SetQuarters;
property
BaseAngle : integer
read
FBaseAngle write
SetBaseAngle default
90
;
L'implémentation des six méthodes est triviale, excepté SetBaseAngle qui s'assure que la nouvelle valeur est comprise entre 0 (inclus) et 360 (exclus). À cause de l'implémentation non mathématique de l'opérateur mod, nous sommes obligés d'ajouter 360 au résultat s'il est négatif.
procedure
TCircleChart.SetSpoke(New : integer
);
begin
if
New <= 0
then
exit;
FSpoke := New;
Invalidate;
end
;
procedure
TCircleChart.SetBrush(New : TBrush);
begin
FBrush.Assign(New);
end
;
procedure
TCircleChart.SetPen(New : TPen);
begin
FPen.Assign(New);
end
;
procedure
TCircleChart.SetQuarters(New : TChartQuarters);
begin
FQuarters.Assign(New);
end
;
procedure
TCircleChart.SetBaseAngle(New : integer
);
begin
FBaseAngle := New mod
360
;
if
FBaseAngle < 0
then
inc(FBaseAngle, 360
);
Invalidate;
end
;
procedure
TCircleChart.GraphicsChange(Sender : TObject);
begin
Invalidate;
end
;
Finalement, voici l'implémentation des constructeur et destructeur pour initialiser et finaliser ces propriétés. Notez également l'affectation de clNone à la propriété Color que nous offre TControl.
constructor
TCircleChart.Create(AOwner : TComponent);
begin
inherited
;
Color := clNone;
FSpoke := 100
;
FBrush := TBrush.Create;
FBrush.OnChange := GraphicsChange;
FPen := TPen.Create;
FPen.OnChange := GraphicsChange;
FQuarters := TChartQuarters.Create(Self
);
FBaseAngle := 90
;
end
;
destructor
TCircleChart.Destroy;
begin
FQuarters.Free;
FPen.Free;
FBrush.Free;
inherited
;
end
;
Nous avons maintenant tous les outils en main pour dessiner les différents quartiers.
VI. Dessiner les quartiers▲
Maintenant que nous pouvons connaître le rayon, la couleur de fond, les quartiers, et autres informations, nous pouvons nous lancer dans l'écriture plus avancée de la méthode Paint, censée dessiner complètement le composant.
VI-A. Un peu d'algorithmique▲
Comme l'algorithme utilisé pour dessiner les quartiers n'est pas trivial, nous allons d'abord faire un peu d'algorithmique pour le rédiger.
Commençons par définir les variables dont nous aurons besoin :
Tout d'abord, nous allons calculer les coordonnées du centre du cercle ainsi que le TRect du carré circonscrit :
soit Center de type TPoint <- centre du rectangle client du contrôle;
soit CircRect de type TRect <- carré de côté 2*Spoke centré sur Center;
Ces deux données, avec les propriétés, permettront de dessiner relativement simplement le contrôle.
Ensuite, nous devons dessiner le disque de fond, sauf si Color vaut clNone :
si Color est différent de clNone :
dessiner une Ellipse pleine de couleur Color sans bord dans CircRect;
finsi;
Il faut maintenant dessiner le disque de milieu de plan avec les informations des propriétés Brush et Pen.
dessiner une Ellipse dans CircRect avec les informations
de pinceau et de crayon données par Brush et Pen;
Le fond est maintenant dessiné, nous allons donc dessiner les différents quartiers.
Pour cela, nous aurons besoin de quelques variables supplémentaires :
// quartier courantsoit Quarter de type TChartQuarter;
// informations de graphismes du quartier courant dans son étatsoit Graphics de type TChartQuarterGraphics;
// angle de départ, de milieu et de fin du quartiersoient MinAngle, MidAngle et MaxAngle de type Single;
// points sur la circonférence correspondants aux anglessoient MinPt, MidPt et MaxPt de type TPoint;
// point sur lequel centrer le textesoit TextPos de type TPoint;
// le texte à afficher dans le quartiersoit Text de type string;
Les trois angles sont des valeurs d'angles absolues en radians à partir de l'angle 0 dans le cercle trigonométrique, soit à droite. MinAngle est la valeur de l'angle au départ du quartier, MaxAngle à l'extrémité, et MidAngle au milieu des deux. Les trois points sont les points correspondants à ces angles dans le cercle dessiné par notre composant.
MinAngle sera à chaque fois le MaxAngle du quartier précédent, sauf la première fois. Pour simplifier les calculs et ne pas ajouter de tests inutiles, nous initialisons donc MaxAngle à la valeur de départ, qui est définie par BaseAngle (attention : il faut transformer la valeur en radians).
MaxAngle <- DegreeToRadian(BaseAngle);
Ensuite nous pouvons faire notre itération sur les quartiers :
pour chaque Quarter dans Quarters :
...
finpour;
D'abord nous testons si la valeur de ce quartier ne vaut pas 0, auquel cas il ne faut pas effectuer le traitement, parce que cela serait cause d'erreurs (le dessin du quartier en lui-même faisant alors les 360° au lieu de 0) :
pour chaque Quarter dans Quarters :
si Quarter.Percent vaut 0 :
passer à l'occurence suivante;
finsi;
finpour;
Ensuite, on récupère les informations de graphismes en fonction de l'état (normal, abaissé ou désactivé) du quartier :
pour chaque Quarter dans Quarters :
...
si Quarter est désactivé :
Graphics <- Quarter.DisabledGraphics;
sinon si Quarter est abaissé :
Graphics <- Quarter.DownGraphics;
sinon :
Graphics <- Quarter.Graphics;
finsi;
finpour;
À présent, il faut calculer les nouvelles valeurs des trois angles. Comme nous l'avons dit plus haut, MinAngle est simplement le MaxAngle de l'angle précédent. MaxAngle, lui, est le nouveau MinAngle plus la valeur en radians du quartier (connus en pourcent par la propriété Percent). Finalement, MidAngle est la moyenne des deux.
pour chaque Quarter dans Quarters :
...
MinAngle <- MaxAngle;
MaxAngle <- MinAngle + PercentToRadian(Quarter.Percent);
MidAngle <- Moyenne(MinAngle, MaxAngle);
finpour;
Voici maintenant la partie mathématique de l'histoire : calculer les coordonnées des points correspondants à ces angles sur la circonférence de notre disque. En imaginant qu'on veuille le savoir pour le cercle trigonométrique, on peut utiliser les valeurs trigonométriques (cosinus et sinus) de l'angle directement comme abscisse et ordonnée. Ensuite, il suffit de multiplier les valeurs obtenues par la longueur du rayon, puisque, finalement, notre cercle n'est qu'un agrandissement du cercle trigonométrique avec comme facteur la longueur du rayon. Il faut encore arrondir ces valeurs à l'entier le plus proche pour pouvoir les enregistrer dans un TPoint.
L'ordonnée que nous obtenons alors est une ordonnées mathématique, alors que nous avons besoin de l'ordonnée informatique pour pouvoir situer le point sur le canevas. C'est pourquoi nous retranchons cette valeur à la hauteur de notre contrôle.
pour chaque Quarter dans Quarters :
...
MinPt <- Point(Round(Spoke * Cos(MinAngle)) + Center.X,
Height - Round(Spoke * Sin(MinAngle)) - Center.Y);
MidPt <- Point(Round(Spoke * Cos(MidAngle)) + Center.X,
Height - Round(Spoke * Sin(MidAngle)) - Center.Y);
MaxPt <- Point(Round(Spoke * Cos(MaxAngle)) + Center.X,
Height - Round(Spoke * Sin(MaxAngle)) - Center.Y);
finpour;
Nous pouvons maintenant dessiner le quartier, avec la partie BackgroundBrush de Graphics.
pour chaque Quarter dans Quarters :
...
dessiner un Quartier d'Ellipse dans CircRect à partir de MinPt jusque
MaxPt avec les informations de pinceau de Graphics.BackgroundBrush;
finpour;
Finalement, il faut dessiner le texte avec la fonte définie par Graphics.Font et un fond défini par Graphics.FontBrush, à moins que ShowText ne vaille False. Nous centrerons le texte sur le point au milieu de Center et MidPoint.
pour chaque Quarter dans Quarters :
...
si Quarter.ShowText :
TextPos <- point au milieu de Center et MidPt;
si Quarter.Text est différent de '' :
Text <- Format('%s\r\n(%f%%)', Quarter.Text, Quarter.Percent);
sinon :
Text <- Format('%f%%', Quarter.Percent);
finsi;
écrire le texte Text avec le fond Graphics.FontBrush et
la police Graphics.Font centré sur TextPos;
finsi;
finpour;
Voici donc l'algorithme complet. Il ne reste plus qu'à le transposer en code Delphi.
soit Center de type TPoint <- centre du rectangle client du contrôle;
soit CircRect de type TRect <- carré de côté 2*Spoke centré sur Center;
// quartier courantsoit Quarter de type TChartQuarter;
// informations de graphismes du quartier courant dans son étatsoit Graphics de type TChartQuarterGraphics;
// angle de départ, de milieu et de fin du quartiersoient MinAngle, MidAngle et MaxAngle de type Single;
// points sur la circonférence correspondants aux anglessoient MinPt, MidPt et MaxPt de type TPoint;
// point sur lequel centrer le textesoit TextPos de type TPoint;
// le texte à afficher dans le quartiersoit Text de type string;
si Color est différent de clNone :
dessiner une Ellipse pleine de couleur Color sans bord dans CircRect;
finsi;
dessiner une Ellipse dans CircRect avec les informations
de pinceau et de crayon données par Brush et Pen;
MaxAngle <- DegreeToRadian(BaseAngle);
pour chaque Quarter dans Quarters :
si Quarter.Percent vaut 0 :
passer à l'occurence suivante;
finsi;
si Quarter est désactivé :
Graphics <- Quarter.DisabledGraphics;
sinon si Quarter est abaissé :
Graphics <- Quarter.DownGraphics;
sinon :
Graphics <- Quarter.Graphics;
finsi;
MinAngle <- MaxAngle;
MaxAngle <- MinAngle + PercentToRadian(Quarter.Percent);
MidAngle <- Moyenne(MinAngle, MaxAngle);
MinPt <- Point(Round(Spoke * Cos(MinAngle)) + Center.X,
Height - Round(Spoke * Sin(MinAngle)) - Center.Y);
MidPt <- Point(Round(Spoke * Cos(MidAngle)) + Center.X,
Height - Round(Spoke * Sin(MidAngle)) - Center.Y);
MaxPt <- Point(Round(Spoke * Cos(MaxAngle)) + Center.X,
Height - Round(Spoke * Sin(MaxAngle)) - Center.Y);
dessiner un Quartier d'Ellipse dans CircRect à partir de MinPt jusque MaxPt
avec les informations de pinceau de Graphics.BackgroundBrush;
si Quarter.ShowText :
TextPos <- point au milieu de Center et MidPt;
si Quarter.Text est différent de '' :
Text <- Format('%s\r\n(%f%%)', Quarter.Text, Quarter.Percent);
sinon :
Text <- Format('%f%%', Quarter.Percent);
finsi;
écrire le texte Text avec le fond Graphics.FontBrush et
la police Graphics.Font centré sur TextPos;
finsi;
finpour;
VI-B. Le code Delphi▲
Maintenant que notre algorithme est au point, nous pouvons le transposer en Delphi. Reprenons donc la méthode Paint et vidons-là de son contenu actuel (nous avions mis un petit code juste pour ne pas la laisser vide).
procedure
TCircleChart.Paint;
begin
end
;
Nous allons maintenant suivre l'algo pas à pas et le transposer. Commençons par les variables.
soit Center de type TPoint <- centre du rectangle client du contrôle;
soit CircRect de type TRect <- carré de côté 2*Spoke centré sur Center;
// quartier courantsoit Quarter de type TChartQuarter;
// informations de graphismes du quartier courant dans son étatsoit Graphics de type TChartQuarterGraphics;
// angle de départ, de milieu et de fin du quartiersoient MinAngle, MidAngle et MaxAngle de type Single;
// points sur la circonférence correspondants aux anglessoient MinPt, MidPt et MaxPt de type TPoint;
// point sur lequel centrer le textesoit TextPos de type TPoint;
// le texte à afficher dans le quartiersoit Text de type string;
À cela nous ajouterons une variable I de type integer, dont nous aurons besoin pour implémenter la boucle, ainsi qu'une variable DrawTextRect de type TRect, qui servira lors du calcul de la position du texte, qui n'est pas triviale à coder en Delphi.
procedure
TCircleChart.Paint;
var
Center : TPoint; // Centre du cercle
CircRect : TRect; // Carré circonscrit au cercle
I : integer
;
Quarter : TChartQuarter;
Graphics : TChartQuarterGraphics;
MinAngle, MidAngle, MaxAngle : Single
;
MinPt, MidPt, MaxPt, TextPos : TPoint;
Text : string
;
DrawTextRect : TRect;
begin
// Calcul des données concernant le disque
Center := Point(Width div
2
, Height div
2
);
CircRect := Rect(Center.X-Spoke, Center.Y-Spoke, Center.X+Spoke, Center.Y+Spoke);
end
;
Passons maintenant au dessin du fond.
si Color est différent de clNone :
dessiner une Ellipse pleine de couleur Color sans bord dans CircRect;
finsi;
dessiner une Ellipse dans CircRect avec les informations
de pinceau et de crayon données par Brush et Pen;
Pour faciliter les choses, nous utiliserons une instruction with sur Canvas.
with
Canvas do
begin
// Dessin de la couleur de fond
if
Color <> clNone then
begin
Brush.Color := Color;
Brush.Style := bsSolid;
Pen.Style := psClear;
Ellipse(CircRect);
end
;
// Dessin du disque
Brush.Assign(Self
.Brush);
Pen.Assign(Self
.Pen);
Ellipse(CircRect);
end
;
Notez que nous avons dû utiliser Self.Brush et Self.Pen car ces deux propriétés étaient cachées par leurs homographes de Canvas.
MaxAngle <- DegreeToRadian(BaseAngle);
pour chaque Quarter dans Quarters :
...
finpour;
L'appel à la fonction DegreeToRadian sera remplacé par le calcul correspondant en Delphi.
Pour implémenter la boucle, nous devons itérer sur la variable I plutôt que sur Quarter à cause de la façon de coder les tableaux en Delphi.
// Dessin des différents quartiers
MaxAngle := FBaseAngle * Pi / 180
;
for
I := 0
to
Quarters.Count-1
do
begin
Quarter := Quarters[I];
end
;
si Quarter.Percent vaut 0 :
passer à l'occurence suivante;
finsi;
si Quarter est désactivé :
Graphics <- Quarter.DisabledGraphics;
sinon si Quarter est abaissé :
Graphics <- Quarter.DownGraphics;
sinon :
Graphics <- Quarter.Graphics;
finsi;
Pour ce passage la traduction est littérale :
if
Quarter.Percent = 0
.0
then
Continue;
if
not
Quarter.Enabled then
Graphics := Quarter.DisabledGraphics
else
if
Quarter.Down then
Graphics := Quarter.DownGraphics
else
Graphics := Quarter.Graphics;
Nous arrivons au passage mathématique :
MinAngle <- MaxAngle;
MaxAngle <- MinAngle + PercentToRadian(Quarter.Percent);
MidAngle <- Moyenne(MinAngle, MaxAngle);
MinPt <- Point(Round(Spoke * Cos(MinAngle)) + Center.X,
Height - Round(Spoke * Sin(MinAngle)) - Center.Y);
MidPt <- Point(Round(Spoke * Cos(MidAngle)) + Center.X,
Height - Round(Spoke * Sin(MidAngle)) - Center.Y);
MaxPt <- Point(Round(Spoke * Cos(MaxAngle)) + Center.X,
Height - Round(Spoke * Sin(MaxAngle)) - Center.Y);
Ici nous nous apercevons avec joie des extensions mathématiques de Delphi : la traduction est également littérale, mis à part le remplacement de PercentToRadian en le calcul correspondant. Notez qu'il faut rajouter l'unité Math à la clause uses pour pouvoir utiliser certaines fonctions présentées ci-dessus.
// Avancement des angles
MinAngle := MaxAngle;
MaxAngle := MinAngle + (Quarter.Percent * 2
*Pi / 100
);
MidAngle := (MinAngle + MaxAngle) / 2
;
// Calcul des points
MinPt := Point(Round(Spoke * Cos(MinAngle)) + Center.X, Height - Round(Spoke * Sin(MinAngle)) - Center.Y);
MidPt := Point(Round(Spoke * Cos(MidAngle)) + Center.X, Height - Round(Spoke * Sin(MidAngle)) - Center.Y);
MaxPt := Point(Round(Spoke * Cos(MaxAngle)) + Center.X, Height - Round(Spoke * Sin(MaxAngle)) - Center.Y);
Voici le passage qui peut faire peur aux développeurs qui ne connaissent pas bien la classe TCanvas. En effet, il s'agit de dessiner un quartier de disque.
dessiner un Quartier d'Ellipse dans CircRect à partir de MinPt jusque MaxPt
avec les informations de pinceau de Graphics.BackgroundBrush;
Heureusement, la méthode Pie de TCanvas nous permet de faire cela très aisément.
Voici le prototype de cette méthode :
procedure
Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4 : integer
);
Extrait de l'aide de Delphi 2005 :
Utilisez Pie pour dessiner dans l'image une figure en forme de part de tarte. Cette forme est définie par
l'ellipse inscrite dans le rectangle déterminé par les points (X1,Y1) et (X2,Y2). La section dessinée est déterminée par
deux lignes, partant du centre de l'ellipse jusqu'aux points (X3,Y3) et (X4,Y4).
Le contour est dessiné en utilisant Pen et la forme est remplie en utilisant Brush.
À nouveau, afin de gagner de la place et donc de la lisibilité, nous utiliserons une instruction with cette fois sur CircRect. On remarque au passage qu'il existe donc des occasion où cette instruction peut se révéler très efficace sans begin...end.
// Dessin du quartier
Brush.Assign(Graphics.BackgroundBrush);
with
CircRect do
Pie(Left, Top, Right, Bottom, MinPt.X, MinPt.Y, MaxPt.X, MaxPt.Y);
Il ne reste plus que l'affichage du texte :
si Quarter.ShowText :
TextPos <- point au milieu de Center et MidPt;
si Quarter.Text est différent de '' :
Text <- Format('%s\r\n(%f%%)', Quarter.Text, Quarter.Percent);
sinon :
Text <- Format('%f%%', Quarter.Percent);
finsi;
écrire le texte Text avec le fond Graphics.FontBrush et
la police Graphics.Font centré sur TextPos;
finsi;
Si l'implémentation des trois premières instructions est triviale, il n'en est pas de même pour l'écriture du texte. Commençons toujours par ces trois instructions :
// Calcul de la position du texte et affichage du texte
if
Quarter.ShowText then
begin
Brush.Assign(Graphics.TextBrush);
Font.Assign(Graphics.Font);
TextPos := Point((Center.X+MidPt.X) div
2
, (Center.Y+MidPt.Y) div
2
);
if
Quarter.Text <> ''
then
Text := Format('%s'#13#10'(%f%%)'
, [Quarter.Text, Quarter.Percent])
else
Text := Format('%f%%'
, [Quarter.Percent]);
// écrire le texte
end
;
Le problème de l'écriture réside dans sa dernière spécification ("centré sur TextPos"). Il faut pour cela parvenir à mesurer la largeur et la hauteur qu'occupera le texte dans la police spécifiée. Il existe bien des méthodes TextWidth et TextHeight dans TCanvas mais celles-ci sont imprécises.
En réalité, il n'existe aucune solution avec les méthodes de la VCL. En revanche, une API permet de le faire : DrawText.
Pour utiliser cette méthode dans notre cas, il faut d'abord l'appeler avec le flag DT_CALCRECT afin de calculer les dimensions du rectangle dans lequel s'écrira le texte. Ensuite, il faut l'appeler une seconde fois sans ce flag, cette fois avec le bon rectangle, pour écrire réellement le texte.
Avant le premier appel, nous devons initialiser le rectangle. L'impératif est alors que sa largeur soit la largeur maximale que peut prendre le texte : nous utiliserons la largeur de notre contrôle.
DrawTextRect := Rect(0
, 0
, Width, 0
);
Ensuite, nous appelons DrawText comme suit :
DrawText(Handle, PChar(Text), -1
, DrawTextRect, DT_CALCRECT or
DT_CENTER or
DT_NOPREFIX);
Le premier paramètre est un handle de contexte de dessin (device context) : c'est le handle de notre canevas (nous sommes toujours dans le with correspondant). Le second est un pointeur vers la chaîne à écrire. Le troisième est la longueur de cette chaîne ; s'il vaut -1, la chaîne est considérée comme étant à zéro terminal, ce qui est notre cas puisque nous l'avons transtypée en PChar. Le quatrième est le rectangle dans lequel écrire le texte. Et le dernier est un ensemble de drapeaux d'options d'écriture.
Cet appel n'écrit rien, à cause du flag DT_CALCRECT. En revanche, après cet appel, les propriété Right et Bottom de DrawTextRect auront été ajustée à la largeur et la hauteur que prendra le texte pour s'afficher.
Nous devons maintenant modifier légèrement le rectangle obtenu pour le centrer sur TextPos :
Jamais deux sans trois, voici encore une utilisation bizarre de l'instruction with, puisque nous spécifions deux objets à utiliser.
with
TextPos, DrawTextRect do
DrawTextRect := Rect(X - Right div
2
, Y - Bottom div
2
, X + Right div
2
, Y + Bottom div
2
);
Finalement, il ne reste plus qu'à appeler DrawText pour la deuxième fois, sans le paramètre DT_CALCRECT pour écrire effectivement le texte :
DrawText(Handle, PChar(Text), -1
, DrawTextRect, DT_CENTER or
DT_NOPREFIX);
Nous avons finalement terminé cette méthode de dessin. Nous avons pu à cette occasion découvrir plusieurs astuces de dessin, notamment l'utilisation des fonctions trigonométrique et de l'API DrawText.
Voici le code complet de cette méthode :
procedure
TCircleChart.Paint;
var
Center : TPoint; // Centre du cercle
CircRect : TRect; // Carré circonscrit au cercle
I : integer
;
Quarter : TChartQuarter;
Graphics : TChartQuarterGraphics;
MinAngle, MidAngle, MaxAngle : Single
;
MinPt, MidPt, MaxPt, TextPos : TPoint;
Text : string
;
DrawTextRect : TRect;
begin
// Calcul des données concernant le disque
Center := Point(Width div
2
, Height div
2
);
CircRect := Rect(Center.X-Spoke, Center.Y-Spoke, Center.X+Spoke, Center.Y+Spoke);
with
Canvas do
begin
// Dessin de la couleur de fond
if
Color <> clNone then
begin
Brush.Color := Color;
Brush.Style := bsSolid;
Pen.Style := psClear;
Ellipse(CircRect);
end
;
// Dessin du disque
Brush.Assign(Self
.Brush);
Pen.Assign(Self
.Pen);
Ellipse(CircRect);
// Dessin des différents quartiers
MaxAngle := FBaseAngle * Pi / 180
;
for
I := 0
to
Quarters.Count-1
do
begin
Quarter := Quarters[I];
if
Quarter.Percent = 0
.0
then
Continue;
if
not
Quarter.Enabled then
Graphics := Quarter.DisabledGraphics
else
if
Quarter.Down then
Graphics := Quarter.DownGraphics
else
Graphics := Quarter.Graphics;
// Avancement des angles
MinAngle := MaxAngle;
MaxAngle := MinAngle + (Quarter.Percent * 2
*Pi / 100
);
MidAngle := (MinAngle + MaxAngle) / 2
;
// Calcul des points
MinPt := Point(Round(Spoke * Cos(MinAngle)) + Center.X, Height - Round(Spoke * Sin(MinAngle)) - Center.Y);
MidPt := Point(Round(Spoke * Cos(MidAngle)) + Center.X, Height - Round(Spoke * Sin(MidAngle)) - Center.Y);
MaxPt := Point(Round(Spoke * Cos(MaxAngle)) + Center.X, Height - Round(Spoke * Sin(MaxAngle)) - Center.Y);
// Dessin du quartier
Brush.Assign(Graphics.BackgroundBrush);
with
CircRect do
Pie(Left, Top, Right, Bottom, MinPt.X, MinPt.Y, MaxPt.X, MaxPt.Y);
// Calcul de la position du texte et affichage du texte
if
Quarter.ShowText then
begin
Brush.Assign(Graphics.TextBrush);
Font.Assign(Graphics.Font);
TextPos := Point((Center.X+MidPt.X) div
2
, (Center.Y+MidPt.Y) div
2
);
if
Quarter.Text <> ''
then
Text := Format('%s'#13#10'(%f%%)'
, [Quarter.Text, Quarter.Percent])
else
Text := Format('%f%%'
, [Quarter.Percent]);
DrawTextRect := Rect(0
, 0
, Width, 0
);
DrawText(Handle, PChar(Text), -1
, DrawTextRect, DT_CALCRECT or
DT_CENTER or
DT_NOPREFIX);
with
TextPos, DrawTextRect do
DrawTextRect := Rect(X - Right div
2
, Y - Bottom div
2
, X + Right div
2
, Y + Bottom div
2
);
DrawText(Handle, PChar(Text), -1
, DrawTextRect, DT_CENTER or
DT_NOPREFIX);
end
;
end
;
end
;
end
;