Main content

## Computer programming

# Generating 3D shapes

So we have a cube now, but what if we want to change its position or size? Or what if we want a rectangular cuboid or many cuboids? With our current code, we would have to change the nodes one-by-one, which would be nuisance. What we would like is a simple method to create a cuboid with a certain position and dimensions. In other words, we want a function that maps a position and dimensions into an array of nodes and an array of edges.

### Defining a cuboid

A cuboid has three dimensions: width, height and depth:

It also has a position in 3D space, giving us six parameters. There are a couple of ways we could define the position of the cuboid: we could define its center or we could define one corner. The former is probably more common, but I think the latter is easier to use.

Our function needs to return both the nodes and edges array. One way to return two variables is to package the variables into an object, with a key for

`nodes`

and a key for `edges`

. Note, you can use any string to refer to the variable, I just find it easier to use the same word.```
// Create a cuboid with a vertex at (x, y, z)
// with width, w, height, h, and depth, d.
var createCuboid = function(x, y, z, w, h, d) {
var nodes = [];
var edges = [];
var shape = { 'nodes': nodes, 'edges': edges };
return shape;
};
```

If we used that function to create a cuboid, we'd access the first node like this:

```
var shape = createCuboid(0, 0, 0, 100, 160, 50);
var node0 = shape.nodes[0];
```

This will set

`node0`

to the first value in the `nodes`

array. At the moment, however, there are no values in the nodes or edges arrays.We define the nodes as being every combination of position with or without the corresponding dimension. Edges are defined the same way as before (except rather than define each of the edges individually first, I define them all at once). Note that this function allows you to specify negative dimensions for the cuboid.

```
var createCuboid = function(x, y, z, w, h, d) {
var nodes = [[x, y, z ], [x, y, z+d], [x, y+h, z ], [x, y+h, z+d], [x+w, y, z ], [x+w, y, z+d], [x+w, y+h, z ], [x+w, y+h, z+d]];
var edges = [[0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7]];
return { 'nodes': nodes, 'edges': edges};
};
```

We can then create a cuboid with width 100, height 160, depth 50 and one node on the origin like this:

`var shape = createCuboid(0, 0, 0, 100, 160, 50);`

Since our previous code just references global

`nodes`

and `edges`

variables, we need to store our object's properties into those globals:`var nodes = shape.nodes; var edges = shape.edges;`

You can see the complete code below.

### Working with multiple shapes

We can create shapes with different dimensions, what if we want more than one? Whenever we want a variable number of things, an array is useful, so lets create an array of shapes.

```
var shape1 = createCuboid(-120, -20, -20, 240, 40, 40);
var shape2 = createCuboid(-120, -50, -30, -20, 100, 60);
var shape3 = createCuboid( 120, -50, -30, 20, 100, 60);
var shapes = [shape1, shape2, shape3];
```

Now we need to change the display and rotate functions to work with an array of objects. Start by wrapping the code to display edges in a for loop that loops through all the shapes:

```
// Draw edges
stroke(edgeColor);
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
var nodes = shapes[shapeNum].nodes;
var edges = shapes[shapeNum].edges;
for (var e = 0; e < edges.length; e++) {
var n0 = edges[e][0];
var n1 = edges[e][1];
var node0 = nodes[n0];
var node1 = nodes[n1];
line(node0[0], node0[1], node1[0], node1[1]);
}
}
```

And a similar for loop for displaying nodes:

```
// Draw nodes
fill(nodeColor);
noStroke();
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
var nodes = shapes[shapeNum].nodes;
for (var n = 0; n < nodes.length; n++) {
var node = nodes[n]; ellipse(node[0], node[1], nodeSize, nodeSize);
}
}
```

We could add a similar for loop to each of the rotate functions, but I think it's more flexible to pass the array of nodes to each function - that way we can rotate shapes independently of one another. For example, the rotateZ3D() function would look like this:

`var rotateZ3D = function(theta, nodes) { ... };`

Now when we use the mouse to rotate, we have to loop through the shapes and call the function for each one:

```
mouseDragged = function() {
var dx = mouseX - pmouseX;
var dy = mouseY - pmouseY;
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
var nodes = shapes[shapeNum].nodes;
rotateY3D(dx, nodes);
rotateX3D(dy, nodes);
}
};
```

Make sure you remove any other calls to the rotate functions that do not pass in any nodes. You can see the complete code below.

## Want to join the conversation?

- How would you create a three-dimensional shape with faces?(55 votes)
- At some point I would like to add that to the tutorial. It requires a fair bit more work and mathematics. Here's a program to get you started: https://www.khanacademy.org/cs/shaded-box/1066372924(67 votes)

- In the future will Khan Academy have a 3D canvas?(23 votes)
- Processing has rudimentary 3D built in. However it is disabled, which forces us to learn more about the math behind 3D graphics rather than letting the computer do everything for us.

https://github.com/musicalglass/AKA/wiki/3D(31 votes)

- Is there a way to make 3D shapes without using trigonometry? I am only nine years old, but I love doing coding. But of course I don't know trigonometry.(24 votes)
- I don't think that you need to know Trig to use these functions anymore than you needed it to invoke
`rotate`

in 2D land. Similarly you don't need to know Linear Algebra to use`translate`

,`scale`

or`rotate`

. Knowing the higher maths may help some appreciate what is going on (or send them running for the exits).(18 votes)

- Is it possible to generate a 3D object isometrically? If so, what are the mathematics used in creating one?(8 votes)
- Yes it is and the mathmatics used is multiplecation(1 vote)

- Is it possible to use anything other than points and lines in the construction of 3D shapes? For example, if I wanted to create a 3D cylinder, could I somehow use ellipses for the 2 faces, or would I have to substitute a tediously crafted regular polygon that has sufficient sides to bear at least a resemblance to a real circle?(5 votes)
- A simple
`for`

loop would take the tedium out of crafting a regular polygon and give you what most graphics hardware likes to process. Notwithstanding, math can be used to transform an ellipse into its corresponding ellipse in projected 3D.

https://www.khanacademy.org/computer-programming/regular-polygons/1304459398(5 votes)

- How would you make a 3D shape smaller as it gets further away from the "camera" (I know that there isn't exactly a camera, but how would you make things smaller as their Z value decreases?)(5 votes)
- First assume the camera is fixed and located at a fairly far away positive Z location, most likely centered on the canvas, e.g.

Then the perspective point,`camera = (0, 0, 1000)`

`pp`

of point`p`

is computed with

Working backwards, note that (1) the value of`d = distanceBetween(camera, p)`

scaleFactor = camera.z / d

pp = (scaleFactor*p.x, scaleFactor*p.y, p.z)`pp.z`

should not matter since perspective is only used for rendering onto the 2D canvas, (2) the`scaleFactor`

is one (a noop) at the 3D universe's origin, and less than (or greater than) one for points farther (or nearer), and (3)`distanceBetween`

simply uses the Pythagorean theorem for computing the distance between two 3D points.

See line 305 at https://www.khanacademy.org/computer-programming/3d-perspective-shading/4521460558135296(2 votes)

- What does the keyword
`map`

do in javascript, and if possible could you also write a simple code to demonstrate how it can be used. Thanks in advanced! :)(3 votes)- The function allegedly maps a value in one range to its corresponding value in another range. In fact, the value and its mapping can be "out of range".

Given any number`x`

, the`map`

function returns`y`

where

where`y = mx + b`

`x`

is the first argument to the function and`m`

and`b`

are determined by last four arguments to the function. Specifically`m`

and`b`

are determined by the Algebraic line passing through the two points`(low1, low2)`

and`(high1, high2)`

.

For example, we (should) know that water freezes at 0° Celsius or 32° Fahrenheit and boils at 100° Celsius or 212° Fahrenheit. What is the Celsius equivalent of 86°F?

What is the Fahrenheit equivalent of -40°C?`answer = map(86, 32, 212, 0, 100);`

`answer = map(-40, 0, 100, 32, 212);`

So the`map`

function lets us use linear equations without knowing Algebra.(5 votes)

- could you make a tetrahedron with this?(2 votes)
- Certainly. You can make any shape with flat surfaces.(4 votes)

- Are there any ambitious coders out there with the ability to expand upon this code to create a Menger sponge?(2 votes)
- I'll bite. Here is brute force Menger Sponge: https://www.khanacademy.org/computer-programming/menger-sponge/5000305485152256(3 votes)

- If you are drawing faces, how do you figure out which order to draw them in. (Assuming the view-port displays it completely isometrically.)(2 votes)
- You can calculate the center of each face. That's the average of its vertices (nodes).

So for every face you'll have an x, y and a z-coordinate.

Sort your faces by their z-coordinate. Then draw them, starting with the furthest one.(2 votes)