diff --git a/.clang-format b/.clang-format
index 67037c8..9f843f2 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,35 +1,180 @@
#
-# clang format
+# Clang-Format configuration file
#
-# Copyright (c) 2023 Okawa Yusuke
+# Copyright (c) 2022-2023 Okawa Yusuke
#
-# key reference: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+# Style options reference: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+#
+
+#
+# インデントにタブを使用しない
#
UseTab: Never
+
+#
+# インデント幅
+#
+
IndentWidth: 4
-ConstructorInitializerIndentWidth: 4
+
+#
+# コンティニュエーションインデント幅
+#
+
ContinuationIndentWidth: 4
-TabWidth: 4
+
+#
+# 行コメントの前のスペースの数
+#
+
SpacesBeforeTrailingComments: 4
+
+#
+# 引数が複数行にまたがる場合のスタイル
+#
+
BinPackParameters: true
+
+#
+# 複数行で連続する `=` の位置を揃える
+#
+
AlignConsecutiveAssignments: true
+
+#
+# 複数行で連続する宣言の位置を揃える
+#
+
AlignConsecutiveDeclarations: true
-AlignEscapedNewlinesLeft: true
+
+#
+# エスケープされた改行を可能な限り左に配置
+#
+
+AlignEscapedNewlinesLeft: false
+
+#
+# 中括弧の前で改行する
+#
+
BreakBeforeBraces: Allman
+
+#
+# 関数の引数のインデント
+#
+
AllowShortIfStatementsOnASingleLine: false
-IndentCaseLabels: false
+
+#
+# 最大行幅 (0: 無制限)
+#
+
ColumnLimit: 0
-AccessModifierOffset: -4
+
+#
+# 最大数空白行数
+#
+
+MaxEmptyLinesToKeep: 2
+
+#
+# 名前空間のインデント
+#
+
NamespaceIndentation: All
+
+#
+# 名前空間の終わりに名前空間名をコメントで付ける
+#
+
FixNamespaceComments: true
+
+#
+# 引数にラムダ式を渡す場合に1行にする
+#
+
AllowShortLambdasOnASingleLine: Inline
+
+#
+# アクセス修飾子のインデント
+#
+
+AccessModifierOffset: -4
+
+#
+# 継承時、基底クラス名のインデントスタイル
+#
+# class DerivedClass
+# : public BaseClass
+# , public Interface
+#
+
BreakInheritanceList: BeforeComma
+
+#
+# コンストラクタの初期化リストのインデントスタイル
+#
+# Constructor()
+# : initializer1()
+# , initializer2()
+#
+
+BreakConstructorInitializers: BeforeComma
+
+#
+# コンストラクタの初期化リストのインデント幅
+#
+
+ConstructorInitializerIndentWidth: 4
+
+#
+# 中括弧のスタイル
+#
+# true : int array[] = { 1, 2, 3, 4, 5 };
+# false: int array[] = {1, 2, 3, 4, 5};
+#
+
Cpp11BracedListStyle: false
+
+#
+# Switch 文の case のインデントをなくす
+#
+
+IndentCaseLabels: false
+
+#
+# Switch 文の case 内が単数行の場合に1行にする
+#
+
AllowShortCaseLabelsOnASingleLine: true
+
+#
+# インクルードファイルのソート
+#
+
SortIncludes: false
-PPIndentWidth: 4
-BreakConstructorInitializers: BeforeComma
+
+#
+# ポインタの `*` の位置
+#
+
PointerAlignment: Left
+
+#
+# 参照型の `&` の位置
+#
+
ReferenceAlignment: Left
+
+#
+# プリプロセッサディレクティブのインデント
+#
+
+PPIndentWidth: 4
+
+#
+# ネストされたプリプロセッサディレクティブのインデント
+#
+
IndentPPDirectives: AfterHash
diff --git a/README.md b/README.md
index 45fec43..b6a3596 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,57 @@
# EFSim [![GitHub Pages Deploy](https://github.com/CaseyNelson314/EFSim/actions/workflows/deploy.yml/badge.svg)](https://github.com/CaseyNelson314/EFSim/actions/workflows/deploy.yml)
-![image](https://github.com/CaseyNelson314/EFSim/assets/91818705/c5345b3e-93be-4dfb-8a40-c3afebc93056)
+![image](https://github.com/CaseyNelson314/EFSim/assets/91818705/d9f6cd13-0ae5-4ea5-91fb-b2342f9f348f)
-Electric Field Simulator
+三次元電界シミュレータ
-## Usage
+## 使用方法
-### Camera View Control
+ドラッグで視点の回転、Shift+ドラッグで視点の移動ができます。
-rotation: `Drag`
+左上の各電荷ボタンを押すことで、シミュレーション空間に電荷を追加できます。
-position: `Shift` + `Drag`
+空間内で電荷を選択することで、パラメータ編集モードに切り替わり、電荷の位置、回転角、電荷量、電荷密度などのパラメーターを編集できます。(編集できるパラメータは電荷の種類によって異なります)
-## Local Execute
+## 電界ベクトル式算出
-### Setup
+ガウスの法則より電界の強さを求め、電界ベクトルを求める。
-Install NodeJS
+$$
+\sum EndS = \frac{\sum Q}{\varepsilon_0}
+$$
-```sh
-winget install -e --id OpenJS.NodeJS
-```
+電荷の種別ごとの電界、電界ベクトル算出式
+
+| 電荷種別 | 電界(スカラ) | 電界(ベクトル) |
+| ------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+| 点電荷 | $ E(r) = \frac{Q}{4 \pi \varepsilon_0 r^{2}} $ | $ \bm{E}(\bm{r}) = \frac{Q}{4 \pi \varepsilon_0 \lvert\bm{r}\rvert^{3}} \bm{r} $ |
+| 無限長線電荷 | $ E(r) = \frac{\lambda}{2 \pi \varepsilon_0 r} $ | $ \bm{E}(\bm{r}) = \frac{\lambda}{2 \pi \varepsilon_0\lvert\bm{r}\rvert^{2}} \bm{r} $ |
+| 無限面電荷 | $ E(r) = \frac{\sigma}{2 \varepsilon_0} $ | $ \bm{E}(\bm{r}) = \frac{\sigma}{2 \varepsilon_0 \lvert\bm{r}\rvert} \bm{r}$ |
+| 球表面電荷 | $ \begin{cases} E(r) = \frac{\sigma a^{2}}{\varepsilon_0 r^{2}} & (a \leq r) \\ E(r) = 0 & (0 \leq r < a) \end{cases} $ | $ \begin{cases} \bm{E}(\bm{r}) = \frac{\sigma a^{2}}{\varepsilon_0 \lvert\bm{r}\rvert^{3}} \bm{r} & (a \leq \lvert\bm{r}\rvert) \\ \bm{E}(\bm{r}) = \bm{0} & (0 \leq \lvert\bm{r}\rvert < a) \end{cases} $ |
+| 球体積電荷 | $ \begin{cases} E(r) = \frac{\rho a^{3}}{3 \varepsilon_0 r^{2}} & (a \leq r) \\ E(r) = \frac{\rho r}{3 \varepsilon_0} & (0 \leq r < a) \end{cases} $ | $ \begin{cases} \bm{E}(\bm{r}) = \frac{\rho a^{3}}{3 \varepsilon_0 \lvert\bm{r}\rvert^{3}} \bm{r} & (a \leq \lvert\bm{r}\rvert) \\ \bm{E}(\bm{r}) = \frac{\rho}{3 \varepsilon_0} \bm{r} & (0 \leq \lvert\bm{r}\rvert < a) \end{cases} $ |
+| 円筒表面電荷 | $ \begin{cases} E(r) = \frac{\sigma a}{\varepsilon_0 r} & (a \leq r) \\ E(r) = 0 & (0 \leq r < a) \end{cases} $ | $ \begin{cases} \bm{E}(\bm{r}) = \frac{ \sigma a}{\varepsilon_0 \lvert\bm{r}\rvert^{2}} \bm{r} & (a \leq \lvert\bm{r}\rvert) \\ \bm{E}(\bm{r}) = \bm{0} & (0 \leq \lvert\bm{r}\rvert < a) \end{cases} $ |
+| 円筒体積電荷 | $ \begin{cases} E(r) = \frac{\rho a^{2}}{2 \varepsilon_0 r} & (a \leq r) \\ E(r) = \frac{\rho r}{2 \varepsilon_0} & (0 \leq r < a) \end{cases} $ | $ \begin{cases} \bm{E}(\bm{r}) = \frac{\rho a^{2}}{2 \varepsilon_0 \lvert\bm{r}\rvert^{2}} \bm{r} & (a \leq \lvert\bm{r}\rvert) \\ \bm{E}(\bm{r}) = \frac{\rho}{2 \varepsilon_0} \bm{r} & (0 \leq \lvert\bm{r}\rvert < a) \end{cases} $ |
+
+| 値 | 意味 | 単位 |
+| :---------------: | :------------------: | :----------------: |
+| $ \varepsilon_0 $ | 真空中の誘電率 | $ \mathrm{F/m} $ |
+| $ q $ | 電荷量 | $ \mathrm{C} $ |
+| $ \lambda $ | 線電荷密度 | $ \mathrm{C/m} $ |
+| $ \sigma $ | 面電荷密度 | $ \mathrm{C/m^2} $ |
+| $ \rho $ | 体積電荷密度 | $ \mathrm{C/m^3} $ |
+| $ r $ | 電荷との距離 | $ \mathrm{m} $ |
+| $ \bm{r} $ | 電荷との距離ベクトル | $ (\mathrm{m}) $ |
+| $ a $ | 半径 | $ \mathrm{m} $ |
+
+## 開発者用
+
+### ローカル実行
+
+[ターミナル](https://apps.microsoft.com/detail/9N0DX20HK701?hl=ja-jp&gl=JP) [NodeJS](https://nodejs.org/en/download/package-manager#windows-1) [Git](https://git-scm.com/download/win) を使用します。ターミナルを開き、以下のコマンドを実行することで、ローカル環境でアプリケーションを実行できます。
-Clone
+GitHub から本プロジェクトをクローン
```sh
git clone https://github.com/CaseyNelson314/EFSim.git
@@ -32,8 +59,139 @@ cd EFSim
npm install
```
-### Launch local server
+ローカルサーバーを起動
-```
+```sh
npm run dev
```
+
+表示される URL をクリックしブラウザを起動
+
+### ディレクトリ構造
+
+```
+.github/
+ workflows/
+ deploy.yml ...自動デプロイスクリプト
+
+app/
+ script/ ...TypeScriptファイル置き場
+ static/ ...画像等置き場
+ style/ ...CSSファイル置き場
+ index.html ...Webページ
+
+.clang-format ...コードの自動整形設定ファイル
+```
+
+### 電気力線の描画手法
+
+[three.js](https://threejs.org/) フレームワークを使い描画を行っています。TypeScript で記述しています。
+
+電気力線は電界ベクトルをつないだ線であるため、以下のアルゴリズムで生成できます。
+
+1. 電気力線の出る適当な方向を決め、その方向に力線を長さ 1 だけ進める。
+
+2. 進めた先の座標における電界ベクトルを求め、その方向に長さ 1 の力線を長さ 1 だけ進める。
+
+3. 2.を他の電荷に当たる、長さ制限になるまで繰り返し 1 本の電気力線を生成。
+
+4. 1.で決めた方向を調整することで複数の電気力線を生成。
+
+
+
+### クラス相関
+
+各電荷は各電荷クラスを基に作成されます。各電荷クラスは `Charge` 抽象クラスを継承しています。また各電荷に共通する処理を仮想関数として `Charge` クラス内で宣言し、各電荷クラスが実装しています。
+
+以下に `PointCharge` (点電荷)、`SphereVolumeCharge` (球内に均一に分布する電荷) が `Charge` クラスを継承している例を示します。ここでは任意の座標の電界ベクトルを取得できる `electricFieldVector` 関数を各電荷クラス側がオーバーライドし実装しています。
+
+```mermaid
+classDiagram
+ class Charge {
+ <>
+ abstract electricFieldVector(Vector3) Vector3
+ }
+
+ Charge <|-- PointCharge
+ Charge <|-- SphereVolumeCharge
+
+ class PointCharge {
+ charge: number
+ override electricFieldVector(Vector3) Vector3
+ }
+
+ class SphereVolumeCharge {
+ radius: number
+ volumeDensity: number
+ override electricFieldVector(Vector3) Vector3
+ }
+```
+
+通常異なる型のオブジェクトを配列で管理することはできませんが、各電荷クラスが `Charge` クラスを継承したことで、電荷クラスのオブジェクトを配列で管理できるようになっています。
+
+```typescript
+const charges: Charge[] = [];
+
+charges.push(new PointCharge(...));
+charges.push(new SphereVolumeCharge(...));
+```
+
+配列で管理できるようになると電荷の種類に依存しないコードを記述できます。たとえば次のようなコードで、任意の座標での全電荷からの電界ベクトルを求めることができます。
+
+```typescript
+// 電荷の配列
+const charges: Charge[] = [];
+
+// 点電荷、球体積電荷を追加
+charges.push(new PointCharge(...));
+charges.push(new SphereVolumeCharge(...));
+
+// 電界ベクトルを合成
+let electricFieldVector = new THREE.Vector3();
+for (const charge of charges) {
+ electricFieldVector.add(charge.electricFieldVector(position));
+}
+```
+
+### 新しい種類の電荷を追加する
+
+1. app/script/ ディレクトリに適当な名前の TypeScript ファイルを作成
+
+2. 電荷クラスを追加
+
+ 追加する電荷クラスを作成し、`Charge` クラスを継承する。
+
+ メインファイルから参照できるようにクラスをエクスポートしておく。
+
+ また依存する関数やクラスがある場合、適宜インポートする。
+
+ ```typescript
+ import { Charge } from "./charge";
+
+ export class SampleCharge extends Charge {}
+ ```
+
+
diff --git a/app/script/Sample.ts b/app/script/Sample.ts
new file mode 100644
index 0000000..4eadb93
--- /dev/null
+++ b/app/script/Sample.ts
@@ -0,0 +1,39 @@
+import * as THREE from 'three';
+import { Charge, ChargeType } from './charge';
+import { Editor } from './editor';
+
+export class SampleCharge extends Charge {
+
+ override getChargeType = () => {
+ return ChargeType.Plus;
+ }
+
+ override distanceFrom = (position: THREE.Vector3) => {
+ return new THREE.Vector3();
+ }
+
+ override isContact = (distance: THREE.Vector3) => {
+ return false;
+ }
+
+ override electricFieldVector = (position: THREE.Vector3) => {
+ return new THREE.Vector3();
+ }
+
+ override electricForceLinesDirection = () => {
+ return [];
+ }
+
+ override dispose = () => {
+ return;
+ }
+
+ override toJSON() {
+ return {};
+ }
+
+ override createEditor = () => {
+ return new Editor();
+ }
+
+}
\ No newline at end of file
diff --git a/app/script/charge.ts b/app/script/charge.ts
index cf4775f..6fd84f9 100644
--- a/app/script/charge.ts
+++ b/app/script/charge.ts
@@ -74,7 +74,7 @@ export abstract class Charge extends THREE.Mesh {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
abstract dispose: () => void;
diff --git a/app/script/field3d.ts b/app/script/field3d.ts
index 76f2cf8..a1e8255 100644
--- a/app/script/field3d.ts
+++ b/app/script/field3d.ts
@@ -12,17 +12,11 @@ import { MeshLineGeometry, MeshLineMaterial } from '@lume/three-meshline'
const ElectricFieldVector = (
charges: Charge[],
position: THREE.Vector3
-) => {
-
- let electricFieldVector = new THREE.Vector3();
-
- for (const charge of charges) {
- electricFieldVector.add(charge.electricFieldVector(position));
- }
-
- return electricFieldVector;
+) =>
+ charges.reduce((electricFieldVector, charge) => {
+ return electricFieldVector.add(charge.electricFieldVector(position));
+ }, new THREE.Vector3());
-};
/**
diff --git a/app/script/infinityCylinderSurfaceCharge.ts b/app/script/infinityCylinderSurfaceCharge.ts
index d633479..38bc3d3 100644
--- a/app/script/infinityCylinderSurfaceCharge.ts
+++ b/app/script/infinityCylinderSurfaceCharge.ts
@@ -148,7 +148,7 @@ export class InfinityCylinderSurfaceCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => {
diff --git a/app/script/infinityCylinderVolumeCharge.ts b/app/script/infinityCylinderVolumeCharge.ts
index 279adab..a3384e6 100644
--- a/app/script/infinityCylinderVolumeCharge.ts
+++ b/app/script/infinityCylinderVolumeCharge.ts
@@ -138,7 +138,7 @@ export class InfinityCylinderVolumeCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => {
diff --git a/app/script/infinityLineCharge.ts b/app/script/infinityLineCharge.ts
index 4f900a8..8f71902 100644
--- a/app/script/infinityLineCharge.ts
+++ b/app/script/infinityLineCharge.ts
@@ -128,7 +128,7 @@ export class InfinityLineCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => {
diff --git a/app/script/infinitySurfaceCharge.ts b/app/script/infinitySurfaceCharge.ts
index 0e314ad..5c26dd2 100644
--- a/app/script/infinitySurfaceCharge.ts
+++ b/app/script/infinitySurfaceCharge.ts
@@ -131,7 +131,7 @@ export class InfinitySurfaceCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => {
this.geometry.dispose();
diff --git a/app/script/pointCharge.ts b/app/script/pointCharge.ts
index fb77e5a..f0564b6 100644
--- a/app/script/pointCharge.ts
+++ b/app/script/pointCharge.ts
@@ -100,7 +100,7 @@ export class PointCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => { }
diff --git a/app/script/sphereSurfaceCharge.ts b/app/script/sphereSurfaceCharge.ts
index 2eb5771..c46bf29 100644
--- a/app/script/sphereSurfaceCharge.ts
+++ b/app/script/sphereSurfaceCharge.ts
@@ -88,9 +88,9 @@ export class SphereSurfaceCharge extends Charge {
return new THREE.Vector3(); // 観測点が球の内部にある場合
}
else {
- // E=(σa^2)/(εr^2)
+ // E=(σa^2)/(εr^3)
return diffVector.multiplyScalar(
- (this.arealDensity * this.radius ** 2) / (permittivity * diffLengthSq)
+ (this.arealDensity * this.radius ** 2) / (permittivity * diffLengthSq ** (3/2))
);
}
@@ -115,7 +115,7 @@ export class SphereSurfaceCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => {
diff --git a/app/script/sphereVolumeCharge.ts b/app/script/sphereVolumeCharge.ts
index 1b5376d..7f4aaff 100644
--- a/app/script/sphereVolumeCharge.ts
+++ b/app/script/sphereVolumeCharge.ts
@@ -89,8 +89,8 @@ export class SphereVolumeCharge extends Charge {
return diffVector.multiplyScalar(this.volumeDensity * Math.sqrt(diffLengthSq) / (3 * permittivity));
}
else {
- // E=(ρa^3/3ε) / r^2
- return diffVector.multiplyScalar((this.volumeDensity * this.radius ** 3) / (3 * permittivity * diffLengthSq));
+ // E=(ρa^3/3ε) / r^3
+ return diffVector.multiplyScalar((this.volumeDensity * this.radius ** 3) / (3 * permittivity * diffLengthSq ** (3/2)));
}
}
@@ -114,7 +114,7 @@ export class SphereVolumeCharge extends Charge {
/**
* 解放
- * @note ジオメトリやマテリアルの破棄を行う
+ * @note ジオメトリの破棄等を行う
*/
override dispose = () => {
diff --git a/app/script/test.json b/app/script/test.json
index 0c92208..d7658ea 100644
--- a/app/script/test.json
+++ b/app/script/test.json
@@ -10,24 +10,35 @@
"scene": [
{
"name": "双極子",
- "pointCharges": [
+ "charges": [
{
+ "name": "pointCharge",
"position": [0, 0, 0],
"charge": 1
},
{
+ "name": "pointCharge",
"position": [0, 1, 0],
"charge": -1
}
]
},
{
- "name": "線電荷",
- "lineCharges": [
+ "name": "線双極子",
+ "charges": [
{
+ "name": "lineCharge",
"position": [0, 0, 0],
+ "rotation": [0, 1, 0],
"charge": 1,
- "direction": [0, 1, 0]
+ "length": 1
+ },
+ {
+ "name": "lineCharge",
+ "position": [10, 0, 0],
+ "rotation": [0, 1, 0],
+ "charge": -1,
+ "length": 1
}
]
}