2017年11月26日星期日

Are Humans Able to See and Understand the Forth Dimension?

Many people who has interests about dimension know this book: Flatland. Written by Edwin A. Abbott decades ago. This book takes the second dimension as a example to describe and explain the features, differences and possible communications between two adjacent dimensions. I do not know what is your feeling after reading this book. But I am pretty sure that I expect some day a higher dimension creature can come to our dimension world and take us into the forth dimension and have a look.

However, I am a little bit worry about if I can see and understand the forth dimension. Or, should I say if humans are able to see and understand the forth dimension. Although there are plenty of explanation on Internet about the ten dimensions, or you can present high dimension with pure mathematical structure. But none of them have the ability to establish a forth dimension image in our heads. Recently, I start trying to find out the possibility of glancing at the forth dimension world.

Considering I have no friend in the forth dimension world, I try to play a role of creature from the third dimension and take a trip to the second dimension. Just like the story in Flatland. The difference is that some presumption will be replaced by my understanding. Here we go.

Today's the second dimension world get a sunny day. Mr. Blue Rectangle and Ms. Orange Circle meet in this flat plane. As the flat plane has no z-axis existed, all the creatures here have no the conception: Height, which means their height are zero. Length and width are understandable to them only. From the higher dimension, I can have a bird view to observe the flat plane and those creatures have no idea about my observation at all. After billions of years of resolution, Mr. BR and Ms. OC have an black eye at the edge of their body. They can see each other in this flat world with the special eye. Why the eye is special? Because of their zero height. In the third dimension, the object is not existed if the height of it is zero. But I have to let the story continue, so, Mr. BR and Ms. OC can see each other. I do not know what looks like in their each other's eye, but let us say they can sense each other with the eye.



In my third dimension world, it is a sunny day as well. But Mr. BR and Ms. OC do not feel any light from my world because z-axis is unknown to them. Can not wait to see the amazing result, I drag Mr. BR into the third dimension and I can see that Mr. BR get lost in his new position. For me, yes, Mr. BR is out of his own flat plane, but for Mr. BR, he just passed through infinite number of other second dimensions which exist in my third dimension world and stopped at another second dimension. At this moment, Ms. OC disappeared in his eye and the sun is gone as well.

I told Mr. BR that he just flied through and saw thousands different planes, and can he see and understand the higher dimension? After a long time thinking, Mr. BR said, what I can see is still a second dimension view and I am glad to realize that there is a high dimension exist but I cannot fully understand it. Mr. BR continued, my body and mind are generated inside the second dimension, my eye can only see the objects inside flat plane and being restricted physically to see those 2D objects. None of us can beyond our physical limitation although you dragged me to in and out so many flat planes which are parts of your third dimension world.



After placing Mr. BR back to his own plane, I asked myself, could I physically change Mr. BR to a 3D object? Bending him or giving him height? Maybe then he is able to feel and see what I can. But in that way, Mr. BR is not Mr.BR anymore.

Now, this story between 3D and 2D can show me some rules here to apply to my question between 3D and 4D world:

  1. Lower dimension creatures are not able to see the real higher dimension world because the physical restrictions.
  2. Lower dimension creatures can try to understand the higher dimension world because a lower dimension world is a part of the higher dimension; But is not possible to fully understand it because there are infinite number of lower dimension worlds are included in a higher dimension space, which means no way to see the whole map.
  3. Higher dimension creatures might not be able to get into any single lower dimension. High dimension objects depend on the combination of lower dimension spaces to exist. Getting into a lower dimension space means throwing a lot of advanced features, which could be impossible.
  4. Higher dimension creatures could understand lower dimensions in theory. However, if there is any kind of living creature or energy over there? Maybe, there is a Mr. BR survives in a practical second dimension.

A little bit disappointed, right? Humans eyes are not designed to see through the third dimension and humans brain can even not fully imagine the higher dimension. What about the computer technology and AI? Considering they are created by humans and can only show the simulated result on a 2D screen, I do not believe they can draw a human understandable forth dimension picture.

Conclusion: Everything in our world are generated by rules of the third dimension. Our universe is included by a higher dimension and includes infinite number of lower dimension worlds. Unfortunately, we have no ability to see and fully understand higher dimensions and can not jump into a lower dimension to see what is happening there in practice. Maybe, the third dimension is the only dimension can be full of lives and lower dimensions are just the fundamental to support it and high dimensions are just high level containers to organize all possible third dimensions in many different ways.




2017年9月21日星期四

Same-Sex Marriage: Except YES or NO, What is Left to Choose?

On the way to my office this morning, a nice lady hand over a leaflet: Marriage Equality. It's about a fair go. No doubt, she want people to vote YES for same-sex marriage. And I begin to realize that the tension between YES and NO is getting hotter and hotter. It looks like there is no space for people who is willing to stay outside the battle.

I am not sure if all the people who are going to vote understand the meanings of YES or NO. I hope they are not making their decision simply because they feelingly like it or not. There are several related issues behind the scene.

First of all, religious marriage issue. YES means religious leaders have to accept homo couples. Personally, I do not think this is a very huge problem. As long as the law and society can accept the situation, religion is the last thing we should think about it. I do not mean that religion is not important. I just want to say the law has definitely much higher priority than religious belief.

Secondly, it is about parenting. For now, two dads or two moms are not able to make people in a natural way. Does that mean they cannot be good parents? Or, have no enough experiences? To tell the truth, I do think that a family with a female and a male as parents is a good combination because different genders can complement each other. On the other hand, I do not has any arguments to support the view that same-sex parents have no ability to bring up children well. Just like you know, no matter same sex or not, not every parents are qualified enough or not.

No way to let me say YES or NO to same-sex marriage currently. I am not wise enough to give the answer. So I hope to figure it out with a primary measure: What are the advantages and disadvantages?

Let us start with disadvantages. Two gents or ladies have special feelings between them and they decide to stay together with each other and keep a promise, maybe, forever. Immediate result is that there is not natural baby is going to come from them. Will it cause a human population shrinking issue? Not likely in next 1000 years in my mind. Additionally, some straight gents and ladies lose some chances to find their another half. Without same-sex marriage might be able to help a little?

What about the advantages? Those kids lost their original parents around the world may get a chance to be taken care by homo couples. How many the couples can achieve good parents? I do not know. What is the possible affection to the next generations? Inside and outside those same-sex marriage families? I do not know.

I still have not answer for voting YES or NO. But I gradually believe that it is a matter of children and the approval of laws. If those people who are yelling for YES only fight for Marriage Equality, I do not think they have thought it through. They just sink deeply in their own freedom belief. If those people who are pushing NO only fight for protecting their kids away from the idea, I would like to say that they are trying to install their own opinions to their children. It is not true willing from children. Sooner or later, children will recognize the existence of the concepts.

From the other side of the coin, this is a love issue. There are so many different kinds of loves exist in human kind. I believe there is one kind of love which is not natural enough or majority between homo couple. And that love is not evil as some harmful heresy in this world. So I accept the existence of the concepts.  But how far the concepts can go and should go? Only God can tell.

Considering I have not supplied any nutritious advice here, just let me suggest something ridiculous. Taking the love between homo couple as a relationship of love, and we do not call this relationship as Marriage. Let us call it Garriage. Writing a new chapter in our law book for Garriage, which includes all the same rights and responsibilities like Marriage (some modification might be needed). People can choose to get married or get garried. Someday, if we find out something wrong with Garriage, just erase the chapter from the law book. Nothing need to be touched inside Marriage chapter.

YES or NO? Not so easy, please let those people has no answer like me, stay in peace, and wonder the future.

2017年8月31日星期四

How to extract SVG data from TTF and OTF with Java and C/C++

Most people already knew that TTF and OTF are the most popular font formats we are using nowadays in our computers. A number of creative font designers work on all kinds of beautiful fonts to help us to present our mind on screens or papers. When you get a font file, every word has its own space inside to store some information about how to draw the word. In effect, TTF and OTF normally store vector graphics to represent the words. If a font file only include 26 English letters, the size of the font file would be pretty small(about 100KB or less); But for a Chinese font file, the size of the file could be much bigger(larger than 10MB) as there are more than 30 thousands Chinese words available to use.

Considering all those vector graphics available in a font file, we should be able to extract word glyph outlines and translate them into SVG format. Do we need to read TTF or OTF specification before we start extracting? Not really, there are some utilities and libraries can help us to achieve this goal.

LibraryLanguageDescription
Apache™ BatikJavaBatik is a SVG toolkit which include a utility called ttf2svg: lets you convert a range of characters from a TrueType Font into the SVG font format.
FreeType ProjectC/C++FreeType is a freely available software library to render fonts. 

ttf2svg example can be found here: https://xmlgraphics.apache.org/batik/tools/font-converter.html

FreeType supplies a example to extract vector glyph and translate into SVG: https://www.freetype.org/freetype2/docs/tutorial/example5.cpp

The example from FreeType is too simple and available for one English letter. But it can be easily modified to achieve other purpose, such as handling Chinese words and reading from text file and writing vector information into JSON or SVG file. You can find this modified version here: font2svg.cpp. This program need a word.txt as input and gonna generate a word.json and a word.svg at the same directory after running. You can has any words you want to extract in word.txt and make sure words are available in the TTF or OTF file you supplied to the program.

g++ -o font2svg.exe font2svg.cpp -I/usr/include/freetype2 -I/usr/include -L/lib -lfreetype.dll -lz


If you are working in Windows environment, Cygwin can help you to compile and link the example. Just remember to install Devel at least.

Please remember, some fonts come from open source projects and some fonts have their own copyrights or licence requirements. Make sure you are using libraries to treat them in a correct and respectful way.

2017年8月16日星期三

Create Your Own Maze In Your Browser With D3.js (2) Generating Animation

In "Create Your Own Maze In Your Browser With D3.js (1)", we have known the secret of how to generate a random maze with JavaScript and D3.js. Today, I am going to push it a little bit further: to show the how process of maze generating.


As I mentioned before, you can use Recursive Process or not to achieve the random maze generator. For an animation show, we'd better keep recursive process away. Yes, based on my last post, the maze generator is pretty nature to support animation. Here is the plan:


  • Step 1: show all the room soundly with four walls in the maze;
  • Step 2: show a red spot at the beginning room to generate the maze;
  • Step 3: change the room colour as we visit it as find a neighbour room as current room and move red spot to the current room.
  • Step 4: use a interval timer to execute Step 3 every 100 milliseconds until all the rooms have been visited.

As we trace the visited rooms with our own stack, it is easy to drive the animation step by step and each step will process one room only. As you can see in the SVG animation in the post, red spot will tell you where is the current room and visited room will be painted with a different colour.

Have to say, there is no much different between this animation version and last static version. D3.js can really help us focus on the data and logic and make the SVG drawing and animation a piece of cake.

Here are the differences you may want to know:


  • We need to render the maze at the very beginning and need to update the maze after every step. So in this animation version, we have renderMaze() and updateMaze() to finish these two different jobs. Be careful, D3.js treats generating new html elements and updating html element with data in different ways.


var renderMaze = function(){

    // this is data binding for first layer of array.
    var row = svg.selectAll(".row")
        .data(matrix)
        .enter().append("g")
        .attr("class", "row");

    // this is data binding for second layer of array. Every room is a group for multi-shapes.
    var column = row.selectAll(".square")
        .data(function(d) { return d; })
        .enter().append("g").attr("class","square");

    // append rect as room background.
    column.append("rect")
        .attr("class", function(d) { return d.xPos + "-" + d.yPos; })
        .attr("x", function(d) { return d.xPos; })
        .attr("y", function(d) { return d.yPos; })
        .attr("width", roomWidth)
        .attr("height", roomHeight)
        .style("fill", roomBackground);

    // append left wall;
    column.append("line")
        .attr("class", "left")
        .attr("x1", function(d) { return d.xPos; })
        .attr("y1", function(d) { return d.yPos; })
        .attr("x2", function(d) { return d.xPos;})
        .attr("y2", function(d) { return d.yPos + roomHeight;})
        .attr("stroke-width", function(d) { return d.left; })
        .style("stroke", wallColour);

    // append right wall;
    column.append("line")
        .attr("class", "right")
        .attr("x1", function(d) { return d.xPos + roomWidth; })
        .attr("y1", function(d) { return d.yPos; })
        .attr("x2", function(d) { return d.xPos + roomWidth;})
        .attr("y2", function(d) { return d.yPos + roomHeight;})
        .attr("stroke-width", function(d) { return d.right; })
        .style("stroke", wallColour);

    // append top wall;
    column.append("line")
        .attr("class", "top")
        .attr("x1", function(d) { return d.xPos; })
        .attr("y1", function(d) { return d.yPos; })
        .attr("x2", function(d) { return d.xPos + roomWidth;})
        .attr("y2", function(d) { return d.yPos;})
        .attr("stroke-width", function(d) { return d.top; })
        .style("stroke", wallColour);

    // append bottom wall;
    column.append("line")
        .attr("class", "bottom")
        .attr("x1", function(d) { return d.xPos; })
        .attr("y1", function(d) { return d.yPos + roomHeight; })
        .attr("x2", function(d) { return d.xPos + roomWidth;})
        .attr("y2", function(d) { return d.yPos + roomHeight;})
        .attr("stroke-width", function(d) { return d.bottom; })
        .style("stroke", wallColour);

    // we need to current flag to show where is the current room.
    svg.append("circle")
        .attr("class", "current")
        .attr("cx", spot.xPos)
        .attr("cy", spot.yPos)
        .attr("r", spot.r)
        .style("fill", spot.color);

}

var updateMaze = function(){

    // this is data binding for first layer of array.
    var row = svg.selectAll(".row")
        .data(matrix)
        .attr("class", "row");

    // this is data binding for second layer of array. Every room is a group for multi-shapes.
    var column = row.selectAll(".square")
        .data(function(d) { return d; });

    // append rect as room background.
    column.selectAll("rect")
        .attr("x", function(d) { return d.xPos; })
        .attr("y", function(d) { return d.yPos; })
        .attr("width", roomWidth)
        .attr("height", roomHeight)
        .style("fill", function(d) { return d.backgroupd;});

    // append left wall;
    column.selectAll(".left")
        .attr("x1", function(d) { return d.xPos; })
        .attr("y1", function(d) { return d.yPos; })
        .attr("x2", function(d) { return d.xPos;})
        .attr("y2", function(d) { return d.yPos + roomHeight;})
        .attr("stroke-width", function(d) { return d.left; })
        .style("stroke", wallColour);

    // append right wall;
    column.selectAll(".right")
        .attr("x1", function(d) { return d.xPos + roomWidth; })
        .attr("y1", function(d) { return d.yPos; })
        .attr("x2", function(d) { return d.xPos + roomWidth;})
        .attr("y2", function(d) { return d.yPos + roomHeight;})
        .attr("stroke-width", function(d) { return d.right; })
        .style("stroke", wallColour);

    // append top wall;
    column.selectAll(".top")
        .attr("x1", function(d) { return d.xPos; })
        .attr("y1", function(d) { return d.yPos; })
        .attr("x2", function(d) { return d.xPos + roomWidth;})
        .attr("y2", function(d) { return d.yPos;})
        .attr("stroke-width", function(d) { return d.top; })
        .style("stroke", wallColour);

    // append bottom wall;
    column.selectAll(".bottom")
        .attr("x1", function(d) { return d.xPos; })
        .attr("y1", function(d) { return d.yPos + roomHeight; })
        .attr("x2", function(d) { return d.xPos + roomWidth;})
        .attr("y2", function(d) { return d.yPos + roomHeight;})
        .attr("stroke-width", function(d) { return d.bottom; })
        .style("stroke", wallColour);

    // we need to current flag to show where is the current room.
    svg.select(".current")
        .attr("cx", spot.xPos)
        .attr("cy", spot.yPos)
        .attr("r", spot.r)
        .style("fill", spot.color);

}


  • You need to start a interval timer to drive the process forward, here is the sample:
var performGenStep = function(){
    visitRoom();
    updateMaze();
}

// By visiting the room, we can go to next room until all the rooms have been visited.
var genRandomMaze = function(){
    var firstX = Math.floor(Math.random() * row);
    var firstY = Math.floor(Math.random() * col);
    console.log("First room to start Row - Col: " + firstY + " - " + firstX);

    var room = matrix[firstY][firstX];
    stack.push(room);

    timer = $interval(performGenStep, 100);
    
}

The last thing you might need to pay attention is the new svg element in the maze, red spot. I encourage to press F12 to check the JavaScript source code in this blog page, and find out places we need to handle the red spot during the maze generating. Let me know if you need some help.

Warning: as the maze is 20x20, it might take a long time to finished the whole generating process.





2017年8月10日星期四

Create Your Own Maze In Your Browser With D3.js (1) How to do it

Maze is a fun game for everyone. Rather than waiting for a new maze from other people, you can have your own unlimited number of mazes. Yes, in this blog, we are going to focus on how to make a randomly generated maze in your web browser with D3.js.

I hope you have heard D3.js before because this is a very famous JavaScript library. Quote: a JavaScript library for manipulating documents based on data(https://d3js.org). I am gonna start from a very beginning to build a random maze generator. In this small project, AngularJS is used as a application framework, which makes this blog more like an web application, and encapsulates the maze related JavaScript codes and SVG elements into a HTML element.

A Simple Maze Algorithm
There are several maze generating algorithms available already. So, we will go with a easy one and I call it Knocking Down Method. Of course, there is official name for the algorithm. But I will describe the algorithm in an understandable way and get rid of some unnecessary details. OK, let us assume that the maze we will generate is a square based, which means the maze is a two dimensions array, like a grid. It could be 5x5, 10x10 or 19x19. Here is the steps:


  1. Take every grid cell as a room and every room has four walls to seal the room.
  2. Randomly find a room in grid;
  3. mark this room as visited;
  4. Randomly select one of the walls of the room;
  5. Detect if there is a room next to the wall: true then go to 6; false then go to 7;
  6. Knock the walls (one wall from this room and another wall from the next room) down and visit the new room if there is a room available next to the wall and this new room is not visited before: true then go back to 3; false then go to 7;
  7. Find another wall of the room and go back to 5.
The whole process will stop when all the rooms in the grid have been visited. And every room will lose at least one wall. Then you can see the maze has been generated. Just like a magic, right? In fact, according to this algorithm, any two rooms in this grid have and only have one way to connect each other. So you can easily indicate the start and finish points of the maze.

Because we randomly pick up the starting room and randomly knock down the walls, the maze is totally different with others after every time you refresh your web page.

Source Code with D3.js
We has several functions defined in JavsScript to achieve the maze generation:

initAttrs();
initSvg();
initMaze();
genRandomMaze();
renderMaze();

Let us go through them one by one.


  • initAttrs(): some attributes need to be prepared for maze generation. row and col define the two dimension array. startX, startY, endX and endY is the entry and exit of the maze.
                // get attributes values from this directive.
                var row = attrs.row;
                var col = attrs.col;
                var startX = attrs.startX;
                var startY = attrs.startY;
                var endX = attrs.endX;
                var endY = attrs.endY;
                var roomWidth = 10;
                var roomHeight = 10;
                var mazeBackground = attrs.mazeBackground;
                var roomBackground = attrs.roomBackground;
                var wallColour = attrs.wallColour;
                var wallWidth = 1;

                // define maze related variables.
                var matrix = [];
                var svg = null;
                var stack = [];


                // Make sure all the initial parameters have the correct values.
                var initAttrs = function(){
                    if(row == null || row == undefined || row < 3){
                        row = 20;
                    }

                    if(col == null || col == undefined || col < 3){
                        col = 20;
                    }

                    if(startX == null || startX == undefined || startX < 1 || startX > col){
                        startX = 1;
                    }

                    if(startY == null || startY == undefined || startY < 1 || startY > row){
                        startX = 1;
                    }

                    if(endX == null || endX == undefined || endX < 1 || endX > col){
                        endX = col;
                    }

                    if(endY == null || endY == undefined || endY < 1 || endY > row){
                        endY = row;
                    }

                    if(mazeBackground == null || mazeBackground == undefined){
                        mazeBackground = "lightgray";
                    }

                    if(roomBackground == null || roomBackground == undefined){
                        roomBackground = "lightgray";
                    }

                    if(wallColour == null || wallColour == undefined){
                        wallColour = "black";
                    }

                }


  • initSvg(): Using D3.js, it is very easy to insert a SVG element into your web page and start to all kinds of shapes into the SVG panel.
                // generate a SVG element as a core part of this directive.
                var initSvg = function(){
                    // generate root svg element with responsive CSS.
                    svg = d3.select(elements[0])
                    .append("svg").attr("width", 400)
                    .attr("height",400);
                }


  • initMaze(): At the beginning, every room in the maze has four walls, we need to prepare all the room objects and put them into the two dimensions array.
                // make sure we knock down walls in a room randomly.
                var disorder = function(directions) {
                    var i, pos, temp;
                    // randomly pick up two elements and move them to the end of the array to generate a random list.
                    for (i = 0; i < 2; i++) {
                        pos = Math.floor(Math.random() * directions.length);
                        temp = directions[pos];
                        directions.splice(pos, 1);
                        directions.push(temp);
                    }
                    return directions;
                }

                // initialize all the rooms in the maze and every room has four walls at the beginning.
                var initMaze = function(){
                    var svgWidth = 400;
                    var svgHeight = 400;

                    // width of room should be between 10 and 50.
                    if(Math.floor(svgWidth / col) > 50 || Math.floor(svgWidth / col) < 10){
                        roomWidth = 50;
                    }else{
                        roomWidth = Math.floor(svgWidth / col) - 1;
                    }

                    // Height of room should be between 5 and 10.
                    if(Math.floor(svgHeight / row) > 50 || Math.floor(svgHeight / row) < 10){
                        roomHeight = 50;
                    }else{
                        roomHeight = Math.floor(svgHeight / row) - 1;
                    }

                    // generate all the rooms with all the walls.
                    var x = 1;
                    var y = 1;
                    for(var i = 1; i <= row; i++){
                        var rooms = [];
                        for(var j = 1; j <= col; j++){
                            var room = {};
                            room.top = wallWidth;
                            room.bottom = wallWidth;
                            room.left = wallWidth;
                            room.right = wallWidth;
                            room.xPos = x;
                            room.yPos = y;
                            room.row = i;
                            room.col = j;
                            room.visited = false;
                            //generate a random neighbour rooms visiting list: 0 - left, 1 - right, 2 - top, 3 bottom.
                            room.directions = disorder([0, 1, 2, 3]);
                            rooms.push(room);
                            x += roomWidth;
                        }
                        x = 1;
                        y += roomHeight;
                        matrix.push(rooms);
                    }
                }


  • genRandomMaze(): Visiting a room and find next room to visit is the core part of the maze generation algorithm.

                // Need to check the availability of the neighbour room. Not all the room have an available neighbour in any direction.
                var getNeighbourRoom = function(room, direction){
                    var offsetX = 0;
                    var offsetY = 0;
                    switch(direction){
                        case 0:
                            offsetX -= 1;
                            break;
                        case 1:
                            offsetX += 1;
                            break;
                        case 2:
                            offsetY -= 1;
                            break;
                        case 3:
                            offsetY += 1;
                            break; 
                        default:
                    }
                    var newX = room.col + offsetX;
                    var newY = room.row + offsetY;
                    console.log("Neighbour Row - Col: " + newY + " - " + newX);
                    if(newX < 1 || newX > col || newY < 1 || newY > row){
                        return null;
                    }else{
                        return matrix[newY - 1][newX - 1];
                    }
                }

                // We need to record the room we have visited before we knock down a wall and move to next room.
                var visitRoom = function(){
                    while(stack.length > 0){
                        var room = stack.pop();
                        if(room.visited && room.directions.length == 0){
                            continue;
                        }
                        // record current room and ready to knock down a random wall.
                        stack.push(room);
                        room.visited = true;
                        var tmp = null;
                        switch(room.directions[0]){
                            case 0:
                                tmp = getNeighbourRoom(room, 0);
                                if(tmp != null && tmp != undefined && !tmp.visited){
                                    room.left = 0;
                                    tmp.right = 0;
                                    stack.push(tmp);
                                }
                                break;
                            case 1:
                                tmp = getNeighbourRoom(room, 1);
                                if(tmp != null && tmp != undefined && !tmp.visited){
                                    room.right = 0;
                                    stack.push(tmp);
                                    tmp.left = 0;
                                }
                                break;
                            case 2:
                                tmp = getNeighbourRoom(room, 2);
                                if(tmp != null && tmp != undefined && !tmp.visited){
                                    room.top = 0;
                                    stack.push(tmp);
                                    tmp.bottom = 0;
                                }
                                break;
                            case 3:
                                tmp = getNeighbourRoom(room, 3);
                                if(tmp != null && tmp != undefined && !tmp.visited){
                                    room.bottom = 0;
                                    stack.push(tmp);
                                    tmp.top = 0;
                                }
                                break; 
                            default:
                        }
                        // Wall knocked down need to be removed.
                        room.directions.splice(0,1);
                    }
                }

                // By visiting the room, we can go to next room until all the rooms have been visited.
                var genRandomMaze = function(){
                    var firstX = Math.floor(Math.random() * row);
                    var firstY = Math.floor(Math.random() * col);
                    console.log("First room to start Row - Col: " + firstY + " - " + firstX);

                    var room = matrix[firstY][firstX];
                    stack.push(room);
                    visitRoom();
                }


  • renderMaze(): This part is finished with D3.js. D3 bind our two dimensions array with room shapes. Every room is combined with one rectangle (as background) and four lines (as walls).

                var renderMaze = function(){

                    // this is data binding for first layer of array.
                    var row = svg.selectAll(".row")
                        .data(matrix)
                        .enter().append("g")
                        .attr("class", "row");

                    // this is data binding for second layer of array. Every room is a group for multi-shapes.
                    var column = row.selectAll(".square")
                        .data(function(d) { return d; })
                        .enter().append("g").attr("class", function(d) { return d.xPos + "_" + d.yPos; });

                    // append rect as room background.
                    column.append("rect")
                        .attr("class","square")
                        .attr("x", function(d) { return d.xPos; })
                        .attr("y", function(d) { return d.yPos; })
                        .attr("width", roomWidth)
                        .attr("height", roomHeight)
                        .style("fill", roomBackground);

                    // append left wall;
                    column.append("line")
                        .attr("x1", function(d) { return d.xPos; })
                        .attr("y1", function(d) { return d.yPos; })
                        .attr("x2", function(d) { return d.xPos;})
                        .attr("y2", function(d) { return d.yPos + roomHeight;})
                        .attr("stroke-width", function(d) { return d.left; })
                        .style("stroke", wallColour);

                    // append right wall;
                    column.append("line")
                        .attr("x1", function(d) { return d.xPos + roomWidth; })
                        .attr("y1", function(d) { return d.yPos; })
                        .attr("x2", function(d) { return d.xPos + roomWidth;})
                        .attr("y2", function(d) { return d.yPos + roomHeight;})
                        .attr("stroke-width", function(d) { return d.right; })
                        .style("stroke", wallColour);

                    // append top wall;
                    column.append("line")
                        .attr("x1", function(d) { return d.xPos; })
                        .attr("y1", function(d) { return d.yPos; })
                        .attr("x2", function(d) { return d.xPos + roomWidth;})
                        .attr("y2", function(d) { return d.yPos;})
                        .attr("stroke-width", function(d) { return d.top; })
                        .style("stroke", wallColour);

                    // append bottom wall;
                    column.append("line")
                        .attr("x1", function(d) { return d.xPos; })
                        .attr("y1", function(d) { return d.yPos + roomHeight; })
                        .attr("x2", function(d) { return d.xPos + roomWidth;})
                        .attr("y2", function(d) { return d.yPos + roomHeight;})
                        .attr("stroke-width", function(d) { return d.bottom; })
                        .style("stroke", wallColour);

                }

If you press F12, you can see all the JavaScript source code and it is not difficult to understand. There is one thing I need to mention that we can use recursive process to achieve the maze generation as well. But here we are using our own stack to make it work because a heavy recursive process is not healthy enough for JavaScript in your web browser.

Next time, we will make this maze a little bit more fun. Because we are not using recursive way to generate the maze, it would be easy for us to show the generation process in an animation way. Let me know if you like it!