Skip to content

Instantly share code, notes, and snippets.

@sknjpn
Created July 10, 2017 13:51
Show Gist options
  • Save sknjpn/f1e975d295c8b2056b75d81bf7b6f5d2 to your computer and use it in GitHub Desktop.
Save sknjpn/f1e975d295c8b2056b75d81bf7b6f5d2 to your computer and use it in GitHub Desktop.
OpenSiv3Dで書き直しました。磁石のシミュレータです。ご自由にお持ちください。
# include <Siv3D.hpp> // OpenSiv3D v0.1.5
//方位磁針
struct Compass
{
Compass(const Vec2& _pos) : pos(_pos), ang(Vec2::Up()), v(0) {}
Vec2 pos; //位置座標
Vec2 ang; //現在角度
double v; //回転速度
};
//モノポール
struct Monopole
{
Monopole(const Vec2& _pos, const bool& _isNPole, const double& _strength) : pos(_pos), isNPole(_isNPole), strength(_strength) {}
bool isNPole; //true:N極 false:S極
Vec2 pos; //位置座標
double strength; //磁力の強さ
};
void Main()
{
//画面の基本的な設定
Graphics::SetBackground(Color(11, 22, 33));
Window::SetTitle(L"Compass Simulator with OpenSiv3D");
Window::Resize(1280, 720);
const double s = 0.25; //描画領域の補完速度、視点追従性が変わる
const Triangle needle(0, -3, 15, 0, 0, 3); //方位磁針の針
const Font mFont(32, Typeface::Bold); //N、Sを描画するためのフォント
const Font gFont(16); //多用途フォント
RectF dRegion(Window::Size()); //仮想描画領域
RectF sRegion(dRegion); //実描画領域
//配列でコンパスとモノポールは管理する
Array<Compass> compass;
Array<Monopole> monopole;
const double d = 32.0; //方位磁針同士の間隔
//偶数列のコンパスを配置 1.73 = root3
for (double x = 0; x < 1024; x += 1.73*d)
for (double y = 0; y < 1024; y += d)
compass.emplace_back(Vec2(x, y));
//奇数列のコンパスを配置 0.86 = root3/2
for (double x = 0.86*d; x < 1024; x += 1.73*d)
for (double y = 0.5*d; y < 1024; y += d)
compass.emplace_back(Vec2(x, y));
//モノポールの配置
for (int i = 0; i < 10; i++)
monopole.emplace_back(RandomVec2(Window::ClientRect()), RandomBool(), 100);
Monopole* sMonopole = NULL; //選択中のモノポール
while (System::Update())
{
//t1のスコープを限定
{
//Transfomer2Dで視点移動を行う。
const Transformer2D t1(Mat3x2::Translate(-sRegion.pos).scale(Window::Size().y / sRegion.size.y), true);
//描画領域をマウスホイールで操作する
dRegion = dRegion.scaledAt(Cursor::PosF(), 1 + Mouse::Wheel() / 10.0);
//描画領域のスムーズな変化
sRegion.pos = sRegion.pos*(1 - s) + dRegion.pos*s;
sRegion.size = sRegion.size*(1 - s) + dRegion.size*s;
//Monopoleの操作
if (MouseL.up()) sMonopole = NULL;
//Monopoleの選択
for (auto& m : monopole) if (Circle(m.pos, 24).leftClicked()) sMonopole = &m;
if (sMonopole != NULL) sMonopole->pos = Cursor::PosF();
//Compass更新
for (auto& c : compass)
{
c.ang.rotate(c.v); //回転速度の分だけ回転させる
c.v *= 0.99; //回転速度の減衰
//Monopoleの影響を演算
for (const auto& m : monopole)
{
//N極とS極の場合で反転させる。力は距離の二乗に反比例
const double force = (m.isNPole ? -1 : 1) * m.strength / ((m.pos - c.pos).lengthSq() + 16.0); // + 16.0は3次元的座標のずれを表現
//力のモーメントは外積によって算出できる。 (m.pos-c.pos)がゼロベクトルの可能性に注意
if (!(m.pos - c.pos).isZero()) c.v += c.ang.cross((m.pos - c.pos).normalized())*force;
}
}
//Compass描画
for (const auto& c : compass)
{
//容器の描画
Circle(c.pos, 16).draw(Palette::Gray);
//赤い方の針の描画
needle.rotatedAt(0, 0, atan2(c.ang.y, c.ang.x)).movedBy(c.pos).draw(Palette::Red);
//他方の針の描画
needle.rotatedAt(0, 0, atan2(c.ang.y, c.ang.x) + 3.14).movedBy(c.pos).draw(Palette::White);
//金具の描画
Circle(c.pos, 2).draw(Palette::Gray);
}
//Monopoleの描画
for (const auto& m : monopole)
{
//共通色の定義
const Color color(m.isNPole ? Palette::Red : Palette::Blue);
//容器の描画
Circle(m.pos, 24).draw(color);
//中心の文字を描画
mFont(m.isNPole ? L"N" : L"S").drawAt(m.pos, Palette::Black);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment