psemek/examples/triangulation.cpp

170 lines
4.1 KiB
C++

#include <psemek/app/app.hpp>
#include <psemek/app/main.hpp>
#include <psemek/gfx/painter.hpp>
#include <psemek/gfx/gl.hpp>
#include <psemek/geom/scale.hpp>
#include <psemek/geom/camera.hpp>
#include <psemek/geom/constants.hpp>
#include <psemek/cg/bbox.hpp>
#include <psemek/cg/triangulation/ear_clipping.hpp>
#include <psemek/cg/triangulation/monotone.hpp>
#include <psemek/cg/triangulation/delaunay.hpp>
#include <psemek/log/log.hpp>
#include <psemek/prof/profiler.hpp>
#include <psemek/util/clock.hpp>
#include <psemek/util/to_string.hpp>
#include <psemek/geom/homogeneous.hpp>
#include <psemek/geom/swizzle.hpp>
#include <psemek/random/generator.hpp>
#include <psemek/random/uniform.hpp>
#include <fstream>
using namespace psemek;
struct triangulation_app
: app::app
{
triangulation_app()
: app("Triangulation example", 16)
{
std::ifstream in(PSEMEK_EXAMPLES_DIR "/turkey");
while (true)
{
geom::point<float, 2> p;
in >> p[0] >> p[1];
if (!in)
break;
points_.push_back(p);
}
std::reverse(points_.begin(), points_.end());
for (std::size_t i = 0; i < points_.size();)
{
std::size_t j = (i + 1) % points_.size();
if (points_[i] == points_[j])
{
points_.erase(points_.begin() + i);
}
else
{
++i;
}
}
log::info() << points_.size() << " input points";
bbox_ = cg::bbox(points_.begin(), points_.end());
camera_center_ = bbox_.center();
camera_size_ = std::max(bbox_[0].length(), bbox_[1].length()) * 1.125f;
camera_size_tgt_ = camera_size_;
{
prof::profiler prof("triangulate");
auto dcel = cg::ear_clipping<std::uint16_t>(geom::fast, points_.begin(), points_.end());
edges_ = cg::edge_mesh(dcel);
triangles_ = cg::triangle_mesh(dcel);
}
prof::dump();
log::info() << (triangles_.size() / 3) << " triangles";
}
void on_left_button_down() override
{
app::on_left_button_down();
if (mouse())
drag_start_ = mouse();
}
void on_left_button_up() override
{
app::on_left_button_up();
drag_start_ = std::nullopt;
}
void on_mouse_wheel(int delta) override
{
camera_size_tgt_ *= std::pow(0.8f, delta);
}
void update() override
{
float const dt = clock_.restart().count();
if (drag_start_ && mouse())
{
auto delta = *mouse() - *drag_start_;
delta[1] *= -1;
camera_center_ -= geom::cast<float>(delta) * camera_size_ / (1.f * height());
drag_start_ = mouse();
}
camera_size_ += (camera_size_tgt_ - camera_size_) * (1.f - std::exp(- 20.f * dt));
}
void present() override
{
gl::ClearColor(1.f, 1.f, 1.f, 1.f);
gl::Clear(gl::COLOR_BUFFER_BIT);
float aspect_ratio = (width() * 1.f) / height();
geom::box<float, 2> view_bbox = geom::expand(geom::box<float, 2>::singleton(camera_center_), geom::vector{camera_size_ * 0.5f * aspect_ratio, camera_size_ * 0.5f});
float line_width = 4.f * camera_size_ / height();
auto edge = [this, line_width](auto i0, auto i1, gfx::color_rgba const & color)
{
painter_.line(points_[i0], points_[i1], line_width, color, true);
};
random::generator rng;
for (auto const & e : edges_)
edge(e[0], e[1], gfx::black);
for (auto const & t : triangles_)
{
gfx::color_rgba c;
c[0] = random::uniform<std::uint8_t>(rng, {0, 255});
c[1] = random::uniform<std::uint8_t>(rng, {0, 255});
c[2] = random::uniform<std::uint8_t>(rng, {0, 255});
c[3] = 255;
painter_.triangle(points_[t[0]], points_[t[1]], points_[t[2]], c);
}
for (std::size_t i = 0; i < points_.size(); ++i)
{
edge(i, (i + 1) % points_.size(), gfx::black);
}
auto camera_transform = geom::orthographic_camera{view_bbox}.transform();
painter_.render(camera_transform);
}
private:
std::vector<geom::point<float, 2>> points_;
std::vector<geom::segment<std::uint16_t>> edges_;
std::vector<geom::triangle<std::uint16_t>> triangles_;
geom::box<float, 2> bbox_;
geom::point<float, 2> camera_center_;
float camera_size_;
float camera_size_tgt_;
std::optional<geom::point<int, 2>> drag_start_;
gfx::painter painter_;
util::clock<std::chrono::duration<float>, std::chrono::high_resolution_clock> clock_;
};
int main()
{
return app::main<triangulation_app>();
}