Skip to content
Snippets Groups Projects
Select Git revision
  • c41a858095d0c790a7512a866bb54b5027e796b5
  • master default
  • cleaner
  • testdata
4 results

sirius.cpp

Blame
  • Adrián Robotka's avatar
    Robotka István Adrián authored
    c41a8580
    History
    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>