summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/axis-manip.h2
-rw-r--r--src/display/sp-canvas.cpp11
-rw-r--r--src/line-geometry.cpp82
-rw-r--r--src/line-geometry.h9
-rw-r--r--src/perspective-line.cpp18
-rw-r--r--src/perspective-line.h5
-rw-r--r--src/perspective3d.cpp3
-rw-r--r--src/vanishing-point.cpp93
8 files changed, 191 insertions, 32 deletions
diff --git a/src/axis-manip.h b/src/axis-manip.h
index 8574bf3ff..027d17ea5 100644
--- a/src/axis-manip.h
+++ b/src/axis-manip.h
@@ -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 {
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index fef2fcc0c..c53b1e61b 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -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();
+ }
}
/**
diff --git a/src/line-geometry.cpp b/src/line-geometry.cpp
index d36b1b63d..68741d8a7 100644
--- a/src/line-geometry.cpp
+++ b/src/line-geometry.cpp
@@ -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();
diff --git a/src/line-geometry.h b/src/line-geometry.h
index b1eae366c..7e731d4bc 100644
--- a/src/line-geometry.h
+++ b/src/line-geometry.h
@@ -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);
diff --git a/src/perspective-line.cpp b/src/perspective-line.cpp
index e5596cc1b..9ee2d3578 100644
--- a/src/perspective-line.cpp
+++ b/src/perspective-line.cpp
@@ -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
/*
diff --git a/src/perspective-line.h b/src/perspective-line.h
index 8f218803b..cf8f2ba54 100644
--- a/src/perspective-line.h
+++ b/src/perspective-line.h
@@ -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;
diff --git a/src/perspective3d.cpp b/src/perspective3d.cpp
index 0d4877f37..c9c56e78d 100644
--- a/src/perspective3d.cpp
+++ b/src/perspective3d.cpp
@@ -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
diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp
index ac7300b15..2426c9954 100644
--- a/src/vanishing-point.cpp
+++ b/src/vanishing-point.cpp
@@ -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) {