#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 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(geom::fast, points_.begin(), points_.end()); auto dcel = cg::monotone_triangulation(geom::fast, points_.begin(), points_.end()); edges_ = cg::edge_mesh(dcel); triangles_ = cg::triangle_mesh(dcel); } prof::dump(); log::info() << edges_.size() << " edges"; log::info() << triangles_.size() << " triangles"; log::info() << "Euler chi: " << (points_.size() - edges_.size() + triangles_.size()); closest_points_.resize(points_.size()); std::iota(closest_points_.begin(), closest_points_.end(), std::uint16_t{0}); } 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(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 view_bbox = geom::expand(geom::box::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 & t : triangles_) { gfx::color_rgba c; c[0] = random::uniform(rng, {0, 255}); c[1] = random::uniform(rng, {0, 255}); c[2] = random::uniform(rng, {0, 255}); c[3] = 255; painter_.triangle(points_[t[0]], points_[t[1]], points_[t[2]], c); } // for (auto const & e : edges_) // edge(e[0], e[1], gfx::black); 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); if (auto m = mouse(); m) { geom::point m_world; m_world[0] = geom::lerp(view_bbox[0], (*m)[0] * 1.f / width()); m_world[1] = geom::lerp(view_bbox[1], 1.f - (*m)[1] * 1.f / height()); auto compare = [&](auto i, auto j){ return geom::distance(points_[i], m_world) < geom::distance(points_[j], m_world); }; std::size_t const n_closest = 8; std::partial_sort(closest_points_.begin(), closest_points_.begin() + n_closest, closest_points_.end(), compare); float max_distance = camera_size_ / 32.f; for (std::size_t j = 0; j < n_closest; ++j) { auto i = closest_points_[j]; if (geom::distance(points_[i], m_world) > max_distance) break; gfx::painter::text_options opts; opts.c = {0, 0, 0, 255}; opts.x = gfx::painter::x_align::left; opts.y = gfx::painter::y_align::bottom; opts.scale = 2.f; auto p = geom::swizzle<0, 1>(geom::as_point(camera_transform * geom::homogeneous(geom::swizzle<0, 1, -1>(points_[i])))); p[0] = std::round((p[0] * 0.5f + 0.5f) * width()); p[1] = std::round((0.5f - p[1] * 0.5f) * height()); painter_.text(p, util::to_string(i), opts); } } painter_.render(geom::window_camera{width(), height()}.transform()); } private: std::vector> points_; std::vector> edges_; std::vector> triangles_; geom::box bbox_; geom::point camera_center_; float camera_size_; float camera_size_tgt_; std::optional> drag_start_; std::vector closest_points_; gfx::painter painter_; util::clock, std::chrono::high_resolution_clock> clock_; }; int main() { return app::main(); }