Created
July 9, 2017 12:21
-
-
Save sknjpn/27c0ec14726650368b4bba84cc010777 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# include <Siv3D.hpp> // OpenSiv3D v0.1.5 | |
struct Glass | |
{ | |
Glass(const Vec2& _pos, const double& _n, const double& _r) | |
: pos(_pos), n(_n), r(_r) {} | |
Vec2 pos; | |
double n; | |
double r; | |
Circle circle() const { return Circle(pos, r); } | |
}; | |
struct Lazer | |
{ | |
Lazer(const Vec2& _pos, const Vec2& _ang) | |
: pos(_pos), ang(_ang) {} | |
Vec2 pos; | |
Vec2 ang; | |
}; | |
void Main() | |
{ | |
Graphics::SetBackground(Color(11, 22, 33)); | |
Window::SetTitle(L"ガラス球の光学特性"); | |
Window::Resize(1280, 720); | |
const Font font(24); | |
Array<Glass> glass; | |
Array<Lazer> lazer; | |
double n = 1; | |
Mat3x2 transform(Mat3x2::Identity()); | |
for (int i = 0; i < 100; i++) | |
glass.emplace_back(Vec2(0, i * 33), 1.5, 16); | |
for (int x = 0; x < 10; x++) | |
for (int y = 0; y < 10; y++) | |
glass.emplace_back(Vec2(0, -700) + Vec2(x*1.73*0.5, y + (x % 2)*0.5) * 64, 1.5, 31); | |
for (int i = 0; i < 10; i++) | |
lazer.emplace_back(Vec2(64, 0 + i * 8), Vec2(1.0, 0.0)); | |
for (int i = 0; i < 30; i++) | |
lazer.emplace_back(Vec2(64, 512 + i * 4), Vec2(1.0, 0.0)); | |
for (int i = 0; i < 100; i++) | |
lazer.emplace_back(Vec2(64, 1024 + i * 8), Vec2(1.0, 0.0)); | |
Glass* selectedGlass = NULL; | |
Lazer* rSelectedLazer = NULL; | |
Lazer* lSelectedLazer = NULL; | |
while (System::Update()) | |
{ | |
if (!KeyControl.pressed() && !KeyShift.pressed()) transform = transform.scale((10 - Mouse::Wheel()) / 10.0, Cursor::PosF()); | |
else | |
{ | |
const Transformer2D t1(transform, true); | |
for (auto& g : glass) | |
{ | |
if (g.circle().mouseOver()) | |
{ | |
if (KeyShift.pressed()) g.n = Max(1.0, Min(5.0, g.n + Mouse::Wheel() / 10.0)); | |
else g.r *= (10 - Mouse::Wheel()) / 10.0; | |
} | |
} | |
} | |
{ | |
const Transformer2D t1(transform, true); | |
if (MouseL.up()) selectedGlass = NULL; | |
if (selectedGlass != NULL) selectedGlass->pos = Cursor::PosF(); | |
if (MouseL.up()) lSelectedLazer = NULL; | |
if (lSelectedLazer != NULL) lSelectedLazer->pos = Cursor::PosF(); | |
if (MouseR.up()) rSelectedLazer = NULL; | |
if (rSelectedLazer != NULL && !(Cursor::PosF() - rSelectedLazer->pos).isZero()) rSelectedLazer->ang = (Cursor::PosF() - rSelectedLazer->pos).normalized(); | |
} | |
for (;;) | |
{ | |
bool flag = false; | |
for (auto& g1 : glass) | |
{ | |
for (auto& g2 : glass) | |
{ | |
if (&g1 != &g2 && g1.circle().intersects(g2.circle())) | |
{ | |
if (&g1 != selectedGlass) g1.pos += (g1.pos - g2.pos).normalized(); | |
if (&g2 != selectedGlass) g2.pos += (g2.pos - g1.pos).normalized(); | |
flag = true; | |
} | |
} | |
} | |
if (!flag) break; | |
} | |
for (auto& l : lazer) | |
{ | |
const Transformer2D t1(transform, true); | |
double len = 4096; | |
Vec2 pos = l.pos; | |
Vec2 ang = l.ang; | |
Glass* nowGlass = NULL; | |
for (auto& g : glass) if (g.circle().intersects(l.pos)) nowGlass = &g; | |
for (;;) | |
{ | |
Vec2 nearPos; | |
Glass* nearGlass = NULL; | |
const auto ray = Line(pos, pos + ang * len); | |
for (auto& g : glass) | |
{ | |
auto list = g.circle().intersectsAt(ray); | |
if (list) | |
{ | |
for (auto& i : list.value()) | |
{ | |
if (!(i - ray.begin).isZero() && (nearGlass == NULL || (i - ray.begin).length() < (nearPos - ray.begin).length())) | |
{ | |
nearGlass = &g; | |
nearPos = i; | |
} | |
} | |
} | |
} | |
if (nowGlass == NULL) | |
{ | |
if (nearGlass != NULL) | |
{ | |
Circle(nearPos, 4).draw(ColorF(Palette::Red, 1.0)); | |
Line(pos, nearPos).draw(2, ColorF(Palette::Red, 1.0)); | |
auto a = n / nearGlass->n* (nearGlass->pos - nearPos).normalized().cross(ang); | |
ang = Vec2::Right().rotated(atan2(nearGlass->pos.y - nearPos.y, nearGlass->pos.x - nearPos.x) + asin(a)); | |
len -= (nearPos - pos).length(); | |
pos = nearPos + ang*0.01; | |
nowGlass = nearGlass; | |
} | |
else | |
{ | |
ray.draw(2, ColorF(Palette::Red, 1.0)); | |
break; | |
} | |
} | |
else | |
{ | |
if (nearGlass != NULL) | |
{ | |
Circle(nearPos, 4).draw(ColorF(Palette::Red, 1.0)); | |
Line(pos, nearPos).draw(2, ColorF(Palette::Red, 1.0)); | |
auto a = nowGlass->n / n* (nearGlass->pos - nearPos).normalized().cross(ang); | |
if (abs(a) <= 1) | |
{ | |
ang = Vec2::Right().rotated(atan2(nearPos.y - nearGlass->pos.y, nearPos.x - nearGlass->pos.x) - asin(a)); | |
nowGlass = NULL; | |
} | |
else | |
{ | |
ang.rotate(-(atan2(ang.y, ang.x) - atan2(nearPos.y - nearGlass->pos.y, nearPos.x - nearGlass->pos.x)) * 2); | |
ang = -ang; | |
} | |
len -= (nearPos - pos).length(); | |
pos = nearPos + ang*0.01; | |
} | |
else | |
{ | |
ray.draw(2, ColorF(Palette::Red, 1.0)); | |
break; | |
} | |
} | |
} | |
} | |
for (auto& g : glass) | |
{ | |
const Transformer2D t1(transform, true); | |
g.circle().draw(ColorF(Palette::Skyblue, (g.n - 1) / 8.0)).drawFrame(2.0, ColorF(Palette::Skyblue, g.circle().mouseOver() ? 1.0 : 0.5)); | |
font(g.n).drawAt(g.pos); | |
if (g.circle().leftClicked()) selectedGlass = &g; | |
} | |
for (auto& l : lazer) | |
{ | |
const Transformer2D t(transform, true); | |
Circle(l.pos + l.ang * 8, 4).draw(ColorF(Palette::Red, 1.0)); | |
auto r = Rect(-8, -3, 16, 6).rotated(atan2(l.ang.y, l.ang.x)).movedBy(l.pos); | |
r.draw(Palette::White).drawFrame(1, 0, Palette::Gray); | |
if (r.rightClicked()) rSelectedLazer = &l; | |
if (r.leftClicked() && selectedGlass == NULL) lSelectedLazer = &l; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment