Creating an object


16 Sep 2019 Code on Github

Introduction

In this tutorial, we'll start by creating the objects we'll use to represent 3D shapes. You can find all the code for this tutorial here.

One of the simplest shapes to make is a cube, so let's start by making a 200 × 200 × 200 pixel cube, centred at the origin (0, 0, 0).

Nodes

First we need an object to define a point in 3D space. I'm going to use a class, but you could a function expression. I'm going to put this code in a file called canvas3d.js.

class Node {
    constructor(x=0, y=0, z=0) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

We can test that the class works by creating a new Node object with some coordinates. If you leave out a coordinate, it should get set to 0.

const n = new Node(100, -100);
console.log(n);
// Node {x: 100, y: -100, z: 0}

Now we can create an array of Node objects representing the eight nodes of a cube. Notice that the nodes represent all eight possible permutation of 100 and -100.

const nodes = ([
    new Node(-100, -100, -100),
    new Node(-100, -100,  100),
    new Node(-100,  100, -100),
    new Node(-100,  100,  100),
    new Node( 100, -100, -100),
    new Node( 100, -100,  100),
    new Node( 100,  100, -100),
    new Node( 100,  100,  100),
]);

Edges

Next we need an Edge object, to represent the connection between two nodes.

class Edge {
    constructor(node1, node2) {
        this.node1 = node1;
        this.node2 = node2;
    }
}

Then we can create an array of edges objects representing the 12 edges of the cube. Each edge object takes two nodes, from the nodes array.

The tricky part is making sure you join the right edges together. This becomes easier once you can view the shape.

const edges = ([
    new Edge(nodes[0], nodes[1]),
    new Edge(nodes[1], nodes[3]),
    new Edge(nodes[3], nodes[2]),
    new Edge(nodes[2], nodes[0]),
    new Edge(nodes[4], nodes[5]),
    new Edge(nodes[5], nodes[7]),
    new Edge(nodes[7], nodes[6]),
    new Edge(nodes[6], nodes[4]),
    new Edge(nodes[0], nodes[4]),
    new Edge(nodes[1], nodes[5]),
    new Edge(nodes[2], nodes[6]),
    new Edge(nodes[3], nodes[7]),
]);

Shapes

Now let's create a Shape object to package up the nodes and edges and make them easier to work with.

class Shape3D {
    constructor(nodes=[], edges=[]) {
        this.nodes = [];
        this.edges = [];
    }
}

Now we can create a shape like so:

const shape = new Shape3D();

Adding nodes

If you look above at how we created an array of nodes, you can see a lot of repetition. We can create an addNodes method to avoid some of this and save ourselves time later.

addNodes(nodes) {
    this.nodes = this.nodes.concat(
        nodes.map(([x, y, z]) =>
            new Node(x, y, z)
        )
    );
}

Now we can add nodes to the shape by passing in a 2D array of coordinates.

shape.addNodes([
    [-100, -100, -100],
    [-100, -100,  100],
    [-100,  100, -100],
    [-100,  100,  100],
    [ 100, -100, -100],
    [ 100, -100,  100],
    [ 100,  100, -100],
    [ 100,  100,  100],
]);

Adding edges

We can add a similar method to add edges. This will take an array of node indices, look them up in the nodes array and build edges that link them.

addEdges(edges) {
    this.edges = this.edges.concat(
        edges.map(([n1, n2]) => {
            const node1 = this.nodes[n1];
            const node2 = this.nodes[n2];
            if (!node1) {
                throw `Node ${n1} not found`;
            }
            if (!node2) {
                throw `Node ${n2} not found`;
            }
            return new Edge(node1, node2);
        })
    );
}

Now, we can add edges with a lot less repetition.

shape.addEdges([
    [0, 1], [1, 3], [3, 2], [2, 0],
    [4, 5], [5, 7], [7, 6], [6, 4],
    [0, 4], [1, 5], [2, 6], [3, 7],
]);

Summary

In this tutorial we made Node, Edge and Shape objects, which is what we'll use to represent 3D objects. In the next tutorial, we'll display the shape that we've created.