Draw

The CRT unit suffices for simple graphics, but most graphical console applications require the graph unit. The WinGraph unit (together with the WinCrt and WinMouse units) used in many student programs has extended functionality. Our first demonstration program using the graph window puts labelled marks every 100 pixels along the x and y axes, so that you can estimate the coordinates to use for your shapes. It shows how to select the graphics mode automatically.

program ShowAxes;
  {$mode objfpc}{$H+}
uses
  SysUtils, Graph;
var
  Gd, Gm : smallint;
  Count, MaxX, MaxY : integer;
  strMark : string;

begin
  Gd := Detect;  //Set graphics driver to 0.
  Gm := 0;       //Graphics mode will be set automatically.
  InitGraph(Gd, Gm, ''); //Open the graph window
  MaxX := getMaxX;
  MaxY := getMaxY;
  setTextStyle(ScriptFont, HorizDir, 1);
  //Put labelled marks every 100 pixels along the x axis.
  for Count := 1 to MaxX DIV 100 do
    begin
      line(Count * 100, 0, Count * 100, 20);
      strMark := 'x = ' + intToStr(Count * 100);
      moveTo(Count * 100 - textWidth(strMark), 25);
      outText(strMark);
    end;
  //Put labelled marks every 100 pixels along the y axis.
  for Count := 1 to MaxY DIV 100 do
    begin
      line(0, Count * 100, 20, Count * 100);
      strMark := 'y = ' + intToStr(Count * 100);
      moveTo(25, Count * 100 - textHeight(strMark) DIV 2);
      outText(strMark);
    end;
  write('Please press return to exit. ');
  readln;
end.

The following screenshot shows part of the output.

Axes

Axes

You can draw many shapes with the Graph unit, as demonstrated in program Draw. This program divides the window into four rows and four columns and draws each shape within one of the 16 cells. The program uses the following procedures and functions in the Graph unit.

  • SetColor(TextColour) sets the text colour to TextColour.
  • Rectangle(x1, x2, y1, y2) draws a rectangle outline with upper left corner at x1,y1 and lower left corner at x2,y2.
  • SetFillStyle(FillType, FillColour) sets the fill type (e.g. SolidFill, BkSlashFill) and the fill colour.
  • SetLineStyle( LineStyle, Pattern, Thickness) sets the style of a line. LineStyle may be, for example, Solidln, Dashedln or UserBitln. Thickness can be set to NormWidth or ThickWidth. Pattern is ignored and can be set to 0 unless your style is UserBitln.
  • Bar(x1, x2, y1, y2) draws a filled rectangle with upper left corner at x1,y1 and lower left corner at x2,y2.
  • Bar3d(x1, x2, y1, y2, BarDepth, Top)draws a 3-dimensional bar with upper left corner at x1,y1 and lower left corner at x2,y2. BarDepth specifies the depth of the bar in pixels. If the Boolean variable Top is True, then the top will be 3-dimensional.
  • Circle(x1, y1, CircleRadius) draws a circle with centre at x1,y1 and radius CircleRadius.
  • FillEllipse(x1, y1, xRadius, yRadius2) draws a filled ellipse with centre x1,y1 and radii xRadius and yRadius;
  • MoveTo(x, y) moves the cursor to point x,y.
  • LineTo(x, y) draws a line from the cursor position to point x,y.
  • Ellipse(x, y, Direction1, Direction2, xRadius, yRadius) draws a partial ellipse with centre x,y, radii xRadius and yRadius, between Direction1 and Direction2. Directions are measures in degrees, with 0 being along the x- axis (pointing right) and 90 along the y-axis (pointing up).
  • Arc(x, y, Direction1, Direction2, CircleRadius) draws an arc of a circle with centre x,y and radius CircleRadius from Direction1 to Direction2 (defined as for the partial ellipse).
  • DrawPoly(NumberOfPoints, ArrayOfPoints) draws a polygon. ArrayOfPoints is an array holding the x and y coordinates of each point, beginning and ending with the coordinates of the first point. NumberOfPoints is the total number of points in the array, which is one more than the number of vertices in the polygon.
  • PutPixel(x, y, PixelColour) draws a pixel of colour PixelColour at point x,y.

Figure 1, the full-screen output from program Draw running on Windows Vista, shows some incomplete shapes. The workaround to obtain the desired output shown in Figure 2 was to minimise the graphic window then maximise it. Apparently the redraw works better than the first attempt. Figure 3 shows the output (after the workaround) on selecting the quarter size option. This code demonstrates how vector graphics are scaled easily and effectively.

Incomplete image

Incomplete image

Complete image

Complete image

Quarter scale image

Quarter scale image

program Draw;
  {$mode objfpc}{$H+}
uses
  Graph;
const
  HB = 10;     //Horizontal border
  VB = 10;     //Vertical border
var
  Gd, Gm : smallint;
  MaxX, MaxY, Col1, Col2, Col3, Row1, Row2, Row3, CentreX, CentreY,
  Count, StartX, StartY, X, Y : integer;
  Pentagon : array[1..12] of word;
  Triangle : array[1..8] of word;
  Choice : char;

procedure DrawRow1;
begin
  //Red rectangle
  setColor(Red);
  rectangle(HB , VB, Col1 - HB, Row1 - VB);
  //Yellow filled rectangle
  setFillStyle(SolidFill, Yellow);
  bar(Col1 + HB, VB, Col2-HB, Row1 - VB);
  //Green circle
  setColor(Green);
  CentreX :=  (Col3 + Col2) DIV 2;
  CentreY :=  Row1 DIV 2;
  circle(CentreX, CentreY, (Row1 DIV 2)- VB);
  //Blue filled ellipse
  setColor(Blue);
  setFillStyle(SolidFill, Blue);
  CentreX := CentreX + Col1;
  fillEllipse(CentreX, CentreY, (Col1 DIV 2)- HB, (Row1 DIV 2)- VB);
end;

procedure DrawRow2;
begin
  //Cyan line (solid)
  setColor(Cyan);
  line (HB, Row1 + VB, Col1 - HB, Row2 - VB);
  //Brown dashed line
  SetLineStyle(DashedLn, 0, ThickWidth);
  moveTo( Col1 + HB, Row2 - VB);
  setColor(Brown);
  lineTo(Col2 - HB, Row1 + VB);
  //white ellipse
  setColor(White);
  setLineStyle(SolidLn, 0, NormWidth);
  CentreX :=  (Col3 + Col2) DIV 2;
  CentreY :=  (Row2 + Row1) DIV 2;
  ellipse(CentreX, CentreY, 0, 360, (Col1 DIV 2) - HB, (Row1 DIV 2) - VB);
  //Magenta filled circle
  setColor(magenta);
  setFillStyle(BkSlashFill, Magenta);
  CentreX :=  (Col3 + MaxX) DIV 2;
  CentreY :=  (Row2 + Row1) DIV 2;
  fillEllipse(CentreX, CentreY, (Row1 DIV 2)- VB, (Row1 DIV 2)- VB);
end;

procedure DrawRow3;
begin
  //Three quarters of light cyan ellipse
  setColor(LightCyan);
  CentreX :=  Col1 DIV 2;
  CentreY :=  (Row2 + Row3) DIV 2;
  ellipse(CentreX, CentreY, 0, 270, (Col1 DIV 2) - HB, (Row1 DIV 2) - VB);
  //Dark grey shaded rectangle
  setFillStyle(SolidFill, DarkGray);
  setcolor(DarkGray);
  bar3d(Col1 + HB, Row2 + VB, Col2 - HB, Row3 - VB, 10, True);
  //Light green pentagon
  setColor(LightGreen);
  Pentagon[1] := (Col3 + Col2) DIV 2;  Pentagon[2] :=  Row2 + VB;
  Pentagon[3] :=  Col3 - HB;           Pentagon[4] :=  (Row2 + Row3) DIV 2;
  Pentagon[5] :=  Col3 - (Col1 DIV 3); Pentagon[6] :=  Row3 - VB;
  Pentagon[7] :=  Col2 + (Col1 DIV 3); Pentagon[8] :=  Row3 - VB;
  Pentagon[9] :=  Col2 +  HB;          Pentagon[10] :=  (Row2 + Row3) DIV 2;
  Pentagon[11] := Pentagon[1];         Pentagon[12] := Pentagon[2];
  drawpoly(6, Pentagon);
  //Light red filled triangle
  setFillStyle(SolidFill, LightRed);
  setColor(LightRed);
  Triangle[1] := (Col3 + MaxX) DIV 2;  Triangle[2] :=  Row2 + VB;
  Triangle[3] :=  MaxX - HB;           Triangle[4] :=  Row3 - VB;
  Triangle[5] :=  Col3 + HB;           Triangle[6] :=  Row3 - VB;
  Triangle[7] :=  Triangle[1];         Triangle[8] :=  Triangle[2];
  fillpoly(4, Triangle);
end;

procedure DrawRow4;
begin
  //Light magenta sector of ellipse
  setFillStyle(SolidFill, LightMagenta);
  CentreX :=  Col1 DIV 2;
  CentreY :=  (Row3 + MaxY) DIV 2;
  //270 degrees is direction of y axis and 360 degrees is angle of y axis
  sector(CentreX, CentreY, 270, 360, (Col1 DIV 2) - HB, (Row1 DIV 2) - VB);
  //Plot points for straight lines
  for Count := 1 to (Row1 DIV 2) - VB do
    begin
      putPixel(Col1 + HB + Count, Row3 + VB + Count, LightBlue);
    end;
  StartX := Col1 + HB + Count;
  StartY := Row3 + VB + Count;
  for Count := 1 to (Row1 DIV 2) - VB do
    begin
      putPixel(StartX + Count, StartY - Count, LightBlue);
    end;
  //Yellow text
  setColor(Yellow);
  setTextStyle(ScriptFont, HorizDir, 2);
  moveTo(col1 + HB, (Row3 + MaxY) DIV 2);
  outText('www.pp4s.co.uk');
  //White curve
  Count := 1;
  X := Col2 + HB + (Count * 10);
  Y := Row3 + VB + (Count * Count) DIV 5;
  while (X <= Col3 - HB) and (Y <= MaxY - VB) do
    begin
      //Plot curve (4 pixels per point to make it more visible)
      putPixel(X, Y, White);
      putPixel(X - 1, Y, White);
      putPixel(X, Y - 1, White);
      putPixel(X - 1, Y - 1, White);
      inc(Count);
      X := Col2 + HB + (Count * 10);
      Y := Row3 + VB + (Count * Count) DIV 5;
    end;
  //Light green arc
  setColor(LightGreen);
  arc((col3 + MaxX) DIV 2, (Row3 + MaxY) DIV 2, 0, 180, (Row1 DIV 2) - VB);
end;

begin 
  write('When the graphics window opens, you may need to restore'+ 
        ' down or minimise'#13#10'the window then maximise it' +
        ' to see all of the shapes.'#13#10#13#10,'Type F for' +
        ' full size or Q for quarter size. ');
  repeat
    readln(Choice);
  until Choice in ['F','f','Q','q'];
  write('Press return to exit. ');
  Gd := Detect; 
  Gm := 0;
  InitGraph(Gd, Gm, '');

  if Choice in ['F','f'] then
    begin
      MaxX := GetMaxX;
      MaxY := GetMaxY;
    end
  else
    begin
      MaxX := GetMaxX DIV 2;
      MaxY := GetMaxY DIV 2;
    end;
  //Divide the area into columns and rows
  Col1 := MaxX DIV 4;  //End of column
  Col2 := MaxX DIV 2;
  Col3 := Col1 + Col2;
  Row1 := MaxY DIV 4;   //End of row
  Row2 := MaxY DIV 2;
  Row3 := Row1 + Row2;
  setLineStyle(Solidln, 0, Thickwidth);
  setColor(White);
  //Set background colour
  setBkColor(White);
  setFillStyle(SolidFill, LightGray);
  bar(0,0,MaxX,maxY);
  //Draw
  DrawRow1;
  DrawRow2;
  DrawRow3;
  DrawRow4;
  readln;
end.

Student programs using the WinGraph unit

See this part of the table of programs for many fine examples of the use of the WinGraph unit. We recommend in particular GASP and Knowledge by Peter Hearnshaw and Game Of Life by James Hall.

Programming - a skill for life!

Vector Graphics using the CRT, Graph and WinGraph units and output to SVG file