p5.jsでつくるおとぎの国の世界観

# はじめに
p5.jsというライブラリを利用してchatGPTと共に作成したアートの紹介です。
「なんかいい」をモットーに作っています。

# 作品紹介
<iframe width="560" height="315" src="https://www.youtube.com/embed/MQJrP_CacLk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

```math
『おとぎの国』
```

# 構想
とにかく不思議な動きをするものを作りたいと思い作り始めました。初めは複雑怪奇なものを狙っていたのですが、chatGPTによって提示された淡い色の図形に触発されておとぎの国の雰囲気を目指しました。

# chatGPTと調整
`"不思議", "複雑", "カオス", "感動的", "綺麗", "没入感"`などのシソーラスを何度も入れながら、球体を動かすアニメーションを作るように指示しました。構想で述べた通り、複雑怪奇を狙ったいたのですが、`"感動的", "綺麗"`あたりが効いたのか淡い色の図形が生成されました。予想とは異なりましたが良いと感じたので、
`
感動する幾何学アニメーションをp5.jsを使用して作成してください。
`
と念押しして、円形で淡い色をもった図形が動くアニメーションができました。

ここから、円を参考にして他の図形も作成していきました。その様子は[拡張性](https://qiita.com/keigo1110/items/cbe1f9c9f390e45511fe#%E6%8B%A1%E5%BC%B5%E6%80%A7)で述べます。

# 作品解説
## 下準備
必要なファイルや実行方法がわからない人は以下を参考にしてください。

https://qiita.com/keigo1110/items/543423379813b49e7415

## コード
コードはchatGPTが生成しています。数字は一部調整しています。
```fairyland.js
//きれいな幾何図形がうごく
let shapes = [];
let numShapes = 80;
let minRadius = 10;
let maxRadius = 120;

function setup() {
  createCanvas(2000, 1200);
  for (let i = 0; i < numShapes; i++) {
    let shapeType = floor(random(6));
    let shape;
    if (shapeType === 0 ) {
      shape = new Circle();
    } else if (shapeType === 1){
      shape = new Square();
    } else if (shapeType === 2) {
      shape = new Triangle();
    } else if (shapeType === 3) {
      shape = new Star();
    } else {
      shape = new Heart();
    } 
    shape.init();
    shapes.push(shape);
  }
}

function draw() {
  background(20, 20, 20);
  for (let i = 0; i < shapes.length; i++) {
    shapes[i].update();
    shapes[i].display();
  }
}

class Shape {
  constructor() {
    this.pos = createVector();
    this.vel = createVector();
    this.radius = 0;
    this.color = color(255);
  }

  init() {
    this.pos.x = random(width);
    this.pos.y = random(height);
    this.vel.x = random(-3, 3);
    this.vel.y = random(-3, 3);
    this.radius = random(minRadius, maxRadius);
    this.color = color(random(255), random(255), random(255));
  }

  update() {
    this.pos.add(this.vel);
    if (this.pos.x < -this.radius || this.pos.x > width + this.radius) {
      this.vel.x *= -1;
    }
    if (this.pos.y < -this.radius || this.pos.y > height + this.radius) {
      this.vel.y *= -1;
    }
  }

  display() {}
}

class Circle extends Shape {
  display() {
    noStroke();
    fill(red(this.color), green(this.color), blue(this.color), 100);
    ellipse(this.pos.x, this.pos.y, this.radius * 2);
    strokeWeight(2);
    stroke(255);
    noFill();
    ellipse(this.pos.x, this.pos.y, this.radius * 1.5);
  }
}

class Square extends Shape {
  display() {
    noStroke();
    fill(red(this.color), green(this.color), blue(this.color), 100);
    rectMode(CENTER);
    rect(this.pos.x, this.pos.y, this.radius * 2, this.radius * 2);
    strokeWeight(2);
    stroke(255);
    noFill();
    rect(this.pos.x, this.pos.y, this.radius * 1.5, this.radius * 1.5);
  }
}

class Triangle extends Shape {
  display() {
    noStroke();
    fill(red(this.color), green(this.color), blue(this.color), 100);
    triangle(this.pos.x, this.pos.y - this.radius,
             this.pos.x - this.radius, this.pos.y + this.radius,
             this.pos.x + this.radius, this.pos.y + this.radius);
    strokeWeight(2);
    stroke(255);
    noFill();
    triangle(this.pos.x, this.pos.y - this.radius * 0.75,
             this.pos.x - this.radius * 0.75, this.pos.y + this.radius * 0.75,
             this.pos.x + this.radius * 0.75, this.pos.y + this.radius * 0.75);
  }
}

class Star extends Shape {
  display() {
    noStroke();
    fill(red(this.color), green(this.color), blue(this.color), 100);
    push();
    translate(this.pos.x, this.pos.y);
    beginShape();
    const numPoints = 5;
    const angle = TWO_PI / numPoints;
    const halfAngle = angle / 2.0;
    const outerRadius = this.radius;
    const innerRadius = this.radius * 0.5;
    for (let i = 0; i < TWO_PI; i += angle) {
      const x = outerRadius * cos(i);
      const y = outerRadius * sin(i);
      vertex(x, y);
      const x2 = innerRadius * cos(i + halfAngle);
      const y2 = innerRadius * sin(i + halfAngle);
      vertex(x2, y2);
      strokeWeight(2);
      stroke(255);
    }
    endShape(CLOSE);
    pop();
  }
}

class Heart extends Shape {
  display() {
    noStroke();
    fill(red(this.color), green(this.color), blue(this.color), 100);
    beginShape();
    vertex(this.pos.x, this.pos.y + this.radius / 4);
    bezierVertex(
      this.pos.x + this.radius / 2,
      this.pos.y - this.radius / 2 + this.radius / 3,
      this.pos.x + this.radius / 4,
      this.pos.y - this.radius + this.radius / 3,
      this.pos.x,
      this.pos.y - this.radius / 4
    );
    bezierVertex(
      this.pos.x - this.radius / 4,
      this.pos.y - this.radius + this.radius / 3,
      this.pos.x - this.radius / 2,
      this.pos.y - this.radius / 2 + this.radius / 3,
      this.pos.x,
      this.pos.y + this.radius / 4
    );
    endShape(CLOSE);
    stroke(255);
    strokeWeight(2);
    noFill();
    beginShape();
    vertex(this.pos.x, this.pos.y + this.radius / 4);
    bezierVertex(
      this.pos.x + this.radius / 2,
      this.pos.y - this.radius / 2 + this.radius / 3,
      this.pos.x + this.radius / 4,
      this.pos.y - this.radius + this.radius / 3,
      this.pos.x,
      this.pos.y - this.radius / 4
    );
    bezierVertex(
      this.pos.x - this.radius / 4,
      this.pos.y - this.radius + this.radius / 3,
      this.pos.x - this.radius / 2,
      this.pos.y - this.radius / 2 + this.radius / 3,
      this.pos.x,
      this.pos.y + this.radius / 4
    );
    endShape();
  }
}

```
## 拡張性
『おとぎの国』に様々な図形を追加することができます。
今回はShapeクラスを継承する形でstarクラスを作成しました。

以下に実際星型を追加した時のプロンプトを示します。
![Screenshot 2023-05-04 at 15.51.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3346908/5988b57c-08ba-e7d2-70aa-45a16f1048d5.png)
円を表示するクラスをコピペして、星型を作成するように指示をだしました。

![Screenshot 2023-05-04 at 15.52.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3346908/15f08149-f55f-9ba1-b7ef-e9cd98aa5f9e.png)
すると、gpt-3.5（無料版）でもこのように記述してくれます。（うまくいかなければ、直前にshapeクラスのコードも入力させておいたほうが良いかもしれません）
このコードをコピペします。

次に設定を調整します。
```javascript
function setup() {
  createCanvas(2000, 1200);
  for (let i = 0; i < numShapes; i++) {
    let shapeType = floor(random(6)); //ここも数に合わせて変更すること
    let shape;
    if (shapeType === 0 ) {
      shape = new Circle();
    } else if (shapeType === 1){
      shape = new Square();
    } else if (shapeType === 2) {
      shape = new Triangle();
    } else if (shapeType === 3) { //ここを書き足す。
      shape = new Star();
    } else {
      shape = new Heart();
    } 
    shape.init();
    shapes.push(shape);
  }
}
```
このsetup()の部分のif文による分岐を増やすことで表示するものを増やせます。また、乱数の数字も調整が必要です。

# おわりに
予想していたターゲットと異なる結果でしたが、少しずつ会話する中で別の良いものにたどり着く感じが楽しかったです。図形も簡単に足せるつくりになっていて、拡張性高いのでまだまだ楽しめそうです。