diff --git a/hw04/mainwindow.cpp b/hw04/mainwindow.cpp index 804fd8d..fac4232 100644 --- a/hw04/mainwindow.cpp +++ b/hw04/mainwindow.cpp @@ -10,6 +10,7 @@ { ui->setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/mainwindow.cpp b/hw04/mainwindow.cpp index 804fd8d..fac4232 100644 --- a/hw04/mainwindow.cpp +++ b/hw04/mainwindow.cpp @@ -10,6 +10,7 @@ { ui->setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/oglwidget.cpp b/hw04/oglwidget.cpp index df2af28..9026d6f 100644 --- a/hw04/oglwidget.cpp +++ b/hw04/oglwidget.cpp @@ -11,8 +11,11 @@ static double alpha = 45.0; // rotation angle +/// Read-only mesh Mesh *originalMesh; +/// Outer mesh Mesh *parentMesh; +/// Inner mesh Mesh *childMesh; bool init = false; @@ -22,19 +25,21 @@ int childSubdivCount = 1; float scale = 2.0f; +/// Draw a mesh. +/// \param mesh Mesh to draw void drawMesh(Mesh mesh) { - glPolygonMode(GL_FRONT_AND_BACK , mesh.drawWireframe ? GL_LINE : GL_FILL); + glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL); if (mesh.drawWireframe) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - glBegin( GL_TRIANGLES); + glBegin(GL_TRIANGLES); for (auto tri : mesh.tris) { Vertex a = mesh.pts[tri.iv[0]]; Vertex b = mesh.pts[tri.iv[1]]; Vertex c = mesh.pts[tri.iv[2]]; - Vertex nvec = ((b-a)%(c-a)); + Vertex nvec = ((b - a) % (c - a)); glNormal3fv(nvec.p); @@ -46,6 +51,7 @@ glEnd(); if (!mesh.drawOutline) return; + // draw the edges on top of the faces for (auto tri : mesh.tris) { glLineWidth(3); glBegin( GL_LINE_STRIP); @@ -61,6 +67,7 @@ } } +/// Draw the two meshes void DrawTriag() { if (!init) return; @@ -69,7 +76,7 @@ } -// initialize Open GL lighting and projection matrix +/// initialize Open GL lighting and projection matrix void InitLightingAndProjection() // to be executed once before drawing { // light positions and colors @@ -105,7 +112,7 @@ //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion } -// define material color properties for front and back side +/// define material color properties for front and back side void SetMaterialColor( int side, float r, float g, float b){ float amb[4], dif[4], spe[4]; int i, mat; @@ -159,11 +166,6 @@ InitLightingAndProjection(); // define light sources and projection - -} - -void update() { - } void OGLWidget::paintGL() // draw everything, to be called repeatedly @@ -199,6 +201,9 @@ glViewport(0,0,w,h); } +/// Clone and subdivide the outer mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::parentSubdiv(int i) { parentSubdivCount = i; @@ -207,6 +212,9 @@ parentMesh->subDivLoop(parentSubdivCount); } +/// Clone and subdivide the inner mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::childSubdiv(int i) { childSubdivCount = i; @@ -215,20 +223,34 @@ childMesh->subDivLoop(childSubdivCount); } +/// Whether or not to draw the outer mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawOutline(int i) { drawParent = i == 2; } +/// Whether or not to draw the inner mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawWireframe(int i) { childWireframe = i == 2; childMesh->drawWireframe = childWireframe; } +/// Set global scale. +/// Callback from UI. +/// \param i Slider value void OGLWidget::setScale(int i) { scale = float(i) / 10.0f; } +/// Load a mesh from filepath. +/// Callback from UI. +/// \see bool Mesh::loadData(const string& fileName) +/// \param filename Path to mesh file +/// \return True, if loading was successful bool OGLWidget::loadFile(string filename) { originalMesh = new Mesh(); diff --git a/hw04/mainwindow.cpp b/hw04/mainwindow.cpp index 804fd8d..fac4232 100644 --- a/hw04/mainwindow.cpp +++ b/hw04/mainwindow.cpp @@ -10,6 +10,7 @@ { ui->setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/oglwidget.cpp b/hw04/oglwidget.cpp index df2af28..9026d6f 100644 --- a/hw04/oglwidget.cpp +++ b/hw04/oglwidget.cpp @@ -11,8 +11,11 @@ static double alpha = 45.0; // rotation angle +/// Read-only mesh Mesh *originalMesh; +/// Outer mesh Mesh *parentMesh; +/// Inner mesh Mesh *childMesh; bool init = false; @@ -22,19 +25,21 @@ int childSubdivCount = 1; float scale = 2.0f; +/// Draw a mesh. +/// \param mesh Mesh to draw void drawMesh(Mesh mesh) { - glPolygonMode(GL_FRONT_AND_BACK , mesh.drawWireframe ? GL_LINE : GL_FILL); + glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL); if (mesh.drawWireframe) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - glBegin( GL_TRIANGLES); + glBegin(GL_TRIANGLES); for (auto tri : mesh.tris) { Vertex a = mesh.pts[tri.iv[0]]; Vertex b = mesh.pts[tri.iv[1]]; Vertex c = mesh.pts[tri.iv[2]]; - Vertex nvec = ((b-a)%(c-a)); + Vertex nvec = ((b - a) % (c - a)); glNormal3fv(nvec.p); @@ -46,6 +51,7 @@ glEnd(); if (!mesh.drawOutline) return; + // draw the edges on top of the faces for (auto tri : mesh.tris) { glLineWidth(3); glBegin( GL_LINE_STRIP); @@ -61,6 +67,7 @@ } } +/// Draw the two meshes void DrawTriag() { if (!init) return; @@ -69,7 +76,7 @@ } -// initialize Open GL lighting and projection matrix +/// initialize Open GL lighting and projection matrix void InitLightingAndProjection() // to be executed once before drawing { // light positions and colors @@ -105,7 +112,7 @@ //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion } -// define material color properties for front and back side +/// define material color properties for front and back side void SetMaterialColor( int side, float r, float g, float b){ float amb[4], dif[4], spe[4]; int i, mat; @@ -159,11 +166,6 @@ InitLightingAndProjection(); // define light sources and projection - -} - -void update() { - } void OGLWidget::paintGL() // draw everything, to be called repeatedly @@ -199,6 +201,9 @@ glViewport(0,0,w,h); } +/// Clone and subdivide the outer mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::parentSubdiv(int i) { parentSubdivCount = i; @@ -207,6 +212,9 @@ parentMesh->subDivLoop(parentSubdivCount); } +/// Clone and subdivide the inner mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::childSubdiv(int i) { childSubdivCount = i; @@ -215,20 +223,34 @@ childMesh->subDivLoop(childSubdivCount); } +/// Whether or not to draw the outer mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawOutline(int i) { drawParent = i == 2; } +/// Whether or not to draw the inner mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawWireframe(int i) { childWireframe = i == 2; childMesh->drawWireframe = childWireframe; } +/// Set global scale. +/// Callback from UI. +/// \param i Slider value void OGLWidget::setScale(int i) { scale = float(i) / 10.0f; } +/// Load a mesh from filepath. +/// Callback from UI. +/// \see bool Mesh::loadData(const string& fileName) +/// \param filename Path to mesh file +/// \return True, if loading was successful bool OGLWidget::loadFile(string filename) { originalMesh = new Mesh(); diff --git a/hw04/src/Mesh.cpp b/hw04/src/Mesh.cpp index b925b20..e53f093 100644 --- a/hw04/src/Mesh.cpp +++ b/hw04/src/Mesh.cpp @@ -49,11 +49,13 @@ //getline( file, line); file >> key; if (key == "v") { + // vertex float x, y, z; file >> x >> y >> z; Vertex pVertex = *new Vertex(this, x, y, z); pts.push_back(pVertex); } else if (key == "f") { + // face (only triangles supported) int a, b, c; file >> a >> b >> c; Tri pTriangle = *new Tri(this, a-1, b-1, c-1); @@ -123,6 +125,8 @@ for (int tI = 0; tI < 3; ++tI) { Tri t = tris[triag.it[tI]]; + + // figure out, which neighbour I am to my neighbour int otherEI = 0; for (int j = 0; j < 3; ++j) { if (t.it[j] == i) { @@ -132,11 +136,13 @@ } if (i < triag.it[tI]) { Vertex d = pts[t.iv[otherEI]]; - Vertex e = (1.0f/8.0f)*((((tI == 1 || tI == 2) ? 3.0f : 1.0f)*a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f)*b) + (((tI == 1 || tI == 0) ? 3.0f : 1.0f)*c) + d); + Vertex e = (1.0f / 8.0f) * + ((((tI == 1 || tI == 2) ? 3.0f : 1.0f) * a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f) * b) + + (((tI == 1 || tI == 0) ? 3.0f : 1.0f) * c) + d); triag.ie[tI] = pts.size(); pts.push_back(e); } else { - // edge-mask already calculated + // the calculated edge-mask is already present in the neighbour triag.ie[tI] = t.ie[otherEI]; } @@ -145,14 +151,14 @@ tris[i] = triag; } - for( int i=0; i< pts.size(); i++){ // multiply every vertex with beta - int n = pts[i].valence; // n = valence of v_i + for (auto &pt : pts) { // multiply every vertex with beta + int n = pt.valence; // n = valence of v_i if (n < 3) continue; - float beta = Util::beta_n( n); -// cout << i << " " << n << " " << beta << " " <setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/oglwidget.cpp b/hw04/oglwidget.cpp index df2af28..9026d6f 100644 --- a/hw04/oglwidget.cpp +++ b/hw04/oglwidget.cpp @@ -11,8 +11,11 @@ static double alpha = 45.0; // rotation angle +/// Read-only mesh Mesh *originalMesh; +/// Outer mesh Mesh *parentMesh; +/// Inner mesh Mesh *childMesh; bool init = false; @@ -22,19 +25,21 @@ int childSubdivCount = 1; float scale = 2.0f; +/// Draw a mesh. +/// \param mesh Mesh to draw void drawMesh(Mesh mesh) { - glPolygonMode(GL_FRONT_AND_BACK , mesh.drawWireframe ? GL_LINE : GL_FILL); + glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL); if (mesh.drawWireframe) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - glBegin( GL_TRIANGLES); + glBegin(GL_TRIANGLES); for (auto tri : mesh.tris) { Vertex a = mesh.pts[tri.iv[0]]; Vertex b = mesh.pts[tri.iv[1]]; Vertex c = mesh.pts[tri.iv[2]]; - Vertex nvec = ((b-a)%(c-a)); + Vertex nvec = ((b - a) % (c - a)); glNormal3fv(nvec.p); @@ -46,6 +51,7 @@ glEnd(); if (!mesh.drawOutline) return; + // draw the edges on top of the faces for (auto tri : mesh.tris) { glLineWidth(3); glBegin( GL_LINE_STRIP); @@ -61,6 +67,7 @@ } } +/// Draw the two meshes void DrawTriag() { if (!init) return; @@ -69,7 +76,7 @@ } -// initialize Open GL lighting and projection matrix +/// initialize Open GL lighting and projection matrix void InitLightingAndProjection() // to be executed once before drawing { // light positions and colors @@ -105,7 +112,7 @@ //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion } -// define material color properties for front and back side +/// define material color properties for front and back side void SetMaterialColor( int side, float r, float g, float b){ float amb[4], dif[4], spe[4]; int i, mat; @@ -159,11 +166,6 @@ InitLightingAndProjection(); // define light sources and projection - -} - -void update() { - } void OGLWidget::paintGL() // draw everything, to be called repeatedly @@ -199,6 +201,9 @@ glViewport(0,0,w,h); } +/// Clone and subdivide the outer mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::parentSubdiv(int i) { parentSubdivCount = i; @@ -207,6 +212,9 @@ parentMesh->subDivLoop(parentSubdivCount); } +/// Clone and subdivide the inner mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::childSubdiv(int i) { childSubdivCount = i; @@ -215,20 +223,34 @@ childMesh->subDivLoop(childSubdivCount); } +/// Whether or not to draw the outer mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawOutline(int i) { drawParent = i == 2; } +/// Whether or not to draw the inner mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawWireframe(int i) { childWireframe = i == 2; childMesh->drawWireframe = childWireframe; } +/// Set global scale. +/// Callback from UI. +/// \param i Slider value void OGLWidget::setScale(int i) { scale = float(i) / 10.0f; } +/// Load a mesh from filepath. +/// Callback from UI. +/// \see bool Mesh::loadData(const string& fileName) +/// \param filename Path to mesh file +/// \return True, if loading was successful bool OGLWidget::loadFile(string filename) { originalMesh = new Mesh(); diff --git a/hw04/src/Mesh.cpp b/hw04/src/Mesh.cpp index b925b20..e53f093 100644 --- a/hw04/src/Mesh.cpp +++ b/hw04/src/Mesh.cpp @@ -49,11 +49,13 @@ //getline( file, line); file >> key; if (key == "v") { + // vertex float x, y, z; file >> x >> y >> z; Vertex pVertex = *new Vertex(this, x, y, z); pts.push_back(pVertex); } else if (key == "f") { + // face (only triangles supported) int a, b, c; file >> a >> b >> c; Tri pTriangle = *new Tri(this, a-1, b-1, c-1); @@ -123,6 +125,8 @@ for (int tI = 0; tI < 3; ++tI) { Tri t = tris[triag.it[tI]]; + + // figure out, which neighbour I am to my neighbour int otherEI = 0; for (int j = 0; j < 3; ++j) { if (t.it[j] == i) { @@ -132,11 +136,13 @@ } if (i < triag.it[tI]) { Vertex d = pts[t.iv[otherEI]]; - Vertex e = (1.0f/8.0f)*((((tI == 1 || tI == 2) ? 3.0f : 1.0f)*a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f)*b) + (((tI == 1 || tI == 0) ? 3.0f : 1.0f)*c) + d); + Vertex e = (1.0f / 8.0f) * + ((((tI == 1 || tI == 2) ? 3.0f : 1.0f) * a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f) * b) + + (((tI == 1 || tI == 0) ? 3.0f : 1.0f) * c) + d); triag.ie[tI] = pts.size(); pts.push_back(e); } else { - // edge-mask already calculated + // the calculated edge-mask is already present in the neighbour triag.ie[tI] = t.ie[otherEI]; } @@ -145,14 +151,14 @@ tris[i] = triag; } - for( int i=0; i< pts.size(); i++){ // multiply every vertex with beta - int n = pts[i].valence; // n = valence of v_i + for (auto &pt : pts) { // multiply every vertex with beta + int n = pt.valence; // n = valence of v_i if (n < 3) continue; - float beta = Util::beta_n( n); -// cout << i << " " << n << " " << beta << " " < pts; - vector tris; + vector pts; + vector tris; bool drawWireframe = false; bool drawOutline = false; Mesh(); - bool loadData(const string& fileName); - void saveData(const string& fileName); + + /// Load a mesh from a .obj file. + /// \param fileName Path to file + /// \return True, if loading was successful + bool loadData(const string &fileName); + + /// Save this mesh to as a OBJ file. + /// \param fileName Path to new file + void saveData(const string &fileName); void connectivityAlgo(); + + /// Subdivide this mesh using Loop-Subdivison once. void subDivLoop(); + + /// Subdivide this mesh using Loop-Subdivision count times. + /// \param count How many subdivision steps void subDivLoop(int count); + + /// Subdivide this mesh using the edge midpoints. void subDivEdgeMidpoint(); - Mesh* copy(); + /// Deep-clone this mesh + /// \return New mesh + Mesh *copy(); }; diff --git a/hw04/mainwindow.cpp b/hw04/mainwindow.cpp index 804fd8d..fac4232 100644 --- a/hw04/mainwindow.cpp +++ b/hw04/mainwindow.cpp @@ -10,6 +10,7 @@ { ui->setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/oglwidget.cpp b/hw04/oglwidget.cpp index df2af28..9026d6f 100644 --- a/hw04/oglwidget.cpp +++ b/hw04/oglwidget.cpp @@ -11,8 +11,11 @@ static double alpha = 45.0; // rotation angle +/// Read-only mesh Mesh *originalMesh; +/// Outer mesh Mesh *parentMesh; +/// Inner mesh Mesh *childMesh; bool init = false; @@ -22,19 +25,21 @@ int childSubdivCount = 1; float scale = 2.0f; +/// Draw a mesh. +/// \param mesh Mesh to draw void drawMesh(Mesh mesh) { - glPolygonMode(GL_FRONT_AND_BACK , mesh.drawWireframe ? GL_LINE : GL_FILL); + glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL); if (mesh.drawWireframe) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - glBegin( GL_TRIANGLES); + glBegin(GL_TRIANGLES); for (auto tri : mesh.tris) { Vertex a = mesh.pts[tri.iv[0]]; Vertex b = mesh.pts[tri.iv[1]]; Vertex c = mesh.pts[tri.iv[2]]; - Vertex nvec = ((b-a)%(c-a)); + Vertex nvec = ((b - a) % (c - a)); glNormal3fv(nvec.p); @@ -46,6 +51,7 @@ glEnd(); if (!mesh.drawOutline) return; + // draw the edges on top of the faces for (auto tri : mesh.tris) { glLineWidth(3); glBegin( GL_LINE_STRIP); @@ -61,6 +67,7 @@ } } +/// Draw the two meshes void DrawTriag() { if (!init) return; @@ -69,7 +76,7 @@ } -// initialize Open GL lighting and projection matrix +/// initialize Open GL lighting and projection matrix void InitLightingAndProjection() // to be executed once before drawing { // light positions and colors @@ -105,7 +112,7 @@ //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion } -// define material color properties for front and back side +/// define material color properties for front and back side void SetMaterialColor( int side, float r, float g, float b){ float amb[4], dif[4], spe[4]; int i, mat; @@ -159,11 +166,6 @@ InitLightingAndProjection(); // define light sources and projection - -} - -void update() { - } void OGLWidget::paintGL() // draw everything, to be called repeatedly @@ -199,6 +201,9 @@ glViewport(0,0,w,h); } +/// Clone and subdivide the outer mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::parentSubdiv(int i) { parentSubdivCount = i; @@ -207,6 +212,9 @@ parentMesh->subDivLoop(parentSubdivCount); } +/// Clone and subdivide the inner mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::childSubdiv(int i) { childSubdivCount = i; @@ -215,20 +223,34 @@ childMesh->subDivLoop(childSubdivCount); } +/// Whether or not to draw the outer mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawOutline(int i) { drawParent = i == 2; } +/// Whether or not to draw the inner mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawWireframe(int i) { childWireframe = i == 2; childMesh->drawWireframe = childWireframe; } +/// Set global scale. +/// Callback from UI. +/// \param i Slider value void OGLWidget::setScale(int i) { scale = float(i) / 10.0f; } +/// Load a mesh from filepath. +/// Callback from UI. +/// \see bool Mesh::loadData(const string& fileName) +/// \param filename Path to mesh file +/// \return True, if loading was successful bool OGLWidget::loadFile(string filename) { originalMesh = new Mesh(); diff --git a/hw04/src/Mesh.cpp b/hw04/src/Mesh.cpp index b925b20..e53f093 100644 --- a/hw04/src/Mesh.cpp +++ b/hw04/src/Mesh.cpp @@ -49,11 +49,13 @@ //getline( file, line); file >> key; if (key == "v") { + // vertex float x, y, z; file >> x >> y >> z; Vertex pVertex = *new Vertex(this, x, y, z); pts.push_back(pVertex); } else if (key == "f") { + // face (only triangles supported) int a, b, c; file >> a >> b >> c; Tri pTriangle = *new Tri(this, a-1, b-1, c-1); @@ -123,6 +125,8 @@ for (int tI = 0; tI < 3; ++tI) { Tri t = tris[triag.it[tI]]; + + // figure out, which neighbour I am to my neighbour int otherEI = 0; for (int j = 0; j < 3; ++j) { if (t.it[j] == i) { @@ -132,11 +136,13 @@ } if (i < triag.it[tI]) { Vertex d = pts[t.iv[otherEI]]; - Vertex e = (1.0f/8.0f)*((((tI == 1 || tI == 2) ? 3.0f : 1.0f)*a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f)*b) + (((tI == 1 || tI == 0) ? 3.0f : 1.0f)*c) + d); + Vertex e = (1.0f / 8.0f) * + ((((tI == 1 || tI == 2) ? 3.0f : 1.0f) * a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f) * b) + + (((tI == 1 || tI == 0) ? 3.0f : 1.0f) * c) + d); triag.ie[tI] = pts.size(); pts.push_back(e); } else { - // edge-mask already calculated + // the calculated edge-mask is already present in the neighbour triag.ie[tI] = t.ie[otherEI]; } @@ -145,14 +151,14 @@ tris[i] = triag; } - for( int i=0; i< pts.size(); i++){ // multiply every vertex with beta - int n = pts[i].valence; // n = valence of v_i + for (auto &pt : pts) { // multiply every vertex with beta + int n = pt.valence; // n = valence of v_i if (n < 3) continue; - float beta = Util::beta_n( n); -// cout << i << " " << n << " " << beta << " " < pts; - vector tris; + vector pts; + vector tris; bool drawWireframe = false; bool drawOutline = false; Mesh(); - bool loadData(const string& fileName); - void saveData(const string& fileName); + + /// Load a mesh from a .obj file. + /// \param fileName Path to file + /// \return True, if loading was successful + bool loadData(const string &fileName); + + /// Save this mesh to as a OBJ file. + /// \param fileName Path to new file + void saveData(const string &fileName); void connectivityAlgo(); + + /// Subdivide this mesh using Loop-Subdivison once. void subDivLoop(); + + /// Subdivide this mesh using Loop-Subdivision count times. + /// \param count How many subdivision steps void subDivLoop(int count); + + /// Subdivide this mesh using the edge midpoints. void subDivEdgeMidpoint(); - Mesh* copy(); + /// Deep-clone this mesh + /// \return New mesh + Mesh *copy(); }; diff --git a/hw04/src/Tri.h b/hw04/src/Tri.h index f9eacd2..ec719dc 100644 --- a/hw04/src/Tri.h +++ b/hw04/src/Tri.h @@ -17,12 +17,28 @@ int ie[3]{}; // edge vertex indices Mesh *mesh; // parent mesh - explicit Tri(Mesh* mesh); - explicit Tri(Mesh* mesh, int i[3]); - Tri(Mesh* mesh, int i, int j, int k); + /// Base-constructor + /// \param mesh Parent mesh + explicit Tri(Mesh *mesh); + + /// Create new triangle based on vertex indices of the parent mesh. + /// \param mesh Parent mesh + /// \param i Array of vertex indices + explicit Tri(Mesh *mesh, int i[3]); + + /// Create new triangle based on vertex indices of the parent mesh. + /// \param mesh parent mesh + /// \param i First vertex index + /// \param j Second vertex index + /// \param k Third vertex index + Tri(Mesh *mesh, int i, int j, int k); + void Print(); - Tri* copy(Mesh* mesh); + /// Deep-clone this triangle. + /// \param mesh Parent mesh of the new triangle + /// \return New triangle + Tri *copy(Mesh *mesh); }; diff --git a/hw04/mainwindow.cpp b/hw04/mainwindow.cpp index 804fd8d..fac4232 100644 --- a/hw04/mainwindow.cpp +++ b/hw04/mainwindow.cpp @@ -10,6 +10,7 @@ { ui->setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/oglwidget.cpp b/hw04/oglwidget.cpp index df2af28..9026d6f 100644 --- a/hw04/oglwidget.cpp +++ b/hw04/oglwidget.cpp @@ -11,8 +11,11 @@ static double alpha = 45.0; // rotation angle +/// Read-only mesh Mesh *originalMesh; +/// Outer mesh Mesh *parentMesh; +/// Inner mesh Mesh *childMesh; bool init = false; @@ -22,19 +25,21 @@ int childSubdivCount = 1; float scale = 2.0f; +/// Draw a mesh. +/// \param mesh Mesh to draw void drawMesh(Mesh mesh) { - glPolygonMode(GL_FRONT_AND_BACK , mesh.drawWireframe ? GL_LINE : GL_FILL); + glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL); if (mesh.drawWireframe) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - glBegin( GL_TRIANGLES); + glBegin(GL_TRIANGLES); for (auto tri : mesh.tris) { Vertex a = mesh.pts[tri.iv[0]]; Vertex b = mesh.pts[tri.iv[1]]; Vertex c = mesh.pts[tri.iv[2]]; - Vertex nvec = ((b-a)%(c-a)); + Vertex nvec = ((b - a) % (c - a)); glNormal3fv(nvec.p); @@ -46,6 +51,7 @@ glEnd(); if (!mesh.drawOutline) return; + // draw the edges on top of the faces for (auto tri : mesh.tris) { glLineWidth(3); glBegin( GL_LINE_STRIP); @@ -61,6 +67,7 @@ } } +/// Draw the two meshes void DrawTriag() { if (!init) return; @@ -69,7 +76,7 @@ } -// initialize Open GL lighting and projection matrix +/// initialize Open GL lighting and projection matrix void InitLightingAndProjection() // to be executed once before drawing { // light positions and colors @@ -105,7 +112,7 @@ //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion } -// define material color properties for front and back side +/// define material color properties for front and back side void SetMaterialColor( int side, float r, float g, float b){ float amb[4], dif[4], spe[4]; int i, mat; @@ -159,11 +166,6 @@ InitLightingAndProjection(); // define light sources and projection - -} - -void update() { - } void OGLWidget::paintGL() // draw everything, to be called repeatedly @@ -199,6 +201,9 @@ glViewport(0,0,w,h); } +/// Clone and subdivide the outer mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::parentSubdiv(int i) { parentSubdivCount = i; @@ -207,6 +212,9 @@ parentMesh->subDivLoop(parentSubdivCount); } +/// Clone and subdivide the inner mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::childSubdiv(int i) { childSubdivCount = i; @@ -215,20 +223,34 @@ childMesh->subDivLoop(childSubdivCount); } +/// Whether or not to draw the outer mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawOutline(int i) { drawParent = i == 2; } +/// Whether or not to draw the inner mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawWireframe(int i) { childWireframe = i == 2; childMesh->drawWireframe = childWireframe; } +/// Set global scale. +/// Callback from UI. +/// \param i Slider value void OGLWidget::setScale(int i) { scale = float(i) / 10.0f; } +/// Load a mesh from filepath. +/// Callback from UI. +/// \see bool Mesh::loadData(const string& fileName) +/// \param filename Path to mesh file +/// \return True, if loading was successful bool OGLWidget::loadFile(string filename) { originalMesh = new Mesh(); diff --git a/hw04/src/Mesh.cpp b/hw04/src/Mesh.cpp index b925b20..e53f093 100644 --- a/hw04/src/Mesh.cpp +++ b/hw04/src/Mesh.cpp @@ -49,11 +49,13 @@ //getline( file, line); file >> key; if (key == "v") { + // vertex float x, y, z; file >> x >> y >> z; Vertex pVertex = *new Vertex(this, x, y, z); pts.push_back(pVertex); } else if (key == "f") { + // face (only triangles supported) int a, b, c; file >> a >> b >> c; Tri pTriangle = *new Tri(this, a-1, b-1, c-1); @@ -123,6 +125,8 @@ for (int tI = 0; tI < 3; ++tI) { Tri t = tris[triag.it[tI]]; + + // figure out, which neighbour I am to my neighbour int otherEI = 0; for (int j = 0; j < 3; ++j) { if (t.it[j] == i) { @@ -132,11 +136,13 @@ } if (i < triag.it[tI]) { Vertex d = pts[t.iv[otherEI]]; - Vertex e = (1.0f/8.0f)*((((tI == 1 || tI == 2) ? 3.0f : 1.0f)*a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f)*b) + (((tI == 1 || tI == 0) ? 3.0f : 1.0f)*c) + d); + Vertex e = (1.0f / 8.0f) * + ((((tI == 1 || tI == 2) ? 3.0f : 1.0f) * a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f) * b) + + (((tI == 1 || tI == 0) ? 3.0f : 1.0f) * c) + d); triag.ie[tI] = pts.size(); pts.push_back(e); } else { - // edge-mask already calculated + // the calculated edge-mask is already present in the neighbour triag.ie[tI] = t.ie[otherEI]; } @@ -145,14 +151,14 @@ tris[i] = triag; } - for( int i=0; i< pts.size(); i++){ // multiply every vertex with beta - int n = pts[i].valence; // n = valence of v_i + for (auto &pt : pts) { // multiply every vertex with beta + int n = pt.valence; // n = valence of v_i if (n < 3) continue; - float beta = Util::beta_n( n); -// cout << i << " " << n << " " << beta << " " < pts; - vector tris; + vector pts; + vector tris; bool drawWireframe = false; bool drawOutline = false; Mesh(); - bool loadData(const string& fileName); - void saveData(const string& fileName); + + /// Load a mesh from a .obj file. + /// \param fileName Path to file + /// \return True, if loading was successful + bool loadData(const string &fileName); + + /// Save this mesh to as a OBJ file. + /// \param fileName Path to new file + void saveData(const string &fileName); void connectivityAlgo(); + + /// Subdivide this mesh using Loop-Subdivison once. void subDivLoop(); + + /// Subdivide this mesh using Loop-Subdivision count times. + /// \param count How many subdivision steps void subDivLoop(int count); + + /// Subdivide this mesh using the edge midpoints. void subDivEdgeMidpoint(); - Mesh* copy(); + /// Deep-clone this mesh + /// \return New mesh + Mesh *copy(); }; diff --git a/hw04/src/Tri.h b/hw04/src/Tri.h index f9eacd2..ec719dc 100644 --- a/hw04/src/Tri.h +++ b/hw04/src/Tri.h @@ -17,12 +17,28 @@ int ie[3]{}; // edge vertex indices Mesh *mesh; // parent mesh - explicit Tri(Mesh* mesh); - explicit Tri(Mesh* mesh, int i[3]); - Tri(Mesh* mesh, int i, int j, int k); + /// Base-constructor + /// \param mesh Parent mesh + explicit Tri(Mesh *mesh); + + /// Create new triangle based on vertex indices of the parent mesh. + /// \param mesh Parent mesh + /// \param i Array of vertex indices + explicit Tri(Mesh *mesh, int i[3]); + + /// Create new triangle based on vertex indices of the parent mesh. + /// \param mesh parent mesh + /// \param i First vertex index + /// \param j Second vertex index + /// \param k Third vertex index + Tri(Mesh *mesh, int i, int j, int k); + void Print(); - Tri* copy(Mesh* mesh); + /// Deep-clone this triangle. + /// \param mesh Parent mesh of the new triangle + /// \return New triangle + Tri *copy(Mesh *mesh); }; diff --git a/hw04/src/Util.h b/hw04/src/Util.h index 6b2633a..2e2afab 100644 --- a/hw04/src/Util.h +++ b/hw04/src/Util.h @@ -11,7 +11,10 @@ class Util { public: - static float beta_n( int n); + /// Calculates the value for beta in the vertex mask for the valence of a vertex. + /// \param n Valence of vertex + /// \return Value for beta + static float beta_n(int n); }; diff --git a/hw04/mainwindow.cpp b/hw04/mainwindow.cpp index 804fd8d..fac4232 100644 --- a/hw04/mainwindow.cpp +++ b/hw04/mainwindow.cpp @@ -10,6 +10,7 @@ { ui->setupUi(this); + // disable all controls and wait for valid file loading ui->parentSpinBox->setEnabled(false); ui->childSpinBox->setEnabled(false); ui->drawWireframe->setEnabled(false); @@ -32,9 +33,10 @@ } void MainWindow::loadButtonClick() { - + // try loading the input file bool success = ui->glwidget->loadFile(ui->fileEdit->text().toStdString()); + // when loading was successful, active the controls ui->parentSpinBox->setEnabled(success); ui->childSpinBox->setEnabled(success); ui->drawWireframe->setEnabled(success); diff --git a/hw04/oglwidget.cpp b/hw04/oglwidget.cpp index df2af28..9026d6f 100644 --- a/hw04/oglwidget.cpp +++ b/hw04/oglwidget.cpp @@ -11,8 +11,11 @@ static double alpha = 45.0; // rotation angle +/// Read-only mesh Mesh *originalMesh; +/// Outer mesh Mesh *parentMesh; +/// Inner mesh Mesh *childMesh; bool init = false; @@ -22,19 +25,21 @@ int childSubdivCount = 1; float scale = 2.0f; +/// Draw a mesh. +/// \param mesh Mesh to draw void drawMesh(Mesh mesh) { - glPolygonMode(GL_FRONT_AND_BACK , mesh.drawWireframe ? GL_LINE : GL_FILL); + glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL); if (mesh.drawWireframe) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - glBegin( GL_TRIANGLES); + glBegin(GL_TRIANGLES); for (auto tri : mesh.tris) { Vertex a = mesh.pts[tri.iv[0]]; Vertex b = mesh.pts[tri.iv[1]]; Vertex c = mesh.pts[tri.iv[2]]; - Vertex nvec = ((b-a)%(c-a)); + Vertex nvec = ((b - a) % (c - a)); glNormal3fv(nvec.p); @@ -46,6 +51,7 @@ glEnd(); if (!mesh.drawOutline) return; + // draw the edges on top of the faces for (auto tri : mesh.tris) { glLineWidth(3); glBegin( GL_LINE_STRIP); @@ -61,6 +67,7 @@ } } +/// Draw the two meshes void DrawTriag() { if (!init) return; @@ -69,7 +76,7 @@ } -// initialize Open GL lighting and projection matrix +/// initialize Open GL lighting and projection matrix void InitLightingAndProjection() // to be executed once before drawing { // light positions and colors @@ -105,7 +112,7 @@ //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion } -// define material color properties for front and back side +/// define material color properties for front and back side void SetMaterialColor( int side, float r, float g, float b){ float amb[4], dif[4], spe[4]; int i, mat; @@ -159,11 +166,6 @@ InitLightingAndProjection(); // define light sources and projection - -} - -void update() { - } void OGLWidget::paintGL() // draw everything, to be called repeatedly @@ -199,6 +201,9 @@ glViewport(0,0,w,h); } +/// Clone and subdivide the outer mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::parentSubdiv(int i) { parentSubdivCount = i; @@ -207,6 +212,9 @@ parentMesh->subDivLoop(parentSubdivCount); } +/// Clone and subdivide the inner mesh. +/// Callback from UI. +/// \param i Subdivision steps void OGLWidget::childSubdiv(int i) { childSubdivCount = i; @@ -215,20 +223,34 @@ childMesh->subDivLoop(childSubdivCount); } +/// Whether or not to draw the outer mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawOutline(int i) { drawParent = i == 2; } +/// Whether or not to draw the inner mesh. +/// Callback from UI. +/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked) void OGLWidget::drawWireframe(int i) { childWireframe = i == 2; childMesh->drawWireframe = childWireframe; } +/// Set global scale. +/// Callback from UI. +/// \param i Slider value void OGLWidget::setScale(int i) { scale = float(i) / 10.0f; } +/// Load a mesh from filepath. +/// Callback from UI. +/// \see bool Mesh::loadData(const string& fileName) +/// \param filename Path to mesh file +/// \return True, if loading was successful bool OGLWidget::loadFile(string filename) { originalMesh = new Mesh(); diff --git a/hw04/src/Mesh.cpp b/hw04/src/Mesh.cpp index b925b20..e53f093 100644 --- a/hw04/src/Mesh.cpp +++ b/hw04/src/Mesh.cpp @@ -49,11 +49,13 @@ //getline( file, line); file >> key; if (key == "v") { + // vertex float x, y, z; file >> x >> y >> z; Vertex pVertex = *new Vertex(this, x, y, z); pts.push_back(pVertex); } else if (key == "f") { + // face (only triangles supported) int a, b, c; file >> a >> b >> c; Tri pTriangle = *new Tri(this, a-1, b-1, c-1); @@ -123,6 +125,8 @@ for (int tI = 0; tI < 3; ++tI) { Tri t = tris[triag.it[tI]]; + + // figure out, which neighbour I am to my neighbour int otherEI = 0; for (int j = 0; j < 3; ++j) { if (t.it[j] == i) { @@ -132,11 +136,13 @@ } if (i < triag.it[tI]) { Vertex d = pts[t.iv[otherEI]]; - Vertex e = (1.0f/8.0f)*((((tI == 1 || tI == 2) ? 3.0f : 1.0f)*a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f)*b) + (((tI == 1 || tI == 0) ? 3.0f : 1.0f)*c) + d); + Vertex e = (1.0f / 8.0f) * + ((((tI == 1 || tI == 2) ? 3.0f : 1.0f) * a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f) * b) + + (((tI == 1 || tI == 0) ? 3.0f : 1.0f) * c) + d); triag.ie[tI] = pts.size(); pts.push_back(e); } else { - // edge-mask already calculated + // the calculated edge-mask is already present in the neighbour triag.ie[tI] = t.ie[otherEI]; } @@ -145,14 +151,14 @@ tris[i] = triag; } - for( int i=0; i< pts.size(); i++){ // multiply every vertex with beta - int n = pts[i].valence; // n = valence of v_i + for (auto &pt : pts) { // multiply every vertex with beta + int n = pt.valence; // n = valence of v_i if (n < 3) continue; - float beta = Util::beta_n( n); -// cout << i << " " << n << " " << beta << " " < pts; - vector tris; + vector pts; + vector tris; bool drawWireframe = false; bool drawOutline = false; Mesh(); - bool loadData(const string& fileName); - void saveData(const string& fileName); + + /// Load a mesh from a .obj file. + /// \param fileName Path to file + /// \return True, if loading was successful + bool loadData(const string &fileName); + + /// Save this mesh to as a OBJ file. + /// \param fileName Path to new file + void saveData(const string &fileName); void connectivityAlgo(); + + /// Subdivide this mesh using Loop-Subdivison once. void subDivLoop(); + + /// Subdivide this mesh using Loop-Subdivision count times. + /// \param count How many subdivision steps void subDivLoop(int count); + + /// Subdivide this mesh using the edge midpoints. void subDivEdgeMidpoint(); - Mesh* copy(); + /// Deep-clone this mesh + /// \return New mesh + Mesh *copy(); }; diff --git a/hw04/src/Tri.h b/hw04/src/Tri.h index f9eacd2..ec719dc 100644 --- a/hw04/src/Tri.h +++ b/hw04/src/Tri.h @@ -17,12 +17,28 @@ int ie[3]{}; // edge vertex indices Mesh *mesh; // parent mesh - explicit Tri(Mesh* mesh); - explicit Tri(Mesh* mesh, int i[3]); - Tri(Mesh* mesh, int i, int j, int k); + /// Base-constructor + /// \param mesh Parent mesh + explicit Tri(Mesh *mesh); + + /// Create new triangle based on vertex indices of the parent mesh. + /// \param mesh Parent mesh + /// \param i Array of vertex indices + explicit Tri(Mesh *mesh, int i[3]); + + /// Create new triangle based on vertex indices of the parent mesh. + /// \param mesh parent mesh + /// \param i First vertex index + /// \param j Second vertex index + /// \param k Third vertex index + Tri(Mesh *mesh, int i, int j, int k); + void Print(); - Tri* copy(Mesh* mesh); + /// Deep-clone this triangle. + /// \param mesh Parent mesh of the new triangle + /// \return New triangle + Tri *copy(Mesh *mesh); }; diff --git a/hw04/src/Util.h b/hw04/src/Util.h index 6b2633a..2e2afab 100644 --- a/hw04/src/Util.h +++ b/hw04/src/Util.h @@ -11,7 +11,10 @@ class Util { public: - static float beta_n( int n); + /// Calculates the value for beta in the vertex mask for the valence of a vertex. + /// \param n Valence of vertex + /// \return Value for beta + static float beta_n(int n); }; diff --git a/hw04/src/Vertex.h b/hw04/src/Vertex.h index 3d217b3..6e23bb4 100644 --- a/hw04/src/Vertex.h +++ b/hw04/src/Vertex.h @@ -20,25 +20,78 @@ Mesh *mesh{}; // parent mesh int valence; + /// Base-constructor Vertex(); - explicit Vertex(Mesh* mesh); - explicit Vertex(Mesh* mesh, float point[3]); - Vertex(Mesh* mesh, float x, float y, float z); + + /// Base-constructor with parent mesh. + /// \param mesh Parent mesh + explicit Vertex(Mesh *mesh); + + /// Create new Vertex with three coordinates. + /// \param mesh Parent mesh + /// \param point Array of coordinates (x, y, z) + explicit Vertex(Mesh *mesh, float point[3]); + + /// Create new Vertex with three coordinates. + /// \param mesh Parent mesh + /// \param x X-Coordinate + /// \param y Y-Coordinate + /// \param z Z-Coordinate + Vertex(Mesh *mesh, float x, float y, float z); + void Print() { cout << valence << "x " << p[0] << " " << p[1] << " " << p[2] << endl; } - void operator*=( float a); - void operator+=( Vertex a); - Vertex* copy(Mesh* mesh); + /// Vector scaling. Component wise multiplication. + /// \param a Scalar + void operator*=(float a); + + /// Component wise addition. + /// \param a Addend + void operator+=(Vertex a); + + /// Deep-copy this vertex. + /// \param mesh Parent mesh of the new vertex + /// \return New vertex + Vertex *copy(Mesh *mesh); }; -Vertex operator+( Vertex a, Vertex b); -Vertex operator-( Vertex a, Vertex b); -Vertex operator*( float a, Vertex b); -float operator*( Vertex a, Vertex b); -Vertex operator%( Vertex a, Vertex b); +/// Vector addition. +/// \param a First addend +/// \param b Second addend +/// \return Sum +Vertex operator+(Vertex a, Vertex b); + +/// Vector subtraction. +/// \param a Minuend +/// \param b Subtrahend +/// \return Difference +Vertex operator-(Vertex a, Vertex b); + +/// Vector scaling. Component wise. +/// \param a Scalar +/// \param b Vertex +/// \return Product +Vertex operator*(float a, Vertex b); + +/// Scalar product +/// \param a First vector +/// \param b Second vector +/// \return Scalar product +float operator*(Vertex a, Vertex b); + +/// Cross product +/// \param a First vector +/// \param b Second vector +/// \return Cross product +Vertex operator%(Vertex a, Vertex b); + +/// Check equality based on equal coordinates. +/// \param a First vertex +/// \param b Second vertex +/// \return True if both have the same coordinate values bool operator==(Vertex a, Vertex b);