[Java] Gopher Kimi Yuki Episode II ~ Solar system simulator and dark side that can be made overnight with Daphnia.

142 minute read

Introduction

This article is a story of republican knights who maintain the peace and order of the galaxy struggle to complete a planetary system simulator overnight.

If you have a basic knowledge of programming, by reading this article while actually moving your hands, anyone will be able to easily create a CG program such as the following. jupiter_animation.gif

Gopher You Go Series

This article is a sequel to “Gopher Kimi ga Yuku Episode I”.

For those who value the story, we recommend reading from Episode I first, but since it is not related to the technical subject to handle, reading this article will not hinder understanding.

If you’re interested in Gopher’s physical ability, check out the five GIF animations embedded in Episode I before proceeding to the main part.

Character

Designation Residence Overview
Padawan (Daphnia) Planet Tatooine Has exceptional talent and an amazing Midi-Chlorian rating.
Just after graduating from initiation, she is still in training.
Master Planet Tatooine An unconventional knight who ignores the intentions of the Council.
Having high ability and leadership.
Mr. Gopher Unspecified Agent dispatched to various galaxies.
Has amazing physical ability and sense of balance.
Young Knight Planet Coruscant A hot-blooded man who burns justice.
It’s been several years since I was promoted from Padawan to Knight.
Grand Master Planet Coruscant Republican Knight Elder who leads the Supreme Council.
Supreme Chairman Planet Coruscant Supreme Chairman of the Galactic Republic Senate.

Prerequisites

  • In this article, we will proceed with the development using Processing 3.5.4.
    For this reason it would be nice to have some Java coding experience.
  • Also, the following calculations are performed within the program, but advanced math knowledge is not required.
    • Use simple trigonometric functions to calculate the orbits of planets and satellites.
    • Use simple matrix operations for coordinate transformations.

Development and operating environment

The programs described in this volume have been confirmed to operate only in the following environments, but Processing supports multiple platforms. If you can confirm the operation on Linux or Mac OS, I would be grateful if you could let me know.

  • HW: Thinkpad X1 Extreme
    (NVIDIA GeForce GTX 1050 ti with Max-Q Design)
  • OS: Windows 10 x64 Version 1903

Development tools

Category Tool Purpose
Languages and IDEs Processing 3.5.4 Programming 3DCG in Java
Graphic Editing Gimp 2.10.10 Image material creation and background processing

Both are free tools.

Disclaimer

  • Although the title says “Solar System Simulator,” this paper does not deal with physical models based on Newtonian mechanics (for example, N-body simulation with gravitational interaction).
  • The orbits of the planets and satellites discussed in this article are not elliptical orbits. The orbital body is assumed to move at a constant velocity in a circular orbit centered on the home star on the same plane.
  • The diameter, revolution radius, and revolution period of the celestial body are deformed based on the actual values.
  • The objects (stars, planets, satellites, etc.) handled in the final program are billboards with textures mapped on planes, not 3D models.
  • Despite the appearance of Gohper, there is no Golang topic.
  • The following terms used in this volume are necessary due to the nature of the story, but as of August 2020, they are not expressions generally recognized in Japan.
    • A program and programming language are sometimes called “saver”.
    • Similarly, a programmer is sometimes called a “saver user”.
    • The direction of negative infinity on the number line is sometimes called the “dark side” or “dark side”.
    • Similarly, the positive infinity direction on the number line is sometimes called the “light side”.
  • The embedded video (GIF animation) in the text does not contain audio. If you feel that the sense of presence is insufficient, please prepare the sound source yourself before playing it. By using 5.1ch or 7.1ch sound source and playback device, you will be able to feel more realistic. If you do not have the appropriate sound source available, you can select it from here.

Main story

A long time ago in a galaxy far, far away…. (A long time ago, in a galaxy far away…)

Ginga Senate Was in turmoil. [Independent Star Alliance] led by the Droid Army (https://starwars.fandom.com/en/wiki/%E7%8B%AC%E7%AB%8B%E6%98%9F%E7%B3%BB%E9% (80%A3%E5%90%88) the Congress was in dispute over whether to create an army to counter.

Behind the scenes Kyowa Knight,SecretlyMasterandPadawanPlanetTatooine.

8 pm | Prologue

Planetary Coruscant Republican Knight at the Council Room…

Grand Master “I had a hard time far away”

Master “No, it’s ridiculous. Padawan is a big fan of the big city after a long absence.” Master “By the way…”

Grand Master “The story is…” Grand Master “Actually, in case the bill that is in conflict with the Senate now passes,”

Grand Master “I’m secretly looking for a star to train and train the Clone Army.” Grand Master “I urgently want you to simulate a candidate star system.” Master “I see. And which star system?”

Grand Master “It’s the solar system in the Milky Way galaxy” Master “Are you in that frontier?”

Grand Master “Oh, there’s no turning around for the Independent Star Alliance”


At Planet Korsant/Kyowa Knight Temple…

Young Night “Hi, Daphnia” Young Knight “Why do you make a simulator for the solar system?”

Padawan

Padawan “Yeah. I heard from you a while ago.” Padawan “Why do you call me Daphnia!?”

Young Knight “Gahaha. That’s because you are still small like Daphnia.” Young Knight “Is it possible to make a simulator for Daphnia????”

Padawan (This guy is really sick!!)

Young Knight “Oh, it seems that your teacher came”

Young Knight “Then I’m about to go!” Young Knight “Do your best at best” Young knight (that boy, you still can’t control your emotions) Young Knight (Middy-Chlorian value is strangely high, but it smells dangerous)

Master “Oh! Padawan” Master “Have you been here!”

Master “What’s wrong?” Padawan “…”

Padawan “Hey, am I a daphnia?”

Master “Did you say that?” Master “Gahaha! You’re short”

Padawan “…”

Master “Okay, what is a Daphnia?” Master “I can jump more than 20 times my length!” Master “I don’t want to find such a knight in the galaxy”

Padawan “Suge!” Padawan “Better than Gopher!” Padawan “The best Daphnia!”

Padawan “I wonder if I can make a simulator?” Master “Of course!” Master “This time, as part of my training, I have been instructed to make it mainly by you.”

Padawan “Hey, what is the solar system like?” Padawan “What’s the best in the countryside?”

Master “There is a rocky planet called the Earth of the 3rd planet.” Master “The moon’s moon, the star, is enormous.”

Master “The main purpose of this simulation” Master “To know how much the gravity of the moon affects the Earth.”

Padawan “Heh, what kind of race did you live in??” Master “It seems that the crustal movement is active and the amount of methane and carbon dioxide is large.” Master “There is also information that there seems to be life forms”

Master “This was obtained at the Republic Knight Library” Master “It’s the only image of an earthling”

Padawan “Get it!” Padawan “The Earthlings!”

(Source: Pixabay)

9:00 pm | Introduction of Processing, introduction to 3DCG

Master “Then let’s get started!”Master: Originally, [OpenGL](https://ja.wikipedia.org/wiki/OpenGL#:~:text=OpenGL%EF%BC%88%E3%82%AA%E3%83%BC%E3%83%97%E3%83%B3%E3%82%B8%E3%83%BC%E3%82%A8%E3%83%AB%E3%80%81Open%20Graphics,%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%82%B9%E3%83%A9%E3%82%A4%Iwouldliketouse(E3%83%96%E3%83%A9%E3%83%AA%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82).’’ Master “I’m going to use Processing, a saver for art this time”

Master “It’s a Java-based environment, so you can almost call it Java.” Padawan “Gee! Java? That saver feels solid.” Padawan “It was a red dot in the era of initiation…”

Padawan “I don’t remember…”

Master “Even though it’s Java-based,” Master “Variables and methods needed for graphics have already been prepared.” Master “You can suddenly start using a saver without being aware of the class.” Master “With OpenGL, before entering the 3DCG sword (coding) you want to concentrate on” Master “I have to perform a lot of troublesome rituals,” Master “In that respect, Processing can be said to be positive.”

21:05 | Downloading and installing Processing

Master “Processing IDE can be downloaded here”

Processing Download

Master “I want to say, “Once downloaded, install!” Master “no installer” Master “If you unzip the zip, just throw it in a proper directory.”

21:10 | Starting Processing

Master “Double click the processing.exe in the unzipped directory” Padawan “Oh, double click, super good!” image.png

Master “Enter something in Galactic Standard Basic (Footnote: Japanese)” Padawan “Getting garbled characters!!!” image.png

Master “In that case, in [File][Settings…],” image.png

Master “Choose the right font!” image.png

Padawan “Oh! It’s fixed!” image.png

21:20 | Perceive the space

Master “Okay, what’s important in 3DCG is neither trigonometric functions nor matrix operations!” Master “The most important thing is spatial recognition!”

Master “The world of 3DCG is the same as outer space.”

Master “Once you jump in, you can’t go up or down.” Master “I don’t know if the other person is moving or me.”

Master “Okay, I often see this picture.” image.png

Master “This is the coordinate space of the World coordinate system of Processing.”

Master “Red is X-axis, Green is Y-axis, Blue is Z-axis” Master “First of all, hit this image thoroughly into your head.”

Master “Let’s do this with the shape of the left hand while imagining this figure!” Padawan “Oh, Fleming’s Law!”

Master “Yes, it has the same shape.” Master “Make the thumb, index and middle fingers cross 90 degrees.” Master “Thumb is X-axis, Index finger is Y-axis, Middle finger is Z-axis,” Master “The direction that each finger points is the light side (the direction of positive infinity).”

Master: Keep your thumbs to the right and your forefingers down, without breaking the shape. Master “Where does your middle finger face?” Padawan “Um, I’m facing myself!”

Master “Yes, this is the processing coordinate system, called the “left-handed system.”” Padawan “If so, is there also a “right-handed system”?” Master “Yes. The right-handed system has the Z-axis direction opposite to that of the left-handed system.”

Master “Also, be careful because the Y axis of the Processing coordinate system is downward!”

Master “Look your thumb up to the right and your index finger up” Padawan “Oh, my middle finger points to the master…”

Master “Yes. Even in the same left-handed system, in a library where the Y-axis is facing up.” Master “Be careful because the Z-axis orientation is reversed.”

Master “Imagine the color and direction of each axis again!”

Padawan “Red X axis points right…” Padawan “Green Y-axis is facing down…” Padawan “Blue Z-axis is facing here…”

Master “Yes. Let it soak into your body.”

Master “Also keep in mind the three coordinate planes”

Master “First, the XY plane. This is a high wall that stands in front of you.” Master “in the two-dimensional coordinate system in the era of initiation” Master “I’ve seen you many times.” image.png

Master “Next is the ZX plane, this is the floor.” Master: “Today’s work, this plane should come out a lot.” Master “Remember me” image.png

Master “Finally the YZ plane” Master “When I mistakenly say “I’m sorry!” Master “The shape of the palm” image.png

21:30 | Making a new sketch

Master “In Processing, a project is called a sketch.” Padawan “Drawing!”

Master “Yes. When you launch Processing.” Master “Because a new sketch is automatically created” Master “name and save a sketch that is already open” image.png Master “Choose a place to save, and for the time being, save it as “Learning”.”

Padawan “Oh, I can create a folder for Learning.” Padawan “Learning.pde is formed in it”

Master “Yes. That’s Processing.” Master '' Sketch folder and Sketch file’’

Master “I’m going to add it little by little from now on” Master “Don’t forget to save frequently with Ctrl+S

21:40 | First coding

Master “Then we’re finally in the sword fight” Master “Enter the following in the IDE!” image.png Padawan “Do you need a class definition and a main method?!” Padawan “It’s too easy to be happy (laugh)”

Learning.pde


size(1200, 800, P3D);

beginShape();
vertex(-50, -50, -500);vertex( 50, -50, -500);
vertex( 50, 50, -500);
vertex(-50, 50, -500);
endShape(CLOSE);

Master “Explain this meaning”

  • size(1200, 800, P3D);
    This is the window size specification. 1200 is the width and 800 is the height.
    Since it is 3DCG this time, specify the third parameter as ```P3D

    .

    ```

  • beginShape();
    This is a declaration that tells Processing that a shape will be drawn.
  • vertex(...); x 4 lines
    vertex is a method that draws a polygon that connects several vertices. Here, since the vertices are specified by four vertexes, a quadrangle is drawn.
  • endShape();
    Declaration that the shape has been written. CLOSE passed in the parameter means to close the figure. If this is not specified, it will be an open figure that does not connect the first vertex and the last vertex.

Master “Okay, if the image of the world coordinate system is difficult,” Master “First, think only of the XY plane.” image.png Master “Once you have an image of the XY plane, the next is the Z coordinate.” Master “Z coordinates of all vertices are -500”

Master “Which is -500 in the world coordinate system when viewed from the origin (0,0,0)?”

Padawan “Um, the left thumb is X, the index finger is Y…” Padawan “If you point your thumb X to the right and your index finger Y downward…” Padawan “Middle finger Z, the number gets bigger from this distance…”

image.png

Padawan “-500 is far behind the front!”

Master: “Let’s see if that’s correct.” Master “Click the run button (the play mark icon just above the tab).” image.png Padawan “Oh! It works! It works!” Padawan “What? What is this??” image.png Padawan “One small square…” Padawan “I don’t know if this is the front or the back!” Padawan “This is too dull, this!”

10:00 pm | Conquer the world coordinate system

Master “Then, to better capture the world coordinate system” Master “Try writing a little differently”

22:00 | Preparation for later animation

Master “Enter the following code” Master “Enter by yourself, not by copy and paste!”

Learning.pde


// setup() is called once just after the program starts
void setup() {
  size(1200, 800, P3D); // specify window size and 3DCG
  frameRate(24); // frame rate (fps)
}

// draw() is called many times until the end of the program
// (if fps==24, called 24 times per second)
void draw() {
  background(255, 255, 255); // background color (R,G,B) is white

  // Below is the same as before
  beginShape();
  vertex(-50, -50, -500);
  vertex( 50, -50, -500);
  vertex( 50, 50, -500);
  vertex(-50, 50, -500);
  endShape(CLOSE);
}

Padawan (It’s hard to type unless it’s Vim)

Master “Explain this meaning”

  • void setup() {...}
    This is a method that is called once just after the saver starts.
    • size(1200, 800, P3D);
      This is the same as before.
      The size() method is written in setup().
    • frameRate(24);
      This is a new line to add.
      The number of frames displayed per second in animation, that is, the frame rate (fps: frames per second) is specified.
  • void draw() {...}
    This is a method that is called many times until the end of the program.
    Since the frame rate is set to 24, this program will be called 24 times per second.
    • background(255, 255, 255);
      Here, the background color is specified as white. The parameters are specified in order of R (red), G (green), B (blue).
    • beginShape();
      The rest is the same as before.

Master “Click the Run button” Padawan “Oh, the white background made it more dull…” image.png Padawan “It’s harder to understand than before…”

22:20 | Separate drawing objects into methods

Master “Let’s make it a little easier to understand” Master “I’ll fix it like this”

Learning.pde


// setup() is called once just after the program starts
void setup() {
  size(1200, 800, P3D); // specify window size and 3DCG
  frameRate(24); // frame rate (fps)
}

// draw() is called many times until the end of the program
// (if fps==24, called 24 times per second)
void draw() {
  background(255, 255, 255); // background color (R,G,B) is white
  updateObject1();
}

// (1) Draw a rectangle (still)
void updateObject1() {
  fill(240, 240, 240); // Fill color (R,G,B)
  stroke(255, 0, 255); // border color (R,G,B)
  strokeWeight(2); // border thickness

  beginShape();
  vertex(-50, -50, -500);
  vertex( 50, -50, -500);
  vertex( 50, 50, -500);
  vertex(-50, 50, -500);
  endShape(CLOSE);
}

Master “The changes are as follows.”

  • void updateObject1() {...}
    First, I divided beginShape() ~ endShape() into this method.
    Next, I added the following three lines in this method.
    • fill(240, 240, 240);
      Specify the fill color for drawing objects.
      I’ll keep it light gray here.
    • stroke(255, 0, 255);
      Specify the border color of the drawing object.
      Keep it magenta.
    • strokeWeight(2);
      Specify the thickness of the border of the drawing object.
      Keep it a little thicker 2.
  • It’s just a call to void draw() {...}
    background() and a new call to updateObject1().

Padawan clicked the Run button.

image.png

Padawan “It’s not too flashy…”

22:40 | Introduction of development utility

Master “I’ll make this more understandable from now on.” Master “Click the [▼] button in the IDE and select [New Tab].” image.png

Master “When the New Name dialog opens, type “DevUtils” and click OK.” (Any name is fine) image.pngMaster “Copy and paste the following code into the newly created tab [DevUtils]” Master “For the time being, you don’t have to understand the content. Just copy and paste without thinking!”

Expand code of DevUtils.pde
#### **`DevUtils.pde`** ```java // Processing Utility DevRoom // (c) 2020 y-bash // This software is released under the MIT License. // http://opensource.org/licenses/mit-license.php public void setupDevRoom(float len, int hnum, int vnum) { float radius = len * hnum * 1.25; float depth = len * vnum / 2; int period = 16; setupDevRoom(len, hnum, vnum, radius, depth, period); } public void setupDevRoom(float len, int hnum, int vnum, float radius, float depth, int period) ( gCubeSideLength = abs(len); gCubeHNum = (hnum >= 0) ?hnum :0; gCubeVNum = (vnum >= 0) ?vnum :0; setCameraOrbitalRadius(radius); setCameraMaxDepth(depth); setCameraOrbitalPeriod(period); } public void setDevRoomAutoCameraEnabled(boolean isEnabled) { gIsCameraEnabled = isEnabled; } public void setDevRoomVisible(boolean isVisible) { gIsDevRoomVisible = isVisible; } public void setCameraOrbitalRadius(float radius) { if (radius == 0.0) radius = 0.1; gCameraOrbitalRadius = radius; } public void setCameraMaxDepth(float depth) { gCameraMaxDepth = depth; } public void setCameraOrbitalPeriod(int period) { if (period == 0) period = 1; gCameraOrbitalPeriod = period * 1000; } public void updateDevRoom() { pushStyle(); if (gIsCameraEnabled )updateCamera(); if (gIsDevRoomVisible) drawDevRoom(gCubeSideLength, gCubeHNum, gCubeVNum); popStyle(); } public void mouseClicked() { gIsDevRoomVisible = !gIsDevRoomVisible; } private boolean gIsDevRoomVisible = true; private boolean gIsCameraEnabled = true; private float gCubeSideLength = 200; private int gCubeHNum = 4; private int gCubeVNum = 2; private float gCameraOrbitalRadius = 1000; private float gCameraMaxDepth = 200; private int gCameraOrbitalPeriod = 16000; private void updateCamera() { int ms = millis(); float hAngle = TWO_PI * ms / gCameraOrbitalPeriod; float x = gCameraOrbitalRadius * sin(hAngle); float z = gCameraOrbitalRadius * cos(hAngle); float y = sin(hAngle) * gCameraMaxDepth; camera(x, y, z, 0, 0, 0, 0, 1, 0); } private void drawDevRoom(float len, int hnum, int vnum) { strokeWeight(1); stroke(214, 214, 214); drawCubes(len, hnum, vnum); strokeWeight(3); drawFloor(0, len, hnum); drawAxes(len, hnum, vnum); } private void drawCubes(float len, int hnum, int vnum) (float v1 = -len * vnum / 2; for (int i = 0; i <= vnum; i++) { float y = v1 + i * len; drawFloor(y, len, hnum); } float h1 = -len * hnum / 2; float v2 = -v1; for (int i = 0; i <= hnum; i++) { float x = h1 + i * len; for (int j = 0; j <= hnum; j++) { float z = h1 + j * len; line(x, v1, z, x, v2, z); } } } private void drawFloor(float y, float len, int num) { float h1 = -len * num / 2; float h2 = -h1; for (int i = 0; i <= num; i++) { float cp = h1 + i * len; line(h1, y, cp, h2, y, cp); line(cp, y, h1, cp, y, h2); } } private void drawAxes(float len, int hnum, int vnum) { strokeWeight(2); stroke(255, 0, 0); drawXAxis(len, hnum, vnum); stroke(0, 255, 0); pushMatrix(); rotateZ(0.5 * PI); drawXAxis(len, hnum, vnum); popMatrix(); stroke(0, 0, 255); pushMatrix(); rotateY(-0.5 * PI); drawXAxis(len, hnum, vnum); popMatrix(); } private void drawXAxis(float len, int hnum, int vnum) { float h1 = -len * (hnum + 2) / 2; float h2 = -h1; line(h1, 0, 0, h2, 0, 0); float al = len * max(hnum, vnum) / 60; float aw = al / 3; float ax = h2-al; line(ax, -aw, 0, h2, 0, 0); line(ax, aw, 0, h2, 0, 0); line(ax, 0, -aw, h2, 0, 0); line(ax, 0, aw, h2, 0, 0); } ```

Padawan “I’m really good at copy and paste! It’s a galaxy!” <img width=”500”, alt=”image.png” src=”https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/fb074189-d4a2-a8f8-3b36-62cefb8cc959.png”> Master “When pasted, like this,” Master “Add a line to the draw() method in the Learning tab”

Learning.pde


...
void draw() {
  background(255, 255, 255); // background color (R,G,B) is white

  updateDevRoom(); // <===== postscript =====

  updateObject1();
}
...

Added updateDevRoom(); before updateObject1();, and Padawan clicked the [Run] button. Padawan “Oh, it’s spinning…”

updateObject1.gif (Although it is frame-advanced to save space, it works more smoothly in the actual program)

Master “Actually, everything in this is stationary.” Padawan “Eh??

Master “It’s the camera that’s spinning” Master “If you change the viewpoint of the camera, you can better understand the shape of the three-dimensional object.” Padawan “I see…”

Master “This utility saves us the effort of early development.” Master “From the point of view, first of all, is there an X, Y, Z coordinate axis?” Master “The point where the three intersected is the origin (0, 0, 0)” Master “Do you remember the colors?”

Padawan “Yeah. Red is left to right on the X-axis.” Padawan “Green is Y-axis, from top to bottom,” Padawan “Blue is facing away from the front on the Z axis…” Padawan “Oh, really. The arrow directions are correct!”

Master “Next, are there boxes of cubes?” Master “Because there are 4 x 4 horizontally and 2 vertically.” Master “It’s made up of 32 cubes in all.” Master “One side of the cube is 200”Master “Keep it in mind and look at the rectangle you drew.”

Padawan “Well, it’s 400 with 2 cubes…” Padawan “The purple square is…” Padawan “Oh, I’m stuck in the z-axis a little farther than -400!”

Master “Yes. You can get the approximate position?”

Master “And again, click the screen.” Master “Cubes and axes will not be displayed”

Padawan “Oh, really. Something sober (laughs)”

Master “Click again to go back” Padawan “Oh, I’m back. I’m back.”

11:00 PM | Move the object

23:00 00|Move straight

Master “Rewrite the saver this time!”

Learning.pde


// setup() is called once just after the program starts
void setup() {
  size(1200, 800, P3D); // specify window size and 3DCG
  frameRate(24); // frame rate (fps)
}

// draw() is called many times until the end of the program
// (if fps==24, called 24 times per second)
void draw() {
  background(255, 255, 255); // background color (R,G,B) is white

  updateDevRoom();

  //updateObject1(); // <===== comment out =====
  updateObject2(); // <===== postscript =====
  
}

// (1) Draw a rectangle (still)
void updateObject1() {
  fill(240, 240, 240); // Fill color (R,G,B)
  stroke(255, 0, 255); // border color (R,G,B)
  strokeWeight(2); // border thickness

  beginShape();
  vertex(-50, -50, -500);
  vertex( 50, -50, -500);
  vertex( 50, 50, -500);
  vertex(-50, 50, -500);
  endShape(CLOSE);
}

// (2) Rectangle drawing (quasi-constant linear motion) // <===== Add postscript ↓ =====
float gZ = -600;
void updateObject2() {
  gZ+=10; // Update Z coordinate

  fill(255, 255, 255); // Fill color (R,G,B)
  stroke(0, 255, 255); // border color (R,G,B)
  strokeWeight(2); // border thickness

  beginShape();
  vertex(-40, -40, gZ);
  vertex( 40, -40, gZ);
  vertex( 40, 40, gZ);
  vertex(-40, 40, gZ);
  endShape(CLOSE);
}

Padawan “Well…”

Padawan “Make updateObject2()," Padawan "Call from inside draw()," Padawan "Comment on `updateObject1()

.

.."

Padawan (Dude, copy and paste!)

Master "explain"

* ```float gZ = -600;```<br>Create a global variable gZ and initialize it with -600. <br>gZ stores the Z coordinate position of the object.
* ```gZ+=10;```<br> The value is increased by 10 each time the object is drawn.
* ```vertex(-40, -40, gZ);``` or later <br>Specify gZ as the Z coordinate value when drawing vertices.

Padawan clicked the Run button.

Padawan "Oh, it worked!"
<img width="500" alt="updateObject2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/ea06ee90-21c2-5819-501f-54f27fef772d.gif">

Master “But this implementation has a problem”
Master "Try it with a frame rate of 12"


#### **`Learning.pde`**
```java

  ...
  frameRate(12);
  ...

Padawan “Oh, I’m sloppy…” Master “Yes. This implementation is sensitive to frame rate.”

23:20 | Move without being affected by frame rate

Master “Enter the following” Master “Be careful because some are omitted.”

Learning.pde


...
void draw() {
  ...
  //updateObject2(); // <===== comment out =====
  updateObject3(); // <===== postscript =====
}

...

// (3) Rectangle drawing (real constant linear motion) // <===== Add postscript ↓ =====
void updateObject3() {
  final float START_Y = -600; // Y coordinate at the start
  final float SPEED = 200; // speed per second

  float ms = millis(); // milliseconds since the program started
  float dy = ms * SPEED / 1000; // Moving distance (Y coordinate)
  float y = START_Y + dy; // current position (Y coordinate)

  noFill(); // no fill
  stroke(255, 0, 0); // border color (R,G,B)
  strokeWeight(2); // border thickness

  beginShape();
  vertex(-30, y, -30);
  vertex( 30, y, -30);
  vertex( 30, y, 30);
  vertex(-30, y, 30);
  endShape(CLOSE);
}

Padawan “Um, just like before, comment out updateObject2(),” Padawan “Make updateObject3() and call it from ``draw()

.

.."

Master "explain"

* ```final float START_Y = -600;```<br> The Y coordinate of the object at the start.
* ```final float SPEED = 200;```<br> Speed (seconds). This object advances 200 per second.
* ```float ms = millis();```<br>A function that gets the number of milliseconds elapsed since the saver was activated.
* ```float dy = ms * SPEED / 1000;```<br> The distance moved from the time the saver was activated.
* ```float y = START_Y + dy;```<br> The current position (Y coordinate) of the object.
* ```noFill();```<br> Specifies not to fill the object.
* ```vertex(-30, y, -30);```<br> By specifying the variable y for the Y coordinate value in the vertex specification, the object will move from top to bottom only 200 per second over time. I am trying to proceed (only 0.2 per millisecond).
<img width="500" alt="updateObject3.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/ab35b6b7-32d3-4b6e-db06-3fc2c6310f93.gif">

Padawan "It just moves from top to bottom, it doesn't change..."

Master "Try different frame rates"
Master "You will see that there is no difference in the amount of movement per hour."

Master “Don't forget to set the frame rate back at the end!”


#### **`Learning.pde`**
```java

  ...
  frameRate(24);
  ...

23:40 | Put in a circular orbit

Master “I’ll do this next time!”

Learning.pde


...
void draw() {
  ...
  //updateObject3(); // <===== comment out =====
  updateObject4(); // <===== postscript =====
  
}

...

// (4) Rectangle drawing (circular motion) // <===== Add postscript ↓ =====
void updateObject4() {
  final float R = 500; // revolution radius
  final float SPEED = 60; // angle (Degree)/second

  float ms = millis(); // milliseconds since the program started
  float dd = ms * SPEED / 1000; // Movement angle (Degree)
  float dr = radians(dd); // moving angle (Radian)
  float x = R * cos(dr); // X coordinate
  float y = R * sin(dr); // Y coordinate
  
  fill(255, 255, 255); // Fill color (R,G,B)stroke(0, 0, 255); // border color (R,G,B)
  strokeWeight(2); // border thickness

  beginShape();
  vertex(x-30, y, -30);
  vertex(x+30, y, -30);
  vertex(x+30, y, 30);
  vertex(x-30, y, 30);
  endShape(CLOSE);
}

Master “explain”

  • final float R = 300;
    This is the revolution radius of the object.
  • final float SPEED = 60;
    Degree that advances in 1 second.
  • float ms = millis();
    This is good! It is the number of milliseconds that have elapsed since the start of the saver.
  • float dd = ms * SPEED / 1000;
    The angle advanced from the start of the saver (Degree).

Master “The rest is trigonometric calculation…”

Padawan “Get it!!!” Padawan (I wasn’t good at trigonometric functions…) Padawan (I thought I would do my best if it became a square function, but) Padawan (Initiate didn’t tell me…)

Radian (radian method)

Master “Then it’s easy to review” Master “First of all, it’s from “Radian”.”

Master “Radian is a unit of angle” Master “The length of the arc on the circumference” Master “The angle when the radius of the circle is equal to 1 radian” image.png Master “In terms of degrees, it’s about 57.3 degrees.”

Padawan “Why is that odd number!” Padawan “1 radian = 60 degrees is fine!”

Master “Well, don’t say that…” Master “In the Galactic Unit System (Footnote: International Unit System SI)” Master “It’s a fixed unit”

Master: Multiply this by about 3.14 and you get 180°! Padawan “It’s a pie!” image.png Padawan “But I can’t remember this!” Master “You don’t have to remember this to use Processing.”

Master “But the Processing method is” Master “Because it is necessary to specify the angle in radians” Master “You need to use radians() to convert degrees to radians” Master “Remember this only”

Padawan “I see…” Padawan “The angle is anyway” Padawan “You should pass it wrapped in radians()!”

Master “Also defined constants related to special angles”

Constant meaning frequency
(degree)
radian
(radian)
PI π 180° 3.1415927
TWO_PI 360° 6.2831855
HALF_PI 1/2π 90° 1.5707964
QUARTER_PI 1/4π 45° 0.7853982

Master “It’s a good idea to keep this in mind”

Padawan “Oh! Just this time,”

Sine (sin), cosine (cos), and then tangent (tan)

Master “Next is trigonometric function”

Master “Do you remember the sine, cosine and tangent?” Padawan “Oh! I’ve heard of that name!”

Master “It’s a trigonometric function” Padawan (Hmm?

Master “Aside from what you learned, in general CG” Master “from a certain length and a certain angle'' Master "often used when seeking a other length`”

Master “I only use sine and cosine right now,” Master “Tangente is often used, so let’s explain it together.”

Master “For the time being, these are all,” Master “To find another length from thelengthandangle```’’ Master “Remember when used”

Master “First, it’s a sine. It’s a right triangle.” Master “It can be used to find the "height" from the ```hypotenum length

.

"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/05a8c847-b0a2-c08e-067b-44c5cebb09a2.png)

Master "Next is the cosine. This is a right triangle."
Master "It can be used to find the `"width"` from the ```hypothes length```''
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/0e0992e9-3adc-f9c2-c57b-aec4191c21e2.png)

Master "Next is the tangent, but this has two uses."

Master "The first is a right triangle"
Master "can be used to get ```width`` from ```height```,"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/d20226fa-9d57-88c3-89c7-f775e0bf329e.png)

Master "The other is a right triangle"
Master "It can be used to find `"height"` from ```width```"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/8bcf370c-2e5e-b3eb-c778-8ba873c15479.png)

Padawan "Well..."
Padawan "When you want to know straight from an angle"
Padawan "By sign and cosine..."

Padawan "When you want to know straight from straight"
Padawan "By Tangent..."

Master "And then back to the sign and cosine..."

Master "Slightly broaden your thinking,"
Master "Center at origin (0, 0) on XY plane"
Paster "Let's consider a circle with radius R"

Master "Look at the ```upper left graph ``` in the figure below."
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/6a8efb49-f086-2e38-afc7-d90aed299717.png)

Master "If you apply the sine and cosine from a while ago"
Master “X and Y coordinates of point P on the circumference”
Master "Do you know you can ask?"

Padawan "Ah! Right triangle!!!!"
Padawan "It's the same as the previous figure"

Master "Yes, and, as the remaining three figures show,"
Master “This is over 90°, over 180° and over 270°”
Master "You can ask the same way"

Padawan "What if I get bigger than 360 degrees?"

Master: "You can do the same in that case!"
Master "For example, 375° gives the same result as 15°."

Padawan "Oh, that's right! You can get 15 by subtracting 360!"

Master “However,”
Master "Please note that the angle is `"counterclockwise"`"

Padawan "Is the reverse rotation a minus?"
Master "Yes. If it's clockwise, it's negative."

Master "If you understand this far,"

Master "Now, for objects that make circular motions,"
Master "Keep in mind that you are trying to know the coordinates after a certain period of time"
Master "Let's look at the saver again."

Master "Don't forget that the Y-axis is facing down"
Master "If down, the positive direction of rotation will be `"clockwise```".

Master "Okay, don't think with your head, use your body to capture the space!"

* ```float dr = radians(dd);```<br>The angle dd (degree: degree method) after a certain time from the start of the saver is converted to dr (radian: radian method).
* ```float x = R * cos(dr);```<br> The X coordinate at the angle dr (radian) obtained above is cos(dr) multiplied by the radius R.
* ```float y = R * sin(dr);```<br> Similarly, the Y coordinate is cos(dr) times the radius R.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/b882be2e-8136-8d53-3ad5-272550765945.png)

<img width="500" alt="updateObject4.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/e740a45d-c70c-ae88-7dc1-b9701689c847.gif">

Padawan "Oh, I understand!"
Padawan "I don't mind..."

[](----------------------------------------------------------------------------------)
## Midnight|3D object and coordinate transformation

[](----------------------------------------------------------------------------------)
### 00:00:00 | 3D object (still)
Master "Next is the 3D object"
Master "Enter the following"


#### **`Learning.pde`**
```java

...
void draw() {
  ...
  //updateObject4(); // <===== comment out =====
  updateObject5(); // <===== postscript =====
}

...// (5) Draw 3D object (still) // <===== Add postscript ↓ =====
void updateObject5() {
  noFill(); // no fill
  strokeWeight(2); // border thickness

  stroke(255, 0, 255); // border color (R,G,B)
  box(150);
}

Master “explain”

  • box(150);
    This draws a cube with a side length of 150.
    If you pass three parameters, you can specify the width, height, and depth to make a rectangular parallelepiped.
    No position (coordinates) is specified. It’s drawn at the origin (0,0,0).

Padawan “Oh! It’s too easy to cry.” Padawan “It’s much easier than specifying vertex with Vertex!”

Padawan (I wish I could have told you ahead of time…) Padawan (Why master always starts because it’s difficult?)

updateObject5.gif

Master “Next…” Master “Add the following two lines”

Learning.pde


// (5) Draw 3D object (still)
void updateObject5() {
  noFill(); // no fill
  strokeWeight(2); // border thickness

  stroke(255, 0, 255); // border color (R,G,B)
  box(150);

  stroke(0, 255, 255); // border color (R,G,B) // <===== postscript =====
  sphere(60); // <===== Addendum =====
}

Master “explain”

  • sphere(60);
    This depicts a sphere with a radius of 60.
    No position (coordinates) is specified. It’s drawn at the origin (0,0,0).

updateObject5-2.gif

Padawan “That????? with noFill()” Padawan “It’s painted even though it’s painted “none”…”

Master “This is not filled” Master “It’s made of fine mesh”

Master “I’ll add setCameraOrbitalRadius(150); to setup()”

Learning.pde


void setup() {
  size(1200, 800, P3D);

  setCameraOrbitalRadius(150); // <===== Addendum =====

  frameRate(24);
}
...

Master “This is a utility feature I copied and copied to the DevUtils tab.” Master “It changes the radius of the camera’s orbit.”

Master “If you set the value to 150, you can get very close to the origin (0,0,0).” image.png Padawan “Oh, really. I’m crazy.” Master “Don’t forget to comment out when you’re done.”

Learning.pde


void setup() {
  size(1200, 800, P3D);
  //setCameraOrbitalRadius(150);
  frameRate(24);
}
...

Master “By the way, let me explain the utility’s function here.” Master “Now, just take a quick look.”

Master “The other thing is that the objects are hard to see during development.” Master “I wish I could rotate the camera more slowly.” Master “The cube is in the way” Master “On the contrary, if you want to increase the number of cubes” Master: “Let’s see the details at that time.”

Master “And again, this is not a standard feature of Processing.” Master “Please be careful about that”


Utility description

This utility was created for this article and is intended to save you the initial effort of 3DCG production in Processing. It has a function to display the development room (cubes and coordinate axes) and automatically control the camera.


Method list

| Method name | Where to use | Overview | |:–|:–|:–| |setupDevRoom()|setup()| Setting up the development room. Set the size and number of cubes and various camera conditions. | |setDevRoomAutoCameraEnabled()|setup()| Sets whether to automatically control the camera. | |setDevRoomVisible()|setup()| Sets whether to display the cube or the axes.
This method is called when the screen is clicked during operation, and you can toggle the display on/off. | |setCameraOrbitalRadius()|setup()| Sets the orbital radius (ZX plane) of the camera that revolves around the origin (0,0,0). | |setCameraMaxDepth()|setup()| Sets the maximum vertical depth (Y-axis direction) of the camera position. | |setCameraOrbitalPeriod()|setup()| Sets the revolution period (seconds) of the camera. | |updateDevRoom()|draw()| Draw a development room (cube or coordinate axes). |


Method details

(1) void setupDevRoom(float len, int hnum, int vnum) Development room setup (camera settings are automatically adjusted). It is supposed to be called from within the setup() method. If this method is not called, the default value will be used.

Parameter Default value Description  
len 200 The length of one side of the cube placed in the room.
len==0: The cube, floor, and coordinate axes are not displayed.
len< 0: same as abs(len).
hnum 4 Number of cubes in the horizontal direction (X, Z directions).
hnum<=0: The cube is not displayed, but if vnum>0, the coordinate axis is displayed.
 
vnum 2 Number of cubes in the vertical direction (Y-axis direction).
vnum<=0: The cube is not displayed, but if hnum>0, the axes and floor are displayed.
 

(2) void setupDevRoom(float len, int hnum, int vnum, float radius, float depth, int period) Development room setup (including camera settings). It is supposed to be called from within the setup() method. If this method is not called, the default value will be used.

Parameter Default value Description  
len 200 The length of one side of the cube placed in the room.
len==0: The cube, floor, and coordinate axes are not displayed.
len< 0: same as abs(len).
hnum 4 Number of cubes in the horizontal direction (X, Z directions).
hnum<=0: The cube is not displayed, but if vnum>0, the coordinate axis is displayed.
 
vnum 2 Number of cubes in the vertical direction (Y-axis direction).
vnum<=0: The cube is not displayed, but if hnum>0, the axes and floor are displayed.
 
radius 1.25 times the room width The radius of the orbit of the camera that revolves around the origin (0,0,0) (ZX plane).
radius==0.0: Same as radius==0.1.
radius< 0.0: Operation is undefined. Probably the same as abs(radius).
 
depth Half of room height Maximum vertical depth (Y-axis direction) of camera position.
Moves from -depth to +depth
depth==0.0: No vertical movement (movement in Y-axis direction).
depth< 0.0: The vertical direction is reversed (the phase is shifted by half a cycle).
 
period 16 seconds The revolution period of the camera (seconds).
period==0: same as period==1.
period< 0: The camera revolves in the opposite direction
 

(3) void setDevRoomAutoCameraEnabled(boolean isEnabled) Specify whether to automatically control the camera. It is supposed to be called from within the setup() method.

Parameter Default value Description
isEnabled true true: Automatically controls the camera.
false: Does not automatically control the camera.

(4) void setDevRoomVisible(boolean isVisible) Specifies whether to show the development room (cubes, axes, etc.). It is supposed to be called from within the setup() method, but it may be called from within draw().

Parameter Default value Description
isVisible true true: Display the development room.
false: The development room is not displayed.

(5) void setCameraOrbitalRadius(float radius) Set the trajectory radius (ZX plane) of the camera that revolves around the origin (0,0,0). It is supposed to be called from within the setup() method.

Parameter Default value Description
radius 1.25 times the room width The radius of the orbit of the camera that revolves around the origin (0,0,0) (ZX plane).
radius==0.0: Same as radius==0.1.
radius< 0.0: Operation is undefined. Probably the same as abs(radius).

(6) void setCameraMaxDepth(float depth) Sets the maximum vertical depth (Y-axis direction) of the camera position.It is supposed to be called from within the setup() method.

Parameter Default value Description
depth Half of room height Maximum vertical depth (Y-axis direction) of camera position.
-Move in the range of -depth to +depth
depth==0.0: No vertical movement (movement in Y-axis direction).
depth< 0.0: The direction of vertical movement is reversed (the phase is shifted by half a cycle).

(7) void setCameraOrbitalPeriod(int period) Set the revolution period of the camera. It is supposed to be called from within the setup() method.

Parameter Default value Description
period 16 seconds The revolution period of the camera (seconds).
period==0: same as period==1.
period< 0: The camera revolves in the opposite direction

(8) void updateDevRoom() Update the development room. Update the camera position and draw the development room. It is supposed to be called from within the draw() method.


0 hour 20 minutes-|Constant linear motion

Padawan “3D object, it’s too easy to make me laugh,”

Padawan “But…” Padawan “Because the position cannot be specified” Padawan “I can’t move it????”

Padawan “It’s too sad and sad…”

Master “Gahhaha! Don’t worry because you can move it properly.” Padawan “Eh? Really?”

Master “Enter the following!”

Learning.pde


...
void draw() {
  ...
  //updateObject5(); // <===== comment out =====
  updateObject6(); // <===== postscript =====
}

...

// (6) 3D object (constant linear motion with coordinate conversion) // <===== Addition after this ↓ =====
// Realize movement similar to (3) by coordinate conversion
void drawEarth() {
  fill(224, 224, 255); // Fill color (R,G,B)
  stroke(0, 0, 255); // border color (R,G,B)
  strokeWeight(2); // border thickness
  
  box(80);
}
void updateObject6() {
  final float START = -600; // start position
  final float SPEED = 200; // speed per second

  float ms = millis(); // milliseconds since the program started
  float d = ms * SPEED / 1000; // distance traveled
  float p = START + d; // current position

  pushMatrix();
  translate(p, -100, 50);
  drawEarth();
  popMatrix();
}

Master “explain”

  • void drawEarth() {...}
    First, divide the drawing of the object into this method.
    This time, name it drawEarth(), assuming it is the earth.
    fill(), stroke(), strokeWeight(), ```box(80)

    called in the method Is as I have learned so far.

    ```

  • void updateObject6() {...}
    • Constant constant linear motion that I wrote the constants START, SPEED, variables ms, d, p is almost the same as
    • pushMatrix();
      Push the current matrix onto the stack and declare the start of coordinate transformation.
    • translate(p, -100, 50);
      Translate from the current coordinate (0, 0, 0) to the position (p,-100, 50).
      Since the variable p is specified only for the X coordinate and the constant literal is specified for the rest, you can see that only the X coordinate changes over time, that is, it moves in parallel with the X axis.
    • drawEarth();
      Draw an object that looks like the earth.
    • popMatrix();
      Pop the current matrix from the stack and declare the end of the coordinate transformation.

updateObject6.gif

Padawan “Oh! It works! It works!” Padawan “The earth is a blue, square star, right?”

Master “…”

Local coordinate system

Padawan “But well” Padawan `````translate()->drawEarth()``` is the order,

Padawan “Um…” Padawan “When I move, there is no one I drew.” Padawan “Why does the guy you draw move????”

Master “I noticed good points!” Master “Let’s go over a little more about ```translate()

.

"

Master "This isn't moving what you draw,"
Master "I'm moving the "stage" I draw."

Padawan "He???"

Master "Easy to understand, let's think only on the ZX plane"
Master "Look at the next figure!"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/7d7c236a-a2ab-236d-44ad-908a3b99c2a8.png)
Master "I used to draw directly in the world coordinate system, but..."
Master "There is actually a local coordinate system."

Master "Well, think of it as a coordinate system dedicated to an object."

Master "The left side of the figure is the world I've drawn so far."
Master "Two coordinate axes may overlap?"
Master "The larger one is the world coordinate system, and the smaller one is the local coordinate system."

Master "And..."
Master "The right side of the figure is the world I am drawing this time"

Padawan "Ah! The batten is out of alignment..."
Master "Yes"

Master "It's the ```translate()``` that shifts it."

Master "that is, by ```translate()```"
Master "It means that the local coordinate system is moving in parallel."
Master "And the drawing of objects is done in the local coordinate system."

Padawan "Really"
Padawan "If you look at only the small batten, both right and left"
Padawan "The square drawn with ```vertex()``` is in the same place"

Master "And, the local coordinate system was just before"
Master "I said it was a coordinate system dedicated to an object,"
Master "For example, after you translate() and draw three objects"
Master "All three objects have the same local coordinate system."

Master "Also, run translate() twice in a row."
Master "The first translate() moves the local coordinate system,"
Master "In the second call,"
Master "I will move further from the place I moved the first time."
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/dc06447f-b60a-628e-7f07-b787e2866e43.png)

Master "So you have to declare the beginning and end of the stage to draw,"
Master "It is the ```pushMatrix()``` and ```popMatrix()``` that are responsible for that."

[](----------------------------------------------------------------------------------)
#### Matrix and stack

Master "Matrix is a local coordinate system and a world coordinate system for the time being"
Master "You can think of it as the "current state" of the relationship."

Master "And there is only one in the saver"
Master "I'm sharing one of them from all over the saver."

Master "So, for example, there is a method."
Master "Once I change the Matrix to draw"
Master "I have to put it back after I've finished using it."
Master "I'm having trouble with the next method"

Master "But every time"
Master ```translate(5, 0, 10)``` followed by ```translate(-5, 0, -10)```''
Master "Isn't it hard if I wrote back something like that?"

Master "So Processing shows the current Matrix state"
Master "It has a function to evacuate to the stack."

Master "Have you learned the stack?"
Padawan "Yeah. First in, first out!"
Padawan "The dish-washing guy!!!"

Master "Yes. I mean..."

Master "pushMatrix() pushes the current Matrix onto the stack,"
Master “popMatrix() takes the last saved Matrix off the stack”
Master "Return to the current Matrix"

Master "The following figure shows the coordinate conversion and drawing."
Master "It shows how the state of the stack and Matrix changes."

Master “For clarity only the ZX plane is targeted.”
Master "The time sequence runs from left to right"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/9a5f276f-4213-bf50-131a-c5323eabeec7.png)
Master "I'll tap this image into my head."

[](----------------------------------------------------------------------------------)### 0:40 | Rotation

Master "Enter the following!"


#### **`Learning.pde`**
```java

...
void draw() {
  ...
  //updateObject6(); // <===== comment out =====
  updateObject7(); // <===== postscript =====
}

...

// (7) 3D object (rotation by coordinate conversion) // <===== Addition after this ↓ =====
void updateObject7() {
  final float SPEED = 60; // Degree/second

  float ms = millis(); // milliseconds since the program started
  float dd = ms * SPEED / 1000; // Movement angle (Degree)
  float dr = radians(dd); // moving angle (Radian)

  pushMatrix();
  rotateY(dr);
  drawEarth();
  popMatrix();
}

Master “explain”

  • final float SPEED = 60;
    This is a program that rotates an object, but the angle to move in 1 second is specified here.
  • float dd = ms * SPEED / 1000;
    Angle (degrees) moved since the saver was activated.
  • float dr = radians(dd);
    The angle moved from the start of the saver is corrected to radians.
  • pushMatrix();
    As I told you, the current matrix is being saved.
  • rotateY(dr);
    Rotating the local coordinate system about the Y axis (along the ZX plane) by the angle dr.
    In addition to rotateY(), there are also roateX() and ```rotateZ()

    .

    ```

  • drawEarth();
    The previous drawEarth() is called. You can use objects like this many times with coordinate transformations.
  • popMatrix();
    As I told you, we are restoring the current matrix.

updateObject7.gif

Padawan “Hmm??” Padawan “While it’s spinning,”

Padawan “Can I use trigonometric functions?”

Master “Oh no.”

Padawan “Get it, is there such an easy thing!!!” Padawan (I wish I could have told you ahead of time…) Padawan (Why Master always starts because it’s difficult?)

Master “In addition, the state transitions of the stack and matrix are as follows.” image.png

1:00 am | combine conversions

1:00 pm | Revolution (constant circular motion)

Master “Let’s try a transformation that combines translation and rotation” Master “Enter the following!”

Learning.pde


...
void draw() {
  ...
  //updateObject7(); // <===== comment out =====
  updateObject8(); // <===== postscript =====
}

...

// (8) 3D object (revolved by coordinate transformation) // <===== Added after this ↓ =====
// Realize movement similar to (4) by coordinate conversion
void updateObject8() {
  final float R = 300; // revolution radius
  final float SPEED = 150; // angle (Degree)/second

  float ms = millis(); // milliseconds since the program started
  float dd = ms * SPEED / 1000; // Movement angle (Degree)
  float dr = radians(dd); // moving angle (Radian)

  pushMatrix();
  rotateY(dr);
  translate(R, 0, 0);
  drawEarth();
  popMatrix();
}

Master “explain”

  • final float R = 300;
    This saver orbits the object around the origin (0,0,0) of the world coordinate system, where R is the radius of its orbit. It is.
  • SPEEDmsdddr
    No more explanation needed. dr is the rotation angle from the start of the saver.
  • pushMatrix()roateY(dr)drawEarth()popMatrix()
    This is the same as before.
  • translate(R, 0, 0);
    This is the difference. After rotateY(dr), it is translated by the radius R in the X-axis direction.

Padawan “Oh, it’s turned! It’s turned!” updateObject8.gif

Master “Stack and matrix state transitions are as follows.” image.png

1 hour 20 minutes | Spinning at a distance

Master “If so, try changing the order of conversion in the future.” Master “Enter the following!”

Learning.pde


...
void draw() {
  ...
  //updateObject8(); // <===== comment out =====
  updateObject9(); // <===== postscript =====
}

...

// (9) 3D object (rotate at a distant position by coordinate conversion) // <===== Added after this ↓ =====
// Notice the difference with (8)
void updateObject9() {
  final float R = 300; // revolution radius
  final float SPEED = 150; // Degree/second

  float ms = millis(); // milliseconds since the program started
  float dd = ms * SPEED / 1000; // Movement angle (Degree)
  float dr = radians(dd); // moving angle (Radian)

  pushMatrix();
  translate(R, 0, 0); // just swapped the order
  rotateY(dr); // just changed the order
  drawEarth();
  popMatrix();
}

Master “This is needless to say.” Master “I just changed the order of rotateY() and ```translate()

.

"

Padawan "Oh, I'm spinning around while stopped..."

<img width="500" alt="updateObject9.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/b29d05df-761b-e4c5-c451-6288dce6a5dd.gif">

Master "The camera is also rotating, so you may not be able to understand the rotation movement well."
Master "In that case, use the utility function setCameraOrbitalPeriod()"
Master "It would be good to extend the revolution cycle of the camera."


#### **`Learning.pde`**
```java

...
void setup() {
  size(1200, 800, P3D);
  //setCameraOrbitalRadius(150);

  setCameraOrbitalPeriod(120); // <===== postscript =====

  frameRate(24);
}
...

Master “Don’t forget to comment out when you’re done.”

Learning.pde


...
void setup() {
  size(1200, 800, P3D);
  //setCameraOrbitalRadius(150);
  //setCameraOrbitalPeriod(120);
  frameRate(24);
}
...

Master “Stack and matrix state transitions are as follows.” image.png

Master “Be careful because the behavior changes greatly in the order of coordinate transformation.”

1 hour 40 minutes | Revolve around the revolution body

Master “Then, I’ll move objects that look like the sun, the earth, and the moon.” Master “Enter the following!”

Learning.pde


...
void draw() {
  ...
  //updateObject9(); // <===== comment out =====
  updateObject10(); // <===== postscript =====
}

...

// (10) 3D object (revolves around the revolution body by coordinate transformation) // <===== Add postscript ↓ =====
void drawSun() {
  noFill(); // no fill
  stroke(255, 0, 0); // border color (R,G,B)
  strokeWeight(2); // border thickness
  sphere(60);
}
void drawMoon() {
  fill(255, 255, 128); // Fill color (R,G,B)
  stroke(255, 0, 0); // border color (R,G,B)
  strokeWeight(2); // border thickness
  beginShape();
  vertex(-20, -20, 0);
  vertex( 20, -20, 0);
  vertex( 20, 20, 0);
  vertex(-20, 20, 0);
  endShape(CLOSE);
}
void updateObject10() {
  float ms = millis(); // milliseconds since the program started

  final float E_R = 400; // Earth's revolution radius
  final float E_SPEED = 80; // Earth angle (Degree)/second
  float edd = ms * E_SPEED / 1000; // Earth movement angle (Degree)
  float edr = radians(edd); // Earth movement angle (Radian)

  final float M_R = 100; // Moon radius
  final float M_SPEED = 200; // Moon angle (Degree)/second
  float mdd = ms * M_SPEED / 1000; // Moon movement angle (Degree)
  float mdr = radians(mdd); // Moon movement angle (Radian)

  drawSun();

  pushMatrix();
  {
    rotateY(edr);
    translate(E_R, 0, 0);
    drawEarth();

    pushMatrix();
    {
      rotateY(mdr);
      translate(M_R, 0, 0);
      drawMoon();
    }
    popMatrix();

  }
  popMatrix();
}

Master “explain”

  • void drawSun() {…}
    • This is the same as drawEarth(), which draws the earth object created earlier, but draws an object that imitates the sun. The sun is a “sphere”.
  • void drawMoon() {…}
    • Similarly, it is a method to draw an object that imitates the moon. The moon is a two-dimensional object rectangle.
  • void updateObject10() {…}
    • E_RE_SPEED```````edd``````edr```
      This defines the orbital radius and orbital speed of the earth.
      Since I’ve written it so many times, I don’t need a detailed explanation.
    • M_RM_SPEEDmddmdr
      This defines the orbital radius and orbital speed of the moon.
      Again, no explanation is necessary.
    • Here is the main subject.
    • drawSun(); Draw the “Sun” directly in the world coordinate system.
    • pushMatrix(); Save the current matrix for drawing the “Earth”.
    • {
      • #### **` Rotate the local coordinate system.`**
        
      • ```translate(E_R, 0, 0);

        Move the local coordinate system outward.

        ```

      • drawEarth(); Draw the “Earth”.
      • pushMatrix(); Save the matrix for drawing the “moon”.
      • {
        • #### **` Rotate the local coordinate system.`**
          
        • ```translate(M_R, 0, 0);

          Move the local coordinate system outward.

          ```

        • drawMoon(); Draw the “moon”.
      • }
      • #### **` Restore the matrix.`**
        
    • }
    • #### **` Restore the matrix.`**
      

Master “The important thing here is the drawing of the moon.” Master “After doing coordinate transformations to draw the Earth” Master “At the time of drawing the moon” Master “The matrix has not been restored by popMatrix() yet”

Master “So the local coordinate system of the moon is” Master “For rotation and translation for the earth” Master “The rotation and translation for the moon are combined”

Note that here, in order to make it easy to understand that pushMatrix() is used twice, it is enclosed in a block ```{}

and indented, but this is not necessary.



Padawan "Oh! I'm spinning around!"

<img width="500" alt="updateObject10.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/27797df7-4b8e-4792-c5c0-d7caec9cd01e.gif">

Master "This is the state transition of the stack and matrix of this saver."
Master "This time, it starts with the sun already drawn"

Master "First of all, draw the earth and execute pushmatrix() for the moon."
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/41b7c329-ecba-c1a7-1e8b-e9ce1e306d37.png)

Master "And from drawing the moon to the end"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/ff89d7f4-ac39-3dda-8583-b8c3b5e59e9e.png)

Master "Can you understand?"

Padawan "Yeah! Easy victory!"
Padawan "This is..."

Padawan "While turning around the corner with a pod racer,"
Padawan "You look like you're spinning!"

Master (Well...)

Master (well, I think you understand it)
Master (although some anxiety remains...)

[](----------------------------------------------------------------------------------)
## 2:00 am | Texture mapping and camera
### 2 00: 00 | The leading role appears

Master "I can. It's such a time..."

Master "Padawan"
Master "By morning it may be difficult to implement in 3D objects"

Master "So, to 2D objects"
Master "Learn how to do texture mapping"

Padawan "Texture????"

Master "Yes. This is called "Billboard.""
Master "Old technique that was once used well"

Master "That is, in the three-dimensional space"
Master "Place objects like "signboards""
Master "It's pseudo 3D"

Padawan "Ah, does that mean "writing"?"

Padawan "That's why I was in the initiation era"
Padawan "I did it in a play at a fun party!"

Padawan "I got the dream of "grass" at that time!"

Padawan "I don't know why,"
Padawan "The role of "grass" that was very popular in the desert Tatooine"
Padawan "It wasn't that popular in Coruscant, right?"

Padawan "That's why I won the role easily."
Padawan "I was happy"

Then Gopher showed his face there.

Padawan "What the heck! Gopher!"
Padawan "It's too late!!!"

Padawan "You're in trouble!"
Padawan "Because it's the protagonist"
Padawan "You have to be sure to keep the entry time tight!"

Master "Well, don't say that."
Master "Gopher is busy too."

Master "He just came to the right place"
Master "Gopher, can you take a portrait?"

When I said so, the master released the shutter of Holocam.

Master "Because I output it in PNG mode,"
Master "to sketch folder"
Master "Create a directory named ```data```,"
Master "copy this there"
Master "Save it as gopher.png"
<img width="300" alt="gopher.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/9709b057-c6ce-9aa0-e713-12d0660eff7c.png">

> [Character "Gopher-kun"](https://blog.golang.org/gopher)isbasedonGoMascotdesignedby[RenéeFrench](http://reneefrench.blogspot.com/).Thelicenseis[CCBY3.0](https://creativecommons.org/licenses/by/3.0/deed.ja).

Master "Sketch folder is"
Master "Can be opened with [Sketch] [Open sketch folder]"<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/3b8c14f1-35d9-1830-1c68-09151e17518f.png">

any-dir Learning ├── Learning.pde ├── DevUtils.pde └── data <— create └── gopher.png <— copy


Master "After copying the PNG, type:"


#### **`Learning.pde`**
```java

...
void draw() {
  ...
  //updateObject10(); // <===== comment out =====
  updateObject11(); // <===== postscript =====
}

...

// (11) Texture mapping (still) // <===== Add postscript ↓ =====
void drawGopher() {
  PImage img = loadImage("gopher.png");
  int h = img.height;
  int w = img.width;

  noStroke(); // do not draw the border

  beginShape();
  texture(img);
  vertex(-150, -150, 0, 0, 0);
  vertex( 150, -150, 0, w, 0);
  vertex( 150, 150, 0, w, h);
  vertex(-150, 150, 0, 0, h);
  endShape();
}
void updateObject11() {
  drawGopher();
}

Master “explain”

  • void drawGopher() {...}
    Gopher This is the method to draw you.
    • PImage img = loadImage("gopher.png");
      The image file placed in the data directory under the sketch folder can be loaded like this.
    • int h = img.height;
    • int w = img.width;
      The size of the image can be easily extracted like this.
    • noStroke();
      After that, we attach the texture to the rectangle, but this is done because the border is unnecessary.
    • beginShape();
      Declaration to start drawing the shape.
    • texture(img);
      Here, the image of the texture attached to this shape is specified.
    • vertex();
      It specifies 4 vertices, but it has about 2 more parameters than the previous usage. Details will be described later.
    • endShape();
      The end of the shape is declared, but since the border is not displayed, the parameter CLOSE is not necessary.
  • It just calls ```void updateObject11() {…}``
    drawGopher().

Master “The way to specify vertices is as follows.” image.png Master “If the shape and size don’t match” Master “The image is scaled to fit the shape side” Master “And when the shape side and the image side are not similar” Master “Be careful because the aspect ratio collapses.”

Master “When you display an image with transparent parts in Processing” Master “Despite being transparent” Master “The object behind it may be hidden.” Master “In that case, add the following line to the setup() method.”

Learning.pde


...
void setup() {
  size(1200, 800, P3D);

  hint(ENABLE_DEPTH_SORT); // <===== Addendum =====

  //setCameraOrbitalRadius(150);
  //setCameraOrbitalPeriod(120);
  frameRate(24);
}
...

Padawan “Ah, Petanko Gopher (laughs)” updateObject11.gif

Master “Let’s fix this method so that it can be sized”

Learning.pde


...
// (11) Texture mapping (still) // <===== Modify after this ↓ =====
void drawGopher(float len) {// <===== modified =====
  PImage img = loadImage("gopher.png");
  int h = img.height;
  int w = img.width;

  noStroke(); // do not draw the border

  beginShape();
  texture(img);
  vertex(-len/2, -len/2, 0, 0, 0); // <===== modification =====
  vertex( len/2, -len/2, 0, w, 0); // <===== modification =====
  vertex( len/2, len/2, 0, w, h); // <===== modification =====
  vertex(-len/2, len/2, 0, 0, h); // <===== modification =====
  endShape();
}
void updateObject11() {
  drawGopher(100); // <===== modified =====
}

Master “To explain briefly”

  • void drawGopher(int len)
    You can specify the length of one side of the rectangle (square) to be displayed.
  • vertex(-len/2, -len/2, 0, 0, 0); and later
    The len parameter is used to specify the vertex.
  • drawGopher(100);
    Specifies that one side of the rectangle is 100.

Padawan “Oh, the pets have become smaller (laughs)”

2:10 | Revolve around the revolution body

Master “Enter:”

Learning.pde


...
void draw() {
  ...
  //updateObject11(); // <===== comment out =====
  updateObject12(); // <===== postscript =====
}

...

// (12) Texture mapping (revolve around the orbital body by coordinate conversion) // <===== Added after this ↓ =====
// movement is almost the same as (10)
void updateObject12() {
  float ms = millis(); // milliseconds since the program started

  final float S_LEN = 200; // length of one side of Sun Gopher

  final float E_LEN = 100; // One side of the Earth Gopher
  final float E_R = 400; // Revolution radius of Earth Gopher
  final float E_SPEED = 80; // Earth Gopher angle (Degree)/sec
  float edd = ms * E_SPEED / 1000; // Earth Gopher movement angle (Degree)
  float edr = radians(edd); // Earth Gopher movement angle (Radian)

  final float M_LEN = 50; // length of one side of month Gopher
  final float M_R = 100; // Moon Gopher revolution radius
  final float M_SPEED = 200; // Moon Gopher angle (Degree)/second
  float mdd = ms * M_SPEED / 1000; // Moon Gopher movement angle (Degree)
  float mdr = radians(mdd); // Moon Gopher movement angle (Radian)

  pushMatrix();
  {
    translate(0, -S_LEN / 2, 0);
    drawGopher(S_LEN); // Sun Gopher
  }
  popMatrix();

  pushMatrix();
  {
    rotateY(edr);
    translate(E_R, 0, 0);

    pushMatrix();
    {
      translate(0, -E_LEN / 2, 0);
      drawGopher(E_LEN); // Earth Gopher
    }
    popMatrix();
    
    pushMatrix();
    {
      rotateY(mdr);translate(M_R, -M_LEN / 2, 0);
      drawGopher(M_LEN); // Month Gopher
    }
    popMatrix();

  }
  popMatrix();
}

Master “This is the last time” Master “A method of orbiting the earth and the moon around the sun” Master “It’s a little rewritten for Gopher”

Master “There is almost no difference…” Master “I’m trying to make each Gopher’s feet touch the ZX plane.”

Master “For this, each Gopher-kun with translate()” Master “Shifted vertically (Y-axis direction) by half the length of one side of the rectangle” Master “But I don’t want that effect on other objects.” Master “I’m saving and restoring the matrix before and after.”

```java: Taiyo Gopher’s vertical translation pushMatrix(); // push matrix { // Coordinate transformation in this block does not affect the position and orientation of the Earth and Moon translate(0, -S_LEN / 2, 0); // Vertically shift half the length of the rectangle side drawGopher(S_LEN); // Sun Gopher } popMatrix(); // return matrix


Master "And about Earth Gopher-kun"
Master “Separates vertical (Y-axis direction) and horizontal (X-axis, Z-axis direction) movement components”
Master "Only rotation and horizontal movement components are intended to affect Moon Gopher."

```java: Earth Gopher's vertical translation
// coordinate transformation for orbiting the earth
// The following two lines (horizontal movement with rotation) affect the position and attitude of the moon
rotateY(edr);
translate(E_R, 0, 0); // do not include vertical translation component

pushMatrix(); // push matrix
{
  // coordinate transformation in this block does not affect the moon
  translate(0, -E_LEN / 2, 0); // vertical translation component
  drawGopher(E_LEN); // Earth Gopher
}
popMatrix(); // return matrix

// After that, coordinate conversion and drawing of the moon

Saying that, Padawan clicked the Run button.

Padawan “Oh!” Padawan “Sugetsu!!!”

Padawan “Inabauer!!!” updateObject12.gif

2 hours 20 minutes|Billboard

Master “But this has a problem.” Padawan “The petanko will come out (laughs)”

Master “Yes. That’s why.” Master “From now on, I’ll make sure it doesn’t come out!” Master “Enter:”

Learning.pde


...
void draw() {
  ...
  //updateObject12(); // <===== comment out =====
  updateObject13(); // <===== postscript =====
}

...

// (13) Billboard (revolve around the revolution body by coordinate conversion) // <===== Add postscript ↓ =====
// movement is almost the same as (12)
void turnToCamera() {// <== +
  PMatrix3D m = (PMatrix3D)g.getMatrix(); // <== +
                                                   // <== +
  //column 0 //column 1 //column 2 // <== +
  m.m00 = 1; m.m01 = 0; m.m02 = 0; // row 0 // <== +
  m.m10 = 0; m.m11 = 1; m.m12 = 0; // row 1 // <== +
  m.m20 = 0; m.m21 = 0; m.m22 = 1; // row 2 // <== +
                                                   // <== +
  resetMatrix(); // <== +
  applyMatrix(m); // <== +
} // <== +
void updateObject13() {// <== +
  float ms = millis(); // milliseconds since the program started

  final float S_LEN = 200; // length of one side of Sun Gopher

  final float E_LEN = 100; // One side of the Earth Gopher
  final float E_R = 400; // Revolution radius of Earth Gopher
  final float E_SPEED = 80; // Earth Gopher angle (Degree)/sec
  float edd = ms * E_SPEED / 1000; // Earth Gopher movement angle (Degree)
  float edr = radians(edd); // Earth Gopher movement angle (Radian)

  final float M_LEN = 50; // Length of one side of month Gopher
  final float M_R = 100; // Moon Gopher revolution radius
  final float M_SPEED = 200; // Moon Gopher angle (Degree)/second
  float mdd = ms * M_SPEED / 1000; // Moon Gopher movement angle (Degree)
  float mdr = radians(mdd); // Moon Gopher movement angle (Radian)

  pushMatrix();
  {
    translate(0, -S_LEN / 2, 0);
    turnToCamera(); // <== +
    drawGopher(S_LEN); // Sun Gopher
  }
  popMatrix();

  pushMatrix();
  {
    rotateY(edr);
    translate(E_R, 0, 0);

    pushMatrix();
    {
      translate(0, -E_LEN / 2, 0);
      turnToCamera(); // <== +
      drawGopher(E_LEN); // Earth Gopher
    }
    popMatrix();
    
    pushMatrix();
    {
      rotateY(mdr);
      translate(M_R, -M_LEN / 2, 0);
      turnToCamera(); // <== +
      drawGopher(M_LEN); // Month Gopher
    }
    popMatrix();

  }
  popMatrix();
}

Master “Almost the same as the previous one,” Master “I added // <== + to the added part”

Master “I will explain briefly”

  • void turnToCamera() {...}
    The details of this method will be explained in detail later, but if you call this just before drawing, the texture pasted to the rectangle will always be The attitude can be controlled so as to face the direction.
    But note the following points.
    • This method will not work unless the texture is pasted on the XY plane (or the plane parallel to it) in the local coordinate system.
    • Calling this method will make the texture sticky surface parallel to the screen surface, but this does not mean that the surface is oriented in the strict sense of the camera. .. This should cause some distortion of the objects on the edge of the screen.
  • void updateObject13() {...}
    The ```turnToCamera()

    is called just before drawing each object.

    ```

Master “By the way, if you think only in the ZX plane,” Master “The left side of the figure below is “Strict billboard conversion” Master “The conversion of the method turnToCamera() is on the right” image.png

Padawan “It’s okay if it doesn’t break!” Saying that, Padawan clicked the Run button.

Padawan “Oh, I can’t understand Pettanko!”

updateObject13.gif

Master “Please check if the circular movement is as expected.” Master “If you want to see from a higher or lower perspective” Master “Use the utility to do the following”

Learning.pde


...
void setup() {
  size(1200, 800, P3D);hint(ENABLE_DEPTH_SORT);
  //setCameraOrbitalRadius(150);
  //setCameraOrbitalPeriod(120);

  setCameraMaxDepth(1500); // <===== Added =====

  frameRate(24);
}
...

2:30 | Controlling the camera (stationary)

Master “until now” Master “The camera that was left to the automatic control of the utility.” Master “Teach yourself how to control”

Master “Enter:”

Learning.pde


...
void setup() {
  size(1200, 800, P3D);
  hint(ENABLE_DEPTH_SORT);
  //setCameraOrbitalRadius(150);
  //setCameraOrbitalPeriod(120);
  //setCameraMaxDepth(1500);

  setDevRoomAutoCameraEnabled(false); // <===== Addendum =====

  frameRate(24);
}
...
void draw() {
  background(255, 255, 255);

  updateCamera1(); // <===== postscript =====

  updateDevRoom();

  //updateObject1();
  //updateObject2();
  //updateObject3();
  //updateObject4();
  //updateObject5();
  //updateObject6();
  //updateObject7();
  //updateObject8();
  //updateObject9();
  //updateObject10()
  //updateObject11();
  //updateObject12();
  updateObject13();
}

...

// (14) Camera control (still) // <===== Addition after this ↓ =====
void updateCamera1() {
  float x = 25;
  float y = -70;
  float z = 800;
  camera(x, y, z, 0, 0, 0, 0, 1, 0);
}

Master “explain”

  • void setup() {...}
    • setDevRoomAutoCameraEnabled(false);
      It is a setting to turn off the automatic camera control function of the utility. It must be called before ```frameRate()

      .

      ```

  • void draw() {...}
    • updateCamera1();
      This is a call to the method to control the camera that will be written later. This should be called before drawing each object.
  • void updateCamera1() {...}
    • xyz
      Coordinates that place the viewpoint of the camera.
    • camera(x, y, z, 0, 0, 0, 0, 1, 0);
      Processing method for controlling the camera. It has become.
      • eyeX x coordinate of viewpoint (camera position)
      • eyeY y coordinate of viewpoint (camera position)
      • eyeZ z coordinate of viewpoint (camera position)
      • centerX x coordinate of the line of sight (the coordinate of the point aligned with the center of the screen)
      • centerY y coordinate of the line of sight (the coordinate of the point aligned with the center of the screen)
      • Z coordinate of centerZ line of sight (coordinates of the point aligned with the center of the screen)
      • upX vector indicating the upward direction of the camera (0 or -1 or 1)
      • upY a vector indicating the upward direction of the camera (0 or -1 or 1)
      • upZ vector indicating the upward direction of the camera (0 or -1 or 1)

Master “I mean, this camera setting is” Master “Take the camera to coordinates (25, -75, 800),” Master “Aim the lens at coordinates (0, 0, 0)” Master “Aim the camera head towards the dark side of Y-axis (negative infinity direction)” Master “means”

Master “while glancing at the next figure again” Master “I think of the coordinates of the camera and the image on the screen.” image.png

Padawan clicked the [Execute] button while thinking of the image to be drawn on the screen. updateCamera1.gif

Master “How about? Was it as you imagined?” Padawan “Yeah, Gopher has three!”

2:40 | Move the camera straight (constant linear motion)

Master “Let’s move the camera!” Master “Enter:”

Learning.pde


...
void draw() {
  ...
  //updateCamera1(); // <===== comment out =====
  updateCamera2(); // <===== postscript =====
 ...
}

...

// (15) Camera control (constant speed linear motion) // <===== Addition after this ↓ =====
void updateCamera2() {
  final float START_Z = 1500; // Z coordinate at the start
  final float SPEED = -300; // speed -300
  float ms = millis(); // milliseconds since the program started
  float dz = ms * SPEED / 1000; // Moving distance (Y coordinate)

  float x = 100;
  float y = -50;
  float z = START_Z + dz; // current position (Y coordinate)

  camera(x, y, z, x, y, z-1, 0, 1, 0);
  //camera(x, y, z, 0, 0, 0, 0, 1, 0);
}

Master “explain”

  • START_ZSPEEDmsdz
    It defines the camera start point and movement speed. It’s almost the same as moving the object.
  • xyz
    A variable that represents the coordinates of the current location of the camera. Only the z coordinate changes each time it is drawn.
  • camera(x, y, z, x, y, z-1, 0, 1, 0);
    Camera control. Imagine what happens when you look at the coordinates (x, y, z-1) from the coordinates (x, y, z).
  • //camera(x, y, z, 0, 0, 0, 0, 1, 0);
    Although commented out, the camera control is slightly different from the above. .. Imagine what happens when you look at the coordinates (0, 0, 0) from the coordinates (x, y, z).

Padawan clicked the Run button.

updateCamera2.gif

Padawan “Oh, I thought I would hit the sun Gopher!”

Master “If you can confirm it, try the camera control of the commented out person.”

Learning.pde


...
  //camera(x, y, z, x, y, z-1, 0, 1, 0);
  camera(x, y, z, 0, 0, 0, 0, 1, 0);
...

2:50: Revolve the camera (constant circular motion)

Master “Next is camera revolution”

Learning.pde


...
void draw() {
  background(255, 255, 255); // background color (R,G,B) is white

  //updateCamera1();
  //updateCamera2(); // <===== comment out =====
  updateCamera3(); // <===== postscript =====

  updateDevRoom();

  updateObject1(); // <===== Uncomment =====
  updateObject2(); // <===== Uncomment =====
  updateObject3(); // <===== Uncomment =====updateObject4(); // <===== Uncomment =====
  //updateObject5();
  //updateObject6();
  //updateObject7();
  //updateObject8();
  //updateObject9();
  //updateObject10()
  //updateObject11();
  //updateObject12();
  //updateObject13(); // <===== comment out =====
}

...

// (16) Camera control (revolution) // <===== Add postscript ↓ =====
void updateCamera3() {
  final float R = 1500; // revolution radius
  final float SPEED = 15; // Degree/second

  float ms = millis(); // milliseconds since the program started
  float dd = ms * SPEED / 1000; // Movement angle (Degree)
  float dr = radians(dd); // moving angle (Radian)
  float x = R * cos(dr); // X coordinate
  float z = R * sin(dr); // Z coordinate
  float y = -50;
  
  camera(x, y, z, 0, 0, 0, 0, 1, 0);
}

Master “explain”

  • draw() {...}
    The camera control uses updateCamera3(), and the
    draw object is ```updateObject1()

    .

     ~ ```updateObject4()
    #### **` is used.`**
    
  • void updateCamera3() {...}
    • RSPEEDmsdddr
      Revolve the object such as the radius of revolution and the rotation speed. It’s the same as when
    • xyz
      The trigonometric function is used to find the coordinates to position the camera.
    • camera(x, y, z, 0, 0, 0, 0, 1, 0);
      It looks at the origin (0, 0, 0) of the world coordinate system.

updateCamera3.gif

Padawan “Oh, Gopher!!!!” Padawan “Falling…?”

3:00 am | Model conversion and view conversion

3 o’clock 00 | Matrix identity

Master “Well, so far” Master “using translate() or rotate()” Master “place objects freely,”

Master “using camera()`” Master “Where to put the viewpoint and how to look at the world” Master “I’ve learned how to do that.”

Master “Actually, what used to be called a coordinate system,” Master “Representing a point in space by a set of three numbers (x, y, z)” Master “It’s called Cartesian coordinate system

Master “And” Master “In many CG libraries, including Processing,” Master “When converting coordinates” Master “Representing points in space with a set of four numbers” Master “What is called a homogeneous coordinate system is used.”

Padawan “Unknown!” Padawan “Why is it so difficult?”

Master “Gahaha! You think so?” Master “By the way, using this homogeneous coordinate system” Master “Coordinate transformation such as translation and rotation” Master “It’s very simple to express”

Master “In the history of the galaxy, many knights practiced their discipline.” Master “It’s a great technique that was created as a result of accumulating it.”

Master “Open a new sketch in [File][New]” image.png

Master “When you open a new sketch, go to File> Save As…” Master “Save it with a suitable name such as “Matrix”” image.png

Master “If you save the sketch, type the following:”

Matrix.pde


size(500,500,P3D);

// check the matrix
println("----------------------------------");
resetMatrix(); println("resetMatrix();");
printMatrix();

translate(-3, 0, -3); println("translate(-3, 0, -3);");
printMatrix();

rotateY(radians(30)); println("rotateY(radians(30));");
printMatrix();

After inputting this, when Padawan clicked the [Run] button, the following was output to the console.

image.pnt

Padawan “What’s this??” Master “The identity of the matrix!”

Padawan “What????”

Master “I used to evacuate and restore” Master “What is the matrix?" Master "Actually a set of 16 numbers" Master "You can print its contents with printMatrix()`”

Master “and coordinate translations such as translate() and rotateY() Master: “It’s all possible to express this 16-value combination.”

Padawan “…”

Master “Did you learn “matrix” at the initiation?”

Padawan “Yes! I learned it!” Padawan “I know it’s not the line of shops!”

Master “First information printed to console” Master “after initializing with resetMatrix()” Master “It’s in the current matrix”

console


resetMatrix();
 1.0000 0.0000 0.0000 0.0000
 0.0000 1.0000 0.0000 0.0000
 0.0000 0.0000 1.0000 0.0000
 0.0000 0.0000 0.0000 1.0000

Master “For information on coordinate conversion,” Master “It can be represented by a 4-row × 4-column matrix, that is, a square matrix of the fourth order.” Master “And when initialized with resetMatrix()” Master “The current matrix becomes the " identity matrix ``”

Padawan “???”

Master “What is the identity matrix?” Master “It means unit of identity in matrix multiplication,’’ Master “In normal numbers” Master “It’s a basic value like 0 for addition or 1 for multiplication.”

Master “What value does 0 add to? Master “When multiplying?”

Padawan “Ah! No matter what number you multiply by 1, it doesn’t change!” Padawan “I feel…”

Master “Yes. Coordinate transformation is done by matrix multiplication.” Master “And no matter what (matrix) value is multiplied” Master “It’s the identity matrix that doesn’t change it.”

Padawan “Well…” Padawan “Is it supposed to be 0 when initializing the counter?” Master “Well that’s fine”

Master “And this is the matrix with translate(-3, 0, -3) applied.”

console


translate(-3, 0, -3);
 1.0000 0.0000 0.0000 -3.0000
 0.0000 1.0000 0.0000 0.0000
 0.0000 0.0000 1.0000 -3.0000
 0.0000 0.0000 0.0000 1.0000

Master “And this is the matrix with rotateY(radians(30)); applied”

console


rotateY(radians(30));
 0.8660 0.0000 0.5000 -3.0000
 0.0000 1.0000 0.0000 0.0000
- 0.5000 0.0000 0.8660 -3.0000
 0.0000 0.0000 0.0000 1.0000

Master “Don’t worry about the meaning of each number now.”

Master “But not just roatateY() in the matrix above.” Master “also the translation of the previous translate()” Master “Remember what’s included”

Padawan “Um, translate()” Padawan “If surrounded by pushMatrix() and popMatrix()” Padawan “Is it just rotation?”

Master “If you wonder, check it out.”

Padawan “Well…” Padawan “Push here,…”

Matrix.pde


size(500,500,P3D);

// check the matrix
println("----------------------------------");
resetMatrix(); println("resetMatrix();");
printMatrix();pushMatrix(); // <== added
translate(-3, 0, -3); println("translate(-3, 0, -3);");
printMatrix();
popMatrix(); // <== added

rotateY(radians(30)); println("rotateY(radians(30));");
printMatrix();

Padawan “Ah, really” Padawan “I don’t know the details,” Padawan “-3.0000 on the far right is gone!”

console


...
rotateY(radians(30));
 0.8660 0.0000 0.5000 0.0000
 0.0000 1.0000 0.0000 0.0000
- 0.5000 0.0000 0.8660 0.0000
 0.0000 0.0000 0.0000 1.0000

Matrix multiplication

Master “If you arrange up to here,”

Master “To transform the local coordinate system to the world coordinate system” Master “The matrix you’ve been using” Master “Made of a combination of 16 numbers” Master “This represents a 4-row by 4-column matrix.”

Master “And with this combination of values” Master “I can express all the transformations I’ve done”

Master “Is it okay?”

Padawan “Yeah, I understand something!!” Padawan “It’s not that I don’t mind…”

Master “Then this matrix is in Processing” Master “Let’s think a little about how it is calculated.”

Master “First, let’s review the matrix multiplication.”

Master “The basic 4-by-1 and 1-by-4 multiplication is:”

image.png

Master “And the multiplication of 4x4column and 4x1column” Master “Calculate 4 rows by 4 columns, one row at a time”

Master “for example” Master “" resetMatrix()and" Master "after runningtranslate(-3, 0, -3)" Master "Drawed by executing Vertex(5, 0, 4)`` Master “vertex P (5, 0, 4) in local coordinates is”

Master “Add 1 after (5, 0, 4)” Master “Make it a 4-by-1 matrix” Master “Calculating as follows,” image.png Master “It’s converted to world coordinate vertex P’(2, 0, 1)” image.png

Master “also likewise for rotateY( radians(30) )” Master “can be calculated as:” image.png Master “It’s converted to world coordinate vertex P’(6.33, 0, 0.964)” image.png

Padawan “Ah, I remembered!” Padawan ““Matrix” repeats multiplication and addition like a demon” Padawan “It’s a mendotchi guy!!!”

Padawan “I forgot the rest!”

Affine transformation

Master “rotate is a kind of transformation called linear transformation.” Master: “My fellows have scale and ```shear

.

"

Padawan "Shear????"
Master “When a knight candy (footnote: Kintaro candy) is cut diagonally”
Master "Can you think of the cross section?"

Padawan "Oh! When your uncle made a mistake in cutting"
Padawan "That guy with a dull face!"

Master "I think it's shear."

Master "But I won't scale or shear this time."
Master "If you are interested, study by yourself"

Master "And"
Master “Linear translation plus translation”
Master "I say ```Affine transformation```"

Padawan "Hmm..."

Master "And this affine transformation"
Master "Can be expressed as a 4th order square matrix,"
Master "The components of the matrix associated with each transformation are:"
<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/708e470d-e9b0-bad8-d43d-624b38c47a6d.png">

Master "also about translation and rotation"
Master "The correspondence between methods and matrices is as follows."
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/09b325b1-6b10-ed5d-29d1-dcdffecde155.png)

Master "By the way, in the era of initiation"
Master “Any point P (x, y) on the quadratic plane (XY plane)”
Master “Rotated around the origin (0, 0) by an angle θ”
Master "I think I learned the formula to find the point P'(x', y'),"

Master "Do you remember?"

```x' = x cos θ-y sin θ```
```y' = x sinθ + y cosθ```

Padawan "Yes!"
Padawan "Of course I don't remember!!!"

Master "For example, in the case of 30° rotation, it should have been calculated as follows."
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/bce3e4a1-5bd5-60e4-ddd4-298779bd0640.png)


Master "Calculating the matrix of rotateZ() around the Z axis"
Master "You will find that it matches the above formula."
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/cab823eb-a8d2-0510-628a-33167e5f4967.png)

Padawan "I knew it would match, but..."
Padawan "Well, something is wrong..."

Padawan "Oh! The arrow's direction is different from the previous one."
Padawan "Why are they turning together????"

In his mind, Padawan was comparing the ZX plane with the XY plane.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/b81a4389-8640-6570-e84a-659583ab89f6.png)

Padawan "Because the arrows are reversed,"
Padawan "Um, if you flip it over, the direction of rotation should be reversed..."
Padawan "Well, that????"

Padawan "He was angry (laughs)"

Master "Padawan..."

Padawan (Wow, are you angry...)

[](----------------------------------------------------------------------------------)
#### Rotate orientation

Master "I noticed some good points!"
Padawan "He?"

Master "What to put on the vertical and horizontal axes?"
Master “Which is the light side (positive infinity direction)?”
Master "Because it changes the direction of rotation."
Master "Look at them together"

Master "By default, the vertical axis is downward and the horizontal axis is right in Processing."
Master "Let's see each plane according to it"

Master "Then, the directions of rotation are all clockwise like this:"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/e679e007-698f-9202-11db-e5aa0b4c6d34.png)

Padawan "Ah, Zuri!"
Padawan "X is vertical!"

Master "Yes. To see the ZX plane from the same perspective as the XY plane"
Master "You have to have X on the vertical axis and Z on the horizontal axis."

Master "What is the ZX plane so far?"
Master "I think it will be easier to grasp the image"
Master "I had the X on the horizontal axis."

Master "Remember that Processing is left-handed."

Master “If it’s the ZX plane,”
Master "After pointing your index finger (Y axis) towards you"
Master “If you keep the rest of your fingers facing right and down,”

Master "The thumb (X axis) and middle finger (Z axis) are naturally oriented"
Master "You'll find it matches this picture."

Padawan "Oh, really..."
Padawan "Thumb is down and middle finger is right!"

Master "And"
Master "If you take a closer look at the process of rotateX() rotateY() rotateZ()"
Master "sin (sin) is the vertical axis component, cos (cosine) is the horizontal axis component"
Master "You will see that it is calculated."

Method | Rotation axis | Affected <br> Plane | Plane <br> Horizontal axis | Plane <br> Vertical axis | cosθ<br> (cosine) | sinθ<br> (sine):-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:
rotateZ()|Z axis|XY plane|X axis|Y axis|X component|Y component
rotateY()|Y axis|Z X plane|Z axis|X axis|Z component|X component
rotateX()|X axis|YZ plane|Y axis|Z axis|Y component|Z component

Master “If you remember that cos θ is the X component and sin θ is the Y component”
Master "Because you'll be confused on the ZX and YZ planes."
Master "cos θ is the horizontal axis component (width), and sin θ is the vertical axis component (height)."

Master “In addition, the rotation formula is organized as follows”

Method | Rotation Axis | Affected <br>Plane | Rotation Formula
:-:|:-:|:-:|:-:
rotateZ()|Z axis|XY plane|x' = x cos θ-y sin θ<br>y' = x sin θ + y cos θ
rotateY()|Y-axis|ZX plane|z' = z cos θ-x sin θ<br>x' = z sin θ + x cos θ
rotateX()|X axis|YZ plane|y' = y cos θ-z sin θ<br>z' = y sin θ + z cos θ

Padawan "Oh, my head..."
Padawan "Explosion Shiso!!!"

Master "gahaha"
Master "Well, you don't have to remember the formula."
Master "Because the conversion method calculates the matrix"

Master "However, it is better to keep in mind the direction of rotation of each axis."

Master "If you turn the axis of rotation to yourself"
Master "The positive direction in which the axis rotates is clockwise."

Master "For example, if you pass a positive number to rotateZ(),"
Master "If you turn the Z axis to yourself, the XY plane will rotate clockwise."

Master "And of course, if you pass a negative number to rotateZ(),"
Master "Needless to say, it's counterclockwise."

Master "The "positive direction" of rotation of each axis is as follows."
Master "I want this to penetrate my body."
<img width="500" alt="image.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/0b59f5ae-e0ae-77e9-c5a4-97c90b99dd40.gif">

[](----------------------------------------------------------------------------------)
#### Composite conversion

Master "Next, when combining coordinate transformations"
Master "Let's think about what the matrix looks like"

Master "For example, when you do translate() after rotateY()"
Master: "A single matrix can represent a transformation that combines both."

Padawan "Can you do only two?"
Master "No, no matter how many conversions you stack, you can express it in one matrix."

Master "And if you apply it to an object,"
Master "One time conversion is done"

Padawan "Oh! It's the same as a pocketbook!"
Master "What????"

Padawan "The pocket money book is..."
Padawan "No matter how many times you shop"
Padawan "You can express the "balance" of the month with a single number!"

Padawan "And just add that balance to your balance at the end of last month"
Padawan "You can calculate the balance for this month!"

Coordinate conversion | pocket money book
:--|--:
translate() | pocket money 10 credits
rotateY()|Ame -1 credit
translate() | knight figure -6 credit
rotateZ() | gum -1 credit
rotateX()|Kids Saver -4 credit
**Synthesis matrix**|** Monthly balance -2 credit**

Master "This month is in the red (laughs)"

Master “Well, you can think so”
Master "But in the case of coordinate transformation, it is multiplication, not addition."

Master "And, since each transformation is a 4th order square matrix,"
Master "It's a multiplication of 4th order square matrices"

Master "I will add the following to my newly created sketch"


#### **`Matrix.pde`**
```java

...
// Confirm the composite conversion // Add the following
println("----------------------------------");
resetMatrix();
rotateY(radians(30)); println("rotateY(radians(30));");
printMatrix();

resetMatrix();
rotateX(radians(30)); println("rotateX(radians(30));");
printMatrix();

Master “of RotateY() and RotateX() output to console” Master “Let’s multiply two transformation matrices”

console


- ---------------------------------
rotateY(radians(30));
 0.8660 0.0000 0.5000 0.0000
 0.0000 1.0000 0.0000 0.0000
- 0.5000 0.0000 0.8660 0.0000
 0.0000 0.0000 0.0000 1.0000

rotateX(radians(30));
 1.0000 0.0000 0.0000 0.0000
 0.0000 0.8660 -0.5000 0.0000
 0.0000 0.5000 0.8660 0.0000
 0.0000 0.0000 0.0000 1.0000

Master “Calculation method is as follows” Master “First, calculate the first line as follows,” image.png

Master “You can do the same for the second and subsequent rows.” image.png Padawan “Wow!” Padawan “Mendokuse!!!”

Master “Add the following to the sketch,” Master “Check if the calculation results match”

Matrix.pde


...
resetMatrix(); // Add the following
rotateY(radians(30)); println("rotateY(radians(30));");
rotateX(radians(30)); println("rotateX(radians(30));");
printMatrix();

console


rotateY(radians(30));
rotateX(radians(30));
 0.8660 0.2500 0.4330 0.0000
 0.0000 0.8660 -0.5000 0.0000
- 0.5000 0.4330 0.7500 0.0000
 0.0000 0.0000 0.0000 1.0000

Padawan “Oh, it fits!!!” Padawan “Taka, isn’t it faster to write this saver?”

Master “Would you like to write it?”

Master “How to get the current matrix” Master “Don’t you know because you did it with turnToCamera() a while ago?”

Master “in the method get() of the obtained PMatrix instance,” Master “If you pass a float array (one-dimensional array) with 16 elements” Master “You can get the matrix in an array”

Padawan “Well…” Padawan added the following to Matrix.pde.

Matrix.pde


...
// Multiply matrix by matrix // Add the following
println("----------------------------------");
float[] m1 = new float[16]; // result of rotateY()
float[] m2 = new float[16]; // result of rotateX()
float[] m3 = new float[16]; // Composite result of rotateY() and rotateX()

resetMatrix();
rotateY(radians(30));
((PMatrix3D)g.getMatrix()).get(m1);

resetMatrix();
rotateX(radians(30));
((PMatrix3D)g.getMatrix()).get(m2);

for (int i = 0; i <4; i++)
  for (int j = 0; j <4; j++)
    for (int k = 0; k <4; k++)
      m3[i * 4 + j] += m1[i * 4 + k] * m2[k * 4 + j];

println(m3);

Padawan “Whew, is this right?”

console


- ---------------------------------
[0] 0.8660254
[1] 0.25
[2] 0.4330127
[3] 0.0
[4] 0.0
[5] 0.8660254
[6] -0.5
[7] 0.0
[8] -0.5
[9] 0.4330127
[10] 0.75
[11] 0.0
[12] 0.0
[13] 0.0
[14] 0.0
[15] 1.0

Padawan “Yes! It looks like it fits!”

Padawan “Tec, this is what Processing will do for me” Padawan “Do you mean what you write by yourself??”

Master “No (laughs)”

Padawan “Gee! I was tricked again!!!” Padawan “What is it! Chiku show!”

Master “Well, don’t get rough (laughs)”

Master “The behavior of the library” Master “I also want to make sure that my expectations are met.” Master “I’m sure it will come out” Master: Think of it as a practice for that moment.

3:30 | The identity of the camera

Master “Then add the following to Matrix.pde”

Matrix.pde


...
// camera behavior // add more
println("----------------------------------");camera(50, 0, 100, 50, 0, 0, 0, 1, 0);
println("camera(50, 0, 100, 50, 0, 0, 0, 1, 0);");
printMatrix();

camera(0, 0, 0, 250, 0, -433, 0, 1, 0);
println("camera(0, 0, 0, 250, 0, -433, 0, 1, 0);");
printMatrix();

Master “In the above, the camera is placed in the world coordinate system (see the figure below).” Master “I check the matrix immediately after that” image.png Master “For clarity” Master “I have both the camera and the target on the ZX plane.”

Master “And then, on the left, make your line of sight parallel to the Z axis.” Master “Right is tilting the line of sight from the Z axis by 30°”

Padawan “But why do you check the matrix?”

Padawan “Ah!”

console


- ---------------------------------
camera(50, 0, 100, 50, 0, 0, 0, 1, 0);
 001.0000 000.0000 000.0000 -050.0000
 000.0000 001.0000 000.0000 000.0000
 000.0000 000.0000 001.0000 -100.0000
 000.0000 000.0000 000.0000 001.0000

camera(0, 0, 0, 250, 0, -433, 0, 1, 0);
 0.8660 -0.0000 0.5000 0.0000
 0.0000 1.0000 0.0000 0.0000
- 0.5000 0.0000 0.8660 0.0000
 0.0000 0.0000 0.0000 1.0000

Padawan “There are some familiar numbers!” Padawan “But what is -0.0000?”

Master “It was defined by the Galactic Standardization Organization as No. 754.” Master “A story related to the standard for floating point numbers (footnote: IEEE754].” Master “For now, think of it as the same as a normal 0”

Master “What else?”

Padawan “Yeah. What about 0.866 or 0.5?” Padawan “By repeating addition and multiplication like the demon” Padawan “It’s the number I saw a lot”

Master “Yes.” Master “Let’s type the following to see that.”

Matrix.pde


...
// Comparison with coordinate conversion // Add the following
println("----------------------------------");
resetMatrix();
translate(-50, 0, -100);
println("translate(-50, 0, -100);");
printMatrix();

resetMatrix();
rotateY(radians(30));
println("rotateY(radians(30));");
printMatrix();

When Padawan clicked the [Run] button, the following was output to the console.

console


- ---------------------------------
translate(-50, 0, -100);
 001.0000 000.0000 000.0000 -050.0000
 000.0000 001.0000 000.0000 000.0000
 000.0000 000.0000 001.0000 -100.0000
 000.0000 000.0000 000.0000 001.0000

rotateY(radians(30));
 0.8660 0.0000 0.5000 0.0000
 0.0000 1.0000 0.0000 0.0000
- 0.5000 0.0000 0.8660 0.0000
 0.0000 0.0000 0.0000 1.0000

Padawan “Gee! It’s the same!!!”

Master “Well, let’s compare a little easier.” image.png

Master “Translating the camera” Master “Same as moving the object in the opposite direction.”

Master “To rotate the camera” Master “Rotating the object in the opposite direction…”

Padawan “It’s the same!!!”

Master “Yes. When you call the method camera()” Master: “The current matrix has a reverse transformation set.”

Master “I mean, I’ve been thinking about the world coordinate system so far.” Master “Actually, the camera was the center of the world.”

Master “In the figure below, the left side is the apparent movement” Master “The right side is the actual movement.” image.png

Master “And this is the case for spinning” image.png

Master “Calling camera() like this” Master “The world’s’stage’ is moving and comes back.” Master “And the coordinate conversion and drawing process after that” Master “All will be done on this stage”

Master “Also, even if you move the world stage” Master “About a coordinate system that does not move when stopped” Master “It’s called camera coordinate system.” Master “The camera viewpoint is always at its origin (0, 0, 0)” Master “The camera is always on the dark side of the Z axis (in the direction of negative infinity)” Master “I have my eyes fixed”

Master “So, in the camera coordinate system,” Master “The screen plane is always parallel to the XY plane.”

Master “And done by calling camera()” Master “Coordinate conversion is called "viewing conversion``”

Master “For that, translate(), rotateY(), etc.” Master “To convert coordinates from local coordinate system to world coordinate system” Master “I like modeling conversion,”

Master “Make these two together and call it "model view conversion``”

Padawan “H?”

Padawan “So then, then” Padawan “This little kid just before ↓ ↓ ↓ ↓ ↓…”

Padawan “Oh, it’s spinning…” updateObject1.gif Master “Actually, everything in this is still” Padawan “Eh?? Master “The camera is spinning”

Master “That is a lie (laughs)”

Padawan “What the hell! I think I was right!!!” Padawan (Why master always mess around (anger))

Master “But, with the world coordinate system at the center,” Master “Thinking about placing objects and cameras there” Master “Writing a saver doesn’t hurt at all.” Master “That’s the great thing about this method”

Rationale for changing the posture of the billboard

Master “If you understand this far” Master “I can explain about the posture change of the billboard I just made.”

Master “Let’s take a closer look at turnToCamera()”

Learning.pde


...
void turnToCamera() {
  PMatrix3D m = (PMatrix3D)g.getMatrix();

  //column 0 //column 1 //column 2
  m.m00 = 1; m.m01 = 0; m.m02 = 0; // row 0
  m.m10 = 0; m.m11 = 1; m.m12 = 0; // row 1
  m.m20 = 0; m.m21 = 0; m.m22 = 1; // row 2
  
  resetMatrix();
  applyMatrix(m);
}
...

Master “after fetching the current matrix with g.getMatrix()” Master “Maybe you have set values in the fields m00 to m22?” Master “Actually, this is an element of the matrix.”

Master “Of which, the unit matrix is set for the elements related to posture change” image.png

Padawan “Oh, it’s the same guy that you wear it!”

Master “By doing this, in the process so far” Master “Viewing Transformation (camera() call)” Master “Translated with modeling transforms (calls to rotateY() and translate())” Master “The position of the “stage” remains the same” Master: You can make only that attitude “what wasn’t there.”

Master “The modified matrix m”” Master “I’ll reset it as the current matrix,”

Master “First, with resetMatrix()” Master “Make the current matrix the identity matrix”image.png

Master “Finally, with applyMatrix(m),” Master “I’m multiplying the current matrix by m

Master “Let’s check the actual operation again” Master “Create a new sketch and do the following”

TurnToCameraTest


void setup() {
  size(1000, 300, P3D);
}

void draw() {
  background(255, 255, 255);
  stroke(0, 0, 255);
  fill(240,240,240);
  camera(0, 0, 0, 0, 0, -1, 0, 1, 0); // put the camera's viewpoint at the origin and direct the line of sight to the negative infinity of the Z axis
  drawBoard(300, 45); // tilt the local coordinate system by 45° and move 300 in the X-axis direction to draw the board
  drawBoard(300, 90); // tilt the local coordinate system 90° and move 300 in the X-axis direction to draw the board
  drawBoard(300, 135); // Tilt the local coordinate system by 135° and move 300 in the X-axis direction to draw the board
}

void drawBoard(float xpos, float degree) {
  pushMatrix();
    rotateY(radians(degree)); // rotate the local coordinate system about the Y axis by degree
    translate(xpos, 0, 0); // translate the local coordinate system in the x direction (of the local coordinate system) by xpos
    //turnToCamera(); // Make the XY plane of the local coordinate system parallel to the XY plane of the camera coordinate system
    beginShape(); // draw a board parallel to the XY plane of local coordinates
      vertex(-50, -50, 0);
      vertex( 50, -50, 0);
      vertex( 50, 50, 0);
      vertex(-50, 50, 0);
    endShape(CLOSE);
  popMatrix();
}

void turnToCamera() {// Make the XY plane of the local coordinate system parallel to the XY plane of the camera coordinate system
    PMatrix3D m = (PMatrix3D)g.getMatrix();
    m.m00 = m.m11 = m.m22 = 1;
    m.m01 = m.m02 = m.m10 = m.m12 = m.m20 = m.m21 = 0;
    resetMatrix();
    applyMatrix(m);
}

Master “After running and confirming,” Master “Uncomment the //turnToCamera(); part” Master “I’ll try again” image.png

Master “Did you get the image?”

Padawan “Yeah! Perfect!!” Padawan (I’m not sure, but if you call the method, it will turn around!!)

Master “In addition, I will only supplement one.”

Master “I don’t handle this time,” Master “also components such as scale() and shareX()” Master “Because it is included in the blue part of the figure below,” image.png Master “For example, with scaling” Master “turnToCamera() will behave badly”

3 hours 50 minutes | Code up to here

Master “I’ve made a break up to this point” Master “Look again at Learning.pde and Matrix.pde.”

Expand all code in Learing.pde
#### **`Learning.pde`** ```java // setup() is called once just after the program starts void setup() { size(1200, 800, P3D); // specify window size and 3DCG hint(ENABLE_DEPTH_SORT); //setCameraOrbitalRadius(150); //setCameraOrbitalPeriod(120); //setCameraMaxDepth(1500); setDevRoomAutoCameraEnabled(false); frameRate(24); // frame rate (fps) } // draw() is called many times until the end of the program // (if fps==24, called 24 times per second) void draw() { background(255, 255, 255); // background color (R,G,B) is white //updateCamera1(); //updateCamera2(); updateCamera3(); updateDevRoom(); updateObject1(); updateObject2(); updateObject3(); updateObject4(); //updateObject5(); //updateObject6(); //updateObject7(); //updateObject8(); //updateObject9(); //updateObject10() //updateObject11(); //updateObject12(); //updateObject13(); } // (1) Rectangle drawing (still) void updateObject1() { fill(240, 240, 240); // Fill color (R,G,B) stroke(255, 0, 255); // border color (R,G,B) strokeWeight(2); // border thickness beginShape(); vertex(-50, -50, -500); vertex( 50, -50, -500); vertex( 50, 50, -500); vertex(-50, 50, -500); endShape(CLOSE); } // (2) Draw a rectangle (pseudo constant velocity linear motion) float gZ = -600; void updateObject2() { gZ+=120; // Update Z coordinate fill(255, 255, 255); // Fill color (R,G,B) stroke(0, 255, 255); // border color (R,G,B) strokeWeight(2); // border thickness beginShape(); vertex(-40, -40, gZ); vertex( 40, -40, gZ); vertex( 40, 40, gZ); vertex(-40, 40, gZ); endShape(CLOSE); } // (3) Rectangle drawing (real constant linear motion) void updateObject3() { final float START_Y = -600; // Y coordinate at the start final float SPEED = 200; // speed per second float ms = millis(); // milliseconds since the program started float dy = ms * SPEED / 1000; // Moving distance (Y coordinate) float y = START_Y + dy; // current position (Y coordinate) noFill(); // no fill stroke(255, 0, 0); // border color (R,G,B) strokeWeight(2); // border thickness beginShape(); vertex(-30, y, -30); vertex( 30, y, -30); vertex( 30, y, 30); vertex(-30, y, 30); endShape(CLOSE); } // (4) Draw a rectangle (circular motion) void updateObject4() { final float R = 500; // revolution radius final float SPEED = 60; // Degree/second float ms = millis(); // milliseconds since the program started float dd = ms * SPEED / 1000; // Movement angle (Degree) float dr = radians(dd); // moving angle (Radian) float x = R * cos(dr); // X coordinate float y = R * sin(dr); // Y coordinate fill(255, 255, 255); // Fill color (R,G,B) stroke(0, 0, 255); // border color (R,G,B) strokeWeight(2); // border thickness beginShape(); vertex(x-30, y, -30); vertex(x+30, y, -30); vertex(x+30, y, 30); vertex(x-30, y, 30); endShape(CLOSE); } // (5) Draw 3D object (still) void updateObject5() { noFill(); // no fill strokeWeight(2); // border thickness stroke(255, 0, 255); // border color (R,G,B) box(150);stroke(0, 255, 255); // border color (R,G,B) sphere(60); } // (6) 3D object (constant linear motion with coordinate conversion) // Realize movement similar to (3) by coordinate conversion void drawEarth() { fill(224, 224, 255); // Fill color (R,G,B) stroke(0, 0, 255); // border color (R,G,B) strokeWeight(2); // border thickness box(80); } void updateObject6() { final float START = -600; // start position final float SPEED = 200; // speed per second float ms = millis(); // milliseconds since the program started float d = ms * SPEED / 1000; // distance traveled float p = START + d; // current position pushMatrix(); translate(p, -100, 50); drawEarth(); popMatrix(); } // (7) 3D object (rotation by coordinate conversion) void updateObject7() { final float SPEED = 60; // Degree/second float ms = millis(); // milliseconds since the program started float dd = ms * SPEED / 1000; // Movement angle (Degree) float dr = radians(dd); // moving angle (Radian) pushMatrix(); rotateY(dr); drawEarth(); popMatrix(); } // (8) 3D object (revolved by coordinate transformation) // Realize movement similar to (4) by coordinate conversion void updateObject8() { final float R = 300; // revolution radius final float SPEED = 150; // Degree/second float ms = millis(); // milliseconds since the program started float dd = ms * SPEED / 1000; // Movement angle (Degree) float dr = radians(dd); // moving angle (Radian) pushMatrix(); rotateY(dr); translate(R, 0, 0); drawEarth(); popMatrix(); } // (9) 3D object (rotates at a distant position by coordinate conversion) // Note the difference with (8) void updateObject9() { final float R = 300; // revolution radius final float SPEED = 150; // Degree/second float ms = millis(); // milliseconds since the program started float dd = ms * SPEED / 1000; // Movement angle (Degree) float dr = radians(dd); // moving angle (Radian) pushMatrix(); translate(R, 0, 0); // just swapped the order rotateY(dr); // just changed the order drawEarth(); popMatrix(); } // (10) 3D object (revolves around the revolution body by coordinate transformation) void drawSun() { noFill(); // no fill stroke(255, 0, 0); // border color (R,G,B) strokeWeight(2); // border thickness sphere(60); } void drawMoon() { fill(255, 255, 128); // Fill color (R,G,B) stroke(255, 0, 0); // border color (R,G,B) strokeWeight(2); // border thickness beginShape(); vertex(-20, -20, 0); vertex( 20, -20, 0); vertex( 20, 20, 0); vertex(-20, 20, 0); endShape(CLOSE); } void updateObject10() { float ms = millis(); // milliseconds since the program started final float E_R = 400; // Earth's revolution radius final float E_SPEED = 80; // Earth angle (Degree)/second float edd = ms * E_SPEED / 1000; // Earth movement angle (Degree) float edr = radians(edd); // Earth movement angle (Radian) final float M_R = 100; // Moon radius final float M_SPEED = 200; // Moon angle (Degree)/second float mdd = ms * M_SPEED / 1000; // Moon movement angle (Degree) float mdr = radians(mdd); // Moon movement angle (Radian) drawSun(); pushMatrix(); { rotateY(edr); translate(E_R, 0, 0); drawEarth(); pushMatrix(); { rotateY(mdr); translate(M_R, 0, 0); drawMoon(); } popMatrix(); } popMatrix(); } // (11) Texture mapping (still) void drawGopher(float len) { PImage img = loadImage("gopher.png"); int h = img.height; int w = img.width; noStroke(); // do not draw the border beginShape(); texture(img); vertex(-len/2, -len/2, 0, 0, 0); vertex( len/2, -len/2, 0, w, 0); vertex( len/2, len/2, 0, w, h); vertex(-len/2, len/2, 0, 0, h); endShape(); } void updateObject11() { drawGopher(500); } // (12) Texture mapping (revolve around the revolution body by coordinate transformation) // movement is almost the same as (10) void updateObject12() { float ms = millis(); // milliseconds since the program started final float S_LEN = 200; // length of one side of Sun Gopher final float E_LEN = 100; // One side of the Earth Gopher final float E_R = 400; // Revolution radius of Earth Gopher final float E_SPEED = 80; // Earth Gopher angle (Degree)/sec float edd = ms * E_SPEED / 1000; // Earth Gopher movement angle (Degree) float edr = radians(edd); // Earth Gopher movement angle (Radian) final float M_LEN = 50; // Length of one side of month Gopher final float M_R = 100; // Moon Gopher revolution radius final float M_SPEED = 200; // Moon Gopher angle (Degree)/second float mdd = ms * M_SPEED / 1000; // Moon Gopher movement angle (Degree) float mdr = radians(mdd); // Moon Gopher movement angle (Radian) pushMatrix(); { translate(0, -S_LEN / 2, 0); drawGopher(S_LEN); // Sun Gopher } popMatrix(); pushMatrix(); { rotateY(edr); translate(E_R, 0, 0); pushMatrix(); { translate(0, -E_LEN / 2, 0); drawGopher(E_LEN); // Earth Gopher } popMatrix(); pushMatrix(); { rotateY(mdr); translate(M_R, -M_LEN / 2, 0); drawGopher(M_LEN); // Month Gopher } popMatrix(); } popMatrix(); } // (13) Billboard (revolves around the revolution body by coordinate transformation) // movement is almost the same as (12) void turnToCamera() { PMatrix3D m = (PMatrix3D)g.getMatrix(); //column 0 //column 1 //column 2 m.m00 = 1; m.m01 = 0; m.m02 = 0; // row 0 m.m10 = 0; m.m11 = 1; m.m12 = 0; // row 1 m.m20 = 0; m.m21 = 0; m.m22 = 1; // row 2 resetMatrix(); applyMatrix(m); } void updateObject13() {float ms = millis(); // milliseconds since the program started final float S_LEN = 200; // length of one side of Sun Gopher final float E_LEN = 100; // One side of the Earth Gopher final float E_R = 400; // Revolution radius of Earth Gopher final float E_SPEED = 80; // Earth Gopher angle (Degree)/sec float edd = ms * E_SPEED / 1000; // Earth Gopher movement angle (Degree) float edr = radians(edd); // Earth Gopher movement angle (Radian) final float M_LEN = 50; // Length of one side of month Gopher final float M_R = 100; // Moon Gopher revolution radius final float M_SPEED = 200; // Moon Gopher angle (Degree)/second float mdd = ms * M_SPEED / 1000; // Moon Gopher movement angle (Degree) float mdr = radians(mdd); // Moon Gopher movement angle (Radian) pushMatrix(); { translate(0, -S_LEN / 2, 0); turnToCamera(); drawGopher(S_LEN); // Sun Gopher } popMatrix(); pushMatrix(); { rotateY(edr); translate(E_R, 0, 0); pushMatrix(); { translate(0, -E_LEN / 2, 0); turnToCamera(); drawGopher(E_LEN); // Earth Gopher } popMatrix(); pushMatrix(); { rotateY(mdr); translate(M_R, -M_LEN / 2, 0); turnToCamera(); drawGopher(M_LEN); // Month Gopher } popMatrix(); } popMatrix(); } // (14) Camera control (still) void updateCamera1() { float x = 25; float y = -70; float z = 800; camera(x, y, z, 0, 0, 0, 0, 1, 0); } // (15) Camera control (constant linear motion) void updateCamera2() { final float START_Z = 1500; // Z coordinate at the start final float SPEED = -300; // speed -300 float ms = millis(); // milliseconds since the program started float dz = ms * SPEED / 1000; // Moving distance (Y coordinate) float x = 100; float y = -50; float z = START_Z + dz; // current position (Y coordinate) //camera(x, y, z, x, y, z-1, 0, 1, 0); camera(x, y, z, 0, 0, 0, 0, 1, 0); } // (16) Camera control (revolution) void updateCamera3() { final float R = 1500; // revolution radius final float SPEED = 15; // Degree/second float ms = millis(); // milliseconds since the program started float dd = ms * SPEED / 1000; // Movement angle (Degree) float dr = radians(dd); // moving angle (Radian) float x = R * cos(dr); // X coordinate float z = R * sin(dr); // Z coordinate float y = -50; camera(x, y, z, 0, 0, 0, 0, 1, 0); } ```
Expand all code in Matrix.pde
#### **`Matrix.pde`** ```java size(500,500,P3D); // check the matrix println("----------------------------------"); resetMatrix(); println("resetMatrix();"); printMatrix(); pushMatrix(); translate(-3, 0, -3); println("translate(-3, 0, -3);"); printMatrix(); popMatrix(); rotateY(radians(30)); println("rotateY(radians(30));"); printMatrix(); // check the composite transformation println("----------------------------------"); resetMatrix(); rotateY(radians(30)); println("rotateY(radians(30));"); printMatrix(); resetMatrix(); rotateX(radians(30)); println("rotateX(radians(30));"); printMatrix(); resetMatrix(); rotateY(radians(30)); println("rotateY(radians(30));"); rotateX(radians(30)); println("rotateX(radians(30));"); printMatrix(); // matrix multiplication println("----------------------------------"); float[] m1 = new float[16]; // result of rotateY() float[] m2 = new float[16]; // result of rotateX() float[] m3 = new float[16]; // Composite result of rotateY() and rotateX() resetMatrix(); rotateY(radians(30)); ((PMatrix3D)g.getMatrix()).get(m1); resetMatrix(); rotateX(radians(30)); ((PMatrix3D)g.getMatrix()).get(m2); for (int i = 0; i <4; i++) for (int j = 0; j <4; j++) for (int k = 0; k <4; k++) m3[i * 4 + j] += m1[i * 4 + k] * m2[k * 4 + j]; println(m3); // camera behavior println("----------------------------------"); camera(50, 0, 100, 50, 0, 0, 0, 1, 0); println("camera(50, 0, 100, 50, 0, 0, 0, 1, 0);"); printMatrix(); camera(0, 0, 0, 250, 0, -433, 0, 1, 0); println("camera(0, 0, 0, 250, 0, -433, 0, 1, 0);"); printMatrix(); // Comparison with coordinate transformation println("----------------------------------"); resetMatrix(); translate(-50, 0, -100); println("translate(-50, 0, -100);"); printMatrix(); resetMatrix(); rotateY(radians(30)); println("rotateY(radians(30));"); printMatrix(); ```

4 am | Class design and implementation

4:00 pm | Preparation for implementation

Master “From here, it’s finally time to build a simulator.” Padawan “Oh! Finally… (laughs)”

Master “But before that, on the Processing official website” Master “Let me know the URL of the reference page”

Master “Because there are various methods that I have not taught yet.” Master “I will proceed with the implementation while checking.”

Official Processing Site-Reference

Master “If we do that, we will proceed with development.” Master “First, open a new sketch in [File][New].” Master “Save it as Simurator`”

Master “Next, create a tab named DevUtils with [▼][New Tab]” Master “Put the utility code.”

Master “I don’t need a detailed explanation because I just did it?” Padawan “Yeah. Easy victory!”

Then, a group of Gopher appeared.

Padawan “What? If you think it disappeared, before you know…” Padawan “Appearing in a flashy appearance (laughs)”

Taiyo Gopher Mercury Gopher Venus Gopher    
:–: :–: :–:gp_sun.png
gp_sun.png
gp_mercury.png
gp_mercury.png
gp_venus.png
gp_venus.png
地球 Gopher君 月 Gopher君 火星 Gopher君
gp_earth.png
gp_earth.png
gp_moon.png
gp_moon.png
gp_mars.png
gp_mars.png
木星 Gopher君 土星 Gopher君 天王星 Gopher君
gp_jupiter.png
gp_jupiter.png
gp_saturn.png
gp_saturn.png
gp_uranus.png
gp_uranus.png
海王星 Gopher君 青星 Gopher君 白星 Gopher君
gp_neptune.png
gp_neptune.png
gp_50_blue.png
gp_600_blue.pnggp_50_blue.png
gp_50_white.png
gp_600_white.pnggp_50_white.png
黄星 Gopher君 橙星 Gopher君 赤星 Gopher君
gp_50_yellow.png
gp_600_yellow.pnggp_50_yellow.png
gp_50_orange.png
gp_600_orange.pnggp_50_orange.png
gp_50_red.png
gp_600_red.pnggp_50_red.png

マスター「太陽系の詳しい資料が揃うまで」 マスター「Gopher君が各天体の代役をやってくれることになったんだ」

パダワン「うげっ! 天王星Gopher君かっけー!!!」

パダワン「青星君とか赤星君はなにもん???」 マスター「恒星の表面温度ごとに、5 種類を準備してもらったんだ」

マスター「ホロカムを PNG にしたから、」 マスター「スケッチフォルダーに data フォルダーを作成して」 マスター「これをそこへコピーするんだ」

マスター「なお、メモリ節約のために」 マスター「青星~赤星君の 5 つのファイルは」 マスター「サイズが 50 x 50 の小さい方をコピーするんだ」

マスター「次のようなディレクトリ構成になったはずだ」

スケッチフォルダーの構成


any-dir
    Simulator
    ├── Simulator.pde
    ├── DevUtils.pde
    └── data
        ├── gp_50_blue.png
        ├── gp_50_orange.png
        ├── gp_50_red.png
        ├── gp_50_white.png
        ├── gp_50_yellow.png
        ├── gp_earth.png
        ├── gp_jupiter.png
        ├── gp_mars.png
        ├── gp_mercury.png
        ├── gp_moon.png
        ├── gp_neptune.png
        ├── gp_saturn.png
        ├── gp_sun.png
        ├── gp_uranus.png
        └── gp_venus.png

マスター「そうしたら、シミュレーターの」 マスター「ざっくりとした設計方針を検討しよう」

マスター「描画するオブジェクトは、」 マスター「中心に位置する太陽、」 マスター「それから惑星とその衛星、」 マスター「そして銀河の星々だ」

パダワン「銀河???」

マスター「そうだ。今回の要求では、銀河の星々は」 マスター「ワールド座標系で静止していると考えて良い」

パダワン「あっ、だから青色Gopher君たちが居るのか!」

マスター「また、太陽はワールド座標系の」 マスター「原点 (0, 0, 0) に静止させた方が作りやすいだろう」

マスター「あとは、母星を中心に公転する惑星と衛星だな」

4 時 10 分|Billboard クラス

マスター「まずは基本となるビルボードのクラスだが、」 マスター「これまでビルボードを表示するときに」 マスター「どんな属性を指定した?」

パダワン「看板の大きさと、貼り付ける画像と …」 パダワン「あっ!表示する場所だ!」

マスター「そうだな。」 マスター「そうするとクラス定義は以下のようになるだろうな」

Billboard クラス

カテゴリ 項目 説明    
class Billboard カメラ方向への姿勢変更など、ビルボードの基本機能を実装する。field mImage An image to paste on the billboard.
field mWidth Width of the billboard.    
field mHeight The height of the billboard.    
field mX Billboard X coordinate.    
field mY Billboard Y coordinate.    
field mZ The Z coordinate of the billboard.    
method update() Transform the coordinates and draw. Separate transform() and drawSelf() so that the inheritance destination can be easily overridden.    
method transform() Convert the coordinates.    
method drawSelf() draws itself.    

Master “OK, let’s implement this now” Master “[▼][New tab] create a tab named Billboard” Master “Try writing there”

Padawan “Um, while copying from Learning.pde…”

Padawan wrote the following saver:

Buillboard.pde


// billboard class
public class Billboard {
  private PImage mImage; // image to paste on billboard
  private int mWidth; // billboard width
  private int mHeight; // height of billboard
  private float mX; // billboard X coordinate
  private float mY; // Y coordinate of billboard
  private float mZ; // Z coordinate of billboard

  // constructor
  // param imageName: File name of the image to paste
  // param w: width of billboard
  // param x: X coordinate of billboard
  // param y: Y coordinate of billboard
  // param z: Z coordinate of billboard
  public Billboard(String imageName, int w, float x, float y, float z) (
    this( loadImage(imageName), w, x, y, z);
  }
  
  // constructor
  // param image: image to paste
  // param w: width of billboard
  // param x: X coordinate of billboard
  // param y: Y coordinate of billboard
  // param z: Z coordinate of billboard
  public Billboard(PImage image, int w, float x, float y, float z) (
    mImage = image;
    mWidth = w;
    mHeight = w * mImage.height / mImage.width; // Height calculated from image aspect ratio
    mX = x;
    mY = y;
    mZ = z;
  }

  // Transform and draw
  public void update() {
    pushMatrix();
      transform();
      turnToCamera();
      drawSelf();
    popMatrix();
  }

  // Coordinate transformation
  protected void transform() {
    translate(mX, mY, mZ);
  }

  // draw
  protected void drawSelf() {
    float hw = mWidth / 2;
    float hh = mHeight / 2;
    float iw = mImage.width;
    float ih = mImage.height;
    pushStyle();
      noStroke();
      beginShape();
        texture(mImage);
        vertex(-hw, -hh, 0, 0, 0);
        vertex( hw, -hh, 0, iw, 0);
        vertex( hw, hh, 0, iw, ih);
        vertex(-hw, hh, 0, 0, ih);
      endShape();
    popStyle();
  }
 
  // Change posture to camera
  protected void turnToCamera() {
    PMatrix3D m = (PMatrix3D)g.getMatrix();
    m.m00 = m.m11 = m.m22 = 1;
    m.m01 = m.m02 = m.m10 = m.m12 = m.m20 = m.m21 = 0;
    resetMatrix();
    applyMatrix(m);
  }
}

Padawan “We have two constructors!” Padawan “The guy who specifies the file name,” Padawan “The one that specifies the image object”

Master: Yeah. It’s fine if you need it, though. Master: “It’s okay to prepare the two first.”

Padawan “And update() can be called by anyone” Padawan “Make it public” Padawan “Others can be used by subclasses” Padawan “Protected”

Master “Well, that’s right.”

Master “Actually, the class created by Processing is” Master of “Processing generated classes” Master “Because it is built as an inner class,”

Master “also private members” Master “I can see it from where I can see it.” Master “Well, that’s fine”

Master “Hm? Did you use pushStyle() and popStyle()?”

Padawan “Yeah. I saw it on the reference page”

Processing Official Site-Reference-pushStyle

Padawan “Like the Matrix” Padawan “If you push it, you can change the color and thickness of the line.” Padawan “I thought I could get it and tried writing it (laugh)”

Master “Yes, that’s fine!” Master (after all, this child is an intuitive type)

Master “Okay, then.” Master “Let’s draw using the Billboard class”

Master “Write it on the Simurator tab”

Padawan “Well…” Padawan “Then use blue star and red star!”

Simulator.pde


Billboard gStarBlue;
Billboard gStarRed;

void setup() {
  gStarBlue = new Billboard("gp_50_blue.png", 50, 400, -25, 400);
  gStarRed = new Billboard("gp_50_red.png" ,30, 400, -15, 200);
  
  size(1920, 1080, P3D);
  hint(ENABLE_DEPTH_SORT);
  frameRate(24);
}

void draw() {
  background(255, 255, 255);
  updateDevRoom();
  
  gStarBlue.update();
  gStarRed.update();
}

After writing the saver, Padawan clicked the Run button. test_billboard.gif

Master “How are you?”

Padawan “Yeah. It’s displayed perfectly at the specified position.” Padawan “I’m too easy to cry and cry!!!”

4 hours 20 minutes | Galaxy Class

Master: “Okay. Then use the Billboard class.” Master “I will draw the stars of the galaxy”

Master “I need to draw at least 1000…” Padawan “Hee! 1000?” Master “Oh yes.”

Master “So let’s use random numbers for position designation” Master “method called random()” Master “Let me explain a little because it returns random numbers”

  • This method returns a floating point number in the range 0 <= result <100, if you say
    random(100),
  • If you say random(3, 10), it will return a floating point number in the range 3 <= result <10.

Master “Given these,” Master “The Galaxy class will look like this”

Galaxy class

Category Item Description
class Galaxy Galaxy class. Display about 1000 galaxy stars. Each star is implemented by Billboard class. Images and coordinates to be pasted on the billboard are determined by random numbers.
An array of field mStars Billboard.
method update Draw all stars.

The specifications for drawing the stars are as follows.

  • ZX direction
    The distance from the origin (0, 0, 0) is in the range of 4000 to 5000. In other words, make it fit between the circle with radius 4000 and the circle with radius 5000 centered on the origin.
  • Y direction
    Range from -3000 to 3000.
  • Randomly displays five types of blue, white, yellow, orange, and red from the highest surface temperature of stars.

Master “Then, create a Galaxy in a new tab,” Master “I’ll implement it there.”

Padawan “Well, 2π is 180°…” Padawan “Aha! You may remember (laughs)”

Galaxy.pde


// The stars that make up the galaxy
public class Galaxy {
  private Billboard[] mStars = new Billboard[1000]; // Billboard with only the number of stars

  // constructor
  public Galaxy() {
    // Initialize the image array
    PImage[] images = new PImage[] {
      loadImage("gp_50_blue.png"),
      loadImage("gp_50_white.png"),
      loadImage("gp_50_yellow.png"),loadImage("gp_50_orange.png"),
      loadImage("gp_50_red.png"),
    };

    // loop for the number of stars
    for (int i = 0; i <mStars.length; i++) {
      float angle = random(TWO_PI); // The angle on the ZX plane is random within the range of 0 to 360° (2π)
      float radius = random(4000, 5000); // The distance from the origin on the ZX plane is randomly within the range of 4000-5000
      float x = sin(angle) * radius; // X coordinate of star
      float z = cos(angle) * radius; // Z coordinate of star
      float y = random(-3000, 3000); // The Y coordinate of the star is random in the range -3000 to 3000

      int n = (int)random(5); // The image to be pasted on the billboard is randomly selected from 5 types
      mStars[i] = new Billboard(images[n], 50, x, y, z); // generate billboard and store in array
    }
  }
  
  // Transform all stars and draw
  public void update() {
    for (Billboard star: mStars) {
      star.update();
    }
  }
}

Padawan “Well…” Padawan “If you put 5 images in an array and select them randomly” Padawan “I’ve downcasted to int type, but”

Padawan (If it is a positive number, it will be cut off!) (Footnote: see Episode I)

Padawan “But is this all right?” Master “If that’s the case, I’ll actually move it and check it.”

Padawan “Um, open the Simulator tab,…”

Padawan “This is because you only make objects and call them.” Padawan “Winning! Winning! (laughs)”

Simulator.pde


//Billboard gStarBlue; // ---
//Billboard gStarRed; // ---
Galaxy gGalaxy; // +++

void setup() {
//gStarBlue = new Billboard("gp_50_blue.png" ,50, 400, -25, 400); // ---
//gStarRed = new Billboard("gp_50_red.png" ,30, 400, -15, 200); // ---
  gGalaxy = new Galaxy(); // +++
  
  size(1920, 1080, P3D);
  hint(ENABLE_DEPTH_SORT);
  frameRate(24);
}

void draw() {
  background(255, 255, 255);
  updateDevRoom();
  
//gStarBlue.update(); // ---
//gStarRed.update(); // ---
  gGalaxy.update(); // +++
}

Padawan `````// —is the deleted one,'' Padawan "// +++``` is added!”

Padawan “Goge! Gopher, the colors are noisy (laughs)” image.png

Padawan “Oh, but all the colors are displayed.” Padawan “It looks like the downcast was good!”

4:30 | CelestialObject class

Master “Then, what to make next” Master “Karugamo is a billboard”

Padawan “Eh? Karugamo???”

Master “Yes, like Karugamono parents and children.” Master “It’s a billboard that can pull children.”

Padawan (Good, Karugamo…) Padawan (I’m jealous because I have neither dad nor mom)

Master “When I made Learning.pde a while ago” Master “Do you remember reflecting the Earth’s local coordinates on the moon?”

Padawan “Oh, the moon is spinning around thinking only about its place” Padawan “Isn’t it self-conscious?”

Master “Yes, the sun that pulls the planet,” Master “A behavior common to all planets that have satellites” Master “I’ll mount it on the billboard.”

Padawan “Are you adding to the billboard?” Padawan “But the stars of the galaxy don’t bring children!”

Master “Yes.” Master “Well, you can implement it directly in the Billboard class.” Master “You can create a new class that inherits Billboard”

Padawan “Let’s make a new one!”

Master “And then name it CelestialObject.” Master “Let’s implement the following functions”

CelestialObject class

Category Item Description
class CelestialObject Parent class of revolution body. You can have multiple children (billboards) that are affected by your local coordinate system.
extends Billboard  
field mChildren A list of children.
method addChild() Add a child.
method update() Override. When you’re done transforming and drawing your own, call update() on all of your children.

Master “Create a CelestialObject in a new tab,” Master “Try it there”

Padawan “Well, reference, reference…” Padawan “Oh! You can use ArrayList!”

Processing Official Site-Reference-ArrayList

Padawan “Rururu~♪” Padawan “List and medicine are generic~♪” Padawan “Speaker is Generec~♪” Padawan “That…”

CelestialObject.pde


// Celestial class (draw if there are children)
public class CelestialObject extends Billboard {
  private ArrayList<Billboard> mChildren = null; // children list

  // constructor
  // param imageName Image file name
  // param w celestial body (billboard) width
  // param x X coordinate of celestial body (billboard)
  // param y Y coordinate of celestial body (billboard)
  // param z Z coordinate of celestial body (billboard)
  public CelestialObject(String imageName, int w, float x, float y, float z) (
    super(imageName, w, x, y, z);
  }

  // add children
  public void addChild(Billboard child) {
    if (mChildren == null) {
      mChildren = new ArrayList<Billboard>();
    }
    mChildren.add(child);
  }
  
  public void update() {
    pushMatrix();
      transform();
      turnToCamera();
      drawSelf(); // draw yourself
      updateChildren(); // draw all children
    popMatrix();
  }

  private void updateChildren() {
    if (mChildren != null) {
      for (Billboard child :mChildren) {
        child.update();
      }
    }
  }
}

Padawan “Em,” Padawan “Don’t make an ArrayList with a constructor.” Padawan “Only when adding the first child with addChild()” Padawan “I tried to make it”

Padawan “And overriding update(),” Padawan “Before you call popMatrix()” Padawan “I tried to update”

Padawan “Then, the children carved into updateChildren(),” Padawan “I made this private because I wouldn’t use it anywhere else!”

Padawan “Ah, but before calling updateChildren()” Padawan “turnToCamera() and drawSelf()” Padawan “with pushMatrix() and popMatrix()” Padawan “Should I have tied it up?”

Master “Okay, then.” Master “Let’s try it by actually moving it”

Master “Add to Simulator.pde” Master “Make the sun and its child and try it out.”

Master “If you only update() the sun” Master “It’s important that other planets are drawn”

Padawan “Yeah. Then…” Padawan “Let’s harden the front, back, left and right of the sun with the strongest team (laughs)”

Simulator.pde


Galaxy gGalaxy;
CelestialObject gSun; // +++

void setup() {
  gGalaxy = new Galaxy();
  gSun = new CelestialObject("gp_sun.png", 300, 0, 0, 0); // +++

  CelestialObject earth = new CelestialObject("gp_earth.png", 100, -200, 0, 0); // +++
  CelestialObject jupiter = new CelestialObject("gp_jupiter.png", 100, 200, 0, 0); // +++CelestialObject saturn = new CelestialObject("gp_saturn.png", 100, 0, 0, -200); // +++
  CelestialObject uranus = new CelestialObject("gp_uranus.png", 100, 0, 0, 200); // +++
  
  gSun.addChild(earth); // +++
  gSun.addChild(jupiter); // +++
  gSun.addChild(saturn); // +++
  gSun.addChild(uranus); // +++

  size(1920, 1080, P3D);
  hint(ENABLE_DEPTH_SORT);
  frameRate(2);
}

void draw() {
  background(255, 255, 255);
  updateDevRoom();
  
  gGalaxy.update();
  gSun.update(); // +++
}

Padawan “That????” Padawan “Something strange…”

Padawan “Are you stopped?” celestial_object.gif Padawan “Ah, it’s stopped…” Padawan “It’s working with the camera!!!”

Padawan “Uranus in front of the sun can always see the male figure (laughs)”

Padawan “Sokka…” Padawan “turnToCamera() or…”

Padawan tried rewriting CelestialObject.pde as follows:

CelestialObject.pde


...
  public void update() {
    pushMatrix();
      transform();
      pushMatrix(); // +++
        turnToCamera();
        drawSelf(); // draw yourself
      popMatrix(); // +++
      updateChildren(); // draw all children
    popMatrix();
  }
...

celestial_object2.gif

Master “Yeah. turnToCamera() is.” Master “Reset the rotation components of the local and world coordinate systems” Master “Because it matches the camera coordinate system,”

Master “Thinking around the world coordinate system” Master “There is an object that should be stationary there.” Master: “It moves with the camera.”

4:40 | OrbitalObject class

Master “OK. Then,” Master “The rest is the creation of classes that behave like planets and satellites.”

Master “What attributes do you need for orbital bodies?”

Padawan “Um, radius,…” Padawan “Ah! It’s the speed to spin!!!”

Master “Yeah. The specs will look something like this.”

OrbitalObject class

Category Item Description
class OrbitalObject Orbital class. It makes a circular motion around the parent celestial body. Draw the orbit of the celestial body.
extends CelestialObject  
field mRadius revolution radius
field mPeriod revolution period
mehtod transform() Override. Coordinates are converted according to the revolution radius and revolution cycle.

Padawan “Yeah! If you can do this, you’re almost done!”

Master “Yes, but…” Master “We don’t have the data we need to simulate yet.”

Master “So, now I’m at the Republican Knights Library in Temple.” Master “Collecting information about the solar system”

Master “In the meantime, proceed with your implementation with Gopher.” Master “And, if possible, to the point where the orbit of the celestial body is displayed” Master “Try it alone”

Padawan “Yeah! Easy victory (laughs)”

Master “Then I’m going to the library!” Master “Send collected information as needed”

Padawan (Why is the master always leaving me alone?) Padawan (I don’t like me…)

Padawan (But I’m not lonely today with Gopher) Padawan

Padawan “Well,…” Padawan “I just turned it around, so I’m fine,” Padawan “What should I do to draw a circular trajectory?”

Padawan “Oh! circle() has a method!”

Processing Official Site-Reference-circle

Padawan “But I can only specify x and y…” Padawan “What happens if I draw in P3D?”

Padawan added to Simulator.pde as follows and executed it.

Simulator.pde


...
void draw() {
  background(255, 255, 255);
  updateDevRoom();
  
  gGalaxy.update();
  gSun.update();
  
  fill(192, 192, 255); // +++
  circle(0, 0, 500); // +++
}

Padawan “Oh, I could draw (laughs)” image.png

Padawan “Then I should defeat this Maru…” Padawan “Um, my thumb is X, my index finger is Y…”

Padawan “Ah! You can turn it with your thumb!”

Simulator.pde


...
void draw() {
  background(255, 255, 255);
  updateDevRoom();
  
  gGalaxy.update();
  gSun.update();
  
  rotateX(HALF_PI); // +++
  fill(192, 192, 255);
  circle(0, 0, 500);
}

image.png

Padawan “Ah! It’s too easy to cry (laughs)” Padawan “Then all I have to do is make a class!”

Padawan “Em,” Padawan “Make OrbitalObject in new tab,”

OrbitalObject.pde


// A celestial body revolving around a circular orbit
public class OrbitalObject extends CelestialObject {
  private float mRadius; // revolution radius
  private int mPeriod; // revolution period (ms)

  // constructor
  // param parent orbital mother star
  // param imageName Image file name
  // param w Width of revolution body (billboard)
  // param radius revolution radius
  // param period Revolution period (seconds)
  public OrbitalObject(CelestialObject parent, String imageName, int w, float radius, int period) {
    super(imageName, w, 0, 0, 0);
    mRadius = radius;
    mPeriod = period * 1000;
    parent.addChild(this); // add yourself to the mother star
  }

  // Transform and draw
  public void update() {
    updateOrbit();
    super.update();
  }

  // Coordinate transformation
  protected void transform() {
    int ms = millis();
    float rd = TWO_PI * ms / mPeriod;
    rotateY(rd);
    translate(mRadius, 0, 0);
  }
  
  // draw the trajectory
  protected void updateOrbit() {
    pushMatrix();
      rotateX(HALF_PI);
      pushStyle();
        noFill();
        stroke(48, 48, 48);
        strokeWeight(1);
        circle(0, 0, mRadius * 2);
      popStyle();
    popMatrix();
  }

}

Padawan “Em,”

  • public OrbitalObject(...) {...}
    You can specify the radius, speed and mama with the arguments of the constructor.
    • super(imageName, w, 0, 0, 0);
      For the time being, the place is cheated with (0, 0, 0),
    • mPeriod = period * 1000;
      I’ll take it later in “milliseconds” because it’s a mess, but I’m generous, so I decided to specify it in “seconds”.
    • parent.addChild(this);
      Because it’s a pain, add it to Mom here.
      You don’t need to bring your mom to the field!
  • public void update() {...}
    I overwrote the super-san* updateOrbit();
    Draw only yourself,
    • super.update();
      Leave it to Super-san!
  • protected void updateOrbit() {...}
    I draw Maru!
    • rotateX(HALF_PI);
      Half of the pie 90 degrees~♪ I already remembered it perfectly (laughs)
    • circle(0, 0, mRadius * 2);
      Maru needs a diameter, so I doubled it!

4:50 | SolarSystem Class

Padawan: “I think it’s better to have a class for the Taiyo family…” Padawan “Is it like this?”

SolarSystem class

Category Item Description
class SolarSystem Solar system class. Display the stars of the solar system. Sun implements CelestialObject, planets and satellites implement OrbitalObject class.
method update Draw all stars.

Padawan “Em,” Padawan “Make SolarSystem with new tab,”

Padawan “I don’t know the size of the star and how it turns” Padawan “The parameters are appropriate for the time being.”

SolarSystem.pde


// solar system class
public class SolarSystem {
  private CelestialObject mSun; // sun

  // constructor
  public SolarSystem() {
    mSun = new CelestialObject("gp_sun.png", 200, 0, 0, 0);

    OrbitalObject earth =
      new OrbitalObject(mSun, "gp_earth.png", 50, 333, 16); // Earth
      new OrbitalObject(earth,"gp_moon.png", 25, 77, 7); // month

    new OrbitalObject(mSun, "gp_mercury.png", 30, 111, 8); // Mercury
    new OrbitalObject(mSun, "gp_venus.png", 40, 222, 12); // Venus
    new OrbitalObject(mSun, "gp_mars.png", 40, 444, 20); // Mars
    new OrbitalObject(mSun, "gp_jupiter.png",170, 555, 24); // Jupiter
    new OrbitalObject(mSun, "gp_saturn.png", 160, 666, 28); // Saturn
    new OrbitalObject(mSun, "gp_uranus.png", 80, 777, 32); // Uranus
    new OrbitalObject(mSun, "gp_neptune.png", 70, 888, 36); // Neptune
  }

  // Transform and draw all the stars
  public void update() {
    mSun.update();
  }
}

Padawan “Oh, is this enough?” Padawan “From the moon down, there’s no need for variables (laughs)”

Padawan “The rest is a rewrite of the Simulator tab…”

Simulator.pde


Galaxy gGalaxy;
//CelestialObject gSun; // ---
SolarSystem gSolarSystem; // +++


void setup() {
  gGalaxy = new Galaxy();
  gSolarSystem = new SolarSystem(); // +++
//gSun = new CelestialObject("gp_sun.png", 300, 0, 0, 0); // ---

//CelestialObject earth = new CelestialObject("gp_earth.png", 100, -200, 0, 0); // ---
//CelestialObject jupiter = new CelestialObject("gp_jupiter.png", 100, 200, 0, 0); // ---
//CelestialObject saturn = new CelestialObject("gp_saturn.png", 100, 0, 0, -200); // ---
//CelestialObject uranus = new CelestialObject("gp_uranus.png", 100, 0, 0, 200); // ---

//gSun.addChild(earth); // ---
//gSun.addChild(jupiter); // ---
//gSun.addChild(saturn); // ---
//gSun.addChild(uranus); // ---
  
  size(1920, 1080, P3D);
  hint(ENABLE_DEPTH_SORT);
  frameRate(24);
}

void draw() {
  background(255, 255, 255);
  updateDevRoom();
  
  gGalaxy.update();
  gSolarSystem.update(); // +++
//gSun.update(); // ---
//rotateX(HALF_PI); // ---
//fill(192, 192, 255); // ---
//circle(0, 0, 500); // ---
}

Padawan “Aha! It’s too easy, goose bumps (laughs)”

orbital_object.gif

5 am | Advance implementation

5: 00 | Organize information on planetary systems

Padawan “Ah! Data has arrived from the master!”

After looking at the information for a while, Padawan arranged 18 stars in order from the largest celestial body belonging to the solar system.

Astronomical object Home star Average diameter Average revolution radius Revolution period        
Sun   1,392,000 km            
Jupiter Sun 139,822 km 778,412,010 km 4,332.43 days        
Saturn Sun 116,464 km 1,426,725,400 km 10,786.62 days        
Uranus Sun 50,724 km 2,870,990,000 km 30,773.41 days        
Neptune Sun 49,244 km 4,495,060,000 km 60,189.55 days        
Earth Sun 12,742 km 149,597,871 km 365.25 days        
Venus Sun 12,104 km 108,208,930 km 224.70 days        
Mars Sun 6,780 km 227,920,000 km 686.98 days        
Ganymede Jupiter 5,262 km 1,070,400 km 7.16 days        
Titan Saturn 5,152 km 1,221,865 km 15.95 days        
Mercury Sun 4,879 km 57,910,000 km 87.97 days        
Callisto Jupiter 4,821 km 1,882,700 km 16.69 days        
Io Jupiter 3,643 km 421,700 km 1.77 days        
Month Earth 3,474 km 384,400 km 27.33 days        
Europa Jupiter 3,122 km 671,100 km 3.55 days        
Triton Neptune 2,707km 354,759km (reverse) 5.88 days        
Pluto Sun 2,370km 5,900,898,441km 90,487.28daysEllis Sun 2,326 km 10,139,893,274 km 203,824.11 days

Padawan “Oh! The number is so big that it doesn’t come out at all…”

Therefore, Padawan calculated the conversion value when the diameter of the earth was 50 px and the revolution period was 60 seconds, and arranged the relationship between each celestial body and the mother star.

Stars Planets Satellites Diameter Revolution radius Revolution period
Sun     5,462.25 px    
  Mercury   19.15 px 227,240.62 px 14.45 s
  Venus   47.49 px 424,615.17 px 36.91 s
  Earth   50.00 px 587,026.65 px 60.00 s
    Month 13.63 px 1,508.40 px 4.49 s
  Mars   26.60 px 894,365.09 px 112.85 s
  Jupiter   548.67 px 3,054,512.67 px 711.69 s
    Io 14.30 px 1,654.76 px 0.29 s
    Europa 12.25 px 2,633.42 px 0.58 s
    Ganymede 20.65 px 4,200.28 px 1.18 s
    Callisto 18.92 px 7,387.77 px 2.74 s
  Saturn   457.01 px 5,598,514.36 px 1,771.93 s
    Titan 20.22 px 4,794.64 px 2.62 s
  Uranus   199.04 px 11,265,853.08 px 5,055.18 s
  Neptune   193.23 px 17,638,753.73 px 9,887.40 s
    Triton 10.62 px 1,392.09 px (reverse) 0.97 s
  Pluto   9.30 px 23,155,307.02 px 14,864.44 s
  Ellis   9.13 px 39,789,253.15 px 33,482.40 s

Padawan “This is too big…”

Padawan “But as Master said” Padawan “Is the moon 4 stars worth the same as the Earth…” Padawan “It’s bigger than Mama” Padawan (I want to be big…) Padawan (I want a mom…)

Padawan “The rest…” Padawan “The stars in Io are so fast”

Padawan “Is the star Triton in the opposite direction?” Padawan “Cuckee!”

5:10 | Fix the bug

As Padawan struggled with what to do with the celestial parameters, an old gentleman appeared there.

Supreme Chairman “Hi! Padawan!” Chairman “I’m hearing your rumors from the Grand Master.” Chairman “How is it? Is it all right?”

Padawan “Ah? Uncle??”

Chairman “Oh! I’m sorry for being late…” Chairman “I’m just an old man who chairs the Senate (laughs).”

Padawan “Gee! Supreme Chair??” Padawan “Isn’t that a better person than the Grand Master?”

Padawan “Hmm, uncle, isn’t it a great people meeting now?” Supreme Chair “The Parliament just closed safely”

Padawan “Ohhhhh…” Padawan “Will the war begin?”

Chairman “War should not be possible if possible” Chairman “But sometimes” Chairman: “There are times when we must protect something that is more important than life.” Chairman, “War is needed in those times.”

Padawan “Hmm…” Padawan “I don’t really understand (laughs)”

Chairman “By the way, how is the progress of the simulator?” Padawan “I can afford it. I wonder if I replace the image…”

Chairman “Oh! Great!” Supreme Chairman “Then, Padawan-kun’s Homemade Saver” Chairman “Can you show me?”

Padawan “Cheap!” Padawan “But can’t you slay your uncle??”

After watching the saver for a while, the chairman opened his mouth slowly.

Chairman “I see, I write well, but…” Chairman “I think it’s better to make two changes”

Padawan “Eh? Did you find a bug? Uncle awesome!”

Chairman “One is the coordinates of each planet and satellite at the start” Chairman “Because everyone starts from an angle of 0” Chairman “It will start from the state of series of planets” Chairman “Because it’s a bit unnatural” Chairman “It would be better to scatter the starting points with random numbers.”

Padawan “I see…”

Chairman “The other problem is” Chairman “It is a formula to determine the coordinates of the revolution body”

OrbitalObject.pde


...
float rd = TWO_PI * ms / mPeriod;
...

Padawan “Eh????” Padawan “What’s wrong with this?”

Padawan “Well…” Padawan “mPeriod is the time (milliseconds) required for one lap” Padawan “Because ms is the elapsed time (milliseconds),”

Padawan “For example, 100 places per round” Padawan “If it takes 50, it’s just half of one lap (0.5),” Padawan “One half of 360° (TWO_PI) is 180° (PI) and it fits”

Padawan “Around 100 rounds” Padawan “If it costs 150, it’s a round and a half (1.5).” Padawan “Around 360° (TWO_PI), half added to 540°,”

Padawan “That’s right!”

Padawan “Master said that it’s okay to go over 360°…” Padawan “Eh?! It should be perfect…”

Chairman “Padawan” Chairman: “That’s not the wrong way to think.” Chairman “The problem is that we are using floating point numbers.”

Padawan “Eh???” Padawan “But Processing normally uses float” Padawan “It’s a big number than int!”

Chairman “Yes, the range that can be handled is wider for floats.” Chairman: “The amount of accuracy is sacrificed.”

Chairman “360 or 720, while the number is small” Supreme Chairman “(as long as there are few significant figures) it’s fine” Chairman “But, since the mantissa part can handle about 7 significant digits,” Chairman “I can’t say anything more rigorous.”

Chairman “In the formula to find the angle,” Chairman “After all, the remainder when divided by 360° (or 2π),” Chairman “That is, accuracy is important.”

Chairman “And Floating Point,” Chairman “Similar to the state…”

Chairman: “It’s good when the population is small…” Chairman “But if the population increases to some extent,” Chairman: “A few sacrifices are unavoidable!!!”

Chairman “Do you understand? Padawan-kun…”

Supreme Chairman “Wa-Ru-Ah-!?” Supreme Chairman, “Pa Da Wang, You!!”

Padawan “O, uncle…” Padawan “A little scary…”

Chairman “Oh, sorry, sorry…” Supreme Chairman “The Assembly has just finished” Supreme Chairman “I’m not excited yet (laughs)”

Chairman “Add to OrbitalObject.pde as follows.”

OrbitalObject.pde


...
  protected void transform() {
  //int ms = millis(); // --- comment out
    int ms = millis() + 1000000000; // +++ Addition
    float rd = TWO_PI * ms / mPeriod;
    rotateY(rd);
    translate(mRadius, 0, 0);
  }
...

Chairman “This is a billion milliseconds after launching Saver.” Chairman “I mean, about 11 and a half days later” Chairman “It’s intentional creation”

Padawan clicked the execute button.

Padawan “Gegege!!!” Padawan “The movement is jerks!!!”

Chairman “Open a new sketch and type:”

NewSketch


int period = 3600; // period is 3600 ms (advance 1° every 10 ms)
int[] startingTimes = {// array of starting millisecond values
           0, // Situation a. 1st lap
    36000000, // Status b. 10,001 lap
  1000000800, // Situation c. 277,779 laps
};
println("a.\tb.\tc.");
println("---\t---\t---");
/ / How to increase the angle in 10 millisecond increments under 3 types of elapsed time
for (int n = 0; n <300; n += 10) {
  for (int start: startingTimes) {
    int ms = start + n;
    int rd = round(degrees(TWO_PI * ms / period)) %360;
    print(rd + "°\t");
  }
  println();
}

Chairman “This is the current logic” Chairman “When calculating the revolution body of the 3600 millisecond period,”

Chairman “Situation a. → Immediately after the start of the saver (that is, on the first lap)” Chairman “Situation b. → Just 10,001 laps” Chairman “Situation c. → Just after 277,779 laps”

Chairman, “When 10 ms in 3 different situations” Chairman “It will show how the angle increases.”

Padawan “Because it takes one lap in 3600 milliseconds,” Padawan “It should increase by 1° every 10 milliseconds!”

Chairman “Let’s see the results”

Padawan “Wow, situation c.!!!”

console


a.b.c.
- -- --- ---
0° 0° 352°
1° 1° 0°
2° 2° 0°
3° 3° 0°
4° 4° 0°
5° 5° 0°
6° 6° 0°
7° 7° 8°
8° 8° 8°
9° 9° 8°
10° 10° 8°
11° 11° 8°
12° 12° 8°
13° 13° 8°
14° 14° 8°
15° 15° 8°
16° 16° 8°
17° 17° 8°
18° 18° 8°
19° 19° 8°
20° 20° 16°
21° 21° 16°
22° 22° 16°
23° 23° 16°
24° 24° 16°
25° 25° 16°
26° 26° 32°
27° 27° 32°
28° 28° 32°
29° 29° 32°
```Padawan "The numbers are scattered!"
Padawan "Gege! What is that?"

Chairman "Variable ms, ms / PERIOD"
Supreme Chairman "Do you know that ``quote`` and ```remainder`` are included?"

Padawan "Um, for example, when ```3 and a half laps'',"
Padawan "The time it took for the answer of division to be ``3 laps``"
Padawan "The remainder of the division is the time it took for ```half''`..."

Supreme Chairman "Yes."
Chairman "ms includes ```3 and a half weeks ``` minutes of time"

Chairman "But what is important when calculating angles?"
Supreme Chairman ```'' not three minutes ``` time
Supreme Chairman "It's time for the remaining `"half a round``."

Supreme Chairman "So, the surplus of this ```half circle''`"
Chairman, "Before sacrificing precision in floating point arithmetic"
Chairman "I think it's better to ask for an integer type that maintains precision."

Padawan "Ah!

Padawan rewrote the saver as follows:


#### **`NewSketch`**
```java

...
  //int rd = round(degrees(TWO_PI * ms / period)) %360; // Before correction
    int rd = round(degrees(TWO_PI * (ms %period) / period)); // after modification
...

Padawan “Oh! Perfect!!!”

console


a.b.c.
- -- --- ---
0° 0° 0°
1° 1° 1°
2° 2° 2°
3° 3° 3°
4° 4° 4°
5° 5° 5°
6° 6° 6°
7° 7° 7°
8° 8° 8°
9° 9° 9°
10° 10° 10°
11° 11° 11°
12° 12° 12°
13° 13° 13°
14° 14° 14°
15° 15° 15°
16° 16° 16°
17° 17° 17°
18° 18° 18°
19° 19° 19°
20° 20° 20°
21° 21° 21°
22° 22° 22°
23° 23° 23°
24° 24° 24°
25° 25° 25°
26° 26° 26°
27° 27° 27°
28° 28° 28°
29° 29° 29°

Chairman “The rest is because the return value of millis() is of type int” Chairman “2,147,483,648 ms after the start of saver,” Chairman “That is, after about 25 days” Chairman “I can expect an overflow,” Chairman: “You don’t have to consider that much this time!”

Padawan “Wow! Uncle friendly!!!!”

Padawan “Then fix the OrbitalObject.” Padawan “Trying to fall apart from the beginning” Padawan “I will not let it run!”

OrbitalObject.pde


...
public class OrbitalObject extends CelestialObject {
  private float mRadius; // revolution radius
  private int mPeriod; // revolution period (ms)
  private float mStart; // +++ Addition
  ...
  public OrbitalObject(CelestialObject parent, String imageName, int w, float radius, int period) {
    ...
    mPeriod = period * 1000;
    mStart = random(TWO_PI); // +++ Addition
    parent.addChild(this); // add yourself to the mother star
  }
  ...
  protected void transform() {
  //int ms = millis(); // return later
    int ms = millis() + 1000000000; // erase later
  //float rd = TWO_PI * ms / mPeriod; // --- delete
    float rd = mStart + TWO_PI * (ms %mPeriod) / mPeriod; // +++ Append
    ...
  }
  ...
}

Padawan “I fell apart from the beginning” Padawan “I’m no longer throbbing!”

Padawan finally arranged the transform() method as follows and finished the slaughter of OrbitalObject.

OrbitalObject.pde


...
  // Coordinate transformation
  protected void transform() {
    int ms = millis();
    float rd = mStart + TWO_PI * (ms %mPeriod) / mPeriod;
    rotateY(rd);
    translate(mRadius, 0, 0);
  }
...

Padawan “Old man great!” Padawan “Thank you!”

5 hours 20 minutes | Processing images

Padawan “Oh! Master sent something!”

To Padawan I will send it because I have collected images of celestial bodies of the solar system I will be talking to the Grand Master, so do the following by yourself ・Galaxy stars create their own particles using Gimp ・Solar objects in the solar system are processed with Gimp after confirming the image license. ・Import the created image to the saver

Chairman “I’m looking behind you, so don’t worry and keep working.”

Make particles with Gimp

Padawan launched Gimp to create particle images.

Gimp Official Site-Download Page

Padawan “Well, first, make an image with a black background.” Padawan “Let’s try it with the light effect!”

Padawan “Choose Light Effect`,” particle_1.png

Padawan “Adjust the size with distance,” particle_2.png

Padawan “Select color or adjust the position and press [OK].” particle_3.png

Padawan “Complete!” particle_4.png

Padawan “Hmm? But this…” Padawan “How can I make the back transparent????”

Chairman “If so,” Chairman “I think it would be good to process with color to transparency ...

Chairman “Choose from the menu,” particle_5.png

Chairman “However, if the image has no alpha channel” Chairman “First, the menu item is two higher” Supreme Chairman “You need to select Add Alpha Channel

Chairman “Then, choose a color (black this time) to make it transparent.” particle_6.png

Chairman “How about this?” particle_7.png

Padawan “Oh!!!” Padawan “Old man great!!!”

Padawan “Then the rest of the four colors are also” Padawan “Make the same thing…”

Padawan “Galaxy is full of stars” Padawan “Don’t reduce the image size before saving” Padawan “Don’t forget!

Watch your license

Padawan “Well, next is a license…”

Padawan “Eh????” Padawan “Can’t I use images arbitrarily????”

Classification of licenses such as images

Symbol Claim Description    
PDM public domain Public Domain Mark
A mark that clearly indicates that the original author has waived his or her protection period has expired.
However, be aware of the following points even in works that are in the public domain.
-Since there is authority rights of authors, it is prohibited to modify works that detract from the personality of the author.
-Things with trademark rights may infringe them.
-In the case of photographs of people, etc., there is a risk of **portrait right
infringement and publicity right infringement.
   
CC0 public domain A mark that declares that you do not have any rights.
Differences from PDMCC
Somerightsreserved Aclaimforlimitedrights.
CreativeCommonsLicense
All rights reserved Those claiming copyright.    

Padawan “I thought it would be easier to use public domain, but.” Padawan “I have to be careful…”

Padawan “So, if you take a closer look at CC,” Padawan “There are 4 kinds of conditions…”

CC license type

Mark meaning
BY Displaying credits for your work
SA Publish under CC license with the same combination as the original work
ND Do not modify the original work
NC Do not use for commercial purposes

Padawan “And there are 6 different licenses that combine these,” Padawan “Ugh! It’s complicated!!”

Padawan “Ah, but are these two types related in this time?”

CC license combination

Mark meaning
CC BY Licenses allowed for modification and commercial use, provided that the credits of the original author are displayed. ..
CC BY-SA Displays the credits of the original author, and if modified, publish on CC BY-SA A license that allows secondary use for commercial purposes.
Others CC BY-ND,CCBY-NC,CCBY-NC-SA,CCBY-NC-ND.

Padawan “Then, the license for the image sent by the master is…”

** Image list before processing **

Image License Source          
  Sun ”> Created by NASA
(public domain)
Wikimedia
Commons
     
  Mercury Created by NASA
(public domain)
Wikimedia
Commons
     
Venus Created by NASA
(public domain)
Wikimedia
Commons
       
  Earth Created by NASA
(public domain)
Wikimedia
Commons
     
  Month Luc Viatour
(CC BY-SA 3.0)
Wikimedia
Commons
     
Mars ESA & MPS for OSIRIS Team
MPS/UPD/LAM/IAA/RSSD/
INTA/UPM/DASP/IDA
(CC BY-SA IGO 3.0)
Wikimedia
Commons
       
Jupiter Created by NASA
(public domain)
Wikimedia
Commons
       
Io Created by NASA
(public domain)
Wikimedia
Commons
       
  Europa Created by NASA
(public domain)
Wikimedia
Commons
     
  Ganymede Created by NASA
(public domain)
Wikimedia
Commons
     
  Callisto Created by NASA
(public domain)
Wikimedia
Commons
     
Saturn Created by NASA
(public domain)
Wikimedia
Commons
タイタン <imgwidth=”100”src=”https://upload.wikimedia.org/wikipedia/commons/0/09/Two_Halves_of_Titan.png”> CreatedbyNASA
(publicdomain)
Wikimedia
Commons
  天王星 Created by NASA
(public domain)
Wikimedia
Commons
     
  海王星 WolfmanSF
(CC BY 2.0)
Wikimedia
Commons
     
  トリトン Created by NASA
(public domain)
Wikimedia
Commons
     
  冥王星 Created by NASA
(public domain)
Wikimedia
Commons
     
  エリス Created by NASA
(public domain)
Wikimedia
Commons
     

パダワン「なるほど、注意しなきゃ!」

Gimp で背景を透明にする

パダワン「そしたら、お星さまの背景を透明にするぞ」

パダワン「画像を開いたら、アルファチャンネルを追加して、、」 tp_1.png

パダワン「楕円選択ツールを選んで、」 パダワン「ドラッグドロップで範囲を指定したら、、」 tp_2.png

パダワン「選択範囲を反転させて、、」 tp_3.png

パダワン「消去する」 パダワン「で、これは DELETE キーでも消えますよ!っと …」 tp_4.png

パダワン「あとは、名前を付けてエクスポートで、」 パダワン「拡張子を .png で保存すればオッケーですよ!っと …」 tp_6.png

最高議長「おお!ひとつ言い忘れていた …」 最高議長「さっきのパーティクルの画像もそうであるが、」 最高議長「Gimp で作成した画像を Processing で開こうとすると」 最高議長「エラーになることがあるから、その場合は、」 最高議長「エクスポートのオプションで」 最高議長「赤枠のあたりを調整してみるといいだろう」 tp_7.png

パダワンは最終的に以下の画像を作成した。

加工後の画像リスト

天体 加工画像 オリジナル画像
のライセンス
オリジナル
画像の出典
         
太陽 Sun.png
Sun.png
Created by NASA
(public domain)
Wikimedia
Commons
         
水星 Mercury.png
Mercury.png
Created by NASA
(public domain)
Wikimedia
Commons
         
金星 Venus.png
Venus.png
Created by NASA
(public domain)
Wikimedia
Commons
         
地球 Earth.png
Earth.png
Created by NASA
(public domain)
Wikimedia
Commons
         
Moon.png
Moon.png
Luc Viatour
(CC BY-SA 3.0)
Wikimedia
Commons
火星 Mars.png
<imgwidth=”100”alt=”Mars.png”src=”https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/10db18bc-8cce-8dcc-0352-70e2c5de6b03.png”>
ESA&MPSforOSIRISTeam
MPS/UPD/LAM/IAA/RSSD/
INTA/UPM/DASP/IDA
(CCBY-SAIGO3.0)
Wikimedia
Commons
 
木星 Jupiter.png
Jupiter.png
Created by NASA
(public domain)
Wikimedia
Commons
         
イオ Io.png
Io.png
Created by NASA
(public domain)
Wikimedia
Commons
         
エウロパ Europa.png
Europa.png
Created by NASA
(public domain)
Wikimedia
Commons
         
ガニメデ Ganymede.png
Ganymede.png
Created by NASA
(public domain)
Wikimedia
Commons
         
カリスト Callisto.png
Callisto.png
Created by NASA
(public domain)
Wikimedia
Commons
         
土星 Saturn.png
Saturn.png
Created by NASA
(public domain)
Wikimedia
Commons
         
タイタン Titan.png
Titan.png
Created by NASA
(public domain)
Wikimedia
Commons
         
天王星 Uranus.png
Uranus.png
Created by NASA
(public domain)
Wikimedia
Commons
         
海王星 Neptune.png
Neptune.png
WolfmanSF
(CC BY 2.0)
Wikimedia
Commons
         
トリトン Triton.png
Triton.png
Created by NASA
(public domain)
Wikimedia
Commons
         
冥王星 Pluto.png
Pluto.png
Created by NASA
(public domain)
Wikimedia
Commons
         
エリス Eris.png
Eris.png
Created by NASA
(public domain)
Wikimedia
Commons
         
白い恒星 WhiteStar50.png
WhiteStar50.png
Created by y-bash 本稿          
青い恒星 BlueStar50.png
BlueStar50.png
Created by y-bash 本稿          
黄色い恒星 YellowStar50.png
YellowStar50.png
Created by y-bash 本稿          
オレンジの恒星 OrangeStar50.png
OrangeStar50.png
Created by y-bash 本稿   Red Star RedStar50.png
RedStar50.png
Created by y-bash This article

I do not claim any rights regarding the contributions (image processing) of the images posted here. However, some unprocessed original works are subject to conditions for secondary use including modification. For example, CC BY has the obligation to display credits, and CC BY-SA has the obligation to publish processed works with the same license. When using these, please follow the conditions of the original work. Also, it is not always guaranteed that the licensor of the original work has all the rights regarding the work, including those singing in the public domain. If you use the images posted here, please take these points into account at your own risk.

Padawan “Fee, I was so concentrated!” Padawan “I’m tired…”

6 am | Complete

6 o’clock 00 | Incorporating processed images

Padawan imported the processed image into the data folder and opened the IDE.

Padawan “Well, first of all, Galactic-chan, ttto”

Galaxy.pde


...
  public Galaxy() {
    // Initialize the image array
    PImage[] images = new PImage[] {
    //loadImage("gp_50_blue.png"), // ---
    //loadImage("gp_50_white.png"), // ---
    //loadImage("gp_50_yellow.png"), // ---
    //loadImage("gp_50_orange.png"), // ---
    //loadImage("gp_50_red.png"), // ---

      loadImage("BlueStar50.png"), // +++
      loadImage("WhiteStar50.png"), // +++
      loadImage("YellowStar50.png"), // +++
      loadImage("OrangeStar50.png"), // +++
      loadImage("RedStar50.png"), // +++
    };
...

Padawan “Next is the Sun family, tsuto…”

SolarSystem.pde


  public SolarSystem() {
  //mSun = new CelestialObject("gp_sun.png", 200, 0, 0, 0); // sun // ---
  //OrbitalObject earth = // ---
  // new OrbitalObject(mSun, "gp_earth.png", 50, 333, 16); // Earth // ---
  // new OrbitalObject(earth,"gp_moon.png", 25, 77, 7); // month // ---
  //new OrbitalObject(mSun, "gp_mercury.png", 30, 111, 8); // Mercury // ---
  //new OrbitalObject(mSun, "gp_venus.png", 40, 222, 12); // Venus // ---
  //new OrbitalObject(mSun, "gp_mars.png", 40, 444, 20); // Mars // ---
  //new OrbitalObject(mSun, "gp_jupiter.png",170, 555, 24); // Jupiter // ---
  //new OrbitalObject(mSun, "gp_saturn.png", 160, 666, 28); // Saturn // ---
  //new OrbitalObject(mSun, "gp_uranus.png", 80, 777, 32); // Uranus // ---
  //new OrbitalObject(mSun, "gp_neptune.png", 70, 888, 36); // Neptune // ---

    mSun = new CelestialObject("Sun.png", 50, 0, 0, 0); // +++
    OrbitalObject mercury = new OrbitalObject(mSun, "Mercury.png", 19, 227, 14); // +++
    OrbitalObject venus = new OrbitalObject(mSun, "Venus.png", 47, 425, 37); // +++
    OrbitalObject earth = new OrbitalObject(mSun, "Earth.png", 50, 587, 60); // +++
               /* moon */new OrbitalObject(earth, "Moon.png", 13, 130, 4); // +++
    OrbitalObject mars = new OrbitalObject(mSun, "Mars.png", 26, 894, 112); // +++
    OrbitalObject jupiter = new OrbitalObject(mSun, "Jupiter.png",100,1527, 711); // +++
               /* io */new OrbitalObject(jupiter, "Io.png", 14, 165, 1); // +++
               /* europa */new OrbitalObject(jupiter, "Europa.png", 12, 263, 2); // +++
               /* ganymede */new OrbitalObject(jupiter, "Ganymede.png",21, 420, 3); // +++
               /* callisto */new OrbitalObject(jupiter, "Callisto.png",19, 590, 4); // +++
    OrbitalObject saturn = new OrbitalObject(mSun, "Saturn.png", 100,2800, 886); // +++
               /* titan */new OrbitalObject(saturn, "Titan.png", 20, 479, 3); // +++
    OrbitalObject uranus = new OrbitalObject(mSun, "Uranus.png", 50,3755,1685); // +++
    OrbitalObject neptune = new OrbitalObject(mSun, "Neptune.png", 50,4410,2472); // +++
               /* triton */new OrbitalObject(neptune, "Triton.png", 11, 139, -1); // +++
    OrbitalObject pluto = new OrbitalObject(mSun, "Pluto.png", 9,5789,3716); // +++
    OrbitalObject eris = new OrbitalObject(mSun, "Eris.png", 9,6632,5580); // +++
  }

Padawan “Solar system is too wide,” Padawan “I made the parameters appropriate (laughs)”

Padawan “The rest is…” Padawan “Because it’s a utility obstruction, keep it out of sight.”

Padawan “Disable automatic camera” Padawan “I will look down on it myself…”

Padawan “Ah! Don’t forget to make it dark…”

Simulator.pde


...
void setup() {
  gGalaxy = new Galaxy();
  gSolarSystem = new SolarSystem();

  setDevRoomAutoCameraEnabled(false); // +++
  setDevRoomVisible(false); // +++
  
  size(1920, 1080, P3D);
  hint(ENABLE_DEPTH_SORT);
  frameRate(24);
}

void draw() {
camera( 100, -4000, 100, 0, 0, 0, 0, 1, 0);
//background(255, 255, 255); // ---
  background(0, 0, 0); // +++
  updateDevRoom();
  gGalaxy.update();
  gSolarSystem.update();
}

Padawan “Oh!” image.gif Padawan “It looks kinda small…”

Padawan “It’s almost moving (laughs)”

6:10 | Control the camera

Padawan “But I’m lonely just watching…”

Chairman “If so, then” Chairman “Why don’t you try it yourself?”

Padawan “Eh?

Chairman “Because I only capture key events” Chairman “It’s not difficult”

Padawan “Old man great!!!”Padawan “The master can only teach me sober techniques”

Chairman “Is that right?” Padawan “Yeah. The basics are important!”

Padawan “It’s still stingy!”

Chairman “Hahaha” Supreme Chairman, “Is it coming to me then?”

Chairman “I am the richest man in the galaxy,” Supreme Chairman “You would be an excellent secretary (laughs)”

Padawan “Eh?? Really?” Padawan “Well, uncle”

Padawan “It’s kind, it’s a cool technique, it teaches me.” Padawan “Moreover, rich!”

Supreme Chairman “Nah Padawan,” Chairman “You should express your emotions more straightly!”

Padawan “But…” Padawan “Master is always always” Padawan “Please suppress your emotions…”

Chairman “It’s an old idea…” Chairman “The times are changing now”

Chairman, “Cry when you are sad,” Chairman “When I want to get angry,” Chairman “It’s better to explode your anger!”

Chairman “I don’t think it’s more human?”

Padawan “Hmm” Padawan “I don’t know anything difficult!”

Padawan “Ah! How about a key event?”

Chairman “If you write a method called keyPressed()” Chairman “He will call me when a key is pressed.” Chairman “I just have to look up the variables key and keyCode.”

Processing Official Site-Reference keyPressed key keyCode

Padawan “I see!” Padawan “When you press a character, that character comes into the key.” Padawan “If you press , it will enter the keyCode!”

Padawan “Uh…” Padawan “I’m too easy to cry (laughs)”

Padawan “Then, make a new class!”

CameraController class

Category Item Description
class CameraController Camera control class. Control the camera position with the keyboard. The camera rotates around the Y axis of the world coordinate system on a plane parallel to the ZX plane, and always directs the line of sight to the origin (0,0,0).
field mRadius Current camera trajectory radius (plane parallel to ZX plane)
field mAngle Current camera horizontal position (angle on orbit plane: deg)
field mYCoord vertical position of the current camera (Y coordinate: px)
field mRadiusSpeed Front-back speed seen from the camera (mRadius change: px/sec)
field mRotationalSpeed Speed in the left-right direction as seen from the camera (change amount of mAngle: deg/sec)
field mVerticalSpeed Vertical speed as seen from the camera (change amount of mYCoord: px/sec)
field mPrevMillis keep the value of the previous millis()
method left() When viewed from the camera’s perspective, decrease the amount of change to the right when moving
・to the right, and decrease the amount of change to the left when moving to the left. increase
method right() When viewed from the camera’s perspective, increase the amount of change to the right when moving
・to the right, and increase the amount of change to the left when moving to the left. Lower
method up() When viewed from the camera’s perspective,
・Increase the ascent speed if ascending, and
・Decrease the descending speed if descending
method down() When viewed from the camera’s point of view,
・If ascending, decrease the ascending speed, and
・If descending, increase the descending speed.
method forward() When viewed from the camera’s perspective,
・Increase the forward speed if forward, and decrease the backward speed if backward.
method backward() When viewed from the camera’s perspective,
-decrease the forward speed if forward,
-increase the reverse speed if backward
method printStatus() Prints the current status of the camera (current position and its change) to the console
method update() Updates the camera position

Padawan “Then, make a new tab (CameraController),” Padawan “I’m going to write a saver…”

CameraController.pde


// camera control class
public class CameraController {
  private float mRadius = 1000; // Current camera orbital radius (plane parallel to ZX plane)
  private float mAngle = 0; // Current horizontal position of camera (angle on orbit plane: deg)
  private float mYCoord = 0; // current camera vertical position (Y coordinate: px)

  private int mRadiusSpeed = 0; // Speed in the front-back direction as seen from the camera (variation of mRadius: px/sec)
  private int mRotationalSpeed = 0; // lateral speed as seen from the camera (mAngle change: deg/sec)
  private int mVerticalSpeed = 0; // Vertical speed as seen from the camera (mYCoord change: px/sec)

  private int mPrevMillis = 0; // Last elapsed time

  // slow down the camera rotation speed
  // From the camera perspective,
  // If you are moving to the right, decrease the amount of change to the right,
  // If moving to the left, increase the amount of change to the left
  public void left() {
    mRotationalSpeed -= 5;
    printStatus();
  }

  // increase the rotation speed of the camera
  // From the camera perspective,
  // If you are moving to the right, increase the amount of change to the right,
  // If moving to the left, decrease the amount of change to the left
  public void right() {
    mRotationalSpeed += 5;
    printStatus();
  }

  // speed up the camera
  // From the camera perspective,
  // If you are climbing, increase the climb speed,
  // If it's descending, slow down
  public void up() {
    mVerticalSpeed -= 25;
    printStatus();
  }
  
  // slow the camera up
  // From the camera perspective,
  // If you are climbing, slow down the climb speed,
  // if descending, increase descent speed
  public void down() {
    mVerticalSpeed +=25;
    printStatus();
  }

  // reduce the amount of change in the camera trajectory radius
  // From the camera perspective,
  // If you are moving forward, increase the speed of forward movement,
  // Decrease the retreat speed if you are retreating
  public void forward() {
    mRadiusSpeed -= 10;
    printStatus();
  }
  
  // Increase the amount of change in the camera's orbit radius
  // From the camera perspective,
  // If you are moving forward, reduce the speed
  // If you are in retreat, increase the retreat speed
  public void backward() {
    mRadiusSpeed += 10;
    printStatus();
  }

  // output the current state of the camera to the console
  public void printStatus() {
    print("(R,A,V)=(");
    print((int)mRadius + "px, "+ (int)mAngle + "deg, "+ (int)mYCoord + "px");
    print(") (dR,dA,dV)=(");
    print(mRadiusSpeed + "px/s, "+ mRotationalSpeed + "deg/s, "+ mVerticalSpeed + "px/s");
    println(")");
  }
  
  // update the camera position
  public void update() {
    int ms = millis();

    if (ms/1000 != mPrevMillis/1000) printStatus(); // Display status every 1 second

    int dt = ms-mPrevMillis; // change in time
    mPrevMillis = ms; // save current time

    // update the orbital radius
    mRadius += mRadiusSpeed * dt / 1000.0;
    mRadius = max(mRadius, 100.0); // stop when it's about to hit the sun
    if (mRadius==100.0) mRadiusSpeed = 0;

    // Update rotation angle
    mAngle += mRotationalSpeed * dt / 1000.0;
    mAngle = mAngle %360; // Normalized to fit between 0 and 360
    if (mAngle <0) mAngle += 360;

    // Update vertical position
    mYCoord += mVerticalSpeed * dt / 1000.0;

    float z = mRadius * cos(radians(mAngle));
    float x = mRadius * sin(radians(mAngle));
    float y = mYCoord;
    camera(x, y, z, 0, 0, 0, 0, 1, 0);
  }
}

Padawan “It was easier to write than I expected!”

Padawan “Ah! But which key should I press?” Padawan “I forgot to think about how it works (laughs)”

Key assignment

Key Turning right Turning left
Increase speed Decrease speed
Decrease speed Increase speed
Key Up Down
Speed up Speed down
Reduce speed Increase speed
Key Forwarding Backing
PgUp Increase speed Reduce speed
PgDn Reduce speed Increase speed

Padawan “Oh! There are no key constants!”

Padawan “ is RIGHT,” Padawan “ is LEFT”

Padawan “ is UP,” Padawan ''↓ is DOWN’’

Padawan “Ahaha! It’s too easy to cry…” Padawan “Hmm?”

Padawan “That? No matter how you look at the reference”Padawan “I can’t find PgUp and PgDn constants”

Chairman “Unfortunately…” Chairman “In Processing” Chairman: “These numbers are not available.”

Padawan “Eh???” Padawan “But there must be a constant” Padawan “I don’t know what value will come in!”

Chairman “Open a new sketch,” Chairman “Try to write as follows”

NewSketch


void setup() {
  size(100, 100, P3D);
}
void draw() {
}
void keyPressed() {
  println("key: "+ key + ", keyCode: "+ keyCode);
}

Chairman “If you move, try typing something!”

Padawan “Oh! Uncle Genius?!”

console


key: a, keyCode: 65 # press the'a' key
key: b, keyCode: 66 # press the'b' key
key: c, keyCode: 67 # press the'c' key
key: d, keyCode: 68 # press the'd' key
key: e, keyCode: 69 # press the'e' key
key: f, keyCode: 70 # press the'f' key
key: g, keyCode: 71 # press the'g' key
key: ?, keyCode: 37 # [←] press key
key: ?, keyCode: 39 # [→] press the key
key: ?, keyCode: 38 # [↑] press key
key: ?, keyCode: 40 # [↓] Press the key
key:, keyCode: 16 # [PgUp] Press the key
key:, keyCode: 11 # [PgDn] Press the key
...

Padawan “16 are PgUp and 11 are PgDn!”

Padawan “Then rewrite the Simulator tab!”

Padawan “Then…” Padawan “I don’t need a utility anymore” Padawan “Oops!”

Simulator.pde


Galaxy gGalaxy;
SolarSystem gSolarSystem;
CameraController gCameraController = new CameraController(); // +++

void setup() {
  gGalaxy = new Galaxy();
  gSolarSystem = new SolarSystem();
  //setDevRoomAutoCameraEnabled(false); // ---
  //setDevRoomVisible(false); // ---
  size(1920, 1080, P3D);
  hint(ENABLE_DEPTH_SORT);
  frameRate(24);
}

void draw() {
//camera( 100, -4000, 100, 0, 0, 0, 0, 1, 0); // ---
  background(0, 0, 0);
//updateDevRoom(); // ---
  gCameraController.update(); // +++
  gGalaxy.update();
  gSolarSystem.update();
}

void keyPressed() {// +++
  final int PAGE_UP = 16; // +++
  final int PAGE_DOWN = 11; // +++
  switch (keyCode) {// +++
    case LEFT: gCameraController.left(); break; // +++
    case RIGHT: gCameraController.right(); break; // +++
    case UP: gCameraController.up(); break; // +++
    case DOWN: gCameraController.down(); break; // +++
    case PAGE_UP :gCameraController.forward(); break; // +++
    case PAGE_DOWN :gCameraController.backward();break; // +++
  } // +++
}

Padawan “Okay! Let’s move it!”

CameraController.gif

Padawan “Oh!”

Padawan “Is it done?”

Padawan “It’s done!??”

Padawan “Completed! This!”

Chairman “Congratulations, Padawan!” Chairman “I think it’s good if the test is passed.”

Supreme Chairman “Oh!

Chairman “I have one request,”

Chairman “Because we have a special Gopher,” Chairman “put him in orbit around the moon” Chairman “Can you test it?”

DSGopher.png

Padawan “Ohhhhhhhhh!!!”

Padawan “What’s wrong with you, Gopher!” Padawan “Is your eyes red?”

Chairman “Well, then I have to go.”

Chairman “D.S.Gopher is secret to everyone.” Supreme Chairman “Uncle and Padawan are the secrets of only two people!”

Padawan “Yes! I understand (laughs)”

Chairman “Oh, then…” Chairman “I asked you to come to me, but…” Chairman “Would you like to think seriously?”

Padawan “Oh!!!” Padawan “Thank you, uncle!” Padawan “I love you uncle!!!”

Padawan “But stop now…”

Padawan “With Master” Padawan “To be a saver for the Galactic Ichi” Padawan “Because I promised!!!”

Padawan “Well, uncle!” Padawan “Thank you for being kind!”

Padawan “And, let me know your skills too” Padawan “Thank you very much!”

6:50 | All simulator code

After a while, Master returned to Padawan.

Master “Oh, have you fallen asleep?” Master “But the simulator seems to have been completed!”

Master “I did my best!”

Master “I’m sure I was tired…” Master “I’m going to sleep now”

With that gentle smile, Master began reviewing Saver.

Folder structure

Sketch folder structure


any-dir
    Simulator
    ├── Simulator.pde
 ├── Billboard.pde
    ├── CelestialObject.pde
    ├── OrbitalObject.pde
    ├── Galaxy.pde
    ├── SolarSystem.pde
    ├── CameraController.pde
 └── data
        ├── WhiteStar50.png
        ├── BlueStar50.png
        ├── YellowStar50.png
        ├── OrangeStar50.png
        ├── RedStar50.png
        ├── Sun.png
        ├── Mercury.png
        ├── Venus.png
        ├── Earth.png
        ├── Moon.png
        ├── Mars.png
        ├── Jupiter.png
        ├── Io.png
        ├── Europa.png
        ├── Ganymede.png
        ├── Callisto.png
        ├── Saturn.png
        ├── Titan.png
        ├── Uranus.png
        ├── Neptune.png
        ├── Triton.png
        ├── Pluto.png
      └── Eris.png

Source code

Expand all code in Simulator.pde
#### **`Simulator.pde`** ```java // solar system simulator Galaxy gGalaxy; // Galactic stars SolarSystem gSolarSystem; // Solar system stars CameraController gCameraController = new CameraController(); // camera control void setup() { gGalaxy = new Galaxy(); gSolarSystem = new SolarSystem(); size(1920, 1080, P3D); hint(ENABLE_DEPTH_SORT); frameRate(24); } void draw() { background(0, 0, 0); gCameraController.update(); gGalaxy.update(); gSolarSystem.update(); } void keyPressed() { final int PAGE_UP = 16; // PgUp keyfinal int PAGE_DOWN = 11; // PgDn key switch (keyCode) { case LEFT: gCameraController.left(); break; case RIGHT: gCameraController.right(); break; case UP: gCameraController.up(); break; case DOWN: gCameraController.down(); break; case PAGE_UP: gCameraController.forward(); break; case PAGE_DOWN: gCameraController.backward();break; } } ```
Expand all code in Billboard.pde
#### **`Billboard.pde`** ```java // billboard class public class Billboard { private PImage mImage; // image to paste on billboard private int mWidth; // billboard width private int mHeight; // height of billboard private float mX; // billboard X coordinate private float mY; // Y coordinate of billboard private float mZ; // Z coordinate of billboard // constructor // param imageName: File name of the image to paste // param w: width of billboard // param x: X coordinate of billboard // param y: Y coordinate of billboard // param z: Z coordinate of billboard public Billboard(String imageName, int w, float x, float y, float z) ( this( loadImage(imageName), w, x, y, z); } // constructor // param image: image to paste // param w: width of billboard // param x: X coordinate of billboard // param y: Y coordinate of billboard // param z: Z coordinate of billboard public Billboard(PImage image, int w, float x, float y, float z) ( mImage = image; mWidth = w; mHeight = w * mImage.height / mImage.width; // Height calculated from image aspect ratio mX = x; mY = y; mZ = z; } // Transform and draw public void update() { pushMatrix(); transform(); turnToCamera(); drawSelf(); popMatrix(); } // Coordinate transformation protected void transform() { translate(mX, mY, mZ); } // draw protected void drawSelf() { float hw = mWidth / 2; float hh = mHeight / 2; float iw = mImage.width; float ih = mImage.height; pushStyle(); noStroke(); beginShape(); texture(mImage); vertex(-hw, -hh, 0, 0, 0); vertex( hw, -hh, 0, iw, 0); vertex( hw, hh, 0, iw, ih); vertex(-hw, hh, 0, 0, ih); endShape(); popStyle(); } // Change posture to camera protected void turnToCamera() { PMatrix3D m = (PMatrix3D)g.getMatrix(); m.m00 = m.m11 = m.m22 = 1; m.m01 = m.m02 = m.m10 = m.m12 = m.m20 = m.m21 = 0; resetMatrix(); applyMatrix(m); } } ```
Expand all code in CelestialObject.pde
#### **`CelestialObject.pde`** ```java // Celestial class (draw if there are children) public class CelestialObject extends Billboard { private ArrayList mChildren = null; // children list // constructor // param imageName Image file name // param w celestial body (billboard) width // param x X coordinate of celestial body (billboard) // param y Y coordinate of celestial body (billboard) // param z Z coordinate of celestial body (billboard) public CelestialObject(String imageName, int w, float x, float y, float z) ( super(imageName, w, x, y, z); } // add children public void addChild(Billboard child) { if (mChildren == null) { mChildren = new ArrayList(); } mChildren.add(child); } // coordinate conversion and drawing (override) public void update() { pushMatrix(); transform(); pushMatrix(); turnToCamera(); drawSelf(); // draw yourself popMatrix(); updateChildren(); // draw all children popMatrix(); } // draw all children private void updateChildren() { if (mChildren != null) { for (Billboard child :mChildren) { child.update(); } } } } ``` </div></details>
Expand all code in OrbitalObject.pde
#### **`OrbitalObject.pde`** ```java // A celestial body revolving around a circular orbit public class OrbitalObject extends CelestialObject { private float mRadius; // revolution radius private int mPeriod; // revolution period (ms) private float mStart; // start angle (radians) // constructor // param parent orbital mother star // param imageName Image file name // param w Width of revolution body (billboard) // param radius revolution radius // param period Revolution period (seconds) public OrbitalObject(CelestialObject parent, String imageName, int w, float radius, int period) { super(imageName, w, 0, 0, 0); mRadius = radius; mPeriod = period * 1000; mStart = random(TWO_PI); parent.addChild(this); // add yourself to the mother star } // Transform and draw (override) public void update() { updateOrbit(); super.update(); } // coordinate transformation (override) protected void transform() { int ms = millis(); float rd = mStart + TWO_PI * (ms %mPeriod) / mPeriod; rotateY(rd); translate(mRadius, 0, 0); } // draw the trajectory protected void updateOrbit() { pushMatrix(); rotateX(HALF_PI); pushStyle(); noFill(); stroke(48, 48, 48); strokeWeight(1); circle(0, 0, mRadius*2); popStyle(); popMatrix(); } } ```
Expand all code in Galaxy.pde
#### **`Galaxy.pde`** ```java // The stars that make up the galaxy public class Galaxy { private Billboard[] mStars = new Billboard[1000]; // Billboard with only the number of stars // constructor public Galaxy() {// Initialize the image array PImage[] images = new PImage[] { loadImage("BlueStar50.png"), loadImage("WhiteStar50.png"), loadImage("YellowStar50.png"), loadImage("OrangeStar50.png"), loadImage("RedStar50.png"), }; // loop for the number of stars for (int i = 0; i <mStars.length; i++) { float angle = random(TWO_PI); // The angle on the ZX plane is random within the range of 0 to 360° (2π) float radius = random(4000, 5000); // The distance from the origin on the ZX plane is randomly within the range of 4000-5000 float x = sin(angle) * radius; // X coordinate of star float z = cos(angle) * radius; // Z coordinate of star float y = random(-3000, 3000); // The Y coordinate of the star is random in the range -3000 to 3000 int n = (int)random(5); // The image to be pasted on the billboard is randomly selected from 5 types mStars[i] = new Billboard(images[n], 50, x, y, z); // generate billboard and store in array } } // Transform all stars and draw public void update() { for (Billboard star: mStars) { star.update(); } } } ```
Expand all the code in SolarSystem.pde
#### **`SolarSystem.pde`** ```java // solar system class public class SolarSystem { private CelestialObject mSun; // sun // constructor public SolarSystem() { mSun = new CelestialObject("Sun.png", 50, 0, 0, 0); OrbitalObject mercury = new OrbitalObject(mSun, "Mercury.png", 19, 227, 14); OrbitalObject venus = new OrbitalObject(mSun, "Venus.png", 47, 425, 37); OrbitalObject earth = new OrbitalObject(mSun, "Earth.png", 50, 587, 60); /* moon */new OrbitalObject(earth, "Moon.png", 13, 130, 4); OrbitalObject mars = new OrbitalObject(mSun, "Mars.png", 26, 894, 112); OrbitalObject jupiter = new OrbitalObject(mSun, "Jupiter.png",100,1527, 711); /* io */new OrbitalObject(jupiter, "Io.png", 14, 165, 1); /* europa */new OrbitalObject(jupiter, "Europa.png", 12, 263, 2); /* ganymede */new OrbitalObject(jupiter, "Ganymede.png",21, 420, 3); /* callisto */new OrbitalObject(jupiter, "Callisto.png",19, 590, 4); OrbitalObject saturn = new OrbitalObject(mSun, "Saturn.png", 100,2800, 886); /* titan */new OrbitalObject(saturn, "Titan.png", 20, 479, 3); OrbitalObject uranus = new OrbitalObject(mSun, "Uranus.png", 50,3755,1685); OrbitalObject neptune = new OrbitalObject(mSun, "Neptune.png", 50,4410,2472); /* triton */new OrbitalObject(neptune, "Triton.png", 11, 139, -1); OrbitalObject pluto = new OrbitalObject(mSun, "Pluto.png", 9,5789,3716); OrbitalObject eris = new OrbitalObject(mSun, "Eris.png", 9,6632,5580); } // Transform all stars and draw public void update() { mSun.update(); } } ```
Expand all code of CameraController.pde
#### **`CameraController.pde`** ```java // camera control class public class CameraController { private float mRadius = 1000; // Current camera orbital radius (plane parallel to ZX plane) private float mAngle = 0; // Current horizontal position of camera (angle on orbit plane: deg) private float mYCoord = 0; // current camera vertical position (Y coordinate: px) private int mRadiusSpeed = 0; // Speed in the front-back direction as seen from the camera (variation of mRadius: px/sec) private int mRotationalSpeed = 0; // lateral speed as seen from the camera (mAngle variation: deg/sec) private int mVerticalSpeed = 0; // Vertical speed as seen from the camera (mYCoord change: px/sec) private int mPrevMillis = 0; // Last elapsed time // slow down the camera rotation speed // From the camera perspective, // If you are moving to the right, decrease the amount of change to the right, // If moving to the left, increase the amount of change to the left public void left() { mRotationalSpeed -= 5; printStatus(); } // increase the rotation speed of the camera // From the camera perspective, // If you are moving to the right, increase the amount of change to the right, // If moving to the left, decrease the amount of change to the left public void right() { mRotationalSpeed += 5; printStatus(); } // increase the ascent speed of the camera // From the camera perspective, // If you are climbing, increase the climb speed, // If it is descending, decrease the descending speed public void up() { mVerticalSpeed -= 25; printStatus(); } // slow down the ascent of the camera // From the camera perspective, // If you're climbing, slow down // if descending, increase descent speed public void down() { mVerticalSpeed +=25; printStatus(); } // reduce the amount of change in the orbit radius of the camera // From the camera perspective, // If you are moving forward, increase the speed of forward movement, // Decrease the retreat speed if you are retreating public void forward() { mRadiusSpeed -= 10; printStatus(); } // increase the amount of change in camera radius // From the camera perspective, // If you are moving forward, reduce the speed // If you are in retreat, increase the retreat speed public void backward() { mRadiusSpeed += 10; printStatus(); } // output the current state of the camera to the console public void printStatus() { print("(R,A,V)=("); print((int)mRadius + "px, "+ (int)mAngle + "deg, "+ (int)mYCoord + "px"); print(") (dR,dA,dV)=("); print(mRadiusSpeed + "px/s, "+ mRotationalSpeed + "deg/s, "+ mVerticalSpeed + "px/s"); println(")"); } // update the camera position public void update() { int ms = millis(); if (ms/1000 != mPrevMillis/1000) printStatus(); // Display status every 1 second int dt = ms-mPrevMillis; // Time change mPrevMillis = ms; // save current time // update the orbital radius mRadius += mRadiusSpeed * dt / 1000.0; mRadius = max(mRadius, 100.0); // stop when it's about to hit the sun if (mRadius==100.0) mRadiusSpeed = 0; // Update rotation anglemAngle += mRotationalSpeed * dt / 1000.0; mAngle = mAngle %360; // Normalized to fit between 0 and 360 if (mAngle <0) mAngle += 360; // Update vertical position mYCoord += mVerticalSpeed * dt / 1000.0; float z = mRadius * cos(radians(mAngle)); float x = mRadius * sin(radians(mAngle)); float y = mYCoord; camera(x, y, z, 0, 0, 0, 0, 1, 0); } } ```
[](----------------------------------------------------------------------------------) ## 7 am | Epilogue At the roof of the Temple of the Republic Koryo Knight, Planet Coruscant... Young Knight "Finally, the establishment of the military?" Grand Master "Oh, it's finally here" Young Night "I was in the Senate last night" Young Knight "I'm worried that the Supreme Chairman has received administrative privileges." Grand Master "Because the citizens of the Republic have a strong support for him..." Grand Master "This is also democracy" Young Night "Actually last night from the Supreme Chairman's Office" Young Knight "The place where D.S. Mark Gopher comes out" Young Night "I've seen it" Grand Master "What?!" Young Night "Grand Master!" Young Knight "D.S. is..." Young Knight "Isn't it an abbreviation for the dark side?!" Grand Master"…" Grand Master "It's not there..." Grand Master "if, temporarily," Grand Master "Even if he had some ambition" Grand Master "Anyone will notice" Grand Master "I don't want to do that..." Young Knight "Master..." Young Knight "Do you think it will be a war?" Grand Master "Umu," Grand Master "Reading the future..." Grand Master "Difficult..." When he said that, when he looked, the large city buildings illuminated by the Chaoyang were even more sparkling. [](----------------------------------------------------------------------------------) # Postscript How was it? If you are new to CG programming, you may find it surprisingly easy. If you find it a little unsatisfactory, you may want to try the following improvements. ### Solar system clock A clock that uses the sun as the hour, the earth as the minute, and the moon as the second. ![SolarSystemClock.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/8c6d1555-2fd5-d030-4a73-a44ae90dc49b.jpeg) You can do this by doing something like the following: ・Write text on the billboard - Set the blending mode to "additive synthesis" ・Control the camera so that the sun, earth and moon are always in the angle of view ### spacecraft simulator It is a spacecraft simulator style game. ![SpaceProbeSimulator.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/ad394609-b856-0d9e-ebbb-627f01f9f23a.jpeg) You can do this by doing the following: ・Control the camera more freely ・Always place the billboard that looks like a control panel in front of the camera The billboard method may be outdated, but depending on the idea, it seems that you can still make something interesting. Also, although some high school mathematics is required, if you have a child and have not decided on a free study theme for summer vacation, it would be fun to propose it as a subject. Oh, please be careful about the license of image materials. Then **May the Force be with you!** (May the Force be with you!) [](----------------------------------------------------------------------------------) # A scene after the end roll Planet Coruscant In the Galactic Republic Supreme Chairman's Office... Supreme Chairman "Guahahaaha!" Chairman "What is a citizen?" Supreme Chairman "Foolish man..." Chairman: "It was very easy to hand over the authority!" Chairman, "Kyowa Knights," Chairman "D.S. seems to be mistaken for the dark side." Supreme Chairman "I got a planetary simulator..." Chairman "To seize this galaxy" Chairman "Even if it takes decades," Chairman "I will make this D.S.!" ![DeathStar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634588/122b83b8-447e-ed25-818d-e2b03a774e99.gif) The artificial celestial body in the center of the screen used as the billboard material for this image is the work of adnylangager, and is [CC BY-NC 2.0](https://creativecommons.org/licenses/by-nc/2.0/deed.ja) Is licensed. (Source: [fiickr](https://www.flickr.com/photos/andylangager/8631388149/sizes/o/in/photostream/))