1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

The Nature OF CodeとSiv3D

Posted at

この記事はSiv3D Advent Calendar 2021の12月20日の記事です。

注意

The Nature of Codeをまったく読んでない人は本を読んでからこの記事を読むことを勧めますまたProcessingを知らない人も同様です。

The Nature of Codeとは?

タイトルはThe Nature of Code Simulating Natural Systems with Processingで日本語訳はNature of Code Processingではじめる自然現象のシミュレーションとなっています。単にNature of Codeと呼ばれていることが多いと思います。
内容を簡単に紹介すると数学や物理の式(式はそれほど難しくありません)とProcessingを用いて自然界で動くものやフラクタルなどのシステムを記述しています。

目的

The Nature of Codeに記述されている式や法則あるいは自然界でのシミュレーションとModern C++そしてSiv3Dをを少しづつ理解する事が目的です。
原著ではProcessingで書かれているのですがそれらをC++とSiv3Dに移植します。
また参考とする本は日本語ではなく英語で書かれている本をベースとしています。

範囲と対象

基本的にchapter1からchapter10まで実装します。ただしこの記事では時間などの制約からchapter2の一部のみ実装しています、chapter1やそれ以降の章は来年になると思います。
内容を少しづつ理解することが目的なので更新は1か月~2か月程度(数学的背景を深堀りする場合もあるのでそれ以上かかるかも😅)を目安としています。

実装は以下からChapter2の2.4 Dealing with Massから実装しています。

Chapter2 Forces

Forcesの基本となるMoverクラスは以下のように実装しています。

# include <Siv3D.hpp> 

class Mover {
private:
	Vec2 position;
	Vec2 velocity;
	Vec2 acceleration;
	double mass;
public:
  // デフォルトコンストラクターを必ず入れる
	Mover() = default;
	Mover(const Vec2& position, const double& mass) : position(position), velocity(Vec2(0.0, 0.0)), acceleration(Vec2(0.0, 0.0)), mass(mass) {}


	void applyForce(const Vec2& force) {
		Vec2 calcForce = force / mass;
		acceleration += calcForce;
	}

	void update() {
		velocity += acceleration;
		position += velocity;
		acceleration *= 0.0;
	}

	void render() {
		double scale = 2.0;
		double size = mass * scale;
		const ColorF color = ColorF{ 1.0, 1.0, 1.0, 1.0 };
		Circle{ position, size }.draw(color);
	}

	void checkEdge(const Point& windowSize) {
		if (position.x < 0) {
			position.x = 0;
			velocity.x *= -1;
		}
		else if (position.x > windowSize.x) {
			position.x = windowSize.x;
			velocity.x *= -1;
		}

		if (position.y < 0) {
			position.y = 0;
			velocity.y *= -1;
		}
		else if (position.y > windowSize.y) {
			position.y = windowSize.y;
			velocity.y *= -1;
		}
	}
};

2.4 Dealing with Mass and 2.5 Creating Forces

注意点としてデフォルトコンストラクターを必ず入れないとエラーになります。

void Main()
{

	Scene::SetBackground(ColorF{ 0.0, 0.0, 0.0 });

	const int32 particleMAX = 10;
	const int32 width = Scene::Width();
	const int32 height = Scene::Height();
	const Point windowSize = Point(width, height);
	const Vec2 wind = Vec2(0.05, 0.0);
	const Vec2 gravity = Vec2(0.0, 0.1);
	

	std::vector<Mover> movers(particleMAX);
	for (int32 count = 0; count < particleMAX; count++) {
		const Vec2 position = Vec2(0, 0);
		const double mass = Random<double>(5.0, 20.0);
		Mover mover = Mover(position, mass);
		movers.push_back(mover);
	}

	std::cout << movers.size() << std::endl;

	while (System::Update())
	{
		for (int32 count = 0; count < movers.size(); count++) {
			movers[count].applyForce(wind);
			movers[count].applyForce(gravity);
			movers[count].update();
			movers[count].render();
			movers[count].checkEdge(windowSize);
		}
	}
}

ソースコードはこちらです

2.6 Gravity on Earth and Modeling a Force

void Main()
{

	Scene::SetBackground(ColorF{ 0.0, 0.0, 0.0 });

	const int32 particleMAX = 10;
	const int32 width = Scene::Width();
	const int32 height = Scene::Height();
	const Point windowSize = Point(width, height);
	


	std::vector<Mover> movers(particleMAX);
	for (int32 count = 0; count < particleMAX; count++) {
		const Vec2 position = Vec2(0, 0);
		const double mass = Random<double>(5.0, 20.0);
		Mover mover = Mover(position, mass);
		movers.push_back(mover);
	}

	std::cout << movers.size() << std::endl;

	while (System::Update())
	{
		for (int32 count = 0; count < movers.size(); count++) {
			const double mass = movers[count].mass;
			const Vec2 wind = Vec2(0.05, 0.0);
			const Vec2 gravity = Vec2(0.0, 0.1 * mass);
			movers[count].applyForce(wind);
			movers[count].applyForce(gravity);
			movers[count].update();
			movers[count].render();
			movers[count].checkEdge(windowSize);
		}
	}
}


ソースコードはこちらです

2.7 Friction

ProcessingではnormalizeですがSiv3Dではnormalizedを使ったほうが良いと思います。

void Main()
{

	Scene::SetBackground(ColorF{ 0.0, 0.0, 0.0 });

	const int32 particleMAX = 10;
	const int32 width = Scene::Width();
	const int32 height = Scene::Height();
	const Point windowSize = Point(width, height);

	const float coefficientFriction = 0.01;


	std::vector<Mover> movers(particleMAX);
	for (int32 count = 0; count < particleMAX; count++) {
		const Vec2 position = Vec2(0, 0);
		const double mass = Random<double>(5.0, 20.0);
		Mover mover = Mover(position, mass);
		movers.push_back(mover);
	}

	//std::cout << movers.size() << std::endl;

	while (System::Update())
	{
		for (int32 count = 0; count < movers.size(); count++) {
			const double mass = movers[count].mass;
			const Vec2 wind = Vec2(0.05, 0.0);
			const Vec2 gravity = Vec2(0.0, 0.1 * mass);

			Vec2 friction = movers[count].velocity;
			friction *= -1;
			// ここはnormalizeではない?
			// friction.normalize();
			friction.normalized();
			friction *= coefficientFriction;

			movers[count].applyForce(wind);
			movers[count].applyForce(gravity);
			movers[count].applyForce(friction);
			movers[count].update();
			movers[count].render();
			movers[count].checkEdge(windowSize);
		}
	}
}

ソースコードはこちらです

このあとは?

再度記載しますが今後他の章などを追加します。ただしQiitaは限らないかもしれません。またこの記事で書いたRustとnannouでも実装する予定です。

参考文献

The Nature of Code

The Nature of Code(Kindle版)

公式サイト

Siv3D

公式サイト

Reference

C++

cpprefjp - C++日本語リファレンス

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?