TQSpiderGraph  HowTo.




TQSpiderGraph (v 1.0) is a Delphi visual component, designed and tested using Delphi 6 PE. It comes under the form of a Delphi unit and a "ready to install" component.
It can be found on the web at http://mapage.noos.fr/qnno/pages/delphi_en.htm.


  1. Licence.
  2. Instaling as a component.
  3. Lines values have to be of type Single;
  4. Properties and methods.
    1. Properties and methods list
    2. Directly reaching Axes and Lines properties
    3. Example of defining axes
    4. Example of defining lines, and how to pass them some values to display.
    5. "How can I show, say, axes' max and min to my user ?"

Note : The manipulation of most of the  properties of SpiderGraph's instances is illustrated in the demo source provided, to which you should have a look.


(Most of the code coloring on this page done using Chami's online code coloring tool).



1. Licence

 
    //  This component is freeware, but under copyrights that remain mine for
    //  my parts of the code, and original writters for their parts of the code.
    //  This is mainly the case with the computation of a polygon area (which
    //  can be found anywhere on the web...).

    //  This unit can be freely used in any application, freeware, shareware
    //  or commercial. However, I would apreciate your sending me an email if
    //  you decide to use it. Of course, you use it under your own and single
    //  responsability. Neither me, nor contributors, could be held responsible
    //  for any problem resulting from the use of this unit.  ;-)

    //  It can also be freely distributed, provided this licence and the copyright
    //  notice remains within it unchanged.

    //  copyright © 2004 by Olivier Touzot.



2. Install


Instances of TQSpiderGraph can be dynamically created at run-time, through its constructor, like any other visual component. In this case, you'll need to to assign its "parent" property  in order for it to appear somewhere in its parent ClientRect.

Otherwise, to manipulate SpiderGraphs into Delphi's IDE ("at design time"), you'll have to install it like any other component :

-1- Where will it go ?

      If you look at the Register(); procedure into the source code, you'll find the following lines :

Procedure Register;
Begin  
    RegisterComponents('Samples', [TQSpiderGraph]);
End;

      The 'Samples' string indicates where the component icone will go.
      In the present case, it will appear upon the 'Sample' palette.  You can change this destination if you want.


  -2a- Installing (D6PE):
 
The three files provided :
must have been unzipped in a directory known by the IDE, for example, your  '.\delphiN\lib'  one.

If you want to install it elsewhere, make sure to add the path of this directory in Delphi's Path list.

Once unzipped :
You're done, and can start using it like any other visual component, changing its properties, aso.


  -2b- Uninstalling (D6PE):

This a bit quicker than installing, but the path is almost the same :



3. Lines values have to be of type "Single".

TQSpiderGraph displays lines representing values. These values are bound in a range, corresponding to each axe maxValue and minValue (each axe max and min can be set independently);

The graph knows what to display once you have passed to it as many values per line as the graph has of axes. (Adding lines (and their values) to a graph can be done in two different ways, both shown below).

The graph waits for a particular type of datas : The lines' values have to be Single.

This was the easiest type to handle : Converting integers to single can be easilly made throught something like :

mySingleValue := myOriginalInteger / 1;    

Whereas converting single to integers would have imply rounding resulting in a loss of precision.

Siblingly, the Real Type seemed to be much above what should be needed. If your data are of type real, (and are above the max value of a single...) you'll have to convert them to singles, but losing some precision.





3. Properties and methods

Types used in public and published properties (see below) :

  TQSingleArray      =  Array of Single;
  TQBackGround     = (bgTransparent, bgColored, bgTopBottom, bgBottomTop, bgLeftToRight, bgRightToLeft);
  TQTitlePosition     = (tpTop, tpBottom);
  TQBorderStyle      = (bsNone, bsFlat, bs3D);
  TQHighlightMode = (hmShowPoints, hmWidened, hmColorLine, hmFlashLine);  


TQSpiderGraph public & published properties :
 

At the graph object instance level, properties and method dealing with lines and axes do apply globally to all the lines and/or axes.
Each line and each axe can obviously be reached independantly too, using the specific properties of the wanted object instance (see axes and lines properties.)

For example, the boolean property aGraph.AxesAutoSized := True would set the autoSized property of both Max and min of each axe of the graph to True.
To notify the graph that it's second axe (zero based-> [1]) min property has to be true, and this one only, you would have to reach it explicitely :
aGraph.Axes[1].autoSizeMin := True;


Published properties.

Component layout :
    property Anchors;
       // Usual anchors property.

    property borderStyle : TQBorderStyle;
      // TQBorderStyle   = (bsNone,bsFlat,bs3D);
      // The shape of the component;
      
    property backGround  : TQBackGround;
      // TQBackGround    = (bgTransparent, bgColored, bgTopBottom, bgBottomTop, bgLeftToRight, bgRightToLeft);
      // The whole component background.
      //   . bgTransparent makes it transparent ;
      //   . bgColored will draw the color given in the "backGroundColor" property (bellow) ;
      //   . bgTopBottom, bgBottomTop, bgLeftToRight, bgRightToLeft will draw a gradient,
      //      using the "backGStartColor" and "backGFinalColor" propeprties.

Note that the component background will be drawn everywhere (either transparent, gradient or single color) if  the graph property polygonFill (bellow) is set to False. Otherwise, the polygone area of the graph (defined by the line linking it's summits) will be filled independantly, using the graph's polygonColor  property.
  
    property backGroundColor : TColor;
      // The color to be used for the graph's background, if TQBackGround (above) is set to "bgColored";
     
    property backGStartColor : TColor;
      // Gradient first color to be used for the graph's background, if TQBackGround (above) is set to one of the 4 gradient values ;
      
    property backGFinalColor : TColor;
      // Gradient last color to be used for the graph's background, if TQBackGround (above) is set to one of the 4 gradient values ;
    
    property titleCaption    : String;
      // The title of the graph.
      // It's color, size, aso will depend on the PUBLIC property "titleFont : TFont" (public properties, bellow) ;
      
    property titlePosition   : TQTitlePosition;
      // TQTitlePosition = (tpTop,tpBottom);
      // If tpTop, the title is drawn at the top of the graph, and the lines captions box at the bottom ;
      // If tpBottom, the title is drawn at the bottom of the graph, and the lines captions box at the top ;
         

Axes (Generic accesses):
  
 property polygonFill : Boolean;
       // Default := False;
       // If true, the polygon linking the axes' summits will be filled with the polygonColor below.
       // If False, it will be transparent (ie. the graph's background will show).
    
    property polygonColor : TColor;
      // Default := clWhite;
      // See above.
      
    property axesColor : TColor;
      // The color of the axes and the polygon joining their summits.
      
    property axesCount : Integer;
      // The number of axes of a graph.
      // Can't be bellow 3.
      // Resetting this property resets the lines collection too.
      // In other words :   Set the axes count,
      //                            Then set the lines count
      //                            Then populate the lines values.

    property axesCaptionsFramed : Boolean;
      // If True, the captions near each axe summit are surrounded by a frame.
      // Apply to all axes;
      
    property axesAutoSized   : Boolean;
      // This is a global setter/getter.
      // Setting it (to TRUE or FALSE) will call the autoSizeMin and autoSizeMax method of each
      // axe of the graph, passing it the value received.
      // The setter works on both min AND max properties of each axe ;
      // The getter returns True if ALL the min and max properties of ALL the axes have been set to true using the global setter;

      // Each axe's minValue or maxValue can be reached independently too (see axes properties).

      // (See also some more detailed information about axes behaviour when values to display are outside their theorical range).


Lines (generic accesses) :

    property defaultPenWidth : Word;
      // This is the lines defaultPenWidth. It defaults to 2 pixels and can range from 1 to 255;
      //  Each line penWidth can be changed independantly from others too, accessing it directly
      //  (myGraph.Line[i].penWidth:= some_value_between_1 &_255;)
      
    property showLinesPoints : Boolean;
      // Write only property.
      // If TRUE, the points of all the lines are drawn (little circles).
      // To have only the points of a particular line be drawn, either access the showPoints property of
      //  this particular line directly (myGraph.Line[i].showPoints := True;), or use the HighlightLineByIndex(index : Integer); 
      //  public procedure of your graph, after having set the graph's highlightMode property to hmShowPoints (see public properties, bellow) ;

    property showLinesCaption: Boolean;
      // If TRUE, a box is drawn on the graph, containing each line's caption and (at the left of the caption) a little rectangle filled with it's color.
      //          This box will be drawn at the opposite side of the title :
      //          -> at the bottom of the graph if the titlePosition is set to tpTop
      //          -> at the top of the graph if the titlePosition is set to tpBottom;
    
    property linesBoxTransparent : Boolean;
      // If True the graph background appears bellow this box, otherwise, the background of the box will be drawn using the linesBoxColor.

    property linesBoxColor.: TColor;
      // The color to use to draw the background of the box above,  if linesBoxTransparent is set to True;
    
    property highlightColor  : TColor;
      // The color to be used when highlighting or flashing a line.


MouseMoves :


   property trackMouseMoves : Boolean;
      // Default := True;
      // If False, mouse moves are not tracked at all :
      // -> The "mouseBox" won't ever appear ;
      // -> The events "MouseEnterLine" and "MouseExitLine" are not emitted;
      // Then, if you write some code in the two corresponding published event handler, but
      // trackMouseMoves is set to False, this code won't ever execute.

    property showMouseBox    : Boolean;
      // The box that is displayed by the graph when the mouse is above a line.
      // This box shows the line's caption, and the values of each of the axes for this line.
      // If set to False, this box will no longer appear. However, the "MouseEnterLine" and
      // "MouseExitLine" events (bellow) )will still be emitted.

    property mBoxForColor    : TColor;
      // The font color of EACH MouseBox above.
      // SEE BELOW "mBoxUsesLnColor"
      
    property mBoxBackColor   : TColor;
      // The background color of EACH MouseBox above.
      // SEE BELOW "mBoxUsesLnColor"

    property mBoxUsesLnColor : Boolean;
      // Default := False;
      // If TRUE, EACH "mouse box" will use it's corresponding line's color as font color;
      // Otherwise, all these boxes font color will be set to "mBoxForColor";

   property mBoxParent        : TWinControl;
     //  Defaults to the graph instance itself.
     //  It can be set either to your Form (using, for example "self" in the constructor
     //  of your form), or "Nil." (see the demo source code).
     //  Assigning the form as parent to the boxes showing the lines values allows them
     //  to be drawn on the whole form. Otherwise, (if Nil is passed), the box won't go
     //  beyond the limits of the graph.
       
    property OnMouseEnterLine: TQMouseLineEvt;
    property OnMouseExitLine : TQMouseLineEvt;
      // TQMouseLineEvt = procedure(Sender: TObject; const lineIndex: integer) of object;
      // In both these event handlers, lineIndex is the index of the line the mouse has just entered or leaved.
      // It can be used to reach specific properties of this line, like it's color, caption, values...




Other public properties :

 

    constructor Create(aOwner : TComponent); override;
      // Allow to dynamically create graph instances at run-time ;
      
    destructor  Destroy; override;
      // Allow to Free instances of TQSpiderGraph, if you didn't set their "owner" property.

    axesFont : TFont;
      // Font used to draw the axes captions (near to axes summits);
      
    titleFont : TFont;
      // The graph title Font ;
      // Default properties set are :
      // .Size  := 12;
      // .Color := clBlue;
      // .Style := [fsBold];

    linesCpnFont  : TFont;
      // Font used to draw the box showing the lines names and their associated colors.

   
    property linesCount : Integer;
      // To be set BEFORE working with lines. Tells the graph how many lines it will manage.
      // When this property is set, the graph initialises as many lines objet instances as needed.
      // and they become ready to use.

    
    property axes[ix:Integer]  : TQSpiderAxe;
      // Read only property
      // Gives access to each axe's public properties.
      // As long as TQSpiderGraph property "axes" is implemented as an array of pointers to TQAxes instances,
      // accessing this array with a bad index will return NIL (like in any other similar object, for example,
      // a StringList), and an EInvalidPointer exception will occur.
    
    property lines[ix:Integer] : TQSGLine;
      // Read only property
      // Gives access to each axe's public properties.
      // As long as TQSpiderGraph property "lines" is implemented as an array of pointers to TQLines instances,
      // accessing this array with a bad index will return NIL (like in any other similar object, for example,
      // a StringList), and an EInvalidPointer exception will occur.

    property line : TQSGLine;
      // Read only property
      // Gives access to the "aGraphInstance.lines[0]". Handy access to the first line object of a graph,
      // mainly usefull if a graph has only one line.
      // (a graph has allways at least one line (a "line[0]"), even if this line has no values.)
   


Sorting & Highlighting lines

Spider graphs are usefull to allow your user to visually compare sets of values. To go further with this idea, TQSpiderGraph provides means to "highlight" lines as needed : You may want to temporarily highlight the line which area is the widest, or the smallest, or the line which has the highest value on a particular axe, aso.
In order to do that, some methods are available. They all rely on the graph's highlightMode  which is a combination of several states :

    highlightMode : Set of TQHighlightMode;
      // TQHighlightMode = (hmShowPoints, hmWidened, hmColorLine, hmFlashLine);
      // Decide how selected lines will be highlighted.
      // As long as it is a "Set of", it can be a combination of any of the following :
      //     hmShowPoints : The points of the selected line(s) will be drawn. Other won't.
      //     hmWidened  :     The selected line(s)' pen width will be the double of its default one (this_particular_Line.penWidth);
      //     hmColorLine  :   The line color will be temporarilly overrided using the graph's highlightColor;
      //     hmFlashLine  :    The line will "Flash" some momments, alternating it's color and the graph's highlightColor .
      
      // Any line or group of lines can be highlighted, either directly (by index) or to
      // show the result of a kind of "sort to higlight" request (See procs just below).
      
Here follows a list of the different procedures and functions available :
     
a- Highlighting lines :

    procedure   HighlightLineByIndex(index : Integer); overload;
      // request the graph to highlight the line whose index is "index".

    procedure   HighlightLineByIndex(indexArray:TIntegerDynArray); overload;
      // request the graph to highlight the lines whose indexes are in the array "indexArray".
      
    procedure   HighlightLineByCrit (criteria:Integer; maxWanted: Boolean = True);
      //  Request the graph to highlight the "best" line, for such or such criteria.
      //  If maxWanted is set to True, "best" will mean "Highest", otherwise it will mean "Lowest".
      //  criteria can be :
      //    " -2" : "Sort by Area" : The line(s) with the "best" area will be highlighted ;
      //    " -1" : "Don't highlight any line" : Resets all the lines to their "standard" mode ;
      //    " 0 .. axesCount-1" : Highlight the line(s) with the "best" value on this particular axe.

      //  Note : TQSPiderGraph defines two constants that can be used with this function :
      //     Const  HC_NONE = -1;
      //     Const  HC_AREA = -2;
      //  So that calls like "myGraph.HighlightLineByCrit (HC_NONE);" or "myGraph.HighlightLineByCrit (HC_AREA);"
      //  are fully ok (the first one will simply reset all lines to their "usual" state).
      //  Any call to this procedure with a criteria outside the range [HC_AREA .. axesCount-1] will set
      //  criteria's  value to HC_NONE  thus resetting the lines' state.



The "highlight" state of a line will stay on the graph until you reset it, using for example a call to :
"myGraph.HighlightLineByCrit (HC_NONE);"

    


b- Retrieving lines indexes, without highlighting :

    function    GetBestLineByArea (maxWanted: Boolean = True)  : TIntegerDynArray;
      //  Returns an array containing the indexes of the lines with the widest or smallest area,
      //  depending on maxWanted.
      //  Usually contains one index only. However, in the case where several lines had the exact same area,
      //  all their indexes would be returned (and  Length(result_of_the_function_call)  would tell you how many they are) .
      //  Returns [-1]  if nothing could be found (would occur for example if no lines had values, yet).

    function    GetBestLineByAxe(axeIx:Integer; maxWanted: Boolean = True) : TIntegerDynArray;
      // Returns an array containing the indexes of the lines with the greatest or smallest value for
      //  the axe axeIx, depending on maxWanted.
      //  Usually contains one index only. However, in the case where several lines had the exact same value
      //  for this particular axe, all their indexes would be returned (and  Length(result_of_the_function_call)
      //  would tell you how many they are) .
      //  Returns [-1]  if nothing could be found (would occur for example if no lines had values, yet).
  


Quickly adding or deletting lines :


TQSpiderGraph provides two means to add lines. The first one is to first declare the number of lines a graph will manage, then access in a loop the Values[i] property of each line, as detailed bellow. Setting the linesCount property will have the graph create all the needed lines, aso.

The other way is to use it's "AddLine();" methode, which is a function which returns the index of this new line.

    function    AddLine(Const vals:TQSingleArray) : Integer;
      //  (Note : TQSingleArray = Array of Single;)
      //  Adds a line to the graph, passing to it it's values directly ; (see bellow an example using AddLine);

      //  If you look at the source, you'll see that each time a graph is created, an empty line is added
      //  to its line collection (the ".Lines[0]"). If this "line[0]" has not yet values when you first call
      //  "AddLine();", this new one will become Line[0]. Otherwise the new one will be basically
      //  added to the actual collection.
      //  The function returns the index of this new line (and -1 if a problem occured...)
      //  This index is to be used to set up particular properties of this line,
      //  (like its color, its caption, aso.).
      
    function RemoveLine(lineIndex :Integer) : Boolean;
      // Removes the line[lineIndex] from the graphCollection.
      // If lineIndex is below zero or above linesCount -1, the function returns False (and does nothing...);
      // otherwise, it returns True;
    




TQSpiderGraph stores two kind of objects instances, that you'll have to manipulate at run-time : It's axes and the lines.

Both are arrays of pointers to instances of... (TQSGLines and  TQSpiderAxes)

Both will raise Exceptions if you try to acces an element out of the bounds of the corresponding array ;
Both array's lengths can be known through a reading of the corresponding Count property :
  aGraph.AxesCount;  (Integer)
  aGraph.LinesCount;  (Integer)

Accesses are then of the kind :
  aGraph.Lines[2].Color := clGreen;
  aGraph.Axes[5].autoSized := True;

  Or, if for example you want to give your user the possibility to see only the lines whose values are
  above a certain limit (say 50) for one of its axes (the fourth one) :
 
       For i := 0 To myGraph.linesCount -1 Do
           If myGraph.Lines[i].Values[3] < 50
              Then myGraph.Lines[i].visible := False;
       myGraph.Invalidate;                                 // Visible doesn't call it's parent invalidate, so, do it yourself...

( which is  - obviously -  equivalent to :
       For i := 0 To myGraph.linesCount -1 Do
           myGraph.Lines[i].visible := ( myGraph.Lines[i].Values[3] < 50 ) ;
       myGraph.Invalidate;                                 // Visible doesn't call it's parent invalidate, so, do it yourself... )





Axes and lines public properties and methodes are :


Axes myGraph.Axes[i].XXX ):
    caption : String;
      // The caption to be drawn beside this axe summits;
      // It defaults to this axe index into the constructor.
      
    property autoSizeMin : Boolean; 
      // Default : False;
      // If set to True, This axe's min will be set to
      // (actual min - one tweniest of its actual min and max values).
      
    property autoSizeMax : Boolean;
      // Default : False;
      // If set to True, This axe's max will be set to
      // (actual max + one tweniest of its actual min and max values).

    property autoSized: Boolean;
      // At reading, returns TRUE if AT LEAST ONE of the two properties (autoSizeMax or autoSizeMin)
      // is set to TRUE.
      // In Write access, will set BOTH min and max to Autosized.
      
    property minValue : Single;
      // Allows to read or set this axe's actual min value;
      //  Setting it will reset the autoSizeMin property of this axe to False. However, if you soon
      //  passed some values for this axe (by setting some lines values), and the minValue you are
      //  trying to define is greater than the actual lowest existing value for this axe, the the new minValue
      //  will be discarded.
      
    property maxValue : Single;
      // Allows to read or set this axe's actual max value;
      //  Similarly to above, it is not possible to set as maxValue a value lower than the actual max value
      //  received by this axe (if it soon has some).



About axes maxValue, minValue, and autosizing :


Please note that  the axes allways change their minValue and maxValue when you send values outside the actual range of an axe whatever is the state of their autoSized properties.


For example, if you change nothing to the axes default max and min of a graph, they are set to 0 and 100. But if one of your lines has the following values :

[10, 180, 40, 32],
then the maxValue of the seconf axe ("axe[1]") will be changed to adapt to this "180" value you sent, and which is far above it's actual maxValue. This is needed because :
  • Either the graph changed nothing, and lines could have been drawn anywhere outside the graph's area...
  • Or it would have had to change the received values, to have them fit into each axe. Changing your values (to set them to the axe's max or min didn't seem a good idea either, nor did simply drawing them at the max or min position, because they are not at "max" but above or bellow.)
  • Or stayed the possibility to change axes max or min whenever needed, in order to adapt them to what you send. This is what the graph does.

This dynamic changing of the max and min of an axe are a different thing from the axesAutoSized property seen above. The autoSized property of axes or of an axe tell this axe to change its extrems to narrow them from the values you sent, whatever they are.
Thus, if an axe had only these values to display :
[0.43, 0.45, 0.4, 0.32]
but its actual min and max where, say, 0..100, then autosizing this axe would result in the axe defining its new max and min so that they would be :
  • minValue := the lowest value received - 1/20st of the total range       -> 0.32 - (( 0.43-0.32) / 20)  = 0,3145
  • maxValue := the highest value received + 1/20st of the total range     -> 0.43 + ((0.43-0.32) / 20)  = 0,4355
In a way, the behaviour first described only deals with datas OUTSIDE the theorical range whereas the last one deals deals with both narrow range / wide range datas.




Lines ( myGraph.Lines[i].XXX ):
    caption : String;
      // The caption of this line. It is the one to be drawn in the
      // Lines captions box (beside it's color), if this one is displayed.
      // It defaults to this axe index into the constructor.
      
    property    color      : TColor;
      // This line color;
      // Default : clRed;
    
    property    penWidth   : Word;
      // This line penWidth;
      // Default : TRUE;
      
    property    values     : TQSingleArray ;
      // (Note : TQSingleArray = Array of Single;)
      // The values of this line. One by axe.
      
    property    showPoints : Boolean ;
      // If True, this line's points (little circles) will be drawn.

    property    visible    : Boolean;
      // Allows to temporailly hide a specific line.
      // Visible DOESN'T call the invalidate methode of the graph, so that nothing will change untill :
      // -> some other property is changed,
      // -> you explicitely call the graph "invalidate" methode.
    
    procedure   Hide;
      // Hides a line.
      // Opposite to "visible" behaviour", calls the graph "invalidate" property.
          
    procedure   Show;
      // Shows back a previously hidded line.
      // Opposite to "visible" behaviour", calls the graph "invalidate" property.





Defining axes example :

As long as SpiderGraph instances should most often be defined once only, and not evolve at run-time, the published properties of the Axes collection should be used at design-time to set-up this part of the graph (axesCount, axesColor, polygonFill or not, aso.).
Then, some properties will stay to be defined at run-time.
This is the case with the axes maxValue and minValue, their captions, aso. :
To have all axes autosize themselves, a good idea is to set the axesAutoSized property before adding lines values.
   aGraph.axesAutoSized := True;

The Following graph's axes have been defined partly in the IDE, then in the code, when the time comes to show the graph :


With QSPiderGraph1 Do
Begin
// Caption could have been set in the IDE too.
titleCaption := '- Cars benchmark -';
linesCpnFont.Color := RGB(128,128,128);
axesFont.Color := RGB(128,128,128);
linesBoxColor := QSPiderGraph1.polygonColor;
axesAutoSized := True;
End;

// Now dealing with axes :
QSpiderGraph1.axesCount := 6;
QSpiderGraph1.axes[0].caption := 'Horse power';
QSpiderGraph1.axes[1].caption := 'Weight (kg)';
QSpiderGraph1.axes[2].caption := 'Cylinders';
QSpiderGraph1.axes[3].caption := 'Accel. : 0..100 km/h (in ")';
QSpiderGraph1.axes[4].caption := 'Top speed';
QSpiderGraph1.axes[5].caption := 'Price (USD)';

// ---

(The datas shown on this graph are for demonstration only, come from several sources on the net and may be totaly wrong...)




Defining lines example :
Here again, some properties should be defined if possible at design-time. But (at least) the lines values will have to be set at run-time.

As said earlier, there are several ways to add lines to a graph.

The first one is to add all the lines at once, in a loop.
This is done through accessing the Lines[i].Values property.

For example, in the graph above, the lines values can be added this way (an other way is illustrated bellow):


The thin way :


You've got an array of singles containing some lines of values ( the values corresponding to the graph's axes ).
Each line contains the datas of a line of the graph.
Eg : ("source" is the array where your datas are actually stored : ).
source := [ 
52
2303
12
4.8
290
149900
580
1650
12
3.5
330
279900
aso.





]
The code could look like :

The fat way.

The code of the left column corresponds to the following :

// vars :
var aQSArray : TQSingleArray; // TQSingleArray = array of single;
// is defined in QSpiderGraph.pas ;
Begin
{...}
QSpiderGraph1.linesCount := numOfLinesToAdd;
 SetLenght(aQSArray,numOfcolsOfDatas);
 For lineCount := 0 To numOfLinesToAdd -1 Do
// Do as much times as there are lines to add :
Begin
// Fills the array to send with the values of one line.
For columnIndex := 0 To numOfcolsOfDatas -1 Do
aQSArray[columnIndex] := source[lineCount,columnIndex];
// Adds these values to the correspondinf line
mySGraph.Lines[lineCount].Values := aQSArray;
mySGraph.Lines[lineCount].caption := 'Line number '
+ IntToStr(lineCount);
End;
// Once done, you will have to define a color
// for each of your lines, and the job is done.
End;
//  Adding lines to the graph :
// vars :
var anArray : TQSingleArray; // TQSingleArray = array of single;
// is defined in QSpiderGraph.pas ;
Begin
{...}
// -1- Setting the lines count to have the graph prepare evthg
QSpiderGraph1.linesCount := 5;
// Now sizing a TQSingleArray, and populate it
SetLength(anArray,6);
anArray[0] := 552; // HP
anArray[1] := 2303; // Weight
anArray[2] := 12; // Cyls
anArray[3] := 4.8; // Accel
anArray[4] := 290; // TopSpeed
anArray[5] := 149900; // $
// Adds these values to this line
QSpiderGraph1.lines[0].values := anArray;
// And sets this line's particular properties
QSpiderGraph1.lines[0].color := RGB(145,105,126);
QSpiderGraph1.lines[0].caption := 'Bentley Continental GT';

// And do the same again, with all our lines.
anArray[0] := 580;
anArray[1] := 1650;
anArray[2] := 12;
anArray[3] := 3.5;
anArray[4] := 330;
anArray[5] := 279900;
QSpiderGraph1.lines[1].values := anArray;
QSpiderGraph1.lines[1].color := RGB(0,147,195);
QSpiderGraph1.lines[1].caption := 'Lamborghini Murcielago (coupe)';

anArray[0] := 381;
anArray[1] := 1360;
anArray[2] := 6;
anArray[3] := 4.4;
anArray[4] := 306;
anArray[5] := 143900;
QSpiderGraph1.lines[2].values := anArray;
QSpiderGraph1.lines[2].color := RGB(255,183,209);
QSpiderGraph1.lines[2].caption := 'Porsche 911 (GT3 RS)';

anArray[0] := 655;
anArray[1] := 1170;
anArray[2] := 8;
anArray[3] := 3.5;
anArray[4] := 390;
anArray[5] := 390000;
QSpiderGraph1.lines[3].values := anArray;
QSpiderGraph1.lines[3].color := RGB(215,164,45);
QSpiderGraph1.lines[3].caption := 'Koenigsegg CC';

anArray[0] := 500;
anArray[1] := 1950;
anArray[2] := 12;
anArray[3] := 4.7;
anArray[4] := 250;
anArray[5] := 126000;
QSpiderGraph1.lines[4].values := anArray;
QSpiderGraph1.lines[4].color := RGB(154,186,144);
QSpiderGraph1.lines[4].caption := 'Mercedes-Benz SL 600';

// And that's done for the Graph



An other way to add lines to a graph is the AddLine(); function.

This function will add one line to a graph each time it is called ;
It's prototype is :
    function    AddLine (Const vals:TQSingleArray) : Integer;
> It waits for a pointer at an array of single, as above. This Type is defined in TQSpiderGraph.pas unit
   TQSingleArray   =  Array of Single;
> It returns an integer, which is the index of the line that has just been added. This index allows then to set up the specific properties of this line, like it's color, it's particular penWidth, aso.

The above example could then have been rewrited the following way :
// vars :
var ix : Integer;                               // indexes the AddLine(); function will return.
      aQSArray : TQSingleArray;      // TQSingleArray = array of single;
                                                       // is defined in QSpiderGraph.pas ;
Begin
{...}
  SetLength(aQSArray,  numOfColsOfDatas);
  For lnCount := 0 To numOfLinesToAdd -1 Do
  Begin
     // -1- Fills the array to send to the line
     For dataCount := 0 To numOfcolsOfDatas-1 Do
           aQSArray[dataCount] := source[dataCount];
    // -2- Then Add a line with these datas to the collection
    // Note : If a problem occured, the returned index = "-1";

    ix := mySGraph.AddLine(aQSArray);

     If ix < 0 Then ShowMessage ('Unable to add this line...')
                  Else mySGraph.Lines[ix].caption := 'Line number ' + IntToStr(lnCount +1);

  End
;
End
;




The demo provided contains examples of accessing almost all the properties of a QSpiderGarph instance. Have a look at it's source.




Displaying axes'  max and min properties (and others, too) :

The component does a lot, but not everything, and sometimes you'll be alone with yourself to do some things.

Displaying the component's axes' max and min values is an example of this :

As long as each axe max and / or min value can be set independantly, displaying their values on the graph would have result in something impossible to read, specialy when minValues are considered, cause they would have been drawn so close to the center of the graph, that they would have overlap each other, resulting in something hugly.

However, it is easy  (and I do agree : a little boring...)  to display them "by hand" near the component itself :
You could  put a graph on a panel, leaving enough space on one of the sides of the panel to put there as many labelss as there is axes in the graph, and then somewhere in your code, where you define the component (for example in the procedure adding lines to this graph) add some lines of the kind :

   label1.caption : 'Axe_0 Minimum  = '  + FloatToStStr(myGraph.Axes[0].minValue);  
   label2.caption : 'Axe_0 Maximum = '  + FloatToStStr(myGraph.Axes[0].maxValue);  
aso.

You could display them in anything else too, like a TListView, a TStringGrid, a little memo, aso.

 

Displaying line's values, when there is a lot of axes  :

As you may have see in the demo, the "yellow box" poping-up when the mouse is over a line is sometines so big that it is not possible to read everything.
This shouldn't be so frequent in real usage, because it would certainly not be a good idea to provide to your user graphs with tons of axes at once : Readibility would suffer a lot...

However, the same problem will appear if the graph is relatively little.

In such cases, you could set the graph's showMouseBox  property to False, and react yourself to the "MouseEnterLine" and "MouseExitLine" events.
Each time one of this events is fired, it gives you the index of the line in question, allowing you to read (and then display wherever you want) it's properties (including values, that you should in fact already know).




( Top )