diff --git a/c++/perspective/src/common.h b/c++/perspective/src/common.h index 6a6aff2..1c0c2ea 100644 --- a/c++/perspective/src/common.h +++ b/c++/perspective/src/common.h @@ -52,6 +52,21 @@ public: r(r), g(g), b(b), a(a) { } }; + +inline bool real_less(const Real &a, const Real &b) + { return a < b - real_precision; } +inline bool real_greater(const Real &a, const Real &b) + { return real_less(b, a); } +inline bool real_less_or_equal(const Real &a, const Real &b) + { return !real_less(b, a); } +inline bool real_greater_or_equal(const Real &a, const Real &b) + { return !real_less(a, b); } +inline bool real_equal(const Real &a, const Real &b) + { return !real_less(a, b) && !real_less(b, a); } +inline bool real_not_equal(const Real &a, const Real &b) + { return real_less(a, b) || real_less(b, a); } + + inline int clz(const Int &x) { return x ? __builtin_clz(x) : 32; } inline int clz(const UInt &x) diff --git a/c++/perspective/src/generator.cpp b/c++/perspective/src/generator.cpp index 4457cf5..67a34d8 100644 --- a/c++/perspective/src/generator.cpp +++ b/c++/perspective/src/generator.cpp @@ -77,7 +77,7 @@ Generator::generate(const ULongIntVector2 &coord, bool shrink_cache) const { ULongInt level_y = coord.y ? (coord.y ^ y_minus_one) >> 1 : (ULongInt)-1; ULongInt level = (level_x < level_y ? level_x : level_y) + 1; - if (level < one) { + if (level && level < one) { value /= one/level; if (level_x == level_y) { // square corners diff --git a/c++/perspective/src/mainwindow.cpp b/c++/perspective/src/mainwindow.cpp index ab4ce70..0935197 100644 --- a/c++/perspective/src/mainwindow.cpp +++ b/c++/perspective/src/mainwindow.cpp @@ -5,37 +5,41 @@ #include #include -#include "generator.h" -#include "surface.h" - #include "mainwindow.h" MainWindow::MainWindow(): - src_rect0 (new View::Point( Vector2(-10, -10) )), - src_rect1 (new View::Point( Vector2( 10, 10) )), - dst_rect0 (new View::Point( Vector2(-10, -10) )), - dst_rect1 (new View::Point( Vector2( 10, -10) )), - dst_rect2 (new View::Point( Vector2( 10, 10) )), - dst_rect3 (new View::Point( Vector2(-10, 10) )), - dst_bounds0 (new View::Point( Vector2(-20, -20) )), - dst_bounds1 (new View::Point( Vector2( 20, 20) )) + src_rect0 (new View::Point( Vector2(-1, -1) )), + src_rect1 (new View::Point( Vector2( 1, 1) )), + dst_rect0 (new View::Point( Vector2(-1, -1) )), + dst_rect1 (new View::Point( Vector2( 1, -1) )), + dst_rect2 (new View::Point( Vector2( 1, 1) )), + dst_rect3 (new View::Point( Vector2(-1, 1) )), + dst_bounds0 (new View::Point( Vector2(-2, -2) )), + dst_bounds1 (new View::Point( Vector2( 2, 2) )) { - set_default_size(200, 200); + std::cout << "generator seed: " << generator.seed() << std::endl; + set_default_size(750, 500); Gtk::HBox *hbox = manage(new Gtk::HBox()); hbox->set_homogeneous(true); + src_view.transform.scale(50, 50); src_view.points.push_back(src_rect0); src_view.points.push_back(src_rect1); src_view.signal_point_motion.connect( sigc::mem_fun(*this, &MainWindow::on_point_motion) ); src_view.signal_point_changed.connect( sigc::mem_fun(*this, &MainWindow::on_point_changed) ); + src_view.signal_transform_changed.connect( + sigc::bind( + sigc::mem_fun(*this, &MainWindow::on_view_transform_changed), + &src_view )); src_view.signal_draw_view.connect( sigc::mem_fun(*this, &MainWindow::on_draw_src_view) ); hbox->add(src_view); + dst_view.transform.scale(50, 50); dst_view.points.push_back(dst_rect0); dst_view.points.push_back(dst_rect1); dst_view.points.push_back(dst_rect2); @@ -46,20 +50,25 @@ MainWindow::MainWindow(): sigc::mem_fun(*this, &MainWindow::on_point_motion) ); dst_view.signal_point_changed.connect( sigc::mem_fun(*this, &MainWindow::on_point_changed) ); + src_view.signal_transform_changed.connect( + sigc::bind( + sigc::mem_fun(*this, &MainWindow::on_view_transform_changed), + &dst_view )); dst_view.signal_draw_view.connect( sigc::mem_fun(*this, &MainWindow::on_draw_dst_view) ); hbox->add(dst_view); - hbox->add(*manage(generator_demo())); + //hbox->add(*manage(generator_demo())); add(*hbox); show_all(); + + update_view_surface(); } Gtk::Widget* MainWindow::generator_demo() { std::cout << "generator demo: generate" << std::endl; - Generator generator; DataSurface surface(256, 256); generator.generate(surface, Vector2(0, 0), Vector2(16, 16)); @@ -82,6 +91,32 @@ MainWindow::generator_demo() { } void +MainWindow::update_view_surface() { + Matrix4 transform = src_view.transform.inverted(); + int w = src_view.get_width(); + int h = src_view.get_height(); + + Vector2 rect0(transform * Vector4(0, 0, 0, 1)); + Vector2 rect1(transform * Vector4(w, h, 0, 1)); + + if (w <= 0 || h <= 0) { + view_surface.clear(); + } else + if ( !view_surface + || view_surface->get_width() != w + || view_surface->get_height() != h + || view_surface_rect0 != rect0 + || view_surface_rect1 != rect1 ) + { + DataSurface surface(w, h); + generator.generate(surface, rect0, rect1); + view_surface = surface.to_cairo_surface(); + view_surface_rect0 = rect0; + view_surface_rect1 = rect1; + } +} + +void MainWindow::on_point_motion(const View::PointPtr &/*point*/) { queue_draw(); } @@ -91,10 +126,30 @@ MainWindow::on_point_changed(const View::PointPtr &/*point*/) { } void +MainWindow::on_view_transform_changed(View *view) { + if (view == &src_view) { + update_view_surface(); + view->queue_draw(); + } +} + +void MainWindow::on_draw_src_view(const Cairo::RefPtr &context) { const Real ps = src_view.get_pixel_size(); context->save(); + + if ( !view_surface + || src_view.get_width() != view_surface->get_width() + || src_view.get_height() != view_surface->get_height() ) + update_view_surface(); + if (view_surface) { + context->save(); + src_view.back_transform_context(context); + context->set_source(view_surface, 0, 0); + context->paint(); + context->restore(); + } context->move_to(src_rect0->position.x, src_rect0->position.y); context->line_to(src_rect0->position.x, src_rect1->position.y); diff --git a/c++/perspective/src/mainwindow.h b/c++/perspective/src/mainwindow.h index 9664943..16ee403 100644 --- a/c++/perspective/src/mainwindow.h +++ b/c++/perspective/src/mainwindow.h @@ -5,10 +5,14 @@ #include #include "view.h" +#include "surface.h" +#include "generator.h" class MainWindow: public Gtk::Window { public: + Generator generator; + View src_view; View dst_view; @@ -17,12 +21,19 @@ public: dst_rect0, dst_rect1, dst_rect2, dst_rect3, dst_bounds0, dst_bounds1; + Vector2 view_surface_rect0; + Vector2 view_surface_rect1; + Cairo::RefPtr view_surface; + MainWindow(); Gtk::Widget* generator_demo(); + void update_view_surface(); + void on_point_motion(const View::PointPtr &point); void on_point_changed(const View::PointPtr &point); + void on_view_transform_changed(View *view); void on_draw_src_view(const Cairo::RefPtr &context); void on_draw_dst_view(const Cairo::RefPtr &context); }; diff --git a/c++/perspective/src/matrix.cpp b/c++/perspective/src/matrix.cpp index 45e9f5b..1a67841 100644 --- a/c++/perspective/src/matrix.cpp +++ b/c++/perspective/src/matrix.cpp @@ -29,11 +29,11 @@ static inline int index0(int r0, int r1) static inline int index1(int r0, int r1) { return r1 < 2 ? (r0 < 3 ? 3 : 2) : (r0 > 1 ? 1 : 2); } static inline int index0(int r) - { return r ? 1 : 0; } + { return r > 0 ? 0 : 1; } static inline int index1(int r) - { return r > 0 ? 1 : 2; } + { return r > 1 ? 1 : 2; } static inline int index2(int r) - { return r > 1 ? 2 : 3; } + { return r > 2 ? 2 : 3; } static inline Real det2(const Matrix4 &m, int r, int c, int rr, int cc) { const int r0 = index0(r, rr); @@ -56,13 +56,13 @@ Matrix4::det() const { return m00*det3(*this, 0, 0) - m01*det3(*this, 0, 1) + m02*det3(*this, 0, 2) - m03*det3(*this, 0, 3); } Matrix4 -Matrix4::invert() const { +Matrix4::inverted() const { Vector4 dv( det3(*this, 0, 0), -det3(*this, 0, 1), det3(*this, 0, 2), -det3(*this, 0, 3) ); - Real d = dv.x + dv.y + dv.z + dv.w; + Real d = m00*dv.x + m01*dv.y + m02*dv.z + m03*dv.w; if (fabs(d) <= real_precision) return zero(); diff --git a/c++/perspective/src/matrix.h b/c++/perspective/src/matrix.h index 75684dd..96b35e8 100644 --- a/c++/perspective/src/matrix.h +++ b/c++/perspective/src/matrix.h @@ -42,9 +42,6 @@ public: inline const Vector4& row_z() const { return (*this)[2]; } inline const Vector4& row_w() const { return (*this)[3]; } - Real det() const; - Matrix4 invert() const; - inline Vector4 operator* (const Vector4 &v) const { return row_x()*v.x + row_y()*v.y + row_z()*v.z + row_w()*v.w; } inline Matrix4 operator* (const Matrix4 &other) const { @@ -57,8 +54,29 @@ public: inline Matrix4& operator*= (const Matrix4 &other) { return *this = *this * other; } + Real det() const; + Matrix4 inverted() const; + Matrix4& invert() + { return *this = inverted(); } + + Matrix4& scale(Real x, Real y, Real z = 1.0) { + row_x().vec3() *= x; + row_y().vec3() *= y; + row_z().vec3() *= z; + return *this; + } + Matrix4 scaled(Real x, Real y, Real z = 1.0) + { return Matrix4(*this).scale(x, y, z); } + static inline Matrix4 zero() { return Matrix4(Vector4(), Vector4(), Vector4(), Vector4()); } static inline Matrix4 identity() { return Matrix4(); } + + std::string to_string() const { + return "(" + row_x().to_string() + ", " + + row_y().to_string() + ", " + + row_z().to_string() + ", " + + row_w().to_string() + ")"; + } }; diff --git a/c++/perspective/src/surface.cpp b/c++/perspective/src/surface.cpp index 104fbe2..55124a1 100644 --- a/c++/perspective/src/surface.cpp +++ b/c++/perspective/src/surface.cpp @@ -44,6 +44,54 @@ void Surface::copy(int width, int height, const Color *src_origin, Color *dest_o } } +Glib::RefPtr +Surface::to_pixbuf() const { + if (empty()) + return Glib::RefPtr(); + Glib::RefPtr pixbuf = Gdk::Pixbuf::create( + Gdk::COLORSPACE_RGB, true, 8, width(), height() ); + guint8 *pixels = pixbuf->get_pixels(); + int stride = pixbuf->get_rowstride(); + for(int r = 0; r < height(); ++r) { + const Color *src_row = row(r); + guint8 *pixel = pixels; + pixels += stride; + for(int c = 0; c < width(); ++c) { + const Color &color = src_row[c]; + *(pixel++) = (guint8)(color.r*255.9); + *(pixel++) = (guint8)(color.g*255.9); + *(pixel++) = (guint8)(color.b*255.9); + *(pixel++) = (guint8)(color.a*255.9); + } + } + return pixbuf; +} + +Cairo::RefPtr +Surface::to_cairo_surface() const { + if (empty()) + return Cairo::RefPtr(); + Cairo::RefPtr cairo_surface = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width(), height()); + cairo_surface->flush(); + unsigned char *pixels = cairo_surface->get_data(); + int stride = cairo_surface->get_stride(); + for(int r = 0; r < height(); ++r) { + const Color *src_row = row(r); + unsigned char *pixel = pixels; + pixels += stride; + for(int c = 0; c < width(); ++c) { + const Color &color = src_row[c]; + *(pixel++) = (guint8)(color.r*255.9); + *(pixel++) = (guint8)(color.g*255.9); + *(pixel++) = (guint8)(color.b*255.9); + *(pixel++) = (guint8)(color.a*255.9); + } + } + cairo_surface->mark_dirty(); + return cairo_surface; +} + void DataSurface::clear() { if (Color *data = data_begin()) delete[] data; @@ -85,4 +133,3 @@ void AliasSurface::init(Color *origin, int width, int height, int pitch) { m_origin = origin; } } - diff --git a/c++/perspective/src/surface.h b/c++/perspective/src/surface.h index 69a910c..c149f2f 100644 --- a/c++/perspective/src/surface.h +++ b/c++/perspective/src/surface.h @@ -5,6 +5,9 @@ #include #include +#include +#include + #include "common.h" @@ -60,6 +63,9 @@ public: { return pitch > 0 ? data_count(width, height, pitch) : width; } static void copy(int width, int height, const Color *src_origin, Color *dest_origin, int src_pitch = 0, int dest_pitch = 0); + + Glib::RefPtr to_pixbuf() const; + Cairo::RefPtr to_cairo_surface() const; }; diff --git a/c++/perspective/src/vector.h b/c++/perspective/src/vector.h index 61c2ea0..781d91e 100644 --- a/c++/perspective/src/vector.h +++ b/c++/perspective/src/vector.h @@ -4,6 +4,9 @@ #include +#include +#include + #include "common.h" @@ -201,6 +204,14 @@ public: { return *(SelfType*)c; } inline static const SelfType& cast(const Coord *c) { return *(const SelfType*)c; } + + std::string to_string() const { + std::stringstream stream; + stream << "(" << coords[0]; + for(int i = 1; i < Count; ++i) stream << ", " << coords[i]; + stream << ")"; + return stream.str(); + } }; @@ -225,15 +236,15 @@ public: inline bool operator< (const SelfTypeArg &other) const { for(int i = 0; i < Count; ++i) - if (coords[i] < other.coords[i] - real_precision) return true; - else if (other.coords[i] < coords[i] - real_precision) return false; + if (real_less(coords[i], other.coords[i] - real_precision)) return true; + else if (real_less(other.coords[i], coords[i] - real_precision)) return false; return false; } inline bool operator== (const SelfTypeArg &other) const { for(int i = 0; i < Count; ++i) - if ( coords[i] < other.coords[i] - real_precision - || other.coords[i] < coords[i] - real_precision ) return false; + if ( real_less(coords[i], other.coords[i] - real_precision) + || real_less(other.coords[i], coords[i] - real_precision) ) return false; return true; } diff --git a/c++/perspective/src/view.cpp b/c++/perspective/src/view.cpp index f8b90ff..2a57fc2 100644 --- a/c++/perspective/src/view.cpp +++ b/c++/perspective/src/view.cpp @@ -35,6 +35,25 @@ Real View::get_pixel_size() { return 1.0/sqrt(fabs(transform.m00 * transform.m11)); } +void +View::transform_context(const Cairo::RefPtr& context) { + Cairo::Matrix matrix( + transform.m00, transform.m10, transform.m01, + transform.m11, transform.m30, transform.m31 ); + context->translate(0.5*get_width(), 0.5*get_height()); + context->transform(matrix); +} + +void +View::back_transform_context(const Cairo::RefPtr& context) { + Cairo::Matrix matrix( + transform.m00, transform.m10, transform.m01, + transform.m11, transform.m30, transform.m31 ); + matrix.invert(); + context->transform(matrix); + context->translate(-0.5*get_width(), -0.5*get_height()); +} + bool View::on_draw(const Cairo::RefPtr& context) { context->save(); @@ -66,29 +85,50 @@ View::on_draw(const Cairo::RefPtr& context) { bool View::on_motion_notify_event(GdkEventMotion *event) { - Vector2 position = Vector2(event->x - 0.5*get_width(), event->y - 0.5*get_height()); + Vector2 prev_mouse_position = mouse_position; + mouse_position = Vector2(event->x - 0.5*get_width(), event->y - 0.5*get_height()); if (dragging) { if (selected_point) { - selected_point->position = Vector2(transform.invert()*Vector4(position + selected_point_offset)); + selected_point->position = Vector2(transform.inverted()*Vector4(mouse_position + selected_point_offset, 0, 1)); point_motion(selected_point); queue_draw(); } - } else { - PointPtr point; - for(PointList::iterator i = points.begin(); i != points.end(); ++i) { - (*i)->selected = false; - Vector2 point_position = Vector2(transform * Vector4((*i)->position)); - Vector2 offset = point_position - position; - if (offset.square() <= (*i)->radius * (*i)->radius) - point = *i; - } - if (point) - point->selected = true; - if (selected_point != point) { - selected_point = point; - queue_draw(); + return true; + } + + PointPtr point; + for(PointList::iterator i = points.begin(); i != points.end(); ++i) { + (*i)->selected = false; + Vector2 point_position = Vector2(transform * Vector4((*i)->position, 0, 1)); + Vector2 offset = point_position - mouse_position; + if (offset.square() <= (*i)->radius * (*i)->radius) { + point = *i; + selected_point_offset = offset; } } + if (point) + point->selected = true; + if (selected_point != point) { + selected_point = point; + queue_draw(); + } + + if (event->state & Gdk::BUTTON2_MASK) { + // translate + transform.row_w() += Vector4(mouse_position - prev_mouse_position); + queue_draw(); + } + + if (event->state & Gdk::BUTTON3_MASK) { + // zoom + Real scale = pow(2.0, (prev_mouse_position.y - mouse_position.y)/50.0); // zoom x2 by motion to 50 pixels + Vector2 center = Vector2(transform.inverted() * Vector4(0, 0, 0, 1)); + transform.scale(scale, scale); + transform.row_w() = Vector4(0, 0, 0, 1); + transform.row_w() = transform * Vector4(-center, 0, 1); + queue_draw(); + } + return true; } @@ -106,5 +146,7 @@ View::on_button_release_event(GdkEventButton *event) { if (selected_point) point_changed(selected_point); } + if (event->button == 2 || event->button == 3) + signal_transform_changed(); return true; } diff --git a/c++/perspective/src/view.h b/c++/perspective/src/view.h index cefd251..a59d39c 100644 --- a/c++/perspective/src/view.h +++ b/c++/perspective/src/view.h @@ -48,6 +48,7 @@ private: bool dragging; PointPtr selected_point; Vector2 selected_point_offset; + Vector2 mouse_position; // relative to center of widget public: View(); @@ -56,6 +57,7 @@ public: sigc::signal&> signal_draw_view; sigc::signal signal_point_motion; sigc::signal signal_point_changed; + sigc::signal signal_transform_changed; void point_motion(const PointPtr &point) { signal_point_motion(point); } @@ -63,7 +65,10 @@ public: { point_motion(point); signal_point_changed(point); } Real get_pixel_size(); - + + void transform_context(const Cairo::RefPtr& context); + void back_transform_context(const Cairo::RefPtr& context); + protected: bool on_draw(const Cairo::RefPtr& context) override; bool on_motion_notify_event(GdkEventMotion *event) override;