Code

Draw perspective lines for infinite VPs, too (they are updated during scrolling or...
authorcilix42 <cilix42@users.sourceforge.net>
Fri, 17 Aug 2007 16:22:23 +0000 (16:22 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Fri, 17 Aug 2007 16:22:23 +0000 (16:22 +0000)
src/axis-manip.h
src/display/sp-canvas.cpp
src/line-geometry.cpp
src/line-geometry.h
src/perspective-line.cpp
src/perspective-line.h
src/perspective3d.cpp
src/vanishing-point.cpp

index 8574bf3ff522846523dec4c3c3464c3f7f7b3b58..027d17ea56af1e0ef449da18bc6181e30bcb95b6 100644 (file)
@@ -17,6 +17,8 @@
 
 namespace Box3D {
 
+const double epsilon = 1e-6;
+
 // The X-/Y-/Z-axis corresponds to the first/second/third digit
 // in binary representation, respectively.
 enum Axis {
index fef2fcc0c1f4653ba54b02a3a3dff92cd079ce11..c53b1e61b2a60fbfbfdcd6402ffa0a80257fb875 100644 (file)
@@ -34,6 +34,8 @@
 #include <libnr/nr-matrix-ops.h>
 #include <libnr/nr-convex-hull.h>
 #include "prefs-utils.h"
+#include "box3d-context.h"
+#include "inkscape.h"
 
 // Tiles are a way to minimize the number of redraws, eliminating too small redraws. 
 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
@@ -2193,6 +2195,15 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear,
     } else {
         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
     }
+
+    /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
+    SPEventContext *ec = inkscape_active_event_context();
+    if (SP_IS_3DBOX_CONTEXT (ec)) {
+        // We could avoid redraw during panning by checking the status of is_scrolling, but this is
+        // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
+        SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
+        bc->_vpdrag->updateLines();
+    }
 }
 
 /** 
index d36b1b63d3e507081c293d7b8d39ec65676ab29d..68741d8a707c5ab5694017c34606f266081c92b8 100644 (file)
@@ -75,6 +75,88 @@ NR::Point Line::closest_to(NR::Point const &pt)
     return *result;
 }
 
+inline static double determinant (NR::Point const &a, NR::Point const &b)
+{
+    return (a[NR::X] * b[NR::Y] - a[NR::Y] * b[NR::X]);
+}
+
+/* The coordinates of w with respect to the basis {v1, v2} */
+std::pair<double, double> coordinates (NR::Point const &v1, NR::Point const &v2, NR::Point const &w)
+{
+    double det = determinant (v1, v2);;
+    if (fabs (det) < epsilon) {
+        g_warning ("Vectors do not form a basis.\n");
+        return std::make_pair (0.0, 0.0);
+    }
+
+    double lambda1 = determinant (w, v2) / det;
+    double lambda2 = determinant (v1, w) / det;
+    return std::make_pair (lambda1, lambda2);
+}
+
+/* whether w lies inside the sector spanned by v1 and v2 */
+bool lies_in_sector (NR::Point const &v1, NR::Point const &v2, NR::Point const &w)
+{
+    std::pair<double, double> coords = coordinates (v1, v2, w);
+    return (coords.first >= 0 and coords.second >= 0);
+}
+
+static double pos_angle (NR::Point A, NR::Point B)
+{
+    return fabs (NR::atan2 (A) - NR::atan2 (B));
+}
+
+/*
+ * Returns the two corners of the quadrangle A, B, C, D spanning the edge that is hit by a semiline
+ * starting at pt and going into direction dir.
+ * If none of the sides is hit, it returns a pair containing two identical points.
+ */
+std::pair<NR::Point, NR::Point>
+side_of_intersection (NR::Point const &A, NR::Point const &B, NR::Point const &C, NR::Point const &D,
+                      NR::Point const &pt, NR::Point const &dir)
+{
+    NR::Point dir_A (A - pt);
+    NR::Point dir_B (B - pt);
+    NR::Point dir_C (C - pt);
+    NR::Point dir_D (D - pt);
+
+    std::pair<NR::Point, NR::Point> result;
+    double angle = -1;
+    double tmp_angle;
+
+    if (lies_in_sector (dir_A, dir_B, dir)) {
+        result = std::make_pair (A, B);
+        angle = pos_angle (dir_A, dir_B);
+    }
+    if (lies_in_sector (dir_B, dir_C, dir)) {
+        tmp_angle = pos_angle (dir_B, dir_C);
+        if (tmp_angle > angle) {
+            angle = tmp_angle;
+            result = std::make_pair (B, C);
+        }
+    }
+    if (lies_in_sector (dir_C, dir_D, dir)) {
+        tmp_angle = pos_angle (dir_C, dir_D);
+        if (tmp_angle > angle) {
+            angle = tmp_angle;
+            result = std::make_pair (C, D);
+        }
+    }
+    if (lies_in_sector (dir_D, dir_A, dir)) {
+        tmp_angle = pos_angle (dir_D, dir_A);
+        if (tmp_angle > angle) {
+            angle = tmp_angle;
+            result = std::make_pair (D, A);
+        }
+    }
+    if (angle == -1) {
+        // no intersection found; return a pair containing two identical points
+        return std::make_pair (A, A);
+    } else {
+        return result;
+    }
+}
+
 void create_canvas_point(NR::Point const &pos, double size, guint32 rgba)
 {
     SPDesktop *desktop = inkscape_active_desktop();
index b1eae366c6415ba04ca6425ce13e94d6a89ed42c..7e731d4bc60b79421e1617b32486942457190ae4 100644 (file)
@@ -35,14 +35,19 @@ public:
     NR::Point closest_to(NR::Point const &pt); // returns the point on the line closest to pt 
 
     friend inline std::ostream &operator<< (std::ostream &out_file, const Line &in_line);
-       
-private:
+//private:
     NR::Point pt;
     NR::Point v_dir;
     NR::Point normal;
     NR::Coord d0;
 };
 
+std::pair<double, double> coordinates (NR::Point const &v1, NR::Point const &v2, NR::Point const &w);
+bool lies_in_sector (NR::Point const &v1, NR::Point const &v2, NR::Point const &w);
+std::pair<NR::Point, NR::Point> side_of_intersection (NR::Point const &A, NR::Point const &B,
+                                                      NR::Point const &C, NR::Point const &D,
+                                                      NR::Point const &pt, NR::Point const &dir);
+
 /*** For testing purposes: Draw a knot/node of specified size and color at the given position ***/
 void create_canvas_point(NR::Point const &pos, double size = 4.0, guint32 rgba = 0xff00007f);
 
index e5596cc1b5704a09d024ea58802cccb75a8b54c5..9ee2d3578664005ae10042d948a95a2e6cce2a92 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include "perspective-line.h"
+#include "desktop.h"
 
 namespace Box3D {
 
@@ -50,6 +51,23 @@ NR::Point PerspectiveLine::meet(Line const &line)
     return *intersect(line); // works since intersect() does not return NR::Nothing()
 }
 
+NR::Maybe<NR::Point> PerspectiveLine::intersection_with_viewbox (SPDesktop *desktop)
+{
+    NR::Rect vb = desktop->get_display_area();
+    /* remaining viewbox corners */
+    NR::Point ul (vb.min()[NR::X], vb.max()[NR::Y]);
+    NR::Point lr (vb.max()[NR::X], vb.min()[NR::Y]);
+
+    std::pair <NR::Point, NR::Point> e = side_of_intersection (vb.min(), lr, vb.max(), ul, this->pt, this->v_dir);
+    if (e.first == e.second) {
+        // perspective line lies outside the canvas
+        return NR::Nothing();
+    }
+
+    Line line (e.first, e.second);
+    return this->intersect (line);
+}
+
 } // namespace Box3D 
  
 /*
index 8f218803b56f09c8377fca9e9b7ea77434d341eb..cf8f2ba542c2f4004150faf57bb3f316574c0671 100644 (file)
@@ -17,6 +17,8 @@
 #include "box3d-context.h" 
 #include <glib.h>
 
+class SPDesktop;
+
 namespace Box3D {
 
 class PerspectiveLine : public Box3D::Line {
@@ -30,7 +32,8 @@ public:
     PerspectiveLine (NR::Point const &pt, Box3D::Axis const axis, Perspective3D *perspective);
     NR::Maybe<NR::Point> intersect (Line const &line); // FIXME: Can we make this return only a NR::Point to remove the extra method meet()?
     NR::Point meet (Line const &line);
-       
+    NR::Maybe<NR::Point> intersection_with_viewbox (SPDesktop *desktop);
+
 private:
     Box3D::Axis vp_dir; // direction of the associated VP
     Perspective3D *persp;
index 0d4877f37ec83f406f8913e832b9df2093545b10..c9c56e78dbdeed03f9a75a194da6f48735dc2520 100644 (file)
@@ -310,6 +310,9 @@ Perspective3D::toggle_boxes (Box3D::Axis axis)
         sp_3dbox_reshape_after_VP_toggling (SP_3DBOX (i->data), axis);
     }
     update_box_reprs();
+
+    SP3DBoxContext *bc = SP_3DBOX_CONTEXT (inkscape_active_event_context());
+    bc->_vpdrag->updateDraggers ();
 }
 
 void
index ac7300b157810bc2624cddef6fe468e45f7b4ce7..2426c9954ebb6bc9a770882f9ae3d5fe60de43fe 100644 (file)
@@ -381,31 +381,33 @@ VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp)
     this->point = p;
     this->point_original = p;
 
-    // create the knot
-    this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
-    this->knot->setMode(SP_KNOT_MODE_XOR);
-    this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
-    this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
-    sp_knot_update_ctrl(this->knot);
-
-    // move knot to the given point
-    sp_knot_set_position (this->knot, &this->point, SP_KNOT_STATE_NORMAL);
-    sp_knot_show (this->knot);
-
-    // connect knot's signals
-    g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
-    /***
-    g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this);
-    ***/
-    g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
-    g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
-    /***
-    g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this);
-    ***/
-
-    // add the initial VP (which may be NULL!)
-    this->addVP (vp);
-    //updateKnotShape();
+    if (vp->is_finite()) {
+        // create the knot
+        this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
+        this->knot->setMode(SP_KNOT_MODE_XOR);
+        this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
+        this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
+        sp_knot_update_ctrl(this->knot);
+
+        // move knot to the given point
+        sp_knot_set_position (this->knot, &this->point, SP_KNOT_STATE_NORMAL);
+        sp_knot_show (this->knot);
+
+        // connect knot's signals
+        g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
+        /***
+        g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this);
+        ***/
+        g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
+        g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
+        /***
+        g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this);
+        ***/
+
+        // add the initial VP (which may be NULL!)
+        this->addVP (vp);
+        //updateKnotShape();
+    }
 }
 
 VPDragger::~VPDragger()
@@ -483,8 +485,8 @@ VPDragger::addVP (VanishingPoint *vp)
     if (vp == NULL) {
         return;
     }
-    if (g_slist_find (this->vps, vp)) {
-        // don't add the same VP twice
+    if (!vp->is_finite() || g_slist_find (this->vps, vp)) {
+        // don't add infinite VPs, and don't add the same VP twice
         return;
     }
 
@@ -773,6 +775,7 @@ VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner
 
     VanishingPoint *vp = document->get_persp_of_box (box)->get_vanishing_point (axis);
     if (vp->is_finite()) {
+        // draw perspective lines for finite VPs
         NR::Point pt = vp->get_pos();
         if (this->front_or_rear_lines & 0x1) {
             // draw 'front' perspective lines
@@ -785,8 +788,36 @@ VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner
             this->addLine (corner4, pt, color);
         }
     } else {
-        // TODO: Draw infinite PLs
-        //g_warning ("Perspective lines for infinite vanishing points are not supported yet.\n");
+        // draw perspective lines for infinite VPs
+        NR::Maybe<NR::Point> pt1, pt2, pt3, pt4;
+        Box3D::Perspective3D *persp = this->document->get_persp_of_box (box);
+        SPDesktop *desktop = inkscape_active_desktop (); // FIXME: Store the desktop in VPDrag
+        Box3D::PerspectiveLine pl (corner1, axis, persp);
+        pt1 = pl.intersection_with_viewbox(desktop);
+
+        pl = Box3D::PerspectiveLine (corner2, axis, persp);
+        pt2 = pl.intersection_with_viewbox(desktop);
+
+        pl = Box3D::PerspectiveLine (corner3, axis, persp);
+        pt3 = pl.intersection_with_viewbox(desktop);
+
+        pl = Box3D::PerspectiveLine (corner4, axis, persp);
+        pt4 = pl.intersection_with_viewbox(desktop);
+
+        if (!pt1 || !pt2 || !pt3 || !pt4) {
+            // some perspective lines s are outside the canvas; currently we don't draw any of them
+            return;
+        }
+        if (this->front_or_rear_lines & 0x1) {
+            // draw 'front' perspective lines
+            this->addLine (corner1, *pt1, color);
+            this->addLine (corner2, *pt2, color);
+        }
+        if (this->front_or_rear_lines & 0x2) {
+            // draw 'rear' perspective lines
+            this->addLine (corner3, *pt3, color);
+            this->addLine (corner4, *pt4, color);
+        }
     }
 
 }
@@ -830,6 +861,10 @@ VPDrag::addDragger (VanishingPoint *vp)
         g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)");
         g_assert (vp != NULL);
     }
+    if (!vp->is_finite()) {
+        // don't create draggers for infinite vanishing points
+        return;
+    }
     NR::Point p = vp->get_pos();
 
     for (GList *i = this->draggers; i != NULL; i = i->next) {