From a8c303037641a2cc673ae499fd3d732465bbfaa5 Mon Sep 17 00:00:00 2001
From: Yuwei Xiao <xyw1105@126.com>
Date: Tue, 17 Mar 2020 20:49:25 +0800
Subject: [PATCH] ex2

---
 .gitignore                   |   4 +-
 2-1_bead/BeadSim.cpp         |  23 ++++++
 2-1_bead/BeadSim.h           |  95 ++++++++++++++++++++++
 2-1_bead/CMakeLists.txt      |  43 ++++++++++
 2-1_bead/main.cpp            |  45 +++++++++++
 2-2_pendulum/CMakeLists.txt  |  43 ++++++++++
 2-2_pendulum/PendulumSim.cpp |  29 +++++++
 2-2_pendulum/PendulumSim.h   | 150 +++++++++++++++++++++++++++++++++++
 2-2_pendulum/main.cpp        | 102 ++++++++++++++++++++++++
 CMakeLists.txt               |   8 +-
 10 files changed, 538 insertions(+), 4 deletions(-)
 create mode 100644 2-1_bead/BeadSim.cpp
 create mode 100644 2-1_bead/BeadSim.h
 create mode 100644 2-1_bead/CMakeLists.txt
 create mode 100644 2-1_bead/main.cpp
 create mode 100644 2-2_pendulum/CMakeLists.txt
 create mode 100644 2-2_pendulum/PendulumSim.cpp
 create mode 100644 2-2_pendulum/PendulumSim.h
 create mode 100644 2-2_pendulum/main.cpp

diff --git a/.gitignore b/.gitignore
index d901eda..231f144 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,6 @@ build/
 *.aux
 *.log
 *.gz
-*.out
\ No newline at end of file
+*.out
+
+(old)*
\ No newline at end of file
diff --git a/2-1_bead/BeadSim.cpp b/2-1_bead/BeadSim.cpp
new file mode 100644
index 0000000..6f74a4d
--- /dev/null
+++ b/2-1_bead/BeadSim.cpp
@@ -0,0 +1,23 @@
+#include "BeadSim.h"
+
+
+bool BeadSim::advance() {
+
+
+    auto v = p_bead->getLinearVelocity();
+    auto p = p_bead->getPosition();
+
+    // TODO update position and velcity of p_bead
+    // constraint C(x) = 0.5*(p.dot(p) - m_radius^2) = 0;
+
+
+    m_time += m_dt;
+    m_step++;
+
+    // log
+    if ((m_step % m_log_frequency) == 0) {
+        m_trajectories.back().push_back(p_bead->getPosition());
+    }
+    return false;
+
+}
\ No newline at end of file
diff --git a/2-1_bead/BeadSim.h b/2-1_bead/BeadSim.h
new file mode 100644
index 0000000..63a254f
--- /dev/null
+++ b/2-1_bead/BeadSim.h
@@ -0,0 +1,95 @@
+#include "Simulation.h"
+#include "igl/PI.h"
+
+using namespace std;
+
+
+class BeadSim : public Simulation {
+public:
+    BeadSim() : Simulation() {
+        init();
+        m_trajectories.clear();
+        m_trajectoryColors.clear();
+    }
+
+	virtual void init() override {
+		std::string path = "sphere.off";
+		m_objects.clear();
+		m_objects.push_back(RigidObject(path));
+		p_bead = &m_objects[0];
+
+		m_dt = 5e-2;
+        m_mass = 1.0;
+        m_log_frequency = 30;
+		m_radius = 10;
+        m_gravity << 0, -9.81, 0;
+
+		reset();
+	}
+
+    virtual void resetMembers() override {
+        p_bead->reset();
+        p_bead->setScale(0.1);
+        // initial position, should be valid for constraints
+        p_bead->setPosition(Eigen::Vector3d(m_radius*cos(igl::PI/3), m_radius*sin(igl::PI/3.0), 0));
+
+        if (m_trajectories.size() == 0 || m_trajectories.back().size() > 1) {
+            m_trajectories.push_back(vector<Eigen::Vector3d>());
+            m_trajectoryColors.push_back(m_color);
+        } else {
+            m_trajectoryColors.back() = m_color;
+        }
+    }
+
+    virtual void updateRenderGeometry() override {
+        p_bead->getMesh(m_renderV, m_renderF);
+    }
+
+	virtual bool advance() override;
+
+    virtual void renderRenderGeometry(igl::opengl::glfw::Viewer &viewer) override {
+        viewer.data().set_mesh(m_renderV, m_renderF);
+       
+        for (size_t trajectory = 0; trajectory < m_trajectories.size(); trajectory++) {
+            for (size_t point = 1; point < m_trajectories[trajectory].size(); point++) {
+                viewer.data().add_edges(m_trajectories[trajectory][point-1].transpose(),
+                        m_trajectories[trajectory][point].transpose(),
+                        m_trajectoryColors[trajectory]);
+                // viewer.data().add_points(
+                //     m_trajectories[trajectory][point].transpose(),
+                //     m_trajectoryColors[trajectory]);
+            }
+        }
+    }
+
+    void clearTrajectories() {
+        m_trajectories.clear();
+        m_trajectories.push_back(vector<Eigen::Vector3d>());
+        m_trajectoryColors.clear();
+        m_trajectoryColors.push_back(m_color);
+    }
+    void setRadius(double r) { m_radius = r; }
+    void setLogFrequency(int f) { m_log_frequency = f; }
+    void getTrajectories(int index, Eigen::MatrixX3d &mat) const {
+        int num_points = m_trajectories[index].size();
+        mat.resize(num_points, 3);
+        for (int i = 0; i < num_points; i++) {
+            mat.row(i) = m_trajectories[index][i];
+        }
+    }
+    int getNumTrajectories() const { return m_trajectories.size(); }
+private:
+    RigidObject *p_bead;
+    double m_radius;
+    double m_mass;
+
+    Eigen::Vector3d m_gravity;
+
+    Eigen::MatrixXd m_renderV;  // vertex positions for rendering
+    Eigen::MatrixXi m_renderF;  // face indices for rendering
+
+    int m_log_frequency;  // how often should we log the COM in the GUI
+    vector<vector<Eigen::Vector3d>> m_trajectories;
+    Eigen::RowVector3d m_color;
+    vector<Eigen::RowVector3d> m_trajectoryColors;
+};
\ No newline at end of file
diff --git a/2-1_bead/CMakeLists.txt b/2-1_bead/CMakeLists.txt
new file mode 100644
index 0000000..9464a64
--- /dev/null
+++ b/2-1_bead/CMakeLists.txt
@@ -0,0 +1,43 @@
+
+cmake_minimum_required(VERSION 3.1)
+project(2-bead)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
+set(CMAKE_CXX_FLAGS "-Wall")
+
+# libigl
+option(LIBIGL_USE_STATIC_LIBRARY "Use libigl as static library" OFF)
+option(LIBIGL_WITH_ANTTWEAKBAR       "Use AntTweakBar"    OFF)
+option(LIBIGL_WITH_CGAL              "Use CGAL"           OFF)
+option(LIBIGL_WITH_COMISO            "Use CoMiso"         OFF)
+option(LIBIGL_WITH_CORK              "Use Cork"           OFF)
+option(LIBIGL_WITH_EMBREE            "Use Embree"         OFF)
+option(LIBIGL_WITH_LIM               "Use LIM"            OFF)
+option(LIBIGL_WITH_MATLAB            "Use Matlab"         OFF)
+option(LIBIGL_WITH_MOSEK             "Use MOSEK"          OFF)
+option(LIBIGL_WITH_OPENGL            "Use OpenGL"         ON)
+option(LIBIGL_WITH_OPENGL_GLFW       "Use GLFW"           ON)
+option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui"          ON)
+option(LIBIGL_WITH_PNG               "Use PNG"            OFF)
+option(LIBIGL_WITH_PYTHON            "Use Python"         OFF)
+option(LIBIGL_WITH_TETGEN            "Use Tetgen"         OFF)
+option(LIBIGL_WITH_TRIANGLE          "Use Triangle"       OFF)
+option(LIBIGL_WITH_VIEWER            "Use OpenGL viewer"  ON)
+option(LIBIGL_WITH_XML               "Use XML"            OFF)
+
+if (NOT LIBIGL_FOUND)
+    find_package(LIBIGL REQUIRED QUIET)
+endif()
+
+# Add default project files
+file(GLOB LIBFILES ${PROJECT_SOURCE_DIR}/../include/*.*)
+source_group("Library Files" FILES ${LIBFILES})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include)
+
+# Add your project files
+file(GLOB SRCFILES *.cpp)
+file(GLOB HFILES *.h)
+
+add_definitions(-DIGL_VIEWER_VIEWER_QUIET)
+add_executable(${PROJECT_NAME} ${SRCFILES} ${HFILES} ${LIBFILES} )
+target_link_libraries(${PROJECT_NAME} igl::core igl::opengl_glfw igl::opengl_glfw_imgui)
\ No newline at end of file
diff --git a/2-1_bead/main.cpp b/2-1_bead/main.cpp
new file mode 100644
index 0000000..d8c744e
--- /dev/null
+++ b/2-1_bead/main.cpp
@@ -0,0 +1,45 @@
+#include "BeadSim.h"
+#include "Gui.h"
+
+/*
+ * GUI for the bead simulation.
+ */
+class BeadGui : public Gui {
+   public:
+    // simulation parameters
+    float m_dt = 1e-2;
+    float m_radius = 10.0;
+    int m_log_frequency = 30;
+
+    BeadSim *p_BeadSim = NULL;
+
+    BeadGui() {
+        p_BeadSim = new BeadSim();
+        setSimulation(p_BeadSim);
+
+        start();
+    }
+
+    virtual void updateSimulationParameters() override {
+        // change all parameters of the simulation to the values that are set in the GUI
+        p_BeadSim->setTimestep(m_dt);
+        p_BeadSim->setRadius(m_radius);
+        p_BeadSim->setLogFrequency(m_log_frequency);
+    }
+
+
+    virtual void drawSimulationParameterMenu() override {
+        ImGui::InputFloat("Radius", &m_radius, 0, 0);
+        ImGui::InputFloat("dt", &m_dt, 0, 0);
+    }
+
+    virtual void clearSimulation() override {
+        p_BeadSim->clearTrajectories();
+    }
+};
+
+int main(int argc, char *argv[]) {
+    new BeadGui();
+
+    return 0;
+}
\ No newline at end of file
diff --git a/2-2_pendulum/CMakeLists.txt b/2-2_pendulum/CMakeLists.txt
new file mode 100644
index 0000000..c7541b9
--- /dev/null
+++ b/2-2_pendulum/CMakeLists.txt
@@ -0,0 +1,43 @@
+
+cmake_minimum_required(VERSION 3.1)
+project(2-2_pendulum)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
+set(CMAKE_CXX_FLAGS "-Wall")
+
+# libigl
+option(LIBIGL_USE_STATIC_LIBRARY "Use libigl as static library" OFF)
+option(LIBIGL_WITH_ANTTWEAKBAR       "Use AntTweakBar"    OFF)
+option(LIBIGL_WITH_CGAL              "Use CGAL"           OFF)
+option(LIBIGL_WITH_COMISO            "Use CoMiso"         OFF)
+option(LIBIGL_WITH_CORK              "Use Cork"           OFF)
+option(LIBIGL_WITH_EMBREE            "Use Embree"         OFF)
+option(LIBIGL_WITH_LIM               "Use LIM"            OFF)
+option(LIBIGL_WITH_MATLAB            "Use Matlab"         OFF)
+option(LIBIGL_WITH_MOSEK             "Use MOSEK"          OFF)
+option(LIBIGL_WITH_OPENGL            "Use OpenGL"         ON)
+option(LIBIGL_WITH_OPENGL_GLFW       "Use GLFW"           ON)
+option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui"          ON)
+option(LIBIGL_WITH_PNG               "Use PNG"            OFF)
+option(LIBIGL_WITH_PYTHON            "Use Python"         OFF)
+option(LIBIGL_WITH_TETGEN            "Use Tetgen"         OFF)
+option(LIBIGL_WITH_TRIANGLE          "Use Triangle"       OFF)
+option(LIBIGL_WITH_VIEWER            "Use OpenGL viewer"  ON)
+option(LIBIGL_WITH_XML               "Use XML"            OFF)
+
+if (NOT LIBIGL_FOUND)
+    find_package(LIBIGL REQUIRED QUIET)
+endif()
+
+# Add default project files
+file(GLOB LIBFILES ${PROJECT_SOURCE_DIR}/../include/*.*)
+source_group("Library Files" FILES ${LIBFILES})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include)
+
+# Add your project files
+file(GLOB SRCFILES *.cpp)
+file(GLOB HFILES *.h)
+
+add_definitions(-DIGL_VIEWER_VIEWER_QUIET)
+add_executable(${PROJECT_NAME} ${SRCFILES} ${HFILES} ${LIBFILES} )
+target_link_libraries(${PROJECT_NAME} igl::core igl::opengl_glfw igl::opengl_glfw_imgui)
\ No newline at end of file
diff --git a/2-2_pendulum/PendulumSim.cpp b/2-2_pendulum/PendulumSim.cpp
new file mode 100644
index 0000000..b925948
--- /dev/null
+++ b/2-2_pendulum/PendulumSim.cpp
@@ -0,0 +1,29 @@
+#include "PendulumSim.h"
+
+typedef Eigen::Matrix<double, 6, 1> Vector6d;
+typedef Eigen::Matrix<double, 2, 6> Matrix26d;
+
+bool PendulumSim::advance() {
+    auto pos1 = p1->getPosition();
+    auto pos2 = p2->getPosition();
+    auto vel1 = p1->getLinearVelocity();
+    auto vel2 = p2->getLinearVelocity();
+
+    // TODO update positions and velocities of particle p1, p2
+    // c1 = 0.5 * (x1.dot(x1) - l1^2) = 0
+    // c2 = 0.5 * ((xi-x2).dot(xi-x2) - l2^2) = 0
+
+
+
+    // advance m_time
+    m_time += m_dt;
+    m_step++;
+
+    // log
+    if ((m_step % m_log_frequency) == 0) {
+        m_trajectories[0].push_back(p1->getPosition());
+        m_trajectories[1].push_back(p2->getPosition());
+    }
+
+    return false;
+}
\ No newline at end of file
diff --git a/2-2_pendulum/PendulumSim.h b/2-2_pendulum/PendulumSim.h
new file mode 100644
index 0000000..94adc7e
--- /dev/null
+++ b/2-2_pendulum/PendulumSim.h
@@ -0,0 +1,150 @@
+#include "Simulation.h"
+#include "igl/PI.h"
+
+using namespace std;
+
+struct DoublePendulum {
+public:
+    double m1, m2;
+    double l1, l2;
+};
+
+/*
+ */
+class PendulumSim : public Simulation {
+public:
+    PendulumSim() : Simulation() {
+        init();
+        m_trajectories.clear();
+        m_trajectoryColors.clear();
+    }
+
+    virtual void init() override {
+        std::string path = "sphere.off";
+        m_objects.clear();
+        m_objects.push_back(RigidObject(path));
+        m_objects.push_back(RigidObject(path));
+        p1 = &m_objects[0];
+        p2 = &m_objects[1];
+
+        m_dt = 1e-2;
+        m_log_frequency = 30;
+        m_gravity << 0, -9.81, 0;
+        // visualization color for sphere and trajectories
+        m_color1 << 1.0, 0.5, 0;
+        m_color2 << 0.0, 0.5, 1.0;
+        reset();
+    }
+
+    virtual void resetMembers() override {
+        p1->reset(); p2->reset();
+        p1->setScale(0.04); p2->setScale(0.04);
+        p1->setPosition(Eigen::Vector3d(m_pendulum.l1*cos(igl::PI/4), m_pendulum.l1*sin(igl::PI/4.0), 0));
+        p2->setPosition(Eigen::Vector3d((m_pendulum.l1+m_pendulum.l2)*cos(igl::PI/4), (m_pendulum.l1+m_pendulum.l2)*sin(igl::PI/4.0), 0));
+        p1->setColors(m_color1);
+        p2->setColors(m_color2);
+
+        clearTrajectories();
+    }
+
+    virtual void updateRenderGeometry() override {
+        for (size_t i = 0; i < m_objects.size(); i++) {
+            RigidObject &o = m_objects[i];
+            if (o.getID() < 0) {    // negative id means newly created object, reverse memory for it
+                m_renderVs.emplace_back();
+                m_renderFs.emplace_back();
+            }
+
+            m_objects[i].getMesh(m_renderVs[i], m_renderFs[i]);
+        }
+    }
+
+	virtual bool advance() override;
+
+    virtual void renderRenderGeometry(igl::opengl::glfw::Viewer &viewer) override {
+        // render point
+        for (size_t i = 0; i < m_objects.size(); i++) {
+            RigidObject &o = m_objects[i];
+            if (o.getID() < 0) {
+                int new_id = 0;
+                if (i > 0) {
+                    new_id = viewer.append_mesh();
+                    o.setID(new_id);
+                } else {
+                    o.setID(new_id);
+                }
+
+                size_t meshIndex = viewer.mesh_index(o.getID());
+                viewer.data_list[meshIndex].set_face_based(true);
+                viewer.data_list[meshIndex].point_size = 2.0f;
+                viewer.data_list[meshIndex].clear();
+            }
+            size_t meshIndex = viewer.mesh_index(o.getID());
+
+            viewer.data_list[meshIndex].set_mesh(m_renderVs[i], m_renderFs[i]);
+            viewer.data_list[meshIndex].compute_normals();
+
+            Eigen::MatrixXd color;
+            o.getColors(color);
+            viewer.data_list[meshIndex].set_colors(color);
+        }
+
+        for (size_t trajectory = 0; trajectory < m_trajectories.size(); trajectory++) {
+            if(m_trajectories[trajectory].size() <= 1) 
+                continue;
+            Eigen::MatrixXd p1s, p2s, cs;
+            p1s.resize(m_trajectories[trajectory].size()-1, 3);
+            p2s.resize(m_trajectories[trajectory].size()-1, 3);
+            cs.resize(m_trajectories[trajectory].size()-1, 3);
+            for (size_t point = 1; point < m_trajectories[trajectory].size(); point++) {
+                p1s.row(point-1) << m_trajectories[trajectory][point-1].transpose();
+                p2s.row(point-1) << m_trajectories[trajectory][point].transpose();
+                cs.row(point-1) << m_trajectoryColors[trajectory];
+                // viewer.data().add_edges(m_trajectories[trajectory][point-1].transpose(),
+                //         m_trajectories[trajectory][point].transpose(),
+                //         m_trajectoryColors[trajectory]);
+            }
+            viewer.data(0).add_edges(p1s, p2s, cs);
+        }
+
+		// draw the pendulum
+        viewer.data(0).add_edges(Eigen::RowVector3d::Zero(), p1->getPosition().transpose(), m_color);
+        viewer.data(0).add_edges(p1->getPosition().transpose(), p2->getPosition().transpose(), m_color);
+    }
+
+    void clearTrajectories() {
+        m_trajectories.clear();
+        m_trajectories.push_back(vector<Eigen::Vector3d>());    // for p1
+        m_trajectories.push_back(vector<Eigen::Vector3d>());    // for p2
+        m_trajectoryColors.clear();
+        m_trajectoryColors.push_back(m_color1);                  // p1 color
+        m_trajectoryColors.push_back(m_color2);                  // p1 color
+    }
+
+    void setPendulum(DoublePendulum &s) { m_pendulum = s; }
+    void setLogFrequency(int f) { m_log_frequency = f; }
+
+    void getTrajectories(int index, Eigen::MatrixX3d &mat) const {
+        int num_points = m_trajectories[index].size();
+        mat.resize(num_points, 3);
+        for (int i = 0; i < num_points; i++) {
+            mat.row(i) = m_trajectories[index][i];
+        }
+    }
+    int getNumTrajectories() const { return m_trajectories.size(); }
+
+private:
+
+    Eigen::Vector3d m_gravity;
+
+    DoublePendulum m_pendulum;
+    RigidObject *p1, *p2;
+
+    std::vector<Eigen::MatrixXd> m_renderVs;  // vertex positions for rendering
+    std::vector<Eigen::MatrixXi> m_renderFs;  // face indices for rendering
+
+    int m_log_frequency;  // how often should we log the COM in the GUI
+    vector<vector<Eigen::Vector3d>> m_trajectories;
+    Eigen::RowVector3d m_color, m_color1, m_color2;
+    vector<Eigen::RowVector3d> m_trajectoryColors;
+};
\ No newline at end of file
diff --git a/2-2_pendulum/main.cpp b/2-2_pendulum/main.cpp
new file mode 100644
index 0000000..a10247c
--- /dev/null
+++ b/2-2_pendulum/main.cpp
@@ -0,0 +1,102 @@
+#include <igl/writeOFF.h>
+#include <thread>
+#include "Gui.h"
+#include "Simulator.h"
+#include "PendulumSim.h"
+
+/*
+ */
+class PendulumGui : public Gui {
+   public:
+    // simulation parameters
+    float m_dt = 1e-2;
+    int m_log_frequency = 30;
+
+    DoublePendulum m_pendulum;  // stores properties of the pendulum
+    PendulumSim *p_pendulumSim = NULL;
+
+    // const vector<char const *> m_integrators = {"Analytic", "Explicit Euler", "Symplectic Euler", "Midpoint"};
+    // int m_selected_integrator = 0;
+
+    PendulumGui() {
+        // initialize the pendulum to be used
+        m_pendulum.l1 = 5.0;
+        m_pendulum.l2 = 6.0;
+        m_pendulum.m1 = 0.1;
+        m_pendulum.m2 = 0.3;
+
+        p_pendulumSim = new PendulumSim();
+        p_pendulumSim->setPendulum(m_pendulum);
+        setSimulation(p_pendulumSim);
+
+        // show vertex velocity instead of normal
+        callback_clicked_vertex =
+            [&](int clickedVertexIndex, int clickedObjectIndex, Eigen::Vector3d &pos, Eigen::Vector3d &dir) {
+                RigidObject &o = p_pendulumSim->getObjects()[clickedObjectIndex];
+                pos = o.getVertexPosition(clickedVertexIndex);
+                dir = o.getVelocity(pos);
+            };
+        start();
+    }
+
+    virtual void updateSimulationParameters() override {
+        // change all parameters of the simulation to the values that are set in the GUI
+        p_pendulumSim->setPendulum(m_pendulum);
+        p_pendulumSim->setTimestep(m_dt);
+        p_pendulumSim->setLogFrequency(m_log_frequency);
+    }
+
+    virtual void clearSimulation() override {
+        p_pendulumSim->clearTrajectories();
+    }
+
+    /*
+     * Writes each trajectory to an individual off-file.
+     */
+    void exportTrajectories() {
+        Eigen::MatrixX3d mat;
+        for (int i = 0; i < p_pendulumSim->getNumTrajectories(); i++) {
+            string filename = "trajectory" + to_string(i) + ".off";
+            p_pendulumSim->getTrajectories(i, mat);
+            if (mat.rows() <= 1) {
+                continue;
+            }
+            if (igl::writeOFF(filename, mat, Eigen::MatrixXi())) {
+                cout << "Wrote trajectory to " << filename << endl;
+            } else {
+                cout << "Failed to write trajectory to " << filename << endl;
+            }
+        }
+    }
+
+    virtual bool childKeyCallback(igl::opengl::glfw::Viewer &viewer,
+                                  unsigned int key, int modifiers) override {
+        switch (key) {
+            case 'e':
+            case 'E':
+                exportTrajectories();
+                return true;
+        }
+        return false;
+    }
+
+    virtual void drawSimulationParameterMenu() override {
+        if (ImGui::Button("Export Trajectories", ImVec2(-1, 0))) {
+            exportTrajectories();
+        }
+        ImGui::InputDouble("mass 1", &m_pendulum.m1, 0, 0);
+        ImGui::InputDouble("mass 2", &m_pendulum.m2, 0, 0);
+        ImGui::InputDouble("L1", &m_pendulum.l1, 0, 0);
+        ImGui::InputDouble("L2", &m_pendulum.l2, 0, 0);
+        ImGui::InputFloat("dt", &m_dt, 0, 0);
+        // ImGui::Combo("Integrator", &m_selected_integrator, m_integrators.data(), m_integrators.size());
+        ImGui::InputInt("Log Frequency", &m_log_frequency, 0, 0);
+    }
+};
+
+int main(int argc, char *argv[]) {
+    // create a new instance of the GUI for the pendulum simulation
+    new PendulumGui();
+
+    return 0;
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 54f8925..c49e975 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,8 @@ if (NOT LIBIGL_FOUND)
 endif()
 
 # add_subdirectory(0_dummy)
-add_subdirectory(1-0_earth)
-add_subdirectory(1-1_cannonball)
-add_subdirectory(1-2_spring)
\ No newline at end of file
+# add_subdirectory(1-0_earth)
+# add_subdirectory(1-1_cannonball)
+# add_subdirectory(1-2_spring)
+add_subdirectory(2-1_bead)
+add_subdirectory(2-2_pendulum)
-- 
GitLab