# Modeling

In this section, we model and display three simple shapes: the cube, torus, and cylinder in order.
The completed code is shown `section2_4.rs`

.

## Modeling functions

Previously, we read model data from an external obj file. This time, we will create a model from scratch.
Truck is an OSS that is designed to be a CAD Kernel, and it has already implemented a modeling function
using boundary representation, although it is still simple. Leaving aside the details of the internal
implementation, let's quickly model with the intuitive interface provided by `truck-modeling`

.

### Types

Before we get into modeling, let's talk about the types of topological elements defined in truck.
There are the following types of topological elements in `truck-modeling::topology`

.

`Vertex`

- the minimum topological unit`Edge`

- connecting two different vertices`Wire`

- a list of the edges, whose entity is`VecDeque`

`Face`

- whose boundaries are described by a closed wire`Shell`

- a set of faces, the order of faces does not matter`Solid`

- whose boundaries are described by a closed shell

### Cube

Let's model a cube. The module `builder`

in `truck-modeling`

has a function "tsweep" that sweeps points,
lines, and surfaces along a straight line, so we can draw a cube in just four steps!

`#![allow(unused)] fn main() { // modeling a cube fn cube() -> Solid { let vertex: Vertex = builder::vertex(Point3::new(-1.0, 0.0, -1.0)); let edge: Edge = builder::tsweep(&vertex, 2.0 * Vector3::unit_z()); let face: Face = builder::tsweep(&edge, 2.0 * Vector3::unit_x()); builder::tsweep(&face, 2.0 * Vector3::unit_y()) } }`

If you are trained in shapes, you may have already understood everything from the code alone. From here on, I will explain these four lines of code, adding comments and pictures.

First, put a vertex at the coordinates (-1, 0, -1).

`#![allow(unused)] fn main() { // put a vertex at the point (-1, 0, -1) let vertex: Vertex = builder::vertex(Point3::new(-1.0, 0.0, -1.0)); }`

This is what it looks like in a diagram. It is so obvious that it may be difficult to understand.

Next, sweep the vertex by 2 in the z-axis direction to create a line segment.

`#![allow(unused)] fn main() { // sweep the vertex along the z-axis let edge: Edge = builder::tsweep( // the reference to the vertex &vertex, // sweep along the z-axis for length 2 2.0 * Vector3::unit_z(), ); }`

This is what it looks like in the diagram. Now you can see what we want to do a little bit better.

In the third line, the segment is swept by 2 in the x-axis direction to create a surface.

`#![allow(unused)] fn main() { // sweep the edge along the x-axis let face: Face = builder::tsweep( // the reference to the edge &edge, // sweep along the x-axis for length 2 2.0 * Vector3::unit_x(), ); }`

Euclid once said. A plane is a flat surface with straight lines on it.

Finally, sweep the plane in the y-axis direction to make a cube.

`#![allow(unused)] fn main() { // sweep the face along the y-axis builder::tsweep( // the reference to the face &face, // sweep along the y-axis for length 2 2.0 * Vector3::unit_y(), ) }`

This is the last of the diagrams.

Well, the vector image I tried so hard to draw with the mouse turned out to be a bit rectangular. Even this clumsy author can draw a cube in just four steps with truck!

### Torus

Next, we model a torus. This one is modeled with the function `builder::rsweep`

,
which allows sweeping along a circle arc. One can create a torus in just three lines with truck.

`#![allow(unused)] fn main() { // modeling torus fn torus() -> Shell { let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, 1.0)); let circle: Wire = builder::rsweep(&vertex, Point3::new(0.0, 0.5, 1.0), Vector3::unit_x(), Rad(7.0)); builder::rsweep(&circle, Point3::origin(), Vector3::unit_y(), Rad(7.0)) } }`

These three lines are illustrated in the figure below.

The previous function `cube`

returned `Solid`

, now `torus`

returns `Shell`

.
Since the torus is a closed surface, we can create `Solid`

from the output `Shell`

.
However, since we need to use a more primitive API, we leave it as `Shell`

for now.

Anyway, let's look at it line by line. First, put the vertices.

`#![allow(unused)] fn main() { // put a vertex at the point (0, 0, 1). let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, 1.0)); }`

Next, sweep the vertices along a circle to create a circle.

`#![allow(unused)] fn main() { // sweep the vertex along a circle let circle: Wire = builder::rsweep( // the reference to the vertex &vertex, // a point on the axis Point3::new(0.0, 0.5, 1.0), // the direction of the axis Vector3::unit_x(), // If the specified value is greater than 2π radian, a closed shape will be generated. Rad(7.0), ); }`

Now we are considering 3D rotation. While 2D rotation requires a center and an angle, 3D rotation requires an axis and an angle. The axis of rotation is a line with an orientation, so we need to specify a point on the axis and the direction of the axis.

If you specify an angle whose absolute value is no less than 2π, the curve have a closed topology.
In this example, the value is `7.0`

, which is definitely more than 2π, It may be better to use `2.0 * PI`

.

Now, rotate this circle around the y-axis, and the torus is complete.

`#![allow(unused)] fn main() { // sweep the circle along a circle builder::rsweep( // the reference to the wire &circle, // a point on the axis Point3::origin(), // the direction of the axis Vector3::unit_y(), // If a value no less than 2π radian is specified, a closed shape will be generated. Rad(7.0), ) }`

### Cylinder

Up to here, we have been working with sweeps alone, but this is not the case with cylinders. We need to attach a plane to the closed wire. As usual, let's look at the code first.

`#![allow(unused)] fn main() { // modeling a cylinder fn cylinder() -> Solid { let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, -1.0)); let circle: Wire = builder::rsweep(&vertex, Point3::new(0.0, 1.0, -1.0), Vector3::unit_z(), Rad(7.0)); let disk: Face = builder::try_attach_plane(&vec![circle]).expect("cannot attach plane"); builder::tsweep(&disk, 2.0 * Vector3::unit_z()) } }`

As usual, I will explain it line by line. Taking a vertex and turning it in a circle is the same as the torus.

`#![allow(unused)] fn main() { // put a vertex at the point (0, 0, -1). let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, -1.0)); // sweep the vertex along circle let wire: Wire = builder::rsweep( // the reference to the vertex &vertex, // a point on the axis Point3::new(0.0, 1.0, -1.0), // the direction of the axis Vector3::unit_z(), // If a value greater than 2π radian is specified, a closed shape will be generated. Rad(7.0), ); }`

The problem is next. Create a disk by attaching a plane to the circle.

`#![allow(unused)] fn main() { // make a disk by attaching a plane to the circle let face: Face = builder::try_attach_plane(&vec![wire]).expect("cannot attach plane"); }`

The function `try_attach_plane`

attaches a plane to a closed `Wire`

in the same plane.
As a whole truck, functions with the prefix `try`

or `search`

return `Option`

or `Result`

.
In many cases, we provide unprefixed functions that cause panic, but this function is an exception.

Finally, sweep along the z-axis to complete the cylinder.

`#![allow(unused)] fn main() { // sweep the face along the z-axis builder::tsweep( // the reference to the disk &face, // sweep along the z-axis 2.0 * Vector3::unit_z(), ) }`

## Application handler

The shapes we created in the previous section were Shell and Solid. As with meshes, we need to convert these data into instances so that they can be rendered. Since this process is more time-consuming than mesh's one, we will not run them in the frame, but during application initialization and store the generated instances in member variables in the application handler.

`#![allow(unused)] fn main() { // Declare the application handler struct MyApp { // scene scene: Scene, // current drawn shape current_shape: i32, // the instance of cube cube: ShapeInstance, // the instance of torus torus: ShapeInstance, // the instance of cylinder cylinder: ShapeInstance, } }`

`App::init`

We now override App::init to initialize the application handler. We don't do anything new until the scene initialization, so we just post the code here.

`#![allow(unused)] fn main() { // radius of circumscribed circle let radius = 5.0 * f64::sqrt(2.0); // Useful constants for lights placement. let omega = [0.5, f64::sqrt(3.0) * 0.5]; // the vector of lights let lights = vec![ Light { position: Point3::new(radius * omega[0], 6.0, radius * omega[1]), // The color vector should be divided by 3.0. If not, the white will be satiated. color: Vector3::new(1.0, 1.0, 1.0) / 3.0, ..Default::default() }, Light { position: Point3::new(-radius, 5.0, 0.0), // The color vector should be divided by 3.0. If not, the white will be satiated. color: Vector3::new(1.0, 1.0, 1.0) / 3.0, ..Default::default() }, Light { position: Point3::new(radius * omega[0], 4.0, -radius * omega[1]), // The color vector should be divided by 3.0. If not, the white will be satiated. color: Vector3::new(1.0, 1.0, 1.0) / 3.0, ..Default::default() }, ]; // Create the scene let scene = Scene::new( device_handler.clone(), &SceneDescriptor { // use the default camera camera: Default::default(), lights, ..Default::default() }, ); }`

Model the shape using the created functions so far, and instantiate it as it is.

`#![allow(unused)] fn main() { // create cube instance let cube = scene.create_instance(&cube(), &Default::default()); // create torus instance let torus = scene.create_instance(&torus(), &Default::default()); // create cylinder instance let cylinder = scene.create_instance(&cylinder(), &Default::default()); }`

Let's use these to initialize and return the application handler.

`#![allow(unused)] fn main() { // Return the application handler MyApp { scene, current_shape: -1, cube, torus, cylinder, } }`

`App::update`

This is the updating process for each frame. The camera rotation is almost the same as the previous section, so we just post the code.

`#![allow(unused)] fn main() { // the seconds since the application started. let time = self.scene.elapsed().as_secs_f64(); // the mutable references to the camera let camera = &mut self.scene.descriptor_mut().camera; // update camera matrix camera.matrix = Matrix4::from_axis_angle(Vector3::unit_y(), Rad(time)) * Matrix4::look_at( Point3::new(4.0, 5.0, 4.0), Point3::new(0.0, 1.0, 0.0), Vector3::unit_y(), ) .invert() .unwrap(); }`

Now let's write the process of replacing the model. In this video, each time the camera goes around the

- cube
- torus
- cylinder

is displayed in order. In other words, we divide the number of times the camera went around the model by
three, and the remainder number represents the shape that should be displayed. Since the camera rotates
by one radian per second, it takes `2.0 * PI`

seconds per revolution. Therefore, the number of
revolutions of the camera can be calculated by dividing the application start time by `2.0 * PI`

.

`#![allow(unused)] fn main() { // the number of the shape which should be displayed let laps = (time / (2.0 * PI)) as i32 % 3; }`

If you replace the shape only when the currently instance number `self.current_shape`

is different from
the shape number `laps`

, which is derived from the number of camera lapses, you can complete the behavior
that the shape is replaced every lap.

`#![allow(unused)] fn main() { // the timing for changing the drawn shape if laps != self.current_shape { /* changing the shape */ } }`

Let's write the contents of "changing the shape" process.
First of all, synchronize `self.current_shape`

and `laps`

before forgetting them.

`#![allow(unused)] fn main() { // synchronize variables self.current_shape = laps; }`

Removes the currently rendered object from the scene.

`#![allow(unused)] fn main() { // clear all objects in the scene self.scene.clear_objects(); }`

Register an instance in the scene according to the `laps`

number.

`#![allow(unused)] fn main() { // laps == 0 => cube, laps == 1 => torus, laps == 2 => cylinder match laps { 0 => self.scene.add_objects(&self.cube.render_faces()), 1 => self.scene.add_objects(&self.torus.render_faces()), _ => self.scene.add_objects(&self.cylinder.render_faces()), }; }`

For polygon meshes, we used `Scene::add_object`

, but this time `Scene::add_objects`

is used.
Unlike with polygons, each face of a modeled shape is rendered, so we use `ShapeInstance::render_faces`

to create the `FaceInstance`

vector and register them with `Scene::add_objects`

.

This completes all the new implementation. If you define the usual `main`

function and
the spell `App::render`

, you can play the video at the beginning of this section.