summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 48c0a00)
raw | patch | inline | side by side (parent: 48c0a00)
author | cilix42 <cilix42@users.sourceforge.net> | |
Fri, 17 Aug 2007 16:22:23 +0000 (16:22 +0000) | ||
committer | cilix42 <cilix42@users.sourceforge.net> | |
Fri, 17 Aug 2007 16:22:23 +0000 (16:22 +0000) |
diff --git a/src/axis-manip.h b/src/axis-manip.h
index 8574bf3ff522846523dec4c3c3464c3f7f7b3b58..027d17ea56af1e0ef449da18bc6181e30bcb95b6 100644 (file)
--- a/src/axis-manip.h
+++ b/src/axis-manip.h
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)
#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 d36b1b63d3e507081c293d7b8d39ec65676ab29d..68741d8a707c5ab5694017c34606f266081c92b8 100644 (file)
--- a/src/line-geometry.cpp
+++ b/src/line-geometry.cpp
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 b1eae366c6415ba04ca6425ce13e94d6a89ed42c..7e731d4bc60b79421e1617b32486942457190ae4 100644 (file)
--- a/src/line-geometry.h
+++ b/src/line-geometry.h
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)
--- a/src/perspective-line.cpp
+++ b/src/perspective-line.cpp
*/
#include "perspective-line.h"
+#include "desktop.h"
namespace Box3D {
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 8f218803b56f09c8377fca9e9b7ea77434d341eb..cf8f2ba542c2f4004150faf57bb3f316574c0671 100644 (file)
--- a/src/perspective-line.h
+++ b/src/perspective-line.h
#include "box3d-context.h"
#include <glib.h>
+class SPDesktop;
+
namespace Box3D {
class PerspectiveLine : public Box3D::Line {
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 0d4877f37ec83f406f8913e832b9df2093545b10..c9c56e78dbdeed03f9a75a194da6f48735dc2520 100644 (file)
--- a/src/perspective3d.cpp
+++ b/src/perspective3d.cpp
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)
--- a/src/vanishing-point.cpp
+++ b/src/vanishing-point.cpp
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()
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);
+ }
}
}
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) {