diff --git a/c++/perspective/src/SConstruct b/c++/perspective/src/SConstruct index d1acec6..216a321 100644 --- a/c++/perspective/src/SConstruct +++ b/c++/perspective/src/SConstruct @@ -21,6 +21,7 @@ sources = [ 'main.cpp', 'mainwindow.cpp', 'matrix.cpp', + 'perspectivesandboxview.cpp', 'surface.cpp', 'view.cpp' ] diff --git a/c++/perspective/src/common.h b/c++/perspective/src/common.h index 1c0c2ea..029e22a 100644 --- a/c++/perspective/src/common.h +++ b/c++/perspective/src/common.h @@ -4,6 +4,7 @@ #include #include +#include #include @@ -77,6 +78,11 @@ inline int clz(const ULongInt &x) { return x ? __builtin_clzll(x) : 64; } +inline Real clamp(Real x, Real min, Real max) + { return std::max(min, std::min(max, x)); } +inline int iclamp(int x, int min, int max) + { return std::max(min, std::min(max, x)); } + inline Real flip_clamp(Real x) { x -= floor(0.5*x)*2.0; return 1.0 - fabs(1.0 - x); diff --git a/c++/perspective/src/main.cpp b/c++/perspective/src/main.cpp index 29db451..ab69fdd 100644 --- a/c++/perspective/src/main.cpp +++ b/c++/perspective/src/main.cpp @@ -3,16 +3,19 @@ #include #include "mainwindow.h" +#include "perspectivesandboxview.h" int main(int argc, char **argv) { std::cout << "Lab: prototype of perspective transformation" << std::endl; Glib::RefPtr application = Gtk::Application::create( - argc, argv, "org.morevnaproject.lab.perspective"); + argc, argv, "org.coolbug.lab.perspective"); std::cout << "create window" << std::endl; - MainWindow window; + Gtk::Window window; + window.add(*Gtk::manage(new PerspectiveSandBoxView())); + window.show_all(); std::cout << "run" << std::endl; int result = application->run(window); diff --git a/c++/perspective/src/matrix.cpp b/c++/perspective/src/matrix.cpp index 1a67841..048f9ad 100644 --- a/c++/perspective/src/matrix.cpp +++ b/c++/perspective/src/matrix.cpp @@ -1,75 +1,41 @@ #include "matrix.h" -// remove from sequence 0, 1, 2, 3, two numbers, see following -// example: -// r0 = 1, r1 = 2 -// initial sequence: -// 0, 1, 2, 3 -// remove number at place r0 (1 for ex): -// 0, 2, 3 -// remove number at place r1 (2 for ex): -// 0, 2 -// so result for index0(1, 2) is 0 -// and for index1(1, 2) is 2 -// -// r1\r0 : 0 1 2 3 -// --------------------------- -// -- : _123 0_23 01_3 012_ -// 0 : _.23 ._23 .1_3 .12_ -// 1 : _1.3 0_.3 0._3 0.2_ -// 2 : _12. 0_2. 01_. 01._ -// --------------------------- -// 0 : 23 23 13 12 -// 1 : 13 03 03 02 -// 2 : 12 02 01 01 - -static inline int index0(int r0, int r1) - { return r1 ? (r0 ? 0 : 1) : (r0 > 1 ? 1 : 2); } -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 > 0 ? 0 : 1; } -static inline int index1(int r) - { return r > 1 ? 1 : 2; } -static inline int index2(int r) - { 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); - const int r1 = index1(r, rr); - const int c0 = index0(c, cc); - const int c1 = index1(c, cc); - return m[r0][c0]*m[r1][c1] - m[r0][c1]*m[r1][c0]; -} - -static inline Real det3(const Matrix4 &m, int r, int c) { - const int r0 = index0(r); - return m[r0][index0(c)] * det2(m, r, c, 0, 0) - - m[r0][index1(c)] * det2(m, r, c, 0, 1) - + m[r0][index2(c)] * det2(m, r, c, 0, 2); -} - - Real -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::det() const { + return m00 * (m11*(m22*m33 - m23*m32) + m12*(m23*m31 - m21*m33) + m13*(m21*m32 - m22*m31)) + + m01 * (m01*(m23*m32 - m22*m33) + m02*(m21*m33 - m23*m31) + m03*(m22*m31 - m21*m32)) + + m02 * (m01*(m12*m33 - m13*m32) + m02*(m13*m31 - m11*m33) + m03*(m11*m32 - m12*m31)) + + m03 * (m01*(m13*m22 - m12*m23) + m02*(m11*m23 - m13*m21) + m03*(m12*m21 - m11*m22)); +} Matrix4 Matrix4::inverted() const { - Vector4 dv( - det3(*this, 0, 0), - -det3(*this, 0, 1), - det3(*this, 0, 2), - -det3(*this, 0, 3) ); - Real d = m00*dv.x + m01*dv.y + m02*dv.z + m03*dv.w; - if (fabs(d) <= real_precision) - return zero(); - - d = 1.0/d; - return Matrix4( - Vector4(dv.x, -det3(*this, 1, 0), det3(*this, 2, 0), -det3(*this, 3, 0))*d, - Vector4(dv.y, det3(*this, 1, 1),-det3(*this, 2, 1), det3(*this, 3, 1))*d, - Vector4(dv.z, -det3(*this, 1, 2), det3(*this, 2, 2), -det3(*this, 3, 2))*d, - Vector4(dv.w, det3(*this, 1, 3),-det3(*this, 2, 3), det3(*this, 3, 3))*d ); + Matrix4 r; + r.m00 = m11*(m22*m33 - m23*m32) + m12*(m23*m31 - m21*m33) + m13*(m21*m32 - m22*m31); + r.m10 = m10*(m23*m32 - m22*m33) + m12*(m20*m33 - m23*m30) + m13*(m22*m30 - m20*m32); + r.m20 = m10*(m21*m33 - m23*m31) + m11*(m23*m30 - m20*m33) + m13*(m20*m31 - m21*m30); + r.m30 = m10*(m22*m31 - m21*m32) + m11*(m20*m32 - m22*m30) + m12*(m21*m30 - m20*m31); + + double det = m00*r.m00 + m01*r.m10 + m02*r.m20 + m03*r.m30; + if (fabs(det) <= real_precision) return zero(); + det = 1/det; + r.m00 *= det; + r.m10 *= det; + r.m20 *= det; + r.m30 *= det; + + r.m01 = det*(m01*(m23*m32 - m22*m33) + m02*(m21*m33 - m23*m31) + m03*(m22*m31 - m21*m32)); + r.m11 = det*(m00*(m22*m33 - m23*m32) + m02*(m23*m30 - m20*m33) + m03*(m20*m32 - m22*m30)); + r.m21 = det*(m00*(m23*m31 - m21*m33) + m01*(m20*m33 - m23*m30) + m03*(m21*m30 - m20*m31)); + r.m31 = det*(m00*(m21*m32 - m22*m31) + m01*(m22*m30 - m20*m32) + m02*(m20*m31 - m21*m30)); + r.m02 = det*(m01*(m12*m33 - m13*m32) + m02*(m13*m31 - m11*m33) + m03*(m11*m32 - m12*m31)); + r.m12 = det*(m00*(m13*m32 - m12*m33) + m02*(m10*m33 - m13*m30) + m03*(m12*m30 - m10*m32)); + r.m22 = det*(m00*(m11*m33 - m13*m31) + m01*(m13*m30 - m10*m33) + m03*(m10*m31 - m11*m30)); + r.m32 = det*(m00*(m12*m31 - m11*m32) + m01*(m10*m32 - m12*m30) + m02*(m11*m30 - m10*m31)); + r.m03 = det*(m01*(m13*m22 - m12*m23) + m02*(m11*m23 - m13*m21) + m03*(m12*m21 - m11*m22)); + r.m13 = det*(m00*(m12*m23 - m13*m22) + m02*(m13*m20 - m10*m23) + m03*(m10*m22 - m12*m20)); + r.m23 = det*(m00*(m13*m21 - m11*m23) + m01*(m10*m23 - m13*m20) + m03*(m11*m20 - m10*m21)); + r.m33 = det*(m00*(m11*m22 - m12*m21) + m01*(m12*m20 - m10*m22) + m02*(m10*m21 - m11*m20)); + return r; } diff --git a/c++/perspective/src/matrix.h b/c++/perspective/src/matrix.h index 96b35e8..9c49992 100644 --- a/c++/perspective/src/matrix.h +++ b/c++/perspective/src/matrix.h @@ -2,6 +2,8 @@ #define MATRIX_H +#include + #include "vector.h" @@ -77,6 +79,12 @@ public: + row_z().to_string() + ", " + row_w().to_string() + ")"; } + + Cairo::Matrix to_cairo() const { + return Cairo::Matrix( + m00, m10, m01, + m11, m30, m31 ); + } }; diff --git a/c++/perspective/src/perspectivesandboxview.cpp b/c++/perspective/src/perspectivesandboxview.cpp new file mode 100644 index 0000000..b317084 --- /dev/null +++ b/c++/perspective/src/perspectivesandboxview.cpp @@ -0,0 +1,132 @@ + +#include "perspectivesandboxview.h" + + +View::PointPtr + persp_p0, + persp_px, + persp_py, + persp_p1, + bounds_p0, + bounds_p1; + +PerspectiveSandBoxView::PerspectiveSandBoxView(): + persp_p0 (new View::Point( Vector2(-1, -1) )), + persp_px (new View::Point( Vector2( 1, -1) )), + persp_py (new View::Point( Vector2(-1, 1) )), + persp_p1 (new View::Point( Vector2( 1, 1) )), + bounds_p0 (new View::Point( Vector2(-2, -2) )), + bounds_p1 (new View::Point( Vector2( 2, 2) )) +{ + transform.scale(50, 50); + points.push_back(persp_p0); + points.push_back(persp_px); + points.push_back(persp_py); + points.push_back(persp_p1); + points.push_back(bounds_p0); + points.push_back(bounds_p1); +} + +void +PerspectiveSandBoxView::draw_grid( + const Cairo::RefPtr &context, + const Matrix &matrix, + const Color &color ) +{ + const int count = 100; + const int subcount = 10; + + const Real ps = get_pixel_size(); + + for(int i = -count; i < count; ++i) { + for(int j = -count; j < count; ++j) { + Vector4 src(i/(Real)subcount, j/(Real)subcount, 0, 1); + Vector4 dst = matrix*src; + Real w = dst.w; + if (w > real_precision) { + Vector2 pos(dst.x/w, dst.y/w); + + Real ka = clamp(1/w, 0, 1); + + Real a = color.a*ka; + Real r = 4*ps; + if (i) r *= 0.5; + if (j) r *= 0.5; + if (i % 10) r *= 0.75; + if (j % 10) r *= 0.75; + context->set_source_rgba(color.r, color.g, color.b, a); + context->arc(pos.x, pos.y, r, 0, 2.0*M_PI); + context->fill(); + } + } + } +} + +void +PerspectiveSandBoxView::draw_line( + const Cairo::RefPtr &context, + const Pair2 &bounds, + Real a, + Real b, + Real c, + const Color &color ) +{ + +} + +void +PerspectiveSandBoxView::on_draw_view(const Cairo::RefPtr &context) { + context->save(); + + const Real ps = get_pixel_size(); + context->set_line_width(ps); + + Vector2 p0 = persp_p0->position; + Vector2 px = persp_px->position; + Vector2 py = persp_py->position; + Vector2 p1 = persp_p1->position; + + Pair2 bounds(bounds_p0->position, bounds_p1->position); + + + // perspective matrix + Matrix matrix; + { + Vector2 A = px - p1; + Vector2 B = py - p1; + Vector2 C = p0 + p1 - px - py; + + Real cw = A.y*B.x - A.x*B.y; + Real aw = B.x*C.y - B.y*C.x; + Real bw = A.y*C.x - A.x*C.y; + + Vector2 c = p0*cw; + Vector2 a = px*(cw + aw) - c; + Vector2 b = py*(cw + bw) - c; + + matrix.row_x() = Vector4(a, 0, aw); + matrix.row_y() = Vector4(b, 0, bw); + matrix.row_w() = Vector4(c, 0, cw); + } + + draw_grid(context, matrix, Color(0, 1, 0, 1)); + + // draw frames + context->set_source_rgba(0, 0, 1, 0.75); + context->move_to(p0.x, p0.y); + context->line_to(px.x, px.y); + context->line_to(p1.x, p1.y); + context->line_to(py.x, py.y); + context->close_path(); + context->stroke(); + + context->move_to(bounds.p0.x, bounds.p0.y); + context->line_to(bounds.p1.x, bounds.p0.y); + context->line_to(bounds.p1.x, bounds.p1.y); + context->line_to(bounds.p0.x, bounds.p1.y); + context->close_path(); + context->stroke(); + + context->restore(); +} + diff --git a/c++/perspective/src/perspectivesandboxview.h b/c++/perspective/src/perspectivesandboxview.h new file mode 100644 index 0000000..2a05038 --- /dev/null +++ b/c++/perspective/src/perspectivesandboxview.h @@ -0,0 +1,36 @@ +#ifndef PERSPECTIVESANDBOXVIEW_H +#define PERSPECTIVESANDBOXVIEW_H + +#include "view.h" + + +class PerspectiveSandBoxView: public View { +public: + View::PointPtr + persp_p0, + persp_px, + persp_py, + persp_p1, + bounds_p0, + bounds_p1; + + PerspectiveSandBoxView(); + + void draw_grid( + const Cairo::RefPtr &context, + const Matrix &matrix, + const Color &color ); + + void draw_line( + const Cairo::RefPtr &context, + const Pair2 &bounds, + Real a, + Real b, + Real c, + const Color &color ); + + void on_draw_view(const Cairo::RefPtr &context); +}; + + +#endif diff --git a/c++/perspective/src/vector.h b/c++/perspective/src/vector.h index 781d91e..6e2b563 100644 --- a/c++/perspective/src/vector.h +++ b/c++/perspective/src/vector.h @@ -6,10 +6,21 @@ #include #include +#include #include "common.h" +template +inline bool coord_less(const T &a, const T &b) + { return a < b; } + +template<> +inline bool coord_less(const Real &a, const Real &b) + { return real_less(a, b); } + + + template class VectorBase2T { public: @@ -112,13 +123,10 @@ public: inline const Vector3& vec3() const { return *(const Vector3*)this; }; - inline Vector2& vec2() + inline Vector2& vec2() { return *(Vector2*)this; }; inline const Vector2& vec2() const { return *(const Vector2*)this; }; - - inline SelfType cross(const SelfTypeArg &other) const - { return SelfType(y*other.z - z*other.y, z*other.x - x*other.z, x*other.y - y*other.x, w); } }; @@ -132,7 +140,12 @@ public: using typename ParentType::SelfType; using ParentType::coords; using ParentType::ParentType; // contructors - + + + static inline bool coord_less(const Coord &a, const Coord &b) + { return ::coord_less(a, b); } + + inline VectorT() { } inline explicit VectorT(const Coord *c) { @@ -154,14 +167,15 @@ public: inline bool operator< (const SelfTypeArg &other) const { for(int i = 0; i < Count; ++i) - if (coords[i] < other.coords[i]) return true; - else if (other.coords[i] < coords[i]) return false; + if (coord_less(coords[i], other.coords[i])) return true; else + if (coord_less(other.coords[i], coords[i])) return false; return false; } inline bool operator== (const SelfTypeArg &other) const { for(int i = 0; i < Count; ++i) - if (coords[i] != other.coords[i]) return false; + if ( coord_less(coords[i], other.coords[i]) + || coord_less(other.coords[i], coords[i]) ) return false; return true; } @@ -218,41 +232,78 @@ public: template class VectorFT: public VectorT { public: - typedef VectorFT SelfTypeArg; typedef VectorT ParentType; - using ParentType::Count; using typename ParentType::Coord; using typename ParentType::SelfType; + using ParentType::Count; using ParentType::coords; using ParentType::ParentType; // contructors inline SelfType& operator/= (const Coord &c) { return *this *= Coord(1)/c; } - inline SelfType operator/ (const Coord &c) + inline SelfType operator/ (const Coord &c) const { return SelfType(*this) /= c; } inline Coord length() const { return Coord(sqrt(ParentType::square())); } + + inline SelfType normalized() const { + Coord len = length(); + return ParentType::coord_less(Coord(), len) + ? *this / len : SelfType(); + } + inline SelfType normalize() const + { *this = normalized(); return *(SelfType)this; } - inline bool operator< (const SelfTypeArg &other) const { - for(int i = 0; i < Count; ++i) - 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; + inline SelfType persp_divide() const + { return *this / coords[Count - 1]; } +}; + +template +class PairT { +public: + typedef T Vector; + Vector p0; + Vector p1; + + explicit inline PairT(const Vector &p = Vector()): + p0(p), p1(p) { } + inline PairT(const Vector &p0, const Vector &p1): + p0(p0), p1(p1) { } + + inline bool operator< (const PairT &other) const { + return p0 < other.p0 ? true + : other.p0 < p0 ? false + : p1 < other.p1; + } + inline bool operator== (const PairT &other) const + { return !(*this < other) && !(other < *this); } + inline bool operator!= (const PairT &other) const + { return *this < other || other < *this; } + + + inline Vector distance() const + { return p1 - p0; } + inline Vector size() const + { return distance(); } + + inline bool empty() const { + for(int i = 0; i < Vector::Count; ++i) + if (!Vector::coord_less(p0[i], p1[i])) + return true; return false; } - - inline bool operator== (const SelfTypeArg &other) const { - for(int i = 0; i < Count; ++i) - if ( real_less(coords[i], other.coords[i] - real_precision) - || real_less(other.coords[i], coords[i] - real_precision) ) return false; - return true; + inline void sort() { + for(int i = 0; i < Vector::Count; ++i) + if (!Vector::coord_less(p0[i], p1[i])) + std::swap(p0[i], p1[i]); } - - inline bool operator!= (const SelfTypeArg &other) const - { return !(*(const SelfType*)this == other); } + inline PairT sorted() const + { PairT x(*this); x.sort(); return x; } }; + template class Vectors { public: @@ -320,4 +371,17 @@ typedef VectorsFloat::Vector3 ULongIntVector3; typedef VectorsFloat::Vector4 ULongIntVector4; +typedef PairT Pair2; +typedef PairT Pair3; +typedef PairT Pair4; + +typedef PairT IntPair2; +typedef PairT IntPair3; +typedef PairT IntPair4; + +typedef PairT LongIntPair2; +typedef PairT LongIntPair3; +typedef PairT LongIntPair4; + + #endif diff --git a/c++/perspective/src/view.cpp b/c++/perspective/src/view.cpp index 2a57fc2..c477133 100644 --- a/c++/perspective/src/view.cpp +++ b/c++/perspective/src/view.cpp @@ -27,6 +27,11 @@ View::View(): Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK ); + + signal_draw_view.connect(sigc::mem_fun(*this, &View::on_draw_view)); + signal_point_motion.connect(sigc::mem_fun(*this, &View::on_point_motion)); + signal_point_changed.connect(sigc::mem_fun(*this, &View::on_point_changed)); + signal_transform_changed.connect(sigc::mem_fun(*this, &View::on_transform_changed)); } View::~View() { } @@ -35,25 +40,17 @@ 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); +Matrix +View::transform_to_pixels() const { + Matrix matrix = transform; + matrix.row_w().vec2() += Vector2(get_width(), get_height())*0.5; + return 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()); -} - +Matrix +View::transform_from_pixels() const + { return transform_to_pixels().inverted(); } + bool View::on_draw(const Cairo::RefPtr& context) { context->save(); @@ -74,8 +71,7 @@ View::on_draw(const Cairo::RefPtr& context) { context->stroke(); context->restore(); - context->translate(0.5*get_width(), 0.5*get_height()); - context->transform(Cairo::Matrix(transform.m00, transform.m10, transform.m01, transform.m11, transform.m30, transform.m31)); + context->transform( transform_to_pixels().to_cairo() ); signal_draw_view(context); for(PointList::const_iterator i = points.begin(); i != points.end(); ++i) (*i)->draw(context, *this); @@ -86,10 +82,14 @@ View::on_draw(const Cairo::RefPtr& context) { bool View::on_motion_notify_event(GdkEventMotion *event) { Vector2 prev_mouse_position = mouse_position; - mouse_position = Vector2(event->x - 0.5*get_width(), event->y - 0.5*get_height()); + mouse_position = Vector2(event->x, event->y); + + Matrix to_pixels = transform_to_pixels(); + Matrix from_pixels = transform_from_pixels(); + if (dragging) { if (selected_point) { - selected_point->position = Vector2(transform.inverted()*Vector4(mouse_position + selected_point_offset, 0, 1)); + selected_point->position = (from_pixels*Vector4(mouse_position + selected_point_offset, 0, 1)).vec2(); point_motion(selected_point); queue_draw(); } @@ -99,7 +99,7 @@ View::on_motion_notify_event(GdkEventMotion *event) { 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 point_position = (to_pixels*Vector4((*i)->position, 0, 1)).vec2(); Vector2 offset = point_position - mouse_position; if (offset.square() <= (*i)->radius * (*i)->radius) { point = *i; @@ -118,7 +118,7 @@ View::on_motion_notify_event(GdkEventMotion *event) { 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 @@ -150,3 +150,20 @@ View::on_button_release_event(GdkEventButton *event) { signal_transform_changed(); return true; } + + +void +View::on_draw_view(const Cairo::RefPtr&) + { } + +void +View::on_point_motion(const PointPtr&) + { } + +void +View::on_point_changed(const PointPtr&) + { } + +void +View::on_transform_changed() + { } diff --git a/c++/perspective/src/view.h b/c++/perspective/src/view.h index a59d39c..198087e 100644 --- a/c++/perspective/src/view.h +++ b/c++/perspective/src/view.h @@ -66,14 +66,19 @@ public: Real get_pixel_size(); - void transform_context(const Cairo::RefPtr& context); - void back_transform_context(const Cairo::RefPtr& context); + Matrix transform_to_pixels() const; + Matrix transform_from_pixels() const; protected: - bool on_draw(const Cairo::RefPtr& context) override; + bool on_draw(const Cairo::RefPtr &context) override; bool on_motion_notify_event(GdkEventMotion *event) override; bool on_button_press_event(GdkEventButton *event) override; bool on_button_release_event(GdkEventButton *event) override; + + virtual void on_draw_view(const Cairo::RefPtr &context); + virtual void on_point_motion(const PointPtr &point); + virtual void on_point_changed(const PointPtr &point); + virtual void on_transform_changed(); };