Forum > Graphics

Curling Pythagoras Tree: Ready for download

(1/3) > >>

Boleeman:
Got the Extra Feature done, so go down to the thread below to download it.


Hi all.

I was trying to convert the CSharp Pythagoras Tree source from the CSharp helper site (at http://www.csharphelper.com/howtos/howto_pythagorean_tree.html). The code uses vectors to curl the branches. Changing the Alpha makes it curl. Uploaded pic in green shows the levels.

The converted Lazarus tree code sort of works for 1st level and then goes a bit silly after that.

CSharp source:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---private void picCanvas_Paint(object sender, PaintEventArgs e){    e.Graphics.Clear(picCanvas.BackColor);    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;     try    {        int depth = (int)nudDepth.Value;        int length = (int)nudLength.Value;        float alpha =            (float)((double)nudAlpha.Value * Math.PI / 180.0);        float root_x = picCanvas.ClientSize.Width / 2;        float root_y = picCanvas.ClientSize.Height * 0.9f;        VectorF v_base = new VectorF(length, 0);        PointF ll_corner = new PointF(root_x, root_y) - v_base / 2;         Brush brush = null;        if (chkFill.Checked) brush = Brushes.Green;                DrawBranch(e.Graphics, Pens.Black, brush,            depth, ll_corner, v_base, alpha);    }    catch    {    }}  private void DrawBranch(Graphics gr, Pen pen, Brush brush,    int depth, PointF ll_corner, VectorF v_base, float alpha){    // Find the box's corners.    VectorF v_height = v_base.PerpendicularCCW();    PointF[] points =    {        ll_corner,        ll_corner + v_base,        ll_corner + v_base + v_height,        ll_corner + v_height,    };     // Draw this box.    if (brush != null) gr.FillPolygon(brush, points);    gr.DrawPolygon(pen, points);     // If depth > 0, draw the attached branches.    if (depth > 0)    {        // ***********        // Left branch        // ***********        // Calculate the new side length.        double w1 = v_base.Length * Math.Cos(alpha);         // Decompose the new base vector in terms of        // v_base and v_height.        float wb1 = (float)(w1 * Math.Cos(alpha));        float wh1 = (float)(w1 * Math.Sin(alpha));        VectorF v_base1 = v_base.Scale(wb1) + v_height.Scale(wh1);         // Find the lower left corner.        PointF ll_corner1 = ll_corner + v_height;         // Draw the left branch.        DrawBranch(gr, pen, brush, depth - 1,            ll_corner1, v_base1, alpha);         // ************        // Right branch        // ************        // Calculate the new side length.        double beta = Math.PI / 2.0 - alpha;        double w2 = v_base.Length * Math.Sin(alpha);         // Decompose the new base vector in terms of        // v_base and v_height.        float wb2 = (float)(w2 * Math.Cos(beta));        float wh2 = (float)(w2 * Math.Sin(beta));        VectorF v_base2 = v_base.Scale(wb2) - v_height.Scale(wh2);         // Find the lower left corner.        PointF ll_corner2 = ll_corner1 + v_base1;         // Draw the right branch.        DrawBranch(gr, pen, brush, depth - 1,            ll_corner2, v_base2, alpha);    }}  public class VectorF{    public float X, Y;     public VectorF(float x, float y)    {        X = x;        Y = y;    }    public VectorF(PointF point1, PointF point2)    {        X = point2.X - point1.X;        Y = point2.Y - point1.Y;    }    public VectorF(VectorF v)    {        X = v.X;        Y = v.Y;    }     public static VectorF operator +(VectorF v1, VectorF v2)    {        return new VectorF(v1.X + v2.X, v1.Y + v2.Y);    }     public static VectorF operator -(VectorF v1, VectorF v2)    {        return new VectorF(v1.X - v2.X, v1.Y - v2.Y);    }     public static PointF operator +(PointF point, VectorF vector)    {        return new PointF(point.X + vector.X, point.Y + vector.Y);    }     public static PointF operator -(PointF point, VectorF vector)    {        return new PointF(point.X - vector.X, point.Y - vector.Y);    }     public static VectorF operator -(VectorF vector)    {        return -1 * vector;    }     public static VectorF operator *(VectorF vector, float scale)    {        return new VectorF(vector.X * scale, vector.Y * scale);    }    public static VectorF operator *(float scale, VectorF vector)    {        return vector * scale;    }     public static VectorF operator /(VectorF vector, float scale)    {        return new VectorF(vector.X / scale, vector.Y / scale);    }     public float Length    {        get        {            return (float)Math.Sqrt(X * X + Y * Y);        }        set        {            float scale = value / Length;            X *= scale;            Y *= scale;        }    }     // Return a scaled version of the vector.    public VectorF Scale(float scale)    {        return this * scale / Length;    }     // Make the vector unit length.    public void Normalize()    {        Length = 1;    }     // Find the perpendicular vector in the    // counterclockwise direction.    public VectorF PerpendicularCCW()    {        return new VectorF(Y, -X);    }     // Find the perpendicular vector in the    // clockwise direction.    public VectorF PerpendicularCW()    {        return new VectorF(-Y, X);    }} 

Lazarus code:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit PythagorasTree; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,  ExtCtrls, Spin, Math; type   { TForm1 }   TForm1 = class(TForm)    chkFill: TCheckBox;    Label1: TLabel;    Label2: TLabel;    Label3: TLabel;    Label4: TLabel;    PaintBox1: TPaintBox;    nudDepth: TSpinEdit;    nudLength: TSpinEdit;    nudAlpha: TSpinEdit;    procedure chkFillChange(Sender: TObject);    procedure nudAlphaChange(Sender: TObject);    procedure nudDepthChange(Sender: TObject);    procedure nudLengthChange(Sender: TObject);    procedure PaintBox1Paint(Sender: TObject);  private    procedure DrawBranch(ACanvas: TCanvas; APen: TPen; ABrush: TBrush; Depth: Integer;      LL_Corner: TPoint; V_Base: TPoint; Alpha: Single);    function PointLength(const P: TPoint): Double;  public   end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.PaintBox1Paint(Sender: TObject);var  Pen: TPen;  newBrush: TBrush;begin  PaintBox1.Canvas.Clear;  PaintBox1.Canvas.AntialiasingMode:=amOn;   try    Pen := TPen.Create;    newBrush := TBrush.Create;     if chkFill.Checked then      begin        Paintbox1.Canvas.Brush.Color := clRed;        Paintbox1.Canvas.Brush.Style := bsSolid;      end          else      begin        Paintbox1.Canvas.brush.style := bsClear;      end;     DrawBranch(PaintBox1.Canvas, Pen, newBrush,      nudDepth.Value, Point(PaintBox1.Width div 2, Round(PaintBox1.Height *  0.6)),      Point(nudLength.Value, 0), DegToRad(nudAlpha.Value));  finally    Pen.Free;    newBrush.Free;  end;end; procedure TForm1.chkFillChange(Sender: TObject);begin    PaintBox1.Invalidate;end; procedure TForm1.nudAlphaChange(Sender: TObject);begin    PaintBox1.Invalidate;end; procedure TForm1.nudDepthChange(Sender: TObject);begin    PaintBox1.Invalidate;end; procedure TForm1.nudLengthChange(Sender: TObject);begin    PaintBox1.Invalidate;end; function TForm1.PointLength(const P: TPoint): Double;begin  Result := Hypot(P.X, P.Y);end;  procedure TForm1.DrawBranch(ACanvas: TCanvas; APen: TPen; ABrush: TBrush; Depth: Integer;  LL_Corner: TPoint; V_Base: TPoint; Alpha: Single);var  V_Height: TPoint;  Points: array[0..3] of TPoint;  w1, wb1, wh1: Single;  v_base1: TPoint;  ll_corner1: TPoint;  beta, w2, wb2, wh2: Single;  v_base2: TPoint;  ll_corner2: TPoint;begin  // Find the box's corners.  V_Height := Point(- V_Base.Y, V_Base.X);  Points[0] := LL_Corner;  Points[1] := LL_Corner + V_Base;  Points[2] := LL_Corner + V_Base + V_Height;  Points[3] := LL_Corner + V_Height;   // Draw this box.  if ABrush <> nil then    ACanvas.Polygon(Points);  if APen <> nil then    ACanvas.Polygon(Points);   // If depth > 0, draw the attached branches.  if Depth > 0 then  begin    // Left branch    // Calculate the new side length.    w1 := PointLength(V_Base) * Cos(Alpha);     // Decompose the new base vector in terms of    // V_Base and V_Height.    wb1 := w1 * Cos(Alpha);    wh1 := w1 * Sin(Alpha);    v_base1 := Point(Round(wb1), Round(wh1));     // Find the lower left corner.    ll_corner1 := LL_Corner + V_Height;     // Draw the left branch.    DrawBranch(ACanvas, APen, ABrush, Depth - 1, ll_corner1, v_base1, Alpha);     // Right branch    // Calculate the new side length.    beta := Pi / 2.0 - Alpha;    w2 := PointLength(V_Base) * Sin(Alpha);     // Decompose the new base vector in terms of    // V_Base and V_Height.    wb2 := w2 * Cos(beta);    wh2 := w2 * Sin(beta);    v_base2 := Point(Round(wb2), Round(- wh2));     // Find the lower left corner.    ll_corner2 := ll_corner1 + v_base1;     // Draw the right branch.    DrawBranch(ACanvas, APen, ABrush, Depth - 1,      ll_corner2, v_base2, Alpha);  end;end; end.

Boleeman:
Been comparing the 2nd level picture with the actual result of the CSharp expected result and it looks like the problem is with the rotation angle in the next stage of recursion, as the corners of the squares are lining up (see attached picture).

Left branch rotation angle needs to increase and right branch rotation angle needs to decrease (almost looks like by the same amount, in the picture the angle looks like 45 degrees rotation is needed from the 2 aligning corners of the squares in the next stage).

Actually I think I have found the problem.

In the csharp code,

private void DrawBranch(Graphics gr, Pen pen, Brush brush,
    int depth, PointF ll_corner, VectorF v_base, float alpha)
        PointF ll_corner2 = ll_corner1 + v_base1;


In the Lazarus code I have

    // Find the lower left corner.
    ll_corner2 := ll_corner1 + v_base1;

That matches up.

But in the picCanvas_Paint which is equivalent to TForm1.PaintBox1Paint(Sender: TObject)

private void picCanvas_Paint(object sender, PaintEventArgs e)
       PointF ll_corner = new PointF(root_x, root_y) - v_base / 2;


so perhaps the procedure TForm1.PaintBox1Paint(Sender: TObject);  needs to be changed to have the PointF(root_x, root_y) - v_base / 2; part in it.

Boleeman:
Will need to go back to basics with this one and work out where I messed up.

I think somehow it may be a vector orientation or angle mistake that I must have made somewhere.

Anyhow, eventually I will work it out. More to play around with.

jamie:
maybe the window needs to have its World transformation / Orientation set to lower left corner?

Boleeman:
Jamie, you may be correct as what is drawn on the screen seems to be upside down and also mirrored.

Thanks for the advice.

Navigation

[0] Message Index

[#] Next page

Go to full version