From Spiral00 To Spiral01

The code for the Model Concepts project is at model_concepts on GitHub.

The code for the Magic Boxes project is at mb_spirals on GitHub. For teaching or learning purposes, each spiral in Magic Boxes is decomposed into several sub-spirals. For example, to explain a transition from Spiral 00 to Spiral 01 (00-01), the transition is decomposed into small changes (01, 02, …).

In Spiral 00, the code was generated by Dart Editor (generated in January of 2012; the generated code is different now). The “Hello World!” text is displayed in a web page. In Spiral 01, the code from Spiral 00 is changed to display two rectangles (boxes).

Code 00-01.01: mb.dart from Spiral 00.

#import('dart:html');

class Mb {

  Mb() {
  }

  void run() {
    write("Hello World!");
  }

  void write(String message) {
    // the HTML library defines a global "document" variable
    document.querySelector('#status').innerHtml = message;
  }
}

void main() {
  new Mb().run();
}

Code 00-01.02: mb.html from Spiral 00.

<html>
  <head>
    <title>mb</title>
  </head>
  <body>
    <h1>mb</h1>
    <h2 id="status">dart is not running</h2>
    <script type="application/dart" src="mb.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

The generated code displays the “Hello World!” message in a web page (Figure 00-01.01).

Alt Figure 00-01.01: Hello World!

Figure 00-01.01: Hello World!

This code was a starting point for producing Spiral 01 (Figure 00-01.02).

Alt Figure 00-01.02: Two static boxes.

Figure 00-01.02: Two static boxes.

Here, the changes from Spiral 00 to Spiral 01 will be presented as sub-spirals.

The first change is the simplification of the main function.

Code 00-01.03: The minimal main function.

// s00.s01

// Use Ctrl-Shift-J in Chrome to see the Console with the printed text.

main() {
  print('Hello mb');
}

The main function does not have any parameters. The print function belongs to the dart:core library. It prints a text given as the argument of the function to the Console of the Chrome browser. The console is opened by using the Ctrl-Shift-J keys.

Code 00-01.04: The minimal html code.

<html>
  <head>
    <title>mb</title>
  </head>
  <body>
    <h1>mb</h1>
    <script type="application/dart" src="mb.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

The next step is to modify both mb.dart and mb.html to add to the main function the code for drawing a border and two boxes.

Code 00-01.05: Drawings in the main method.

#import('dart:html');
// s01.s00

main() {
  // Get a reference to the canvas.
  var canvas = document.querySelector('#canvas');
  var context = canvas.getContext("2d");
  var width = canvas.width;
  var height = canvas.height;
  context.beginPath();
  // border
  context.rect(0, 0, width, height);
  // box 1
  context.rect(20, 20, 40, 60);
  // box 2
  context.rect(120, 40, 60, 40);
  context.closePath();
  context.stroke();
}

The main function first finds the canvas element in the html page and converts it into a variable with the canvas name. The getContext method on the canvas object (object.method) returns a two dimensional context of the canvas. The width and height variables are initialized to the corresponding values of the canvas object, which are defined in mb.html.

Code 00-01.06: Width and height of canvas.

<html>
  <head>
    <title>mb</title>
  </head>

  <body>
    <section>
      <header>
        <h2>Two Boxes</h2>
      </header>
      <canvas id="canvas" width="600" height="400"></canvas>
      <script type="application/dart" src="mb.dart"></script>
      <script src="packages/browser/dart.js"></script>
    </section>
  </body>
</html>

A border is prepared to be drawn by the rect method as a rectangle with its relative position with respect to canvas at the (x, y) point, and its size equal to the size of canvas. Two boxes are prepared in the similar way. The drawing is defined between the beginPath and closePath methods. The actual drawing is done by the stroke method.

The next change is to introduce types of variables, or more precisely classes of objects. Types are useful in Dart Editor. When they are used, after the current object and the dot after it, Dart Editor offers a list of possible methods.

Code 00-01.07: Types (classes).

#import('dart:html');

// s01.s01

void main() {
  // Get a reference to the canvas.
  CanvasElement canvas = document.querySelector('#canvas');
  CanvasRenderingContext2D context = canvas.getContext("2d");
  int width = canvas.width;
  int height = canvas.height;
  context.beginPath();
  // border
  context.rect(0, 0, width, height);
  // box 1
  context.rect(20, 20, 40, 60);
  // box 2
  context.rect(120, 40, 60, 40);
  context.closePath();
  context.stroke();
}

The CanvasElement class defines a canvas. The CanvasRenderingContext2D class defines a two-dimensional context of canvas. The int class represents integer values. The main method delegates its work to methods of the CanvasRenderingContext2D class. However, it does not return a value or an object of a class, which is pointed out by the void keyword.

The main function is used as the entry point for the code execution. In general, it should be made short by delegating work to other functions.

Code 00-01.08: Function.

#import('dart:html');

// s01.s02

void displayBoxes(CanvasElement canvas) {
  CanvasRenderingContext2D context = canvas.getContext("2d");
  int width = canvas.width;
  int height = canvas.height;
  context.beginPath();
  // border
  context.rect(0, 0, width, height);
  // box 1
  context.rect(20, 20, 40, 60);
  // box 2
  context.rect(120, 40, 60, 40);
  context.closePath();
  context.stroke();
}

void main() {
  // Get a reference to the canvas.
  CanvasElement canvas = document.querySelector('#canvas');
  displayBoxes(canvas);
}

After the canvas object is obtained, it is passed as an argument to the displayBoxes function.

The next step is to keep one objective for each function.

Code 00-01.09: Functions.

#import('dart:html');

// s01.s03

void displayBoxes(CanvasRenderingContext2D context) {
  context.beginPath();
  context.rect(20, 20, 40, 60);
  context.rect(120, 40, 60, 40);
  context.closePath();
  context.stroke();
}

void displayBord(CanvasElement canvas) {
  CanvasRenderingContext2D context = canvas.getContext("2d");
  int width = canvas.width;
  int height = canvas.height;
  context.beginPath();
  context.rect(0, 0, width, height);
  context.closePath();
  context.stroke();
  displayBoxes(context);
}

void main() {
  // Get a reference to the canvas.
  CanvasElement canvas = document.querySelector('#canvas');
  displayBord(canvas);
}

The objective of the main function is to obtain canvas and to delegate drawings to the displayBord function. The objective of the displayBord function is to draw a border and to delegate drawings of boxes to the displayBoxes function.

Up to now, our code consists of functions. The same can be accomplished by defining the Board class that will handle drawings. The class is defined within Dart Editor by using the File menu and the New File… menu item. The new file is named Border.dart.

The main function of mb.dart creates a board as an object of the Board class. This is accomplished by using the new keyword and by the constructor method of the Board class.

Code 00-01.10: The main function creates an object of the Board class.

library mb;

import 'dart:html';

part 'board.dart';

// s01.s04

void main() {
  // Get a reference to the canvas.
  CanvasElement canvas = document.querySelector('#canvas');
  new Board(canvas);
}

Note that the library mb in mb.dart has only one part, which is board.dart.

Code 00-01.11: The Board class.

part of mb;

class Board {
  
  int x = 0;
  int y = 0;
  int width;
  int height;
  
  CanvasRenderingContext2D context;
  
  Board(CanvasElement canvas) {
    context = canvas.getContext("2d");
    width = canvas.width;
    height = canvas.height;
    border();
    init();
  }
  
  void init() {
    context.beginPath();
    context.rect(20, 20, 40, 60);
    context.rect(120, 40, 60, 40);
    context.closePath();
    context.stroke();
  }
  
  void border() {
    context.beginPath();
    context.rect(x, y, width, height);
    context.closePath();
    context.stroke();
  }

}

Conceptually, the Board class represents a place where boxes will be drawn. Its constructor accepts a canvas object of the CanvasElement class. The getContext method of the CanvasElement class applied to the canvas object returns a two dimensional context of the canvas. This context is an attribute (or a field) of the Board class. While methods represent operations, attributes stand for data. Other attributes of the Board class are x, y, width and height. Their values are of the int (integer) type. The x and y attributes are initialized to zero, while the values of the width and height attributes are obtained from the corresponding attributes of the canvas object.

At the end of the construction process, a border of the board is drawn with the border method. Finally, the board is initialized with two boxes drawn by the init method. Methods of the class have a direct access to attributes of the class.

The HTML document is declared to be of the html type. The simple DOCTYPE syntax is introduced by HML5. Two meta elements in the head element, define a character set as utf-8, and an author of the page as Dzenan Ridjanovic. The body element has now the header element that displays the title of the page, in addition to the title of the section where canvas is placed.

Code 00-01.12: The HTML file.

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8"/>
    <meta name="author" content="Dzenan Ridjanovic"/>
    <title>mb</title>
  </head>
  
  <body>
    <header>
      <h1>Magic Boxes in Dart</h1>
    </header>
    
    <section> 
      <header>
        <h2>Two Boxes</h2>
      </header>
      <canvas id="canvas" width="600" height="400"></canvas>
      <script type="application/dart" src="mb.dart"></script>
      <script src="packages/browser/dart.js"></script>
    </section>
  </body>
</html>

Finally, the Box.dart file is created by using the File menu and the New File… menu item.

Code 00-01.13: The Box class.

part of mb;

class Box {
  
  Board board;
  
  int x;
  int y;
  int width;
  int height;
  
  Box(this.board, this.x, this.y, this.width, this.height) {
    draw();
  }
  
  void draw() {
    board.context.beginPath();
    board.context.rect(x, y, width, height);
    board.context.closePath();
    board.context.stroke();
  }

}

The constructor method of the Box class accepts five arguments that are assigned automatically to the corresponding attributes of the class. The this keyword represents the current object of the Box class, and its use as a qualifier of the five parameters indicates the assignment shortcut. A box is drawn by the draw method. The five attributes are used to accomplish the drawing task.

The init method of the Board class constructs two boxes.

Code 00-01.14: Construction of two boxes.

part of mb;

class Board {
  
  int x = 0;
  int y = 0;
  int width;
  int height;
  
  CanvasRenderingContext2D context;
  
  Board(CanvasElement canvas) {
    context = canvas.getContext("2d");
    width = canvas.width;
    height = canvas.height;
    border();
    init();
  }
  
  void init() {
    new Box(this, 20, 20, 40, 60);
    new Box(this, 120, 40, 60, 40);
  }
  
  void border() {
    context.beginPath();
    context.rect(x, y, width, height);
    context.closePath();
    context.stroke();
  }

}

The main method adds a redundant object. Since the board object is not used further in the main method, there was no reason to introduce it.

Also, the document object may be avoided.

Code 00-01.15: Box source.

library mb;

import 'dart:html';

part 'board.dart';
part 'box.dart';

// s01.s05

void main() {
  // Get a reference to the canvas.
  CanvasElement canvas = document.querySelector('#canvas');
  Board board = new Board(canvas);
}
blog comments powered by Disqus