summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 46a230e)
raw | patch | inline | side by side (parent: 46a230e)
author | cilix42 <cilix42@users.sourceforge.net> | |
Fri, 17 Aug 2007 18:42:23 +0000 (18:42 +0000) | ||
committer | cilix42 <cilix42@users.sourceforge.net> | |
Fri, 17 Aug 2007 18:42:23 +0000 (18:42 +0000) |
diff --git a/src/box3d.cpp b/src/box3d.cpp
index dad0ae88cd7df11059a199153ae126dae9a9f9cb..b0195cdb8918d76c1fdd612d64023ff225f31ce5 100644 (file)
--- a/src/box3d.cpp
+++ b/src/box3d.cpp
#include "attributes.h"
#include "svg/stringstream.h"
#include "box3d.h"
+#include "desktop-handles.h"
static void sp_3dbox_class_init(SP3DBoxClass *klass);
static void sp_3dbox_init(SP3DBox *box3d);
@@ -169,6 +170,17 @@ sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr
sp_3dbox_link_to_existing_paths (box, repr);
sp_3dbox_set_ratios (box, Box3D::XYZ);
+
+ // Store the center (if it already exists) and certain corners for later use during center-dragging
+ NR::Maybe<NR::Point> cen = sp_3dbox_get_center (box);
+ if (cen) {
+ box->old_center = *cen;
+ }
+ box->old_corner2 = box->corners[2];
+ box->old_corner1 = box->corners[1];
+ box->old_corner0 = box->corners[0];
+ box->old_corner3 = box->corners[3];
+ box->old_corner5 = box->corners[5];
}
static void
@@ -269,6 +281,17 @@ static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node
sp_3dbox_set_ratios (box);
g_free ((void *) str);
+
+ /* store center and construction-corners for later use during center-dragging */
+ NR::Maybe<NR::Point> cen = sp_3dbox_get_center (box);
+ if (cen) {
+ box->old_center = *cen;
+ }
+ box->old_corner2 = box->corners[2];
+ box->old_corner1 = box->corners[1];
+ box->old_corner0 = box->corners[0];
+ box->old_corner3 = box->corners[3];
+ box->old_corner5 = box->corners[5];
}
if (((SPObjectClass *) (parent_class))->write) {
***/
}
-//static
+static void
+sp_3dbox_set_shape_from_points (SP3DBox *box, NR::Point const &cornerA, NR::Point const &cornerB, NR::Point const &cornerC)
+{
+ sp_3dbox_recompute_corners (box, cornerA, cornerB, cornerC);
+
+ // FIXME: How to handle other contexts???
+ // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
+ if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
+ return;
+ SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
+
+ if (bc->extruded) {
+ box->faces[0]->set_corners (box->corners[0], box->corners[4], box->corners[6], box->corners[2]);
+ box->faces[1]->set_corners (box->corners[1], box->corners[5], box->corners[7], box->corners[3]);
+ box->faces[2]->set_corners (box->corners[0], box->corners[1], box->corners[5], box->corners[4]);
+ box->faces[3]->set_corners (box->corners[2], box->corners[3], box->corners[7], box->corners[6]);
+ box->faces[5]->set_corners (box->corners[4], box->corners[5], box->corners[7], box->corners[6]);
+ }
+ box->faces[4]->set_corners (box->corners[0], box->corners[1], box->corners[3], box->corners[2]);
+
+ sp_3dbox_update_curves (box);
+}
+
void
// FIXME: Note that this is _not_ the virtual set_shape() method inherited from SPShape,
// since SP3DBox is inherited from SPGroup. The following method is "artificially"
// called from sp_3dbox_update().
//sp_3dbox_set_shape(SPShape *shape)
-sp_3dbox_set_shape(SP3DBox *box3d, bool use_previous_corners)
+sp_3dbox_set_shape(SP3DBox *box, bool use_previous_corners)
{
- // FIXME: How to handle other contexts???
- // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
return;
SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
- /* Only update the curves during dragging; setting the svg representations
- is expensive and only done once at the end */
if (!use_previous_corners) {
- sp_3dbox_recompute_corners (box3d, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
+ sp_3dbox_set_shape_from_points (box, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
} else {
- sp_3dbox_recompute_corners (box3d, box3d->corners[2], box3d->corners[1], box3d->corners[5]);
- }
- if (bc->extruded) {
- box3d->faces[0]->set_corners (box3d->corners[0], box3d->corners[4], box3d->corners[6], box3d->corners[2]);
- box3d->faces[1]->set_corners (box3d->corners[1], box3d->corners[5], box3d->corners[7], box3d->corners[3]);
- box3d->faces[2]->set_corners (box3d->corners[0], box3d->corners[1], box3d->corners[5], box3d->corners[4]);
- box3d->faces[3]->set_corners (box3d->corners[2], box3d->corners[3], box3d->corners[7], box3d->corners[6]);
- box3d->faces[5]->set_corners (box3d->corners[4], box3d->corners[5], box3d->corners[7], box3d->corners[6]);
+ sp_3dbox_set_shape_from_points (box, box->corners[2], box->corners[1], box->corners[5]);
}
- box3d->faces[4]->set_corners (box3d->corners[0], box3d->corners[1], box3d->corners[3], box3d->corners[2]);
-
- sp_3dbox_update_curves (box3d);
}
return sp_3dbox_get_midpoint_between_corners (box, 0, 7);
}
+NR::Point
+sp_3dbox_get_midpoint_in_axis_direction (NR::Point const &C, NR::Point const &D, Box3D::Axis axis, Box3D::Perspective3D *persp)
+{
+ Box3D::PerspectiveLine pl (D, axis, persp);
+ return pl.pt_with_given_cross_ratio (C, D, -1.0);
+}
+
// TODO: The following function can probably be rewritten in a much more elegant and robust way
// by using projective coordinates for all points and using the cross ratio.
NR::Maybe<NR::Point>
return g_strdup(os.str().c_str());
}
+// auxiliary function
+static std::pair<NR::Point, NR::Point>
+sp_3dbox_new_midpoints (Box3D::Perspective3D *persp, Box3D::Axis axis, NR::Point const &M0, NR::Point const &M, NR::Point const &A, NR::Point const &B)
+{
+ double cr1 = Box3D::cross_ratio (*persp->get_vanishing_point (axis), M0, M, A);
+ double cr2 = Box3D::cross_ratio (*persp->get_vanishing_point (axis), M, B, M0);
+ if (fabs (cr1 - 1) < Box3D::epsilon) {
+ // FIXME: cr == 1 is a degenerate case; how should we deal with it?
+ return std::make_pair (NR::Point (0,0), NR::Point (0,0));
+ }
+ Box3D::PerspectiveLine pl (M0, axis, persp);
+ NR::Point B_new = pl.pt_with_given_cross_ratio (M0, M, cr1 / (cr1 - 1));
+ NR::Point A_new = pl.pt_with_given_cross_ratio (M0, M, 1 - cr2);
+ return std::make_pair (A_new, B_new);
+}
+
+void sp_3dbox_recompute_XY_corners_from_new_center (SP3DBox *box, NR::Point const new_center)
+{
+ // TODO: Clean this function up
+
+ Box3D::Perspective3D *persp = sp_desktop_document (inkscape_active_desktop())->get_persp_of_box (box);
+ NR::Point old_center = box->old_center;
+
+ NR::Point A0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner2, box->old_corner0, Box3D::Y, persp));
+ NR::Point B0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner1, box->old_corner3, Box3D::Y, persp));
+
+ /* we first move the box along the X-axis ... */
+ Box3D::PerspectiveLine aux_line1 (old_center, Box3D::X, persp);
+ Box3D::PerspectiveLine aux_line2 (new_center, Box3D::Y, persp);
+ NR::Point Z1 = aux_line1.meet (aux_line2);
+
+ Box3D::PerspectiveLine ref_line (B0, Box3D::X, persp);
+ Box3D::PerspectiveLine pline2 (old_center, Box3D::Z, persp);
+ Box3D::PerspectiveLine pline3 (Z1, Box3D::Z, persp);
+ NR::Point M0 = ref_line.meet (pline2);
+ NR::Point M1 = ref_line.meet (pline3);
+
+ std::pair<NR::Point, NR::Point> new_midpts = sp_3dbox_new_midpoints (persp, Box3D::X, M0, M1, A0, B0);
+ NR::Point A1 (new_midpts.first);
+ NR::Point B1 (new_midpts.second);
+
+ /* ... and then along the Y-axis */
+ Box3D::PerspectiveLine pline4 (box->old_corner1, Box3D::X, persp);
+ Box3D::PerspectiveLine pline5 (box->old_corner3, Box3D::X, persp);
+ Box3D::PerspectiveLine aux_line3 (M1, Box3D::Y, persp);
+ NR::Point C1 = aux_line3.meet (pline4);
+ NR::Point D1 = aux_line3.meet (pline5);
+
+ Box3D::PerspectiveLine aux_line4 (new_center, Box3D::Z, persp);
+ NR::Point M2 = aux_line4.meet (aux_line3);
+
+ Box3D::VanishingPoint *vp_y = persp->get_vanishing_point (Box3D::Y);
+ std::pair<NR::Point, NR::Point> other_new_midpts = sp_3dbox_new_midpoints (persp, Box3D::Y, M1, M2, C1, D1);
+ NR::Point C2 (other_new_midpts.first);
+ NR::Point D2 (other_new_midpts.second);
+
+ Box3D::PerspectiveLine plXC (C2, Box3D::X, persp);
+ Box3D::PerspectiveLine plXD (D2, Box3D::X, persp);
+ Box3D::PerspectiveLine plYA (A1, Box3D::Y, persp);
+ Box3D::PerspectiveLine plYB (B1, Box3D::Y, persp);
+
+ NR::Point new_corner2 (plXD.meet (plYA));
+ NR::Point new_corner1 (plXC.meet (plYB));
+
+ NR::Point tmp_corner1 (pline4.meet (plYB));
+ Box3D::PerspectiveLine pline6 (box->old_corner5, Box3D::X, persp);
+ Box3D::PerspectiveLine pline7 (tmp_corner1, Box3D::Z, persp);
+ NR::Point tmp_corner5 (pline6.meet (pline7));
+
+ Box3D::PerspectiveLine pline8 (tmp_corner5, Box3D::Y, persp);
+ Box3D::PerspectiveLine pline9 (new_corner1, Box3D::Z, persp);
+ NR::Point new_corner5 (pline8.meet (pline9));
+
+ sp_3dbox_set_shape_from_points (box, new_corner2, new_corner1, new_corner5);
+}
+
void sp_3dbox_update_perspective_lines()
{
SPEventContext *ec = inkscape_active_event_context();
diff --git a/src/box3d.h b/src/box3d.h
index ed563a42e4444fe1e861435d50dc22a4c68579ae..a47b066634b43b494c27bb6e40d8c4b7013e5d8d 100644 (file)
--- a/src/box3d.h
+++ b/src/box3d.h
guint front_bits; /* used internally to determine which of two parallel faces is supposed to be the front face */
+ // FIXME: If we only allow a single box to be dragged at a time then we can save memory by storing
+ // the old positions centrally in SP3DBoxContext (instead of in each box separately)
+ NR::Point old_center;
+ NR::Point old_corner2;
+ NR::Point old_corner1;
+ NR::Point old_corner0;
+ NR::Point old_corner3;
+ NR::Point old_corner5;
+
gint my_counter; // for testing only
};
void sp_3dbox_reshape_after_VP_toggling (SP3DBox *box, Box3D::Axis axis);
NR::Maybe<NR::Point> sp_3dbox_get_center (SP3DBox *box);
NR::Maybe<NR::Point> sp_3dbox_get_midpoint_between_corners (SP3DBox *box, guint id_corner1, guint id_corner2);
+void sp_3dbox_recompute_XY_corners_from_new_center (SP3DBox *box, NR::Point const new_center);
+NR::Point sp_3dbox_get_midpoint_in_axis_direction (NR::Point const &C, NR::Point const &D, Box3D::Axis axis, Box3D::Perspective3D *persp);
void sp_3dbox_update_perspective_lines();
void sp_3dbox_corners_for_perspective_lines (const SP3DBox * box, Box3D::Axis axis, NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4);
diff --git a/src/line-geometry.cpp b/src/line-geometry.cpp
index 68741d8a707c5ab5694017c34606f266081c92b8..d7b5fb2ecd9bc66022916c2d7c8298f6c8c07a0e 100644 (file)
--- a/src/line-geometry.cpp
+++ b/src/line-geometry.cpp
return *result;
}
+double Line::lambda (NR::Point const pt)
+{
+ double sign = (NR::dot (pt - this->pt, this->v_dir) > 0) ? 1.0 : -1.0;
+ double lambda = sign * NR::L2 (pt - this->pt);
+ // FIXME: It may speed things up (but how much?) if we assume that
+ // pt lies on the line and thus skip the following test
+ NR::Point test = point_from_lambda (lambda);
+ if (!pts_coincide (pt, test)) {
+ g_warning ("Point does not lie on line.\n");
+ return 0;
+ }
+ return lambda;
+}
+
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]);
@@ -157,6 +171,57 @@ side_of_intersection (NR::Point const &A, NR::Point const &B, NR::Point const &C
}
}
+double cross_ratio (NR::Point const &A, NR::Point const &B, NR::Point const &C, NR::Point const &D)
+{
+ Line line (A, D);
+ double lambda_A = line.lambda (A);
+ double lambda_B = line.lambda (B);
+ double lambda_C = line.lambda (C);
+ double lambda_D = line.lambda (D);
+
+ if (fabs (lambda_D - lambda_A) < epsilon || fabs (lambda_C - lambda_B) < epsilon) {
+ // FIXME: What should we return if the cross ratio can't be computed?
+ return 0;
+ //return NR_HUGE;
+ }
+ return (((lambda_C - lambda_A) / (lambda_D - lambda_A)) * ((lambda_D - lambda_B) / (lambda_C - lambda_B)));
+}
+
+double cross_ratio (VanishingPoint const &V, NR::Point const &B, NR::Point const &C, NR::Point const &D)
+{
+ if (V.is_finite()) {
+ return cross_ratio (V.get_pos(), B, C, D);
+ } else {
+ Line line (B, D);
+ double lambda_B = line.lambda (B);
+ double lambda_C = line.lambda (C);
+ double lambda_D = line.lambda (D);
+
+ if (fabs (lambda_C - lambda_B) < epsilon) {
+ // FIXME: What should we return if the cross ratio can't be computed?
+ return 0;
+ //return NR_HUGE;
+ }
+ return (lambda_D - lambda_B) / (lambda_C - lambda_B);
+ }
+}
+
+NR::Point fourth_pt_with_given_cross_ratio (NR::Point const &A, NR::Point const &C, NR::Point const &D, double gamma)
+{
+ Line line (A, D);
+ double lambda_A = line.lambda (A);
+ double lambda_C = line.lambda (C);
+ double lambda_D = line.lambda (D);
+
+ double beta = (lambda_C - lambda_A) / (lambda_D - lambda_A);
+ if (fabs (beta - gamma) < epsilon) {
+ // FIXME: How to handle the case when the point can't be computed?
+ // g_warning ("Cannot compute point with given cross ratio.\n");
+ return NR::Point (0.0, 0.0);
+ }
+ return line.point_from_lambda ((beta * lambda_D - gamma * lambda_C) / (beta - gamma));
+}
+
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 7e731d4bc60b79421e1617b32486942457190ae4..cc0c9aaf4dff86988a8d177c8381bfd0e74e7ee2 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:
+ friend NR::Point fourth_pt_with_given_cross_ratio (NR::Point const &A, NR::Point const &C, NR::Point const &D, double gamma);
+
+ double lambda (NR::Point const pt);
+ inline NR::Point point_from_lambda (double const lambda) {
+ return (pt + lambda * NR::unit_vector (v_dir)); }
+
+protected:
+ inline static bool pts_coincide (NR::Point const pt1, NR::Point const pt2)
+ {
+ return (NR::L2 (pt2 - pt1) < epsilon);
+ }
+
NR::Point pt;
NR::Point v_dir;
NR::Point normal;
NR::Point const &C, NR::Point const &D,
NR::Point const &pt, NR::Point const &dir);
+double cross_ratio (NR::Point const &A, NR::Point const &B, NR::Point const &C, NR::Point const &D);
+double cross_ratio (VanishingPoint const &V, NR::Point const &B, NR::Point const &C, NR::Point const &D);
+NR::Point fourth_pt_with_given_cross_ratio (NR::Point const &A, NR::Point const &C, NR::Point const &D, double gamma);
+
/*** 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/object-edit.cpp b/src/object-edit.cpp
index 4467074d0b7229c9dc87ef385d66035e077af23d..765f903037ec1ea6a1135df2f301458b559515e7 100644 (file)
--- a/src/object-edit.cpp
+++ b/src/object-edit.cpp
@@ -648,6 +648,22 @@ static void sp_3dbox_knot_set(SPItem *item, guint knot_id, NR::Point const &new_
sp_3dbox_set_z_orders (box);
}
+static void sp_3dbox_knot_center_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
+ sp_3dbox_recompute_XY_corners_from_new_center (SP_3DBOX (item), new_pos * i2d);
+
+ sp_3dbox_update_curves (SP_3DBOX(item));
+}
+
+static NR::Point sp_3dbox_knot_center_get(SPItem *item)
+{
+ NR::Maybe<NR::Point> center = sp_3dbox_get_center(SP_3DBOX(item));
+ if (!center) return NR::Point (0, 0);
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
+ return (*center) * i2d;
+}
+
static void sp_3dbox_knot0_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
{
sp_3dbox_knot_set(item, 0, new_pos, origin, state);
_("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
sp_knot_holder_add(knot_holder, sp_3dbox_knot7_set, sp_3dbox_knot7_get, NULL,
_("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
+
+ // center dragging
+ sp_knot_holder_add_full(knot_holder, sp_3dbox_knot_center_set, sp_3dbox_knot_center_get, NULL,
+ SP_KNOT_SHAPE_CROSS, SP_KNOT_MODE_XOR,_("Move the box in perspective."));
+
sp_pat_knot_holder(item, knot_holder);
return knot_holder;
index 9ee2d3578664005ae10042d948a95a2e6cce2a92..90857e6d5ecd10fac2ee887f5db8dfda20068e66 100644 (file)
--- a/src/perspective-line.cpp
+++ b/src/perspective-line.cpp
return *intersect(line); // works since intersect() does not return NR::Nothing()
}
+NR::Point PerspectiveLine::pt_with_given_cross_ratio (NR::Point const &C, NR::Point const &D, double gamma)
+{
+ if (persp->get_vanishing_point (vp_dir)->is_finite()) {
+ NR::Point V (*persp->get_vanishing_point (vp_dir));
+ return fourth_pt_with_given_cross_ratio (V, C, D, gamma);
+ } else {
+ if (fabs (gamma - 1) < epsilon) {
+ g_warning ("Cannot compute point with given cross ratio.\n");
+ return NR::Point (0.0, 0.0);
+ }
+ Line line (C, D);
+ double lambda_C = line.lambda (C);
+ double lambda_D = line.lambda (D);
+ return line.point_from_lambda ((lambda_D - gamma * lambda_C) / (1 - gamma));
+ }
+}
+
NR::Maybe<NR::Point> PerspectiveLine::intersection_with_viewbox (SPDesktop *desktop)
{
NR::Rect vb = desktop->get_display_area();
diff --git a/src/perspective-line.h b/src/perspective-line.h
index cf8f2ba542c2f4004150faf57bb3f316574c0671..90104ffdf963953e86d58efb690e6a896d9efc48 100644 (file)
--- a/src/perspective-line.h
+++ b/src/perspective-line.h
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::Point pt_with_given_cross_ratio (NR::Point const &C, NR::Point const &D, double gamma);
NR::Maybe<NR::Point> intersection_with_viewbox (SPDesktop *desktop);
private: