|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <X11/Xlib.h> |
|
#include <X11/Xutil.h> |
|
#include <X11/Xlocale.h> |
|
|
|
#include <algorithm> |
|
#include <cmath> |
|
#include <iostream> |
|
#include <iomanip> |
|
#include <memory> |
|
#include <string> |
|
#include <vector> |
|
|
|
using namespace std; |
|
|
|
const int BORDER = 2; |
|
const int WIDTH = 640; |
|
const int HEIGHT = 480; |
|
const int MENU_HEIGHT = 24; |
|
const int LIGHT_RADIUS = 7; |
|
const int MAX_REFLECT = 48; |
|
|
|
enum tObj { tLight, tMirror, tArea }; |
|
char sObj[][16] = { "Light", "Mirror", "Area" }; |
|
|
|
enum tMode { tNoneSelected, tSelected, tAddLight, tAddMirror, tInputN, tAddArea, tRotate }; |
|
char sMode[][16] = |
|
{ "Not Selected", "Selected", "Add Light", "Add Mirror", "Input n", "Add Area", "Rotate" }; |
|
|
|
enum mMode { mNormal, mSelected, mRotate }; |
|
|
|
const int NUM_COLOR = 5; |
|
char color_name[NUM_COLOR][10] = { "Black", "Gainsboro", "Red", "White", "DarkGray" }; |
|
unsigned long nColor[NUM_COLOR]; |
|
static Colormap cmap; |
|
unsigned long reds[MAX_REFLECT], grays[100]; |
|
|
|
struct Point { |
|
double x = 0.0; |
|
double y = 0.0; |
|
|
|
Point() {}; |
|
Point(double _x, double _y) : x(_x), y(_y) {}; |
|
|
|
double len(); |
|
double dist(Point p); |
|
double dot(Point p); |
|
double cross(Point p); |
|
|
|
Point midpoint(Point p); |
|
void print(); |
|
void println(); |
|
|
|
Point operator+(Point p); |
|
Point operator-(Point p); |
|
Point& operator+=(Point p); |
|
Point& operator-=(Point p); |
|
bool operator==(Point p); |
|
}; |
|
|
|
Point Point::operator+(Point p) { |
|
return Point(x + p.x, y + p.y); |
|
}; |
|
|
|
Point Point::operator-(Point p) { |
|
return Point(x - p.x, y - p.y); |
|
}; |
|
|
|
Point& Point::operator+=(Point p) { |
|
x += p.x; |
|
y += p.y; |
|
return *this; |
|
}; |
|
|
|
Point& Point::operator-=(Point p) { |
|
x -= p.x; |
|
y -= p.y; |
|
return *this; |
|
}; |
|
|
|
bool Point::operator==(Point p) { |
|
return x == p.x && y == p.y; |
|
}; |
|
|
|
double Point::len() { |
|
return sqrt(x*x + y*y); |
|
}; |
|
|
|
double Point::dist(Point p) { |
|
double a = x - p.x, |
|
b = y - p.y; |
|
return sqrt(a*a + b*b); |
|
}; |
|
|
|
double Point::dot(Point p) { |
|
return x * p.x + y * p.y; |
|
}; |
|
|
|
double Point::cross(Point p) { |
|
return x * p.y - y * p.x; |
|
}; |
|
|
|
Point Point::midpoint(Point p) { |
|
return Point((x + p.x)/2, (y + p.y) / 2); |
|
} |
|
|
|
class Objects; |
|
|
|
class Object { |
|
public: |
|
int type; |
|
Point p1, p2; |
|
|
|
virtual void draw(Display *dpy, Window w, GC gc); |
|
virtual void move(double dx, double dy); |
|
virtual void resize(double dx, double dy); |
|
virtual void print(); |
|
virtual void println(); |
|
}; |
|
|
|
class Light : public Object { |
|
public: |
|
double angle = 0.0; |
|
|
|
Light(Point p, double a) { type = tLight; p1 = p; angle = fmod(a, 2*M_PI); }; |
|
Light(Point p) : Light(p, 0.0) {}; |
|
|
|
void draw(Display *dpy, Window w, GC gc); |
|
void move(double dx, double dy); |
|
void resize(double dx, double dy); |
|
void print(); |
|
|
|
|
|
void drawRay(Display *dpy, Window w, GC gc, Objects objects, |
|
int recursive_count = 0, Point not_reflect = Point(), int lightness = 0); |
|
}; |
|
|
|
class Mirror : public Object { |
|
public: |
|
Mirror(Point _p1, Point _p2) { type = tMirror; p1 = _p1; p2 = _p2; }; |
|
|
|
void draw(Display *dpy, Window w, GC gc); |
|
void move(double dx, double dy); |
|
void resize(double dx, double dy); |
|
void print(); |
|
Point** getPoints(); |
|
}; |
|
|
|
class Area : public Object { |
|
public: |
|
double n = 1.0; // Refractive index |
|
Area(Point _p1, Point _p2, double _n) { type = tArea; p1 = _p1; p2 = _p2; n = _n; }; |
|
Area(Point _p1, Point _p2) : Area(_p1, _p2, 1.0) {}; |
|
|
|
void draw(Display *dpy, Window w, GC gc); |
|
void move(double dx, double dy); |
|
void resize(double dx, double dy); |
|
void print(); |
|
Point** getPoints(); |
|
|
|
bool isInside(Point p); |
|
}; |
|
|
|
class Objects { |
|
Object** objects; |
|
public: |
|
void add(Light *l); |
|
void add(Mirror *m); |
|
void add(Area *a); |
|
void remove(int index); |
|
int select(Point p, bool only_area = false); |
|
double n(Point p); |
|
void draw(Display *dpy, Window w, GC gc); |
|
|
|
Object& operator[](int index); |
|
|
|
int size(); |
|
private: |
|
int _size = 0; |
|
void resize(int size); |
|
}; |
|
|
|
bool getIntersection(Point a, Point b, Point c, Point d, Point* ret); |
|
double angleOfPoints(Point a, Point b, Point cross); |
|
|
|
bool isAddMode(int mode); |
|
bool isButtonEvent(XEvent e); |
|
void drawMenu(int m_mode, int mode, Display *dpy, Window w, GC gc); |
|
Point selectAnotherPoint(Point base, Point cross, Point **points, int size); |
|
void initColors(Display *dpy, Colormap *cmap); |
|
|
|
void Object::draw(Display *dpy, Window w, GC gc) { }; |
|
void Object::move(double dx, double dy) {}; |
|
void Object::resize(double dx, double dy) {}; |
|
|
|
void Light::draw(Display *dpy, Window w, GC gc) { |
|
Point p = Point(p1.x - (LIGHT_RADIUS/2), p1.y - (LIGHT_RADIUS/2)); |
|
XFillArc(dpy, w, gc, p.x, p.y, LIGHT_RADIUS, LIGHT_RADIUS, 0, 360*64); |
|
}; |
|
|
|
void Mirror::draw(Display *dpy, Window w, GC gc) { |
|
XDrawLine(dpy, w, gc, p1.x, p1.y, p2.x, p2.y); |
|
}; |
|
|
|
void Area::draw(Display *dpy, Window w, GC gc) { |
|
int color = round(log10(n) * 100); |
|
if (100 <= color) |
|
color = 99; |
|
|
|
XSetForeground(dpy, gc, grays[color]); // gainsboro |
|
Point lt = Point(min(p1.x, p2.x), min(p1.y, p2.y)); |
|
Point rb = Point(max(p1.x, p2.x), max(p1.y, p2.y)); |
|
XFillRectangle(dpy, w, gc, lt.x, lt.y, rb.x - lt.x, rb.y - lt.y); |
|
XSetForeground(dpy, gc, nColor[0]); // black |
|
}; |
|
|
|
void Light::move(double dx, double dy) { |
|
p1.x += dx; p1.y += dy; |
|
}; |
|
|
|
void Mirror::move(double dx, double dy) { |
|
p1.x += dx; p1.y += dy; |
|
p2.x += dx; p2.y += dy; |
|
}; |
|
|
|
void Area::move(double dx, double dy) { |
|
p1.x += dx; p1.y += dy; |
|
p2.x += dx; p2.y += dy; |
|
}; |
|
|
|
void Light::resize(double dx, double dy) { |
|
p1.x += dx; p1.y += dy; |
|
}; |
|
|
|
void Mirror::resize(double dx, double dy) { |
|
p2.x += dx; p2.y += dy; |
|
}; |
|
|
|
void Area::resize(double dx, double dy) { |
|
p2.x += dx; p2.y += dy; |
|
}; |
|
|
|
void Point::print() { |
|
printf("(%6.2lf, %6.2lf)", x, y); |
|
}; |
|
|
|
void Point::println() { |
|
print(); |
|
cout << endl; |
|
}; |
|
|
|
void Object::print() { |
|
printf("(%6.2lf, %6.2lf), (%6.2lf, %6.2lf)", p1.x, p1.y, p2.x, p2.y); |
|
}; |
|
|
|
void Object::println() { |
|
print(); |
|
cout << endl; |
|
}; |
|
|
|
void Light::print() { |
|
printf("(%6.2lf, %6.2lf), Angle: %6.2lf", p1.x, p1.y, angle); |
|
}; |
|
|
|
void Mirror::print() { |
|
printf("(%6.2lf, %6.2lf), (%6.2lf, %6.2lf)", p1.x, p1.y, p2.x, p2.y); |
|
}; |
|
|
|
void Area::print() { |
|
printf("(%6.2lf, %6.2lf), (%6.2lf, %6.2lf), n: %5.2lf", p1.x, p1.y, p2.x, p2.y, n); |
|
}; |
|
|
|
Point** Mirror::getPoints() { |
|
Point** points = (Point **)malloc(sizeof(Point *) * 2); |
|
points[0] = &p1; |
|
points[1] = &p2; |
|
|
|
return points; |
|
}; |
|
|
|
Point** Area::getPoints() { |
|
Point** points = (Point **)malloc(sizeof(Point *) * 4); |
|
points[0] = new Point(max(p1.x, p2.x), max(p1.y, p2.y)); |
|
points[1] = new Point(max(p1.x, p2.x), min(p1.y, p2.y)); |
|
points[2] = new Point(min(p1.x, p2.x), min(p1.y, p2.y)); |
|
points[3] = new Point(min(p1.x, p2.x), max(p1.y, p2.y)); |
|
|
|
return points; |
|
|
|
}; |
|
|
|
bool Area::isInside(Point p) { |
|
if (min(p1.x, p2.x) <= p.x && p.x <= max(p1.x, p2.x) && |
|
min(p1.y, p2.y) <= p.y && p.y <= max(p1.y, p2.y)) |
|
return true; |
|
return false; |
|
} |
|
|
|
|
|
void Light::drawRay(Display *dpy, Window w, GC gc, |
|
Objects objects, int recursive_count, Point not_reflect, int lightness) { |
|
if (MAX_REFLECT <= recursive_count || MAX_REFLECT <= lightness) |
|
return; |
|
|
|
Point dir = Point(p1.x + 1000 * cos(angle), p1.y + 1000 * sin(angle)); |
|
Point cross, cross_near, tmp; |
|
Object *obj, *collision_to; |
|
bool isCross = false; |
|
double dist = 32768; // inf |
|
Point _not_reflect = not_reflect; |
|
|
|
for (int i=0; i<objects.size(); i++) { |
|
obj = &objects[i]; |
|
|
|
switch (obj->type) { |
|
case tLight: |
|
break; |
|
|
|
case tMirror: |
|
if (getIntersection(p1, dir, obj->p1, obj->p2, &tmp)) { |
|
if (tmp.dist(not_reflect) < 0.1) |
|
break; |
|
|
|
if (tmp.x < 0 || WIDTH < tmp.x || tmp.y < 0 || HEIGHT < tmp.y) |
|
break; |
|
|
|
if (p1.dist(tmp) <= dist) { |
|
cross = tmp; |
|
dist = p1.dist(cross); |
|
cross_near = selectAnotherPoint(p1, cross, static_cast<Mirror *>(obj)->getPoints(), 2); |
|
|
|
_not_reflect = cross; |
|
collision_to = obj; |
|
} |
|
|
|
isCross = true; |
|
} |
|
break; |
|
|
|
case tArea: |
|
Area *area = static_cast<Area *>(obj); |
|
Point **points = area->getPoints(), |
|
**points2 = (Point **)malloc(sizeof(Point *) * 2); |
|
for (int j=0; j<4; j++) { |
|
points2[0] = points[j%4]; |
|
points2[1] = points[(j+1)%4]; |
|
|
|
if (getIntersection(p1, dir, *points2[0], *points2[1], &tmp)) { |
|
if (tmp.dist(not_reflect) < 0.1) |
|
continue; |
|
|
|
if (tmp.x < 0 || WIDTH < tmp.x || tmp.y < 0 || HEIGHT < tmp.y) |
|
continue; |
|
|
|
if (p1.dist(tmp) <= dist) { |
|
cross = tmp; |
|
dist = p1.dist(cross); |
|
cross_near = selectAnotherPoint(p1, cross, points2, 2); |
|
_not_reflect = cross; |
|
collision_to = obj; |
|
} |
|
|
|
isCross = true; |
|
} |
|
} |
|
free(points); |
|
free(points2); |
|
break; |
|
} |
|
} |
|
|
|
if (isCross) { |
|
XSetForeground(dpy, gc, nColor[4]); |
|
XFillArc(dpy, w, gc, cross.x-2, cross.y-2, 4, 4, 0, 360*64); |
|
|
|
XSetForeground(dpy, gc, reds[lightness]); |
|
XDrawLine(dpy, w, gc, p1.x, p1.y, cross.x, cross.y); |
|
XSetForeground(dpy, gc, nColor[0]); // black |
|
|
|
double theta_i = M_PI/2 - fmod(abs(angleOfPoints(p1, cross_near, cross)), M_PI/2); |
|
if (theta_i == M_PI/2) |
|
theta_i = 0; |
|
|
|
|
|
cout << "#" << setw(2) << recursive_count << " "; |
|
cout << "Intersects: "; |
|
cross.print(); cout << " "; |
|
|
|
cout << "angle: " << setw((int)log10(objects.size())+1); |
|
cout << setw(7) << angle/M_PI*180 << "[deg] "; |
|
cout << "i = " << setw(7) << theta_i/M_PI*180 << "[deg] "; |
|
|
|
bool isRefraction = true; |
|
|
|
// Refraction |
|
if (collision_to->type == tArea) { |
|
|
|
double n1 = objects.n(p1), n2; |
|
if (&objects[objects.select(p1, true)] == collision_to) |
|
n2 = 1.0; |
|
else |
|
n2 = static_cast<Area *>(collision_to)->n; |
|
|
|
double theta_r = asin(sin(theta_i) * (n1 / n2)); |
|
|
|
if (isnan(theta_r) || (n1 > n2 && theta_r > asin(n2/n1))) |
|
isRefraction = false; |
|
|
|
if (isRefraction) { |
|
cout << "r = " << setw(7) << theta_r/M_PI*180 << "[deg] " << endl; |
|
|
|
Light r = Light(Point(cross.x, cross.y), fmod(theta_r + angle - theta_i, 2*M_PI)); |
|
r.drawRay(dpy, w, gc, objects, recursive_count+1, _not_reflect, lightness + 1); |
|
} |
|
} |
|
|
|
// Refrection |
|
// if (collision_to->type == tMirror) |
|
if (!isRefraction || collision_to->type == tMirror) { |
|
|
|
double theta_i = M_PI/2 - fmod(angleOfPoints(p1, cross_near, cross), M_PI/2); |
|
double theta_j = angle - theta_i * 2; |
|
if (theta_i != M_PI/2) |
|
theta_j += M_PI; |
|
|
|
Light j = Light(Point(cross.x, cross.y), fmod(theta_j, 2*M_PI)); |
|
j.drawRay(dpy, w, gc, objects, recursive_count+1, _not_reflect, lightness + 1); |
|
} |
|
|
|
} else { |
|
XSetForeground(dpy, gc, reds[lightness]); |
|
XDrawLine(dpy, w, gc, p1.x, p1.y, p1.x + 1000*cos(angle), p1.y + 1000*sin(angle)); |
|
XSetForeground(dpy, gc, nColor[0]); |
|
} |
|
|
|
}; |
|
|
|
void Objects::add(Light *l) { |
|
resize(_size+1); |
|
objects[_size-1] = l; |
|
printf("Add: Light "); |
|
l->println(); |
|
}; |
|
|
|
void Objects::add(Mirror *m) { |
|
resize(_size+1); |
|
objects[_size-1] = m; |
|
printf("Add: Mirror "); |
|
m->println(); |
|
}; |
|
|
|
void Objects::add(Area *a) { |
|
resize(_size+1); |
|
objects[_size-1] = a; |
|
printf("Add: Area "); |
|
a->println(); |
|
}; |
|
|
|
void Objects::remove(int index) { |
|
for (int i=index+1; i<_size; i++) |
|
objects[i-1] = objects[i]; |
|
resize(_size-1); |
|
}; |
|
|
|
int Objects::select(Point p, bool only_area) { |
|
int selected = -1; |
|
Object *obj; |
|
|
|
// Select Area |
|
for (int i=0; i<_size; i++) { |
|
obj = objects[i]; |
|
|
|
if (obj->type == tArea) { |
|
Point p1 = obj->p1, p2 = obj->p2; |
|
double top = min(p1.y, p2.y), bottom = max(p1.y, p2.y), |
|
left = min(p1.x, p2.x), right = max(p1.x, p2.x); |
|
if (left <= p.x && p.x <= right && top <= p.y && p.y <= bottom) |
|
selected = i; |
|
} |
|
} |
|
|
|
if (only_area) |
|
return selected; |
|
|
|
// Select Mirror |
|
for (int i=0; i<_size; i++) { |
|
obj = objects[i]; |
|
|
|
if (obj->type == tMirror) { |
|
Point p1 = obj->p1; |
|
Point p2 = obj->p2; |
|
if ((p2.x-p1.x) != 0) { |
|
double a = (p2.y-p1.y) / (p2.x-p1.x); |
|
double d = p1.y - a*p1.x; |
|
|
|
if (abs(p.y - (a*p.x+d)) <= 5 && |
|
(min(p1.x, p2.x) - 5) <= p.x && p.x <= (max(p1.x, p2.x) + 5) && |
|
(min(p1.y, p2.y) - 5) <= p.y && p.y <= (max(p1.y, p2.y) + 5)) |
|
selected = i; |
|
} else { |
|
if (abs(p.x - p1.x) <= 5 || p1.dist(p) <= 5 || p2.dist(p) <= 5) |
|
selected = i; |
|
} |
|
} |
|
} |
|
|
|
// Select Light |
|
for (int i=0; i<_size; i++) { |
|
obj = objects[i]; |
|
|
|
if (obj->type == tLight && p.dist(obj->p1) <= LIGHT_RADIUS + 5) |
|
selected = i; |
|
} |
|
|
|
if (selected != -1) |
|
printf("Select object: id: %d, type: %s\n", selected, sObj[objects[selected]->type]); |
|
|
|
return selected; |
|
}; |
|
|
|
double Objects::n(Point p) { |
|
int index = select(p, true); |
|
|
|
if (index == -1) |
|
return 1.0; |
|
else |
|
return static_cast<Area *>(objects[index])->n; |
|
} |
|
|
|
void Objects::draw(Display *dpy, Window w, GC gc) { |
|
for (int i=0; i<_size; i++) |
|
if (objects[i]->type == tArea) |
|
objects[i]->draw(dpy, w, gc); |
|
|
|
for (int i=0; i<_size; i++) |
|
if (objects[i]->type == tMirror) |
|
objects[i]->draw(dpy, w, gc); |
|
|
|
for (int i=0; i<_size; i++) |
|
if (objects[i]->type == tLight) |
|
objects[i]->draw(dpy, w, gc); |
|
}; |
|
|
|
Object& Objects::operator[](int index) { |
|
// if (index < 0) |
|
// index = _size + index; |
|
return *objects[index]; |
|
}; |
|
|
|
int Objects::size() { |
|
return _size; |
|
}; |
|
|
|
void Objects::resize(int size) { |
|
printf("Objects resize from %d to %d\n", _size, size); |
|
objects = (Object **)realloc(objects, sizeof(Object *) * size); |
|
if (objects == nullptr) |
|
exit(-1); |
|
_size = size; |
|
}; |
|
|
|
void initColors(Display *dpy, Colormap *cmap) { |
|
XColor red, gray; |
|
red.red = 0xffff; |
|
red.green = 0; |
|
red.blue = 0; |
|
gray.red = gray.green = gray.blue = 0xf000; |
|
|
|
for(int i=0; i<MAX_REFLECT; i++) { |
|
red.green += 0xffff / MAX_REFLECT; |
|
red.blue += 0xffff / MAX_REFLECT; |
|
|
|
XAllocColor(dpy, *cmap, &red); |
|
reds[i] = red.pixel; |
|
} |
|
|
|
for(int i=0; i<100; i++) { |
|
gray.red = gray.green = gray.blue = gray.red - 0xffff / 200; |
|
|
|
XAllocColor(dpy, *cmap, &gray); |
|
grays[i] = gray.pixel; |
|
} |
|
} |