unit U_Genaille;
 {Copyright 2000, Gary Darby, Intellitech Systems Inc., www.DelphiForFun.org

 This program may be used or modified for any non-commercial purpose
 so long as this original notice remains in place.
 All other rights are reserved
 }

{Draw and print Genaille's Rods for multiplication}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Mask, MathCtrl, ExtCtrls, Printers, ComCtrls, Menus;

type
  {Rods object}
  TRodSet = class(TObject)
    boxwidth,boxheight,fontsize,space:integer;
    Procedure  drawbox(const n,index:integer; const P:TPoint; canvas:TCanvas);
    Procedure  DrawComplete(canvas:TCanvas);
  end;

  TForm1 = class(TForm)
    BarSizeBox: TGroupBox;
    Label3: TLabel;
    Label4: TLabel;
    BarSizeLbl: TLabel;
    DrawItBtn: TButton;
    Image1: TImage;
    PrintDialog1: TPrintDialog;
    PrintBtn: TButton;
    FontSizeEdt: TEdit;
    Label5: TLabel;
    FontsizeUpDn: TUpDown;
    BarWidthUpDn: TUpDown;
    BarHeightUpDn: TUpDown;
    BarHeightEdt: TEdit;
    BarWidthEdt: TEdit;
    BkgndBtn: TButton;
    SampleBtn: TButton;
    Usagememo2: TMemo;
    UsageMemo1: TMemo;
    RodRadioGrp: TRadioGroup;
    DigitEdt: TEdit;
    DigitUpDn: TUpDown;
    IndexRadioGrp: TRadioGroup;
    IndexEdt: TEdit;
    IndexUpDn: TUpDown;
    procedure FormActivate(Sender: TObject);
    procedure DrawItBtnClick(Sender: TObject);
    procedure PrintBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BkgndBtnClick(Sender: TObject);
    procedure SampleBtnClick(Sender: TObject);
    procedure SampleBtnExit(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Rods:TRodSet;
    savetop:integer;
  end;

var
  Form1: TForm1;

implementation

uses U_GenailleInfo;

{$R *.DFM}

procedure TForm1.FormActivate(Sender: TObject);
begin
  BarSizelbl.caption:= 'Total rod size is '
                       +InttoStr(BarwidthUpDn.position)
                       + ' X ' + Inttostr(46*BarHeightUpDn.position);
end;

type
  TriangleRec=record
    L,r1,r2:integer;
  end;


{************************* DrawBox **********************}
Procedure TRodset.drawbox(const n,index:integer; const P:TPoint; canvas:TCanvas);
{draw a box for column "n" and row "index" at point "p" on canvas}
var
  p1, p2:integer;
  p1max:integer;
  T1,T2: TriangleRec;  {describes "pointer" triangles}
  labeltop:integer; {first integer to list on left}
  barheight:integer;
  numboxleft:integer;
  i:integer;
  s:string;
Begin
  barheight:=index*boxheight;
  T2.L:=-1; {set no 2nd triangle flag}
  p1:=n*index; {compute the product}
  labeltop:=p1 mod 10; {units digit of product, the top number to display in box}
  p2:= p1 div 10; {the carry digit}
  T1.L:=p2;  {left index = 10s digit}
  t1.R1:=0;
  t1.R2:=index-1;
  p1max:=labeltop+index-1;
  If p1max>=10 then {we will have 2 triangles}
  Begin
    t1.r2:=10-labeltop-1;
    t2.r1:=t1.r2+1;
    t2.r2:=index-1;
    t2.L:=t1.L+1;
  end;
  {Now draw everything}
  with canvas do
  Begin
    pen.color:=clblack;
    brush.color:=clwhite;
    rectangle(p.x,p.y,p.x+boxwidth,p.y+boxheight*index);
    numboxleft:=p.x+3*(boxwidth) div 4; {3/4ths of the way across}
    moveto(numboxleft,p.y);
    lineto(numboxleft,p.y+barheight);
    for i:=0 to index-1 do
    Begin
      s:=inttostr(labeltop mod 10);
      textout(numboxleft+(p.x+boxwidth-numboxleft-textwidth(s)) div 2,
              p.y+i*boxheight+(boxheight-textheight(s)) div 2,
               s);
      inc(labeltop);
    end;
    brush.color:=clgray;
    polygon([point(numboxleft,p.y),
            point(p.x,p.y+t1.L*boxheight +boxheight div 2),
            point(numboxleft,p.y+(t1.r2+1)*boxheight)]);

    If t2.L>=0 then
    Begin
      polygon([point(numboxleft,p.y+t2.r1*boxheight),
               point(p.x,p.y+t2.L*boxheight +boxheight div 2),
               point(numboxleft,p.y+(t2.r2+1)*boxheight)]);
    end;
    brush.color:=clwhite;
  end;
end;

{****************** DrawComplete **********************}
Procedure  TRodSet.DrawComplete(canvas:TCanvas);
{draw a complete set of rods}
var
  digit,index:integer;
  P:TPoint;
  rect:TRect;
  i,j,bhd2,bh:integer;
  numleft:integer;
Begin
  P.x:=boxwidth div 2 + space;
  if fontsize>0 then Canvas.font.size:=fontsize
  else canvas.font.size:= - trunc((boxheight-8) * 72 / canvas.font.PixelsPerInch);
  For digit:=0 to 9 do {for all rods}
  Begin
    with p do
    Begin
      x:=x+(boxwidth+space);
      y:=boxheight div 2;
    end;
    with canvas do
    Begin
      pen.color:=clblack;
      brush.color:=clwhite;
      rectangle(classes.rect(p.x,p.y,p.x+boxwidth,p.y+boxheight));
      textout(p.x+(boxwidth-textwidth(inttostr(digit))) div 2,
              p.y++(boxheight-textheight(inttostr(digit))) div 2,
              inttostr(digit));
    end;
    p.y:=p.y+boxheight;
    for index:= 1 to 9 do    {for all digits in the rod}
    Begin
      with P do y:=y+(index-1)*boxheight;
      Drawbox(digit,index,P,canvas);
    end;
  end;
    {now draw the index bar}
    with canvas do
    Begin
      bhd2:=boxheight div 2;
      rect.top:=bhd2+boxheight;
      rect.left:=boxwidth div 2;
      rect.right:=rect.left+boxwidth;
      rectangle(rect.left , bhd2,
               rect.right, bhd2 + 46*boxheight);
      textout(rect.left+(boxwidth-textwidth('Index')) div 2,
              bhd2+(boxheight-textheight('Index'))div 2,
              'Index');
      numleft:=2*boxwidth div 3;
      for i:=1 to 9 do
      Begin
        bh:=i*boxheight;
        rect.bottom:=rect.top+bh;
        rectangle(rect);
        textout(rect.left+(boxwidth-textwidth(inttostr(i))) div 2,
                rect.top+(bh - textheight(inttostr(i))) div 2,
                inttostr(i));
        for j:= 0 to i-1 do
             textout(rect.left+numleft+(boxwidth-numleft-textwidth(inttostr(j))) div 2,
                     rect.top+j*boxheight+(boxheight-textheight(inttostr(j))) div 2,
                     inttostr(j));
        rect.top:=rect.bottom;
      end;
      moveto(rect.left+numleft, bhd2+boxheight);
      lineto(rect.left+numleft, bhd2+46*boxheight-1);
    end;
end; {Drawcomplete}


{***************** DrawIt  *******************}
procedure TForm1.DrawItBtnClick(Sender: TObject);
var
  digit,index:integer;
  P:TPoint;
  b:TBitmap;
begin
  b:=tBitmap.create;
  b.canvas.brush.color:=clWhite;
  rods.BoxHeight:=BarHeightUpDn.position;
  rods.BoxWidth:=BarWidthUpDn.position;
  rods.fontsize:=FontsizeUpDn.position;
  rods.space:=5; {5 pixels separation}
  with rods, b do
  If RodRadioGrp.Itemindex<>1 then
  Begin
    if IndexRadioGrp.Itemindex<>1 then {draw a single box}
    Begin
      digit:=strtoint(Digitedt.text);
      index:=strtoint(IndexEdt.text);
      height:=index*boxheight;
      width:=boxwidth;
      canvas.Rectangle(0,0,width,height);
      P:=point(0,0);
      Drawbox(digit,index,P,canvas);
    end
    else
    Begin
      {draw a complete rod for a single digit}
      digit:=strtoint(Digitedt.text);
      height:=46*Boxheight+2;
      width:=boxwidth;
      canvas.Rectangle(0,0,width,height);
      P:=Point(0,boxheight);
      for index:= 1 to 9 do
      Begin
        p.y:=p.y+(index-1)*boxheight;
        Drawbox(digit,index,P,canvas);
      end;
    end
  end
  else
  Begin
    if IndexRadioGrp.itemindex<>1 then
    {draw one box for all rods}
    with rods do
    begin
      index:=strtoint(IndexEdt.text);
      height:=(index+1)*(Boxheight+2);
      width:=12*boxwidth+space;
      canvas.Rectangle(0,0,width,height);
      P:=Point(-boxwidth div 2,boxheight);
      for digit:= 0 to 9 do
      Begin
        p.x:=p.x+boxwidth+space;
        Drawbox(digit,index,P,canvas);
      end;
    end
    else
    Begin
      {draw a complete set of rods for all digits}
      height:=47*boxheight;
      width:=(boxwidth+space)*12;
      canvas.Rectangle(0,0,width,height);
      rods.Drawcomplete(canvas);
    end;
  end;
  image1.height:=b.height;
  image1.width:=b.width;
  image1.picture.bitmap.assign(b);
  b.free;
end;

procedure TForm1.PrintBtnClick(Sender: TObject);
{print a set of rods}
begin
  if printdialog1.execute then
  Begin
    Printer.Begindoc;
    {leave room for 47 boxes vertically}
    rods.Boxheight:=printer.PageHeight div (47);
    {leave room for 13 rods - only 11 drawn}
    rods.boxwidth:= printer.pagewidth  div (13)-5;
    rods.fontsize:=0; {use automatic font sizing}
    rods.space:=10;
    (*
    {make the bitmap halfsize - use if not enough memory to draw full page}
    bitmap:=TBitmap.create;
    bitmap.width:=printer.pagewidth div 2;
    bitmap.height:=printer.pageheight div 2;
    rods.Boxheight:=rods.boxheight div 2;
    rods.boxwidth:= rods.boxwidth div 2;
    rods.Drawcomplete(bitmap.canvas);
    {now stretch it to full page size}
    with printer do
      canvas.stretchdraw(rect(0,0,pagewidth-1,pageheight-1), bitmap);
    *)
    rods.DrawComplete(printer.canvas);
    printer.enddoc;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Rods:=TRodSet.create;
end;

procedure TForm1.BkgndBtnClick(Sender: TObject);
begin
  infoform.show;
end;

procedure TForm1.SampleBtnClick(Sender: TObject);
{8563 X 4}
{draw partial index rod and digit rods in solution sequence}
var
  digit,index:integer;
  P:TPoint;
  rect:TRect;
  i,j,n,numhigh,bhd2,bh:integer;
  numleft:integer;
  s:string;
Begin
  s:='8563';
  numhigh:=(1+length(s))*length(s) div 2 +1;
  with rods, image1  do
  begin
    BoxHeight:=(usagememo2.top-usagememo1.top-usagememo1.height) div (numhigh+1);
    BoxWidth:=BarWidthUpDn.position;
    fontsize:=10;
    savetop:=top;
    top:=Usagememo1.top+usagememo1.height+5;
    height:=0;
    height:=(numhigh+1)*Boxheight;
    width:=(boxwidth+4)*(length(s)+2);
    canvas.brush.color:=clwhite;
    canvas.Rectangle(0,0,width,height);
    P.x:=boxwidth div 2 + 2;
    if fontsize>0 then Canvas.font.size:=fontsize
    else canvas.font.size:= - trunc((boxheight-4) * 72 / canvas.font.PixelsPerInch);
    For n:=0 to length(s)-1 do
    Begin
      digit:=strtoint(s[n+1]);
      with p do
      Begin
        x:=x+(boxwidth+2);
        y:=boxheight div 2;
      end;
      with canvas do
      Begin
        pen.color:=clblack;
        rectangle(classes.rect(p.x,p.y,p.x+boxwidth,p.y+boxheight));
        textout(p.x+(boxwidth-textwidth(inttostr(digit))) div 2,
                p.y++(boxheight-textheight(inttostr(digit))) div 2,
                inttostr(digit));
      end;
      p.y:=p.y+boxheight;
      {draw a rod}
      for index:= 1 to length(s) do
      Begin
        with P do y:=y+(index-1)*boxheight;
        Drawbox(digit,index,P,canvas);
      end;
    end;
    {now draw the index bar}
    with canvas do
    Begin
      bhd2:=boxheight div 2;
      rect.top:=bhd2+boxheight;
      rect.left:=boxwidth div 2;
      rect.right:=rect.left+boxwidth;
      rectangle(rect.left , bhd2,
               rect.right, bhd2 + numhigh*boxheight);
      textout(rect.left+(boxwidth-textwidth('Index')) div 2,
              bhd2+(boxheight-textheight('Index'))div 2,
              'Index');
      numleft:=2*boxwidth div 3;
      for i:=1 to 4 do
      Begin
        bh:=i*boxheight;
        rect.bottom:=rect.top+bh;
        rectangle(rect);
        textout(rect.left+(boxwidth-textwidth(inttostr(i))) div 2,
                rect.top+(bh - textheight(inttostr(i))) div 2,
                inttostr(i));
        for j:= 0 to i-1 do
             textout(rect.left+numleft+(boxwidth-numleft-textwidth(inttostr(j))) div 2,
                     rect.top+j*boxheight+(boxheight-textheight(inttostr(j))) div 2,
                     inttostr(j));
        rect.top:=rect.bottom;
      end;
      moveto(rect.left+numleft, bhd2+boxheight);
      lineto(rect.left+numleft, bhd2+numhigh*boxheight-1);
    end;
  end;
  UsageMemo1.visible:=true;
  UsageMemo2.visible:=true;
end;

procedure TForm1.SampleBtnExit(Sender: TObject);
begin
  UsageMemo1.visible:=false;
  UsageMemo2.visible:=false;
  image1.top:=savetop; {restore old top}
  image1.height:=0;
end;

end.