No Name QA
No Name QA

Reputation: 784

Recursion Solution to Flea Fractal Task

I'm trying to solve task 9.3 from book "Think Recursively by Eric S. Roberts (1986)". Let consider that we have simple triangle:

              .
             / \
            /   \
           /     \
          /       \
         /         \
        /           \
       /             \
      /               \
     /                 \
    /                   \
   /                     \
  /_______________________\

Putting a smaller flea on each of two segments that forms the back of the large flea gives the next stage:

              .
             / \
            /   \
           /     \
 _________/       \_________
 \       /         \       /
  \     /           \     /
   \   /             \   /
    \ /               \ /
     /                 \
    /                   \
   /                     \
  /_______________________\

Each of these two new fleas plays host to two smaller fleas, which give us the following menagerie:

              .
             / \
            /   \
     .     /     \     .
 ___/_\___/       \___/_\___
 \       /         \       /
  :     /           \     :
 /_\   /             \   /_\
    \ /               \ /
     /                 \
    /                   \
   /                     \
  /_______________________\

The problem is that I can not figure out the recursion pattern here. I even do not know how should I start to think about this task to find a solution.

For drawing I use 3 functions:

  1. setPointer(x, y) -- sets the pen to coords x,y
  2. vector(dx,dy) -- draw line from x, y to dx, dy
  3. polarVec(l, angle) draw line from x, y with length l by angle (in degrees)

My class for drawing at Canvas:

        var X_CENTER = 400;
        var Y_CENTER = 400;
        function DrawLib() {
            var canvas = document.getElementById("myCanvas");
            var ctx = canvas.getContext("2d");
            var inverted_y = 800; 
            var _dx = 0, _dy = 800;

            this.setPoint = function (x, y) {
                inverted_y = 800;
                inverted_y -= y;
                _dx = x;
                _dy = inverted_y;
                ctx.moveTo(x, inverted_y);
            };
            this.vector = function(dx, dy) {
                _dx += dx;
                _dy -= dy;
                ctx.lineTo(_dx, _dy); 
            };
            this.polarVec = function(lengthPx, angle) {
                var radians = angle * Math.PI / 180;
                this.vector(lengthPx * Math.cos(radians), lengthPx * Math.sin(radians));
            }
            this.stroke = function () {
                ctx.stroke();
            }
        }

        var drawLib = new DrawLib();

My solution (perfectly works only for order 0 and 1):

        function fractLine(order, l, theta) {
            if (order == 0) return drawLib.polarVec(l, theta);
            fractLine(order - 1, 2 * l/3,theta);
            fractLine(order - 1, l/3,theta - 120);
            fractLine(order - 1, l/3,theta + 120);
            fractLine(order - 1,  2* l/3,theta);
        }

        function triangleWithFleas(order, l) {
            //left side of triangle
            drawLib.setPoint(X_CENTER - l/2, Y_CENTER - Math.sqrt(3)* l/4);
            //start drawing
            drawLib.vector(l,0);
            fractLine(order, l, 120);
            fractLine(order, l, 240);
            drawLib.stroke();
        }

        triangleWithFleas(1, 400);

Upvotes: 1

Views: 54

Answers (1)

No Name QA
No Name QA

Reputation: 784

According to @jxh comment, I needed a function that draws a triangle at a particular orientation from a particular starting point. Then, it should recursively calls itself twice, adjusting the orientation and starting point for each call.

I've updated my code. triangle draws the triangle, showFlake recursively draws the flea triangles after calling triangle.

Now it works perfectly.

var X_CENTER = 400;
    var Y_CENTER = 400;
    function DrawLib() {
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var inverted_y = 800; 
        var _dx = 0, _dy = 800;

        this.setPoint = function (x, y) {
            inverted_y = 800;
            inverted_y -= y;
            _dx = x;
            _dy = inverted_y;
            ctx.moveTo(x, inverted_y);
        };
        this.vector = function(dx, dy) {
            _dx += dx;
            _dy -= dy;
            ctx.lineTo(_dx, _dy);
            return {
                x: _dx,
                y: 800 - _dy // coord inversion
            };
        };
        this.polarVec = function(lengthPx, angle, color) {
            var radians = angle * Math.PI / 180;
            return this.vector(lengthPx * Math.cos(radians), lengthPx * Math.sin(radians));
        }
        this.stroke = function () {
            ctx.stroke();
        }
    }

    var drawLib = new DrawLib();


    function triangle(x, y, l, theta) {
        drawLib.setPoint(x, y);
        drawLib.polarVec(l, theta);
        var firstAngle = theta + 120;
        var secondAngle = theta + 240;

        var firstTrianleStartPoint = drawLib.polarVec(2 * l / 3, firstAngle);
        drawLib.polarVec( l / 3, firstAngle);

        var secondTrianleStartPoint = drawLib.polarVec(2 * l / 3, secondAngle);
        drawLib.polarVec(l / 3, secondAngle);

         return {
            x1: firstTrianleStartPoint.x,
            y1: firstTrianleStartPoint.y,
            x2: secondTrianleStartPoint.x,
            y2: secondTrianleStartPoint.y,
        }
    }

    function showFlake(order, x, y, l, theta) {
        if(order === 0) return;

        var coords = triangle(x, y, l, theta);
        showFlake(order - 1, coords.x1, coords.y1, l / 3, theta + -60);
        showFlake(order - 1, coords.x2, coords.y2, l / 3, theta + 60);
    }

    var len = 600
    var x = X_CENTER - len / 2;
    var y = Y_CENTER - Math.sqrt(3) * len / 4;

    showFlake(5, x, y, len, 0);
    drawLib.stroke();

Upvotes: 1

Related Questions