Metaballs

Metaballs are organic-looking n-dimensional ( 2D in our case ) isosurfaces, characterised by their ability to meld together when in close proximity to create single, contiguous objects.

This is a Marching Sqaures Algorithm implementation to simulate Metaballs.
Marching Squares is a 2D algorithm that can be used to generate contour from scalar fields. In simpler words ,In Marching Sqaures algorithm we divide the space into a grid of squares and for each square we calculate the value of the scalar field at the edges of the square and then use a simple thresholding function to determine where to draw the contour.

Code

            
    elem = document.getElementsByClassName("example-container")[0];
    setCanvas(elem);
    
    w = WIDTH;
    h = HEIGHT;
    
    i = 0;
    pos = {
        x: 40,
        y: 40,
        r: 30
    }
    pos1 = {
        x: 120,
        y: 150,
        r: 30
    }
    vel = {
        x: random(4, 5),
        y: random(1, 4)
    }
    vel1 = {
        x: random(4, 5),
        y: random(1, 4)
    }
    
    function velOnCollision(pos, vel) {
    
        if (pos.x + pos.r > w || pos.x - pos.r < 0) {
            vel.x *= -1
        }
        if (pos.y + pos.r > h || pos.y - pos.r < 0) {
            vel.y *= -1;
        }
    
    }
    ii = 2;
    c = 1;
    
    
    
    function draw() {
    
        clearCanvas();
    
    
        pos.x += vel.x;
        pos.y += vel.y;
        pos1.x += vel1.x;
        pos1.y += vel1.y;
    
        velOnCollision(pos, vel);
        velOnCollision(pos1, vel1);
        for (i = 0; i < w; i += ii) {
            for (j = 0; j < h; j += ii) {
                d1 = pos.r / dist(pos.x, pos.y, i, j);
                d2 = pos1.r / dist(pos1.x, pos1.y, i, j);
                d = [d1 + d2];
                d1 = pos.r / dist(pos.x, pos.y, i + ii, j);
                d2 = pos1.r / dist(pos1.x, pos1.y, i + ii, j);
                d.push(d1 + d2);
                d1 = pos.r / dist(pos.x, pos.y, i, j + ii);
                d2 = pos1.r / dist(pos1.x, pos1.y, i, j + ii);
                d.push(d1 + d2);
                d1 = pos.r / dist(pos.x, pos.y, i + ii, j + ii);
                d2 = pos1.r / dist(pos1.x, pos1.y, i + ii, j + ii);
                d.push(d1 + d2);
                
                activeconvers = 0;
                activec = [];
                for (k = 0; k < d.length; k++) {
                if (d[k] > 1) {
                    activeconvers += 1;
                    activec.push(1);
                } else {
                    activec.push(0);
                }
                }
                if (activeconvers == 1) {
                if (activec[0] == 1) {
                    dx = i + (c - d[0]) * ii / (d[1] - d[0]);
                    dy = j + (c - d[0]) * ii / (d[2] - d[0]);
                    new line(dx, j, i, dy, "#695FE6", 2);
                } else if (activec[1] == 1) {
                    dx = i + ((c-d[0])*ii)/(d[1]-d[0]);
                    dy = j + ((c-d[1])*ii)/(d[3]-d[1]);
                    new line(dx, j, i+ii, dy, "#695FE6", 2);
                } else if (activec[2] == 1) {
                    dx = i + ((c-d[2])*ii)/(d[3]-d[2]);
                    dy = j + ((c-d[0])*ii)/(d[2]-d[0]);
                    new line(i, dy, dx, j+ii, "#695FE6", 2);
                } else if (activec[3] == 1) {
                    dx = i + ((c-d[2])*ii)/(d[3]-d[2]);
                    dy = j + ((c-d[1])*ii)/(d[3]-d[1]);
                    new line(i+ii, dy,dx,j+ii, "#695FE6", 2);
                
                }
                } else if (activeconvers == 2) {
                if ((activec[0] == 1 && activec[1] == 1) || (activec[2] == 1 && activec[3] == 1)) {
                    dy1 = j + (c - d[0]) * ii / (d[2] - d[0]);
                    dy2 = j + (c - d[1]) * ii / (d[3] - d[1]);
                    new line(i, dy1, i + ii, dy2, "#695FE6", 2);
                } else if ((activec[0] == 1 && activec[2] == 1) || (activec[1] == 1 && activec[3] == 1)) {
                    dx1 = i + (c - d[0]) * ii / (d[1] - d[0]);
                    dx2 = i + (c - d[2]) * ii / (d[3] - d[2]);
                    new line(dx1, j, dx2, j + ii, "#695FE6", 2);
                }
                
    
                }
                else if(activeconvers ==3){
                if (activec[0] == 0) {
                    dx = i + (c - d[0]) * ii / (d[1] - d[0]);
                    dy = j + (c - d[0]) * ii / (d[2] - d[0]);
                    new line(dx, j, i, dy, "#695FE6", 2);
                } else if (activec[1] == 0) {
                    dx = i + ((c-d[0])*ii)/(d[1]-d[0]);
                    dy = j + ((c-d[1])*ii)/(d[3]-d[1]);
                    new line(dx, j, i+ii, dy, "#695FE6", 2);
                } else if (activec[2] == 0) {
                    dx = i + ((c-d[2])*ii)/(d[3]-d[2]);
                    dy = j + ((c-d[0])*ii)/(d[2]-d[0]);
                    new line(i, dy, dx, j+ii, "#695FE6", 2);
                } else if (activec[3] == 0) {
                    dx = i + ((c-d[2])*ii)/(d[3]-d[2]);
                    dy = j + ((c-d[1])*ii)/(d[3]-d[1]);
                    new line(i+ii, dy,dx,j+ii, "#695FE6", 2);
                
                }         }
            }
        } 
    
        requestAnimationFrame(draw);
      
    }
 
    draw();