Select Git revision
Robotka István Adrián authored
sirius.cpp 15.34 KiB
//=============================================================================================
// Mintaprogram: Z�ld h�romsz�g. Ervenyes 2019. osztol.
//
// A beadott program csak ebben a fajlban lehet, a fajl 1 byte-os ASCII karaktereket tartalmazhat, BOM kihuzando.
// Tilos:
// - mast "beincludolni", illetve mas konyvtarat hasznalni
// - faljmuveleteket vegezni a printf-et kiveve
// - Mashonnan atvett programresszleteket forrasmegjeloles nelkul felhasznalni es
// - felesleges programsorokat a beadott programban hagyni!!!!!!!
// - felesleges kommenteket a beadott programba irni a forrasmegjelolest kommentjeit kiveve
// ---------------------------------------------------------------------------------------------
// A feladatot ANSI C++ nyelvu forditoprogrammal ellenorizzuk, a Visual Studio-hoz kepesti elteresekrol
// es a leggyakoribb hibakrol (pl. ideiglenes objektumot nem lehet referencia tipusnak ertekul adni)
// a hazibeado portal ad egy osszefoglalot.
// ---------------------------------------------------------------------------------------------
// A feladatmegoldasokban csak olyan OpenGL fuggvenyek hasznalhatok, amelyek az oran a feladatkiadasig elhangzottak
// A keretben nem szereplo GLUT fuggvenyek tiltottak.
//
// NYILATKOZAT
// ---------------------------------------------------------------------------------------------
// Nev : Borcsok Barnabas
// Neptun : WC624D
// ---------------------------------------------------------------------------------------------
// ezennel kijelentem, hogy a feladatot magam keszitettem, es ha barmilyen segitseget igenybe vettem vagy
// mas szellemi termeket felhasznaltam, akkor a forrast es az atvett reszt kommentekben egyertelmuen jeloltem.
// A forrasmegjeloles kotelme vonatkozik az eloadas foliakat es a targy oktatoi, illetve a
// grafhazi doktor tanacsait kiveve barmilyen csatornan (szoban, irasban, Interneten, stb.) erkezo minden egyeb
// informaciora (keplet, program, algoritmus, stb.). Kijelentem, hogy a forrasmegjelolessel atvett reszeket is ertem,
// azok helyessegere matematikai bizonyitast tudok adni. Tisztaban vagyok azzal, hogy az atvett reszek nem szamitanak
// a sajat kontribucioba, igy a feladat elfogadasarol a tobbi resz mennyisege es minosege alapjan szuletik dontes.
// Tudomasul veszem, hogy a forrasmegjeloles kotelmenek megsertese eseten a hazifeladatra adhato pontokat
// negativ elojellel szamoljak el es ezzel parhuzamosan eljaras is indul velem szemben.
//=============================================================================================
#include "framework.h"
// vertex shader in GLSL: It is a Raw string (C++11) since it contains new line characters
const char *const vertexSource = R"(
#version 330 // Shader 3.3
precision highp float; // normal floats, makes no difference on desktop computers
uniform mat4 MVP; // uniform variable, the Model-View-Projection transformation matrix
layout(location = 0) in vec2 vp; // Varying input: vp = vertex position is expected in attrib array 0
void main() {
gl_Position = vec4(vp.x, vp.y, 0, 1) * MVP; // transform vp from modeling space to normalized device space
}
)";
// fragment shader in GLSL
const char *const fragmentSource = R"(
#version 330 // Shader 3.3
precision highp float; // normal floats, makes no difference on desktop computers
uniform vec3 color; // uniform variable, the color of the primitive
out vec4 outColor; // computed color of the current pixel
void main() {
outColor = vec4(color, 1); // computed color is the color of the primitive
}
)";
//
// CONSTANTS AND BASIC HELPER FUNCTIONS
//
const int BASE_CIRCLE_SEGMENTS = 100;
const int SIRIUS_CIRCLE_SEGMENTS = 300;
constexpr float degToRad(float d) { return d * M_PI / 180.0; }
GPUProgram gpuProgram; // vertex and fragment shaders
void setColor(vec3 color) {
int location = glGetUniformLocation(gpuProgram.getId(), "color");
glUniform3f(location, color.x, color.y, color.z); // 3 floats
}
void setupMVP() {
float MVPtransf[4][4] = {1, 0, 0, 0, // MVP matrix,
0, 1, 0, 0, // row-major!
0, 0, 1, 0,
0, 0, 0, 1};
int location = glGetUniformLocation(gpuProgram.getId(), "MVP"); // Get the GPU location of uniform variable MVP
glUniformMatrix4fv(location, 1, GL_TRUE,
&MVPtransf[0][0]); // Load a 4x4 row-major float matrix to the specified location
}
//
// CLASSES
//
class Drawable {
protected:
unsigned int vao;
unsigned int vbo; // vertex buffer object
public:
std::vector<vec2> vertices;
virtual void draw() = 0;
void init() {
glGenVertexArrays(1, &vao); // get 1 vao id
glBindVertexArray(vao); // make it active
glGenBuffers(1, &vbo); // Generate 1 buffer
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, // Copy to GPU target
vertices.size() * sizeof(vec2), // # bytes
vertices.data(), // address
GL_STATIC_DRAW); // we do not change later
glEnableVertexAttribArray(0); // AttribArray 0
glVertexAttribPointer(0, // vbo -> AttribArray 0
2, GL_FLOAT,
GL_FALSE, // two floats/attrib, not fixed-point
0, NULL); // stride, offset: tightly packed
}
};
class BaseCircle : public Drawable {
public:
BaseCircle() {
// calculate all the vertices of the base circle:
float step = 360.0 / BASE_CIRCLE_SEGMENTS;
float currAngle = 0;
vertices.reserve(BASE_CIRCLE_SEGMENTS * 3);
for (int i = 0; i < BASE_CIRCLE_SEGMENTS; i++) {
// for each of the triangles, calculate their points
// middle point
vertices.emplace_back(0, 0);
vertices.emplace_back(cos(degToRad(currAngle)), sin(degToRad(currAngle)));
currAngle += step;
vertices.emplace_back(cos(degToRad(currAngle)), sin(degToRad(currAngle)));
}
}
void draw() override {
init();
glBindVertexArray(vao); // Draw call
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
}
};
class Point : public Drawable {
public:
vec2 p;
Point(vec2 p) {
this->p = p;
vertices.emplace_back(p.x, p.y);
init();
}
vec2 inversion() {
return p / dot(p, p);
}
void draw() override {
// printf("drawing points at %f %f\n", vertices[0].x, vertices[0].y);
glBindVertexArray(vao);
setColor(vec3(1, 0, 0));
glPointSize(10);
glDrawArrays(GL_POINTS, 0, 1);
}
};
class Circle {
public:
vec2 c; //center of the circle
float r; //radius of the circle
Circle(vec2 p1, vec2 p2, vec2 p3) {
vec2 n12 = p2 - p1; //normal vector for perpendicular bisector of p1 and p2
float f12 = dot(n12, (p2 + p1) / 2);
vec2 n23 = p3 - p2; //normal vector for perpendicular bisector of p1 and p2
float f23 = dot(n23, (p2 + p3) / 2);
//coordinates for the circle's center
float cx = (f12 * n23.y - f23 * n12.y) / (n12.x * n23.y - n12.y * n23.x);
float cy = (f12 / n12.y) - (n12.x / n12.y) * (f12 * n23.y - n12.y * f23) / (n12.x * n23.y - n12.y * n23.x);
printf("%f %f\n", cx, cy);
c = vec2(cx, cy);
r = sqrtf((c.x - p1.x) * (c.x - p1.x) + (c.y - p1.y) * (c.y - p1.y));
}
};
inline float cross(vec2 a, vec2 b) {
return a.x * b.y - a.y * b.x;
}
inline bool onDifferentSides(vec2 p1, vec2 p2, vec2 q1, vec2 q2) {
vec2 v = p2-p1;
vec2 r = p1;
return cross(v, p1 - r) * cross(v, p2 - r) < 0;
}
inline bool intersectEachOther(vec2 p1, vec2 p2, vec2 q1, vec2 q2) {
return
onDifferentSides(p1, p2, q1, q2) && onDifferentSides(q1, q2, p1, p2);
}
class Triangulated : public Drawable {
std::vector<vec2> remainingVertices;
public:
explicit Triangulated(std::vector<vec2> points) {
remainingVertices = points;
vertices.reserve(remainingVertices.size() * 3);
triangulate();
};
void triangulate() {
while (!remainingVertices.empty()) {
//finding an ear
auto currVertex = remainingVertices.begin();
//check if current vertex is an ear
vec2 currPotDiag1 = *getNextVertex(currVertex);
vec2 currPotDiag2 = *getPreviousVertex(currVertex);
//check if current potential diagonal intersects any edges
// skipping edges that share a vertex with it
for (auto currEdgeStart = getPreviousVertex(getPreviousVertex(currVertex));
currEdgeStart != getNextVertex(getNextVertex(currVertex)); currEdgeStart = getPreviousVertex(currEdgeStart))
if (intersectEachOther(vec2(currEdgeStart->x, currEdgeStart->y),
vec2(getNextVertex(currEdgeStart)->x, getNextVertex(currEdgeStart)->y),
currPotDiag1, currPotDiag2))
break;
//check if current potential diagonal is fully outside the polygon
vec2 infinity(250, 250);
vec2 middle((currPotDiag1 + currPotDiag2)/2);
int numberOfIntersections = 0;
for(auto currEdgeStart = remainingVertices.begin(); currEdgeStart != remainingVertices.begin(); currEdgeStart = getPreviousVertex(currEdgeStart))
if(intersectEachOther(currEdgeStart->x, *getNextVertex(currEdgeStart), infinity, middle))
numberOfIntersections++;
if(numberOfIntersections%2 == 0)
break; // the potential diagonal is fully outside, it's not an ear.
//it's an ear!
vertices.emplace_back(*currVertex);
vertices.emplace_back(currPotDiag1);
vertices.emplace_back(currPotDiag2);
remainingVertices.erase(currVertex);
}
}
std::vector<vec2>::iterator getPreviousVertex(std::vector<vec2>::iterator currVertex) {
if (currVertex == remainingVertices.begin())
return remainingVertices.end();
else
return --currVertex;
}
std::vector<vec2>::iterator getNextVertex(std::vector<vec2>::iterator currVertex) {
if (currVertex == remainingVertices.end())
return remainingVertices.begin();
else
return ++currVertex;
};
void draw() override {
init();
glBindVertexArray(vao); // Draw call
setColor(vec3(0,1,0));
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
}
};
class SiriusTriangle : public Drawable {
std::vector<Point> points;
std::vector<Triangulated> triangulated;
public:
int n = 0;
SiriusTriangle() = default;
void addPoint(vec2 p) {
n++;
points.emplace_back(p);
if (n > 3) printf("Can't add 4th point to a triangle!\n");
printf("%d\n", n);
if (n == 2) generateLineSegment(points[0], points[1]);
if (n == 3) {
generateLineSegment(points[2], points[0]);
generateLineSegment(points[1], points[2]);
triangulated.emplace_back(Triangulated(vertices));
}
}
void generateLineSegment(Point p1, Point p2) {
Circle c(p1.p, p2.p, p2.inversion());
// generating only points between p1 and p2
float angle1 = atan2((p1.p - c.c).y, (p1.p - c.c).x);
printf("Angle for p1 in the circle is %f\n", angle1);
float angle2 = atan2((p2.p - c.c).y, (p2.p - c.c).x);
printf("Angle for p2 in the circle is %f\n", angle2);
//normalize both angles to [0, 2*PI)
if (angle1 < 0) angle1 += 2 * M_PI;
if (angle2 < 0) angle2 += 2 * M_PI;
//rotate both angles by 180 degress and check if the difference is smaller then
float altAngle1 = angle1 + M_PI;
if (altAngle1 > 2 * M_PI) altAngle1 -= 2 * M_PI;
float altAngle2 = angle2 + M_PI;
if (altAngle2 > 2 * M_PI) altAngle2 -= 2 * M_PI;
float angleDiff;
if (fabs(angle1 - angle2) < fabs(altAngle1 - altAngle2))
angleDiff = (angle1 - angle2);
else
angleDiff = (altAngle1 - altAngle2);
//can be negative
float step = angleDiff / SIRIUS_CIRCLE_SEGMENTS;
//start from angle2
float curr = angle2;
for (int i = 0; i < SIRIUS_CIRCLE_SEGMENTS; i++) {
vertices.emplace_back(vec2(
c.r * cos(curr) + c.c.x,
c.r * sin(curr) + c.c.y
));
curr += step;
}
}
void draw() override {
//filling out the triangulated area
if(n == 3)
triangulated[0].draw();
//Time to draw the edges
if (n == 3) {
init();
glBindVertexArray(vao); // Draw call
setColor(vec3(0.6, 0.2, 0.6));
glDrawArrays(GL_LINE_LOOP, 0, vertices.size());
}
for (auto &point: points)
point.draw();
}
};
class SiriusTriangleManager {
public:
std::vector<SiriusTriangle> triangles;
int currTriangle = 0;
int currPoints = 0;
SiriusTriangleManager() = default;
void addPoint(vec2 p) {
if (currPoints == 0) triangles.emplace_back(SiriusTriangle());
triangles[currTriangle].addPoint(p);
currPoints++;
if (currPoints == 3) currTriangle++, currPoints = 0;
}
void draw() {
for (auto triangle: triangles)
triangle.draw();
}
};
BaseCircle baseCircle = BaseCircle();
SiriusTriangleManager siriusTriangleManager = SiriusTriangleManager();
// Initialization, create an OpenGL context
void onInitialization() {
glViewport(0, 0, windowWidth, windowHeight);
printf("%d", intersectEachOther(
vec2(0, 0),
vec2(100,0),
vec2(2, 2),
vec2(2, -4)
));
baseCircle.init();
siriusTriangleManager.addPoint(vec2(-0.6, 0.4));
siriusTriangleManager.addPoint(vec2(-0.8, -0.2));
siriusTriangleManager.addPoint(vec2(-0.2, -0.6));
// create program for the GPU
gpuProgram.create(vertexSource, fragmentSource, "outColor");
}
// Window has become invalid: Redraw
void onDisplay() {
glClearColor(0, 0, 0.4, 1); // background color
glClear(GL_COLOR_BUFFER_BIT); // clear frame buffer
setupMVP();
setColor(vec3(0, 0.3, 0.5));
baseCircle.draw();
siriusTriangleManager.draw();
glutSwapBuffers(); // exchange buffers for double buffering
}
// Key of ASCII code pressed
void onKeyboard(unsigned char key, int pX, int pY) {
if (key == 'd') glutPostRedisplay(); // if d, invalidate display, i.e. redraw
}
// Key of ASCII code released
void onKeyboardUp(unsigned char key, int pX, int pY) {
}
// Move mouse with key pressed
void onMouseMotion(int pX,
int pY) { // pX, pY are the pixel coordinates of the cursor in the coordinate system of the operation system
// Convert to normalized device space
float cX = 2.0f * pX / windowWidth - 1; // flip y axis
float cY = 1.0f - 2.0f * pY / windowHeight;
printf("Mouse moved to (%3.2f, %3.2f)\n", cX, cY);
}
// Mouse click event
void onMouse(int button, int state, int pX,
int pY) { // pX, pY are the pixel coordinates of the cursor in the coordinate system of the operation system
// Convert to normalized device space
float cX = 2.0f * pX / windowWidth - 1; // flip y axis
float cY = 1.0f - 2.0f * pY / windowHeight;
char *buttonStat;
switch (state) {
case GLUT_DOWN:
buttonStat = "pressed";
break;
case GLUT_UP:
buttonStat = "released";
break;
}
switch (button) {
case GLUT_LEFT_BUTTON:
printf("Left button %s at (%3.2f, %3.2f)\n", buttonStat, cX, cY);
break;
case GLUT_MIDDLE_BUTTON:
printf("Middle button %s at (%3.2f, %3.2f)\n", buttonStat, cX, cY);
break;
case GLUT_RIGHT_BUTTON:
printf("Right button %s at (%3.2f, %3.2f)\n", buttonStat, cX, cY);
break;
}
if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON) {
siriusTriangleManager.addPoint(vec2(cX, cY));
siriusTriangleManager.draw();
glutPostRedisplay();
}
}
// Idle event indicating that some time elapsed: do animation here
void onIdle() {
long time = glutGet(GLUT_ELAPSED_TIME); // elapsed time since the start of the program
}
#include <utility>