summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 9e4d630)
raw | patch | inline | side by side (parent: 9e4d630)
author | cilix42 <cilix42@users.sourceforge.net> | |
Thu, 13 Dec 2007 09:45:27 +0000 (09:45 +0000) | ||
committer | cilix42 <cilix42@users.sourceforge.net> | |
Thu, 13 Dec 2007 09:45:27 +0000 (09:45 +0000) |
51 files changed:
diff --git a/src/Makefile_insert b/src/Makefile_insert
index ff597b816fa4b1225ed88b555b356ad2c1c2c966..695d87f8f6b000f644ace91891648e0c2ee458da 100644 (file)
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
bad-uri-exception.h \
box3d.cpp box3d.h \
box3d-context.cpp box3d-context.h \
- box3d-face.cpp box3d-face.h \
+ box3d-side.cpp box3d-side.h \
brokenimage.xpm \
color-rgba.h \
color-profile.cpp color-profile.h \
pen-context.h \
pencil-context.cpp \
pencil-context.h \
+ persp3d.cpp persp3d.h \
+ persp3d-reference.cpp persp3d-reference.h \
perspective-line.cpp perspective-line.h \
- perspective3d.cpp perspective3d.h \
preferences.cpp preferences.h \
preferences-skeleton.h \
menus-skeleton.h \
print.cpp print.h \
profile-manager.cpp \
profile-manager.h \
+ proj_pt.cpp proj_pt.h \
rect-context.cpp rect-context.h \
require-config.h \
rubberband.cpp rubberband.h \
snapped-line.cpp snapped-line.h \
snapped-point.cpp snapped-point.h \
snapper.cpp snapper.h \
+ syseq.h \
+ transf_mat_3x4.cpp transf_mat_3x4.h \
line-snapper.cpp line-snapper.h \
guide-snapper.cpp guide-snapper.h \
object-snapper.cpp object-snapper.h \
diff --git a/src/arc-context.cpp b/src/arc-context.cpp
index c2ec30a64a61a50a298c6e292e75181e2fc61373..82c00fd05f05fe7ea2d028af895076ff6cefdfa0 100644 (file)
--- a/src/arc-context.cpp
+++ b/src/arc-context.cpp
sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
}
- NR::Rect const r = Inkscape::snap_rectangular_box(desktop, ac->item, pt, ac->center, state);
+ bool ctrl_save = false;
+ if ((state & GDK_MOD1_MASK) && (state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK)) {
+ // if Alt is pressed without Shift in addition to Control, temporarily drop the CONTROL mask
+ // so that the ellipse is not constrained to integer ratios
+ ctrl_save = true;
+ state = state ^ GDK_CONTROL_MASK;
+ }
+ NR::Rect r = Inkscape::snap_rectangular_box(desktop, ac->item, pt, ac->center, state);
+ if (ctrl_save) {
+ state = state ^ GDK_CONTROL_MASK;
+ }
+
+ NR::Point dir = r.dimensions() / 2;
+ if (state & GDK_MOD1_MASK) {
+ /* With Alt let the ellipse pass through the mouse pointer */
+ NR::Point c = r.midpoint();
+ if (!ctrl_save) {
+ if (fabs(dir[NR::X]) > 1E-6 && fabs(dir[NR::Y]) > 1E-6) {
+ NR::Matrix const i2d (sp_item_i2d_affine (ac->item));
+ NR::Point new_dir = pt * i2d - c;
+ new_dir[NR::X] *= dir[NR::Y] / dir[NR::X];
+ double lambda = NR::L2(new_dir) / dir[NR::Y];
+ r = NR::Rect (c - lambda*dir, c + lambda*dir);
+ }
+ } else {
+ /* with Alt+Ctrl (without Shift) we generate a perfect circle
+ with diameter click point <--> mouse pointer */
+ double l = NR::L2 (dir);
+ NR::Point d = NR::Point (l, l);
+ r = NR::Rect (c - d, c + d);
+ }
+ }
sp_arc_position_set(SP_ARC(ac->item),
r.midpoint()[NR::X], r.midpoint()[NR::Y],
diff --git a/src/attributes-test.h b/src/attributes-test.h
index 57eb03eb5ae77a78e109e89867c28d3a5a84e3fb..384c0ca373f333f3a05192df9e39fe6acde70626 100644 (file)
--- a/src/attributes-test.h
+++ b/src/attributes-test.h
{"sodipodi:cy", true},
{"sodipodi:rx", true},
{"sodipodi:ry", true},
+ {"inkscape:perspectiveID", true},
+ {"inkscape:corner0", true},
+ {"inkscape:corner7", true},
+ {"inkscape:box3dsidetype", true},
+ {"inkscape:persp3d", true},
+ {"inkscape:vp_x", true},
+ {"inkscape:vp_y", true},
+ {"inkscape:vp_z", true},
+ {"inkscape:persp3d-origin", true},
{"sodipodi:start", true},
{"sodipodi:end", true},
{"sodipodi:open", true},
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 3777c68a1b2d67e92cb7024f527b756c39df8c9a..8232065fc665bdb346dfb5b89e9521f154c46545 100644 (file)
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
/* SPRect */
{SP_ATTR_RX, "rx"},
{SP_ATTR_RY, "ry"},
- /* SP3DBox */
- {SP_ATTR_INKSCAPE_3DBOX, "inkscape:3dbox"},
- {SP_ATTR_INKSCAPE_3DBOX_CORNER_A, "inkscape:box3dcornerA"}, // "upper left front" corner
- {SP_ATTR_INKSCAPE_3DBOX_CORNER_B, "inkscape:box3dcornerB"}, // "lower right front" corner
- {SP_ATTR_INKSCAPE_3DBOX_CORNER_C, "inkscape:box3dcornerC"}, // "lower right rear" corner
- {SP_ATTR_INKSCAPE_3DBOX_PERSPECTIVE, "inkscape:perspective"},
- {SP_ATTR_INKSCAPE_3DBOX_FACE, "inkscape:box3dface"},
+ /* Box3D */
+ {SP_ATTR_INKSCAPE_BOX3D_PERSPECTIVE_ID, "inkscape:perspectiveID"},
+ {SP_ATTR_INKSCAPE_BOX3D_CORNER0, "inkscape:corner0"},
+ {SP_ATTR_INKSCAPE_BOX3D_CORNER7, "inkscape:corner7"},
+ /* Box3DSide */
+ {SP_ATTR_INKSCAPE_BOX3D_SIDE_TYPE, "inkscape:box3dsidetype"}, // XYfront, etc.
+ /* Persp3D */
+ {SP_ATTR_INKSCAPE_PERSP3D, "inkscape:persp3d"},
+ {SP_ATTR_INKSCAPE_PERSP3D_VP_X, "inkscape:vp_x"},
+ {SP_ATTR_INKSCAPE_PERSP3D_VP_Y, "inkscape:vp_y"},
+ {SP_ATTR_INKSCAPE_PERSP3D_VP_Z, "inkscape:vp_z"},
+ {SP_ATTR_INKSCAPE_PERSP3D_ORIGIN, "inkscape:persp3d-origin"},
/* SPEllipse */
{SP_ATTR_R, "r"},
{SP_ATTR_CX, "cx"},
diff --git a/src/attributes.h b/src/attributes.h
index 0962827f8dacbf0e6fb4ec1f4fc82add8f398f25..33e06089351237909ed41d97eb48c0367bb9b1b9 100644 (file)
--- a/src/attributes.h
+++ b/src/attributes.h
/* SPRect */
SP_ATTR_RX,
SP_ATTR_RY,
- /* SP3DBox */
- SP_ATTR_INKSCAPE_3DBOX,
- SP_ATTR_INKSCAPE_3DBOX_CORNER_A, // "upper left front" corner
- SP_ATTR_INKSCAPE_3DBOX_CORNER_B, // "lower right front" corner
- SP_ATTR_INKSCAPE_3DBOX_CORNER_C, // "lower right rear" corner
- SP_ATTR_INKSCAPE_3DBOX_PERSPECTIVE,
- SP_ATTR_INKSCAPE_3DBOX_FACE,
+ /* Box3D */
+ SP_ATTR_INKSCAPE_BOX3D_PERSPECTIVE_ID,
+ SP_ATTR_INKSCAPE_BOX3D_CORNER0, // "upper left front" corner (as a point in 3-space)
+ SP_ATTR_INKSCAPE_BOX3D_CORNER7, // "lower right rear" corner (as a point in 3-space)
+ /* Box3DSide */
+ SP_ATTR_INKSCAPE_BOX3D_SIDE_TYPE,
+ /* Persp3D */
+ SP_ATTR_INKSCAPE_PERSP3D,
+ SP_ATTR_INKSCAPE_PERSP3D_VP_X,
+ SP_ATTR_INKSCAPE_PERSP3D_VP_Y,
+ SP_ATTR_INKSCAPE_PERSP3D_VP_Z,
+ SP_ATTR_INKSCAPE_PERSP3D_ORIGIN,
/* SPEllipse */
SP_ATTR_R,
SP_ATTR_CX,
diff --git a/src/axis-manip.cpp b/src/axis-manip.cpp
index 7539f2324e3118f23cb44e308efb878fd0c1ba66..1eed56439eadc39d0a756a2c7533116b181028e1 100644 (file)
--- a/src/axis-manip.cpp
+++ b/src/axis-manip.cpp
#include "axis-manip.h"
+namespace Proj {
+
+Axis axes[4] = { X, Y, Z, W };
+
+} // namespace Proj
+
+
namespace Box3D {
Axis axes[3] = { X, Y, Z };
diff --git a/src/axis-manip.h b/src/axis-manip.h
index 4ebdb5aab342139b96cac6a9bc43a55675d1ce96..e5cc963ba2614d95f406dad307a24c34c9dfb706 100644 (file)
--- a/src/axis-manip.h
+++ b/src/axis-manip.h
#include <gtk/gtk.h>
#include "libnr/nr-point.h"
+namespace Proj {
+
+enum VPState {
+ FINITE = 0,
+ INFINITE
+};
+
+// The X-/Y-/Z-axis corresponds to the first/second/third digit
+// in binary representation, respectively.
+enum Axis {
+ X = 0,
+ Y = 1,
+ Z = 2,
+ W = 3,
+ NONE
+};
+
+extern Axis axes[4];
+
+inline gchar * string_from_axis (Proj::Axis axis) {
+ switch (axis) {
+ case X:
+ return "X";
+ break;
+ case Y:
+ return "Y";
+ break;
+ case Z:
+ return "Z";
+ break;
+ case W:
+ return "W";
+ break;
+ case NONE:
+ return "NONE";
+ break;
+ }
+ return "";
+}
+
+} // namespace Proj
+
namespace Box3D {
const double epsilon = 1e-6;
REAR = 8
};
+// converts X, Y, Z respectively to 0, 1, 2 (for use as array indices, e.g)
+inline int axis_to_int(Box3D::Axis axis) {
+ switch (axis) {
+ case Box3D::X:
+ return 0;
+ break;
+ case Box3D::Y:
+ return 1;
+ break;
+ case Box3D::Z:
+ return 2;
+ break;
+ case Box3D::NONE:
+ return -1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+inline Proj::Axis toProj(Box3D::Axis axis) {
+ switch (axis) {
+ case Box3D::X:
+ return Proj::X;
+ case Box3D::Y:
+ return Proj::Y;
+ case Box3D::Z:
+ return Proj::Z;
+ case Box3D::NONE:
+ return Proj::NONE;
+ default:
+ g_assert_not_reached();
+ }
+}
+
extern Axis axes[3];
extern Axis planes[3];
extern FrontOrRear face_positions [2];
+} // namespace Box3D
+
+namespace Proj {
+
+inline Box3D::Axis toAffine(Proj::Axis axis) {
+ switch (axis) {
+ case Proj::X:
+ return Box3D::X;
+ case Proj::Y:
+ return Box3D::Y;
+ case Proj::Z:
+ return Box3D::Z;
+ case Proj::NONE:
+ return Box3D::NONE;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+} // namespace Proj
+
+namespace Box3D {
+
// Given a bit sequence that unambiguously specifies the face of a 3D box,
// return a number between 0 and 5 corresponding to that particular face
// (which is normally used to index an array). Return -1 if the bit sequence
// does not specify a face. A face can either be given by its plane (e.g, XY)
// or by the axis that is orthogonal to it (e.g., Z).
+/***
inline gint face_to_int (guint face_id) {
switch (face_id) {
case 1: return 0;
default: return -1;
}
}
+***/
+
+/***
+inline gint int_to_face (guint id) {
+ switch (id) {
+ case 0: return 6;
+ case 1: return 14;
+ case 2: return 5;
+ case 3: return 13;
+ case 4: return 3;
+ case 5: return 11;
+
+ default: return -1;
+ }
+}
+***/
+
+/*
+ * New version:
+ * Identify the axes X, Y, Z with the numbers 0, 1, 2.
+ * A box's face is identified by the axis perpendicular to it.
+ * For a rear face, add 3.
+ */
+// Given a bit sequence that unambiguously specifies the face of a 3D box,
+// return a number between 0 and 5 corresponding to that particular face
+// (which is normally used to index an array). Return -1 if the bit sequence
+// does not specify a face. A face can either be given by its plane (e.g, XY)
+// or by the axis that is orthogonal to it (e.g., Z).
+inline gint face_to_int (guint face_id) {
+ switch (face_id) {
+ case 1: return 0;
+ case 2: return 1;
+ case 4: return 2;
+ case 3: return 2;
+ case 5: return 1;
+ case 6: return 0;
+ case 9: return 3;
+ case 10: return 4;
+ case 12: return 5;
+ case 11: return 5;
+ case 13: return 4;
+ case 14: return 3;
+
+ default: return -1;
+ }
+}
+
+inline gint int_to_face (guint id) {
+ switch (id) {
+ case 0: return Box3D::YZ ^ Box3D::FRONT;
+ case 1: return Box3D::XZ ^ Box3D::FRONT;
+ case 2: return Box3D::XY ^ Box3D::FRONT;
+ case 3: return Box3D::YZ ^ Box3D::REAR;
+ case 4: return Box3D::XZ ^ Box3D::REAR;
+ case 5: return Box3D::XY ^ Box3D::REAR;
+ }
+ return Box3D::NONE; // should not be reached
+}
+
+inline bool is_face_id (guint face_id) {
+ return !((face_id & 0x7) == 0x7);
+}
+
+/**
inline gint opposite_face (guint face_id) {
- return face_id + ((face_id % 2 == 0) ? 1 : -1);
+ return face_id + (((face_id % 2) == 0) ? 1 : -1);
}
+**/
inline guint number_of_axis_directions (Box3D::Axis axis) {
guint num = 0;
return (!(dir & (dir - 1)) && dir);
}
+/***
// Warning: We don't check that axis really unambiguously specifies a plane.
// Make sure this is the case when calling this function.
inline gint face_containing_corner (Box3D::Axis axis, guint corner) {
}
return face_to_int (axis ^ ((corner & axis) ? Box3D::REAR : Box3D::FRONT));
}
-
+***/
/**
* Given two axis directions out of {X, Y, Z} or the corresponding plane, return the remaining one
diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp
index 7ceed0acab2eb8a35fe9731a699469e7622aa833..d74b0e7d1c8c88d29d52f89eaccc6427c03a6f27 100644 (file)
--- a/src/box3d-context.cpp
+++ b/src/box3d-context.cpp
-#define __SP_3DBOX_CONTEXT_C__
+#define __SP_BOX3D_CONTEXT_C__
/*
* 3D box drawing context
#include "xml/node-event-vector.h"
#include "prefs-utils.h"
#include "context-fns.h"
+#include "inkscape.h"
+#include "desktop-style.h"
+#include "transf_mat_3x4.h"
+#include "perspective-line.h"
+#include "persp3d.h"
+#include "box3d-side.h"
+#include "document-private.h" // for debugging (see case GDK_P)
+#include "line-geometry.h"
-static void sp_3dbox_context_class_init(SP3DBoxContextClass *klass);
-static void sp_3dbox_context_init(SP3DBoxContext *box3d_context);
-static void sp_3dbox_context_dispose(GObject *object);
+static void sp_box3d_context_class_init(Box3DContextClass *klass);
+static void sp_box3d_context_init(Box3DContext *box3d_context);
+static void sp_box3d_context_dispose(GObject *object);
-static void sp_3dbox_context_setup(SPEventContext *ec);
-static void sp_3dbox_context_set(SPEventContext *ec, gchar const *key, gchar const *val);
+static void sp_box3d_context_setup(SPEventContext *ec);
-static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEvent *event);
-static gint sp_3dbox_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
+static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEvent *event);
+static gint sp_box3d_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
-static void sp_3dbox_drag(SP3DBoxContext &bc, guint state);
-static void sp_3dbox_finish(SP3DBoxContext *bc);
+static void sp_box3d_drag(Box3DContext &bc, guint state);
+static void sp_box3d_finish(Box3DContext *bc);
static SPEventContextClass *parent_class;
-
-GtkType sp_3dbox_context_get_type()
+GtkType sp_box3d_context_get_type()
{
static GType type = 0;
if (!type) {
GTypeInfo info = {
- sizeof(SP3DBoxContextClass),
+ sizeof(Box3DContextClass),
NULL, NULL,
- (GClassInitFunc) sp_3dbox_context_class_init,
+ (GClassInitFunc) sp_box3d_context_class_init,
NULL, NULL,
- sizeof(SP3DBoxContext),
+ sizeof(Box3DContext),
4,
- (GInstanceInitFunc) sp_3dbox_context_init,
+ (GInstanceInitFunc) sp_box3d_context_init,
NULL, /* value_table */
};
- type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SP3DBoxContext", &info, (GTypeFlags) 0);
+ type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "Box3DContext", &info, (GTypeFlags) 0);
}
return type;
}
-static void sp_3dbox_context_class_init(SP3DBoxContextClass *klass)
+static void sp_box3d_context_class_init(Box3DContextClass *klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
parent_class = (SPEventContextClass *) g_type_class_peek_parent(klass);
- object_class->dispose = sp_3dbox_context_dispose;
+ object_class->dispose = sp_box3d_context_dispose;
- event_context_class->setup = sp_3dbox_context_setup;
- event_context_class->set = sp_3dbox_context_set;
- event_context_class->root_handler = sp_3dbox_context_root_handler;
- event_context_class->item_handler = sp_3dbox_context_item_handler;
+ event_context_class->setup = sp_box3d_context_setup;
+ event_context_class->root_handler = sp_box3d_context_root_handler;
+ event_context_class->item_handler = sp_box3d_context_item_handler;
}
-static void sp_3dbox_context_init(SP3DBoxContext *box3d_context)
+static void sp_box3d_context_init(Box3DContext *box3d_context)
{
SPEventContext *event_context = SP_EVENT_CONTEXT(box3d_context);
new (&box3d_context->sel_changed_connection) sigc::connection();
}
-static void sp_3dbox_context_dispose(GObject *object)
+static void sp_box3d_context_dispose(GObject *object)
{
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT(object);
+ Box3DContext *bc = SP_BOX3D_CONTEXT(object);
SPEventContext *ec = SP_EVENT_CONTEXT(object);
ec->enableGrDrag(false);
/* fixme: This is necessary because we do not grab */
if (bc->item) {
- sp_3dbox_finish(bc);
+ sp_box3d_finish(bc);
}
if (ec->shape_knot_holder) {
\brief Callback that processes the "changed" signal on the selection;
destroys old and creates new knotholder
*/
-void sp_3dbox_context_selection_changed(Inkscape::Selection *selection, gpointer data)
+static void sp_box3d_context_selection_changed(Inkscape::Selection *selection, gpointer data)
{
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT(data);
+ Box3DContext *bc = SP_BOX3D_CONTEXT(data);
SPEventContext *ec = SP_EVENT_CONTEXT(bc);
if (ec->shape_knot_holder) { // destroy knotholder
@@ -180,6 +185,10 @@ void sp_3dbox_context_selection_changed(Inkscape::Selection *selection, gpointer
ec->shape_repr = 0;
}
+ SPDocument *doc = sp_desktop_document(bc->desktop);
+ doc->persps_sel.clear();
+ doc->persps_sel = persp3d_currently_selected(bc);
+
SPItem *item = selection->singleItem();
if (item) {
ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop);
@@ -189,28 +198,17 @@ void sp_3dbox_context_selection_changed(Inkscape::Selection *selection, gpointer
Inkscape::GC::anchor(shape_repr);
sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec);
}
- if (SP_IS_3DBOX (item)) {
- bc->_vpdrag->document->current_perspective = bc->_vpdrag->document->get_persp_of_box (SP_3DBOX (item));
- }
- } else {
- /* If several boxes sharing the same perspective are selected,
- we can still set the current selection accordingly */
- std::set<Box3D::Perspective3D *> perspectives;
- for (GSList *i = (GSList *) selection->itemList(); i != NULL; i = i->next) {
- if (SP_IS_3DBOX (i->data)) {
- perspectives.insert (bc->_vpdrag->document->get_persp_of_box (SP_3DBOX (i->data)));
- }
- }
- if (perspectives.size() == 1) {
- bc->_vpdrag->document->current_perspective = *(perspectives.begin());
- }
- // TODO: What to do if several boxes with different perspectives are selected?
+ }
+
+ if (doc->persps_sel.size() == 1) {
+ // selecting a single box changes the current perspective
+ doc->current_persp3d = *(doc->persps_sel.begin());
}
}
-static void sp_3dbox_context_setup(SPEventContext *ec)
+static void sp_box3d_context_setup(SPEventContext *ec)
{
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT(ec);
+ Box3DContext *bc = SP_BOX3D_CONTEXT(ec);
if (((SPEventContextClass *) parent_class)->setup) {
((SPEventContextClass *) parent_class)->setup(ec);
bc->sel_changed_connection.disconnect();
bc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(
- sigc::bind(sigc::ptr_fun(&sp_3dbox_context_selection_changed), (gpointer)bc)
+ sigc::bind(sigc::ptr_fun(&sp_box3d_context_selection_changed), (gpointer)bc)
);
bc->_vpdrag = new Box3D::VPDrag(sp_desktop_document (ec->desktop));
bc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
}
-static void sp_3dbox_context_set(SPEventContext */*ec*/, gchar const */*key*/, gchar const */*val*/)
-{
- //SP3DBoxContext *bc = SP_3DBOX_CONTEXT(ec);
-
- /* fixme: Proper error handling for non-numeric data. Use a locale-independent function like
- * g_ascii_strtod (or a thin wrapper that does the right thing for invalid values inf/nan). */
- /**
- if ( strcmp(key, "rx") == 0 ) {
- bc->rx = ( val
- ? g_ascii_strtod (val, NULL)
- : 0.0 );
- } else if ( strcmp(key, "ry") == 0 ) {
- bc->ry = ( val
- ? g_ascii_strtod (val, NULL)
- : 0.0 );
- }
- **/
-}
-
-static gint sp_3dbox_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
+static gint sp_box3d_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
{
SPDesktop *desktop = event_context->desktop;
return ret;
}
-static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEvent *event)
+static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEvent *event)
{
static bool dragging;
@@ -297,7 +276,9 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
Inkscape::Selection *selection = sp_desktop_selection (desktop);
int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT(event_context);
+ Box3DContext *bc = SP_BOX3D_CONTEXT(event_context);
+ g_assert (SP_ACTIVE_DOCUMENT->current_persp3d);
+ Persp3D *cur_persp = SP_ACTIVE_DOCUMENT->current_persp3d;
event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
@@ -313,17 +294,24 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
event_context->yp = (gint) button_w[NR::Y];
event_context->within_tolerance = true;
- // remember clicked item, disregarding groups, honoring Alt
+ // remember clicked item, *not* disregarding groups (since a 3D box is a group), honoring Alt
event_context->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, event->button.state & GDK_CONTROL_MASK);
dragging = true;
- /* Position center */
+ /* */
NR::Point const button_dt(desktop->w2d(button_w));
bc->drag_origin = button_dt;
bc->drag_ptB = button_dt;
bc->drag_ptC = button_dt;
+ /* Projective preimages of clicked point under current perspective */
+ bc->drag_origin_proj = cur_persp->tmat.preimage (button_dt, 0, Proj::Z);
+ bc->drag_ptB_proj = bc->drag_origin_proj;
+ bc->drag_ptC_proj = bc->drag_origin_proj;
+ bc->drag_ptC_proj.normalize();
+ bc->drag_ptC_proj[Proj::Z] = 0.25;
+
/* Snap center */
SnapManager const &m = desktop->namedview->snap_manager;
bc->center = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE,
@@ -362,39 +350,52 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
bc->ctrl_dragged = event->motion.state & GDK_CONTROL_MASK;
if (event->motion.state & GDK_SHIFT_MASK && !bc->extruded && bc->item) {
- /* once shift is pressed, set bc->extruded (no need to create further faces;
- all of them are already created in sp_3dbox_init); since we made the rear face
- invisible in the beginning to avoid "flashing", we must set its correct style now */
+ // once shift is pressed, set bc->extruded
bc->extruded = true;
- SP_3DBOX (bc->item)->faces[5]->set_style (NULL, true);
}
if (!bc->extruded) {
bc->drag_ptB = motion_dt;
bc->drag_ptC = motion_dt;
+
+ bc->drag_ptB_proj = cur_persp->tmat.preimage (motion_dt, 0, Proj::Z);
+ bc->drag_ptC_proj = bc->drag_ptB_proj;
+ bc->drag_ptC_proj.normalize();
+ bc->drag_ptC_proj[Proj::Z] = 0.25;
} else {
// Without Ctrl, motion of the extruded corner is constrained to the
// perspective line from drag_ptB to vanishing point Y.
if (!bc->ctrl_dragged) {
- bc->drag_ptC = Box3D::perspective_line_snap (bc->drag_ptB, Box3D::Z, motion_dt, bc->_vpdrag->document->current_perspective);
+ /* snapping */
+ Box3D::PerspectiveLine pline (bc->drag_ptB, Proj::Z, SP_ACTIVE_DOCUMENT->current_persp3d);
+ bc->drag_ptC = pline.closest_to (motion_dt);
+
+ bc->drag_ptB_proj.normalize();
+ bc->drag_ptC_proj = cur_persp->tmat.preimage (bc->drag_ptC, bc->drag_ptB_proj[Proj::X], Proj::X);
} else {
bc->drag_ptC = motion_dt;
+
+ bc->drag_ptB_proj.normalize();
+ bc->drag_ptC_proj = cur_persp->tmat.preimage (motion_dt, bc->drag_ptB_proj[Proj::X], Proj::X);
}
bc->drag_ptC = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, bc->drag_ptC, bc->item).getPoint();
if (bc->ctrl_dragged) {
- Box3D::PerspectiveLine pl1 (NR::Point (event_context->xp, event_context->yp), Box3D::Y, bc->_vpdrag->document->current_perspective);
- Box3D::PerspectiveLine pl2 (bc->drag_ptB, Box3D::X, bc->_vpdrag->document->current_perspective);
- NR::Point corner1 = pl1.meet(pl2);
+ g_print ("TODO: What should happen here?\n");
+ // Update bc->drag_ptB in case we are ctrl-dragging
+ /***
+ Box3D::PerspectiveLine pl1 (NR::Point (event_context->xp, event_context->yp), Box3D::Y, bc->_vpdrag->document->current_perspective);
+ Box3D::PerspectiveLine pl2 (bc->drag_ptB, Box3D::X, bc->_vpdrag->document->current_perspective);
+ NR::Point corner1 = pl1.meet(pl2);
- Box3D::PerspectiveLine pl3 (corner1, Box3D::X, bc->_vpdrag->document->current_perspective);
- Box3D::PerspectiveLine pl4 (bc->drag_ptC, Box3D::Z, bc->_vpdrag->document->current_perspective);
- bc->drag_ptB = pl3.meet(pl4);
+ Box3D::PerspectiveLine pl3 (corner1, Box3D::X, bc->_vpdrag->document->current_perspective);
+ Box3D::PerspectiveLine pl4 (bc->drag_ptC, Box3D::Z, bc->_vpdrag->document->current_perspective);
+ bc->drag_ptB = pl3.meet(pl4);
+ ***/
}
}
-
-
- sp_3dbox_drag(*bc, event->motion.state);
-
+
+ sp_box3d_drag(*bc, event->motion.state);
+
ret = TRUE;
}
break;
@@ -405,7 +406,7 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
if (!event_context->within_tolerance) {
// we've been dragging, finish the box
- sp_3dbox_finish(bc);
+ sp_box3d_finish(bc);
} else if (event_context->item_to_select) {
// no dragging, select clicked item if any
if (event->button.state & GDK_SHIFT_MASK) {
@@ -453,78 +454,100 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
break;
case GDK_bracketright:
- inkscape_active_document()->current_perspective->rotate (Box3D::X, -180/snaps, MOD__ALT);
+ persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::X, -180/snaps, MOD__ALT);
ret = true;
break;
case GDK_bracketleft:
- inkscape_active_document()->current_perspective->rotate (Box3D::X, 180/snaps, MOD__ALT);
+ persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::X, 180/snaps, MOD__ALT);
ret = true;
break;
case GDK_parenright:
- inkscape_active_document()->current_perspective->rotate (Box3D::Y, -180/snaps, MOD__ALT);
+ persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Y, -180/snaps, MOD__ALT);
ret = true;
break;
case GDK_parenleft:
- inkscape_active_document()->current_perspective->rotate (Box3D::Y, 180/snaps, MOD__ALT);
+ persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Y, 180/snaps, MOD__ALT);
ret = true;
break;
case GDK_braceright:
- inkscape_active_document()->current_perspective->rotate (Box3D::Z, -180/snaps, MOD__ALT);
+ persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Z, -180/snaps, MOD__ALT);
ret = true;
break;
case GDK_braceleft:
- inkscape_active_document()->current_perspective->rotate (Box3D::Z, 180/snaps, MOD__ALT);
+ persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Z, 180/snaps, MOD__ALT);
ret = true;
break;
- case GDK_I:
- Box3D::Perspective3D::print_debugging_info();
+ case GDK_O:
+ Box3D::create_canvas_point(persp3d_get_VP(inkscape_active_document()->current_persp3d, Proj::W).affine(),
+ 6, 0xff00ff00);
ret = true;
break;
- case GDK_L:
- if (MOD__CTRL) break; // Don't catch Shift+Ctrl+L (Layers dialog)
- bc->_vpdrag->show_lines = !bc->_vpdrag->show_lines;
- bc->_vpdrag->updateLines();
+ case GDK_I:
+ if (MOD__ALT) {
+ persp3d_print_debugging_info_all (inkscape_active_document());
+ } else {
+ persp3d_print_debugging_info (inkscape_active_document()->current_persp3d);
+ }
ret = true;
break;
- case GDK_A:
- if (MOD__CTRL) break; // Don't catch Ctrl+A ("select all")
- if (bc->_vpdrag->show_lines) {
- bc->_vpdrag->front_or_rear_lines = bc->_vpdrag->front_or_rear_lines ^ 0x2; // toggle rear PLs
+ case GDK_P:
+ {
+ if (MOD__SHIFT && MOD__CTRL) break; // Don't catch Shift+Ctrl+P (Preferences dialog)
+ SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(SP_ACTIVE_DOCUMENT);
+ g_print ("=== Persp3D Objects: ==============================\n");
+ for (SPObject *i = sp_object_first_child(SP_OBJECT(defs)); i != NULL; i = SP_OBJECT_NEXT(i) ) {
+ g_print ("Object encountered\n");
+ if (SP_IS_PERSP3D(i)) {
+ //g_print ("Encountered a Persp3D in defs\n");
+ SP_PERSP3D(i)->tmat.print();
+ g_print ("\n");
+ g_print ("Computing preimage of point (300, 400)\n");
+ SP_PERSP3D(i)->tmat.preimage (NR::Point (300, 400), 0, Proj::Z);
+ g_print ("Computing preimage of point (200, 500)\n");
+ SP_PERSP3D(i)->tmat.preimage (NR::Point (200, 500), 0, Proj::Z);
+ }
}
- bc->_vpdrag->updateLines();
+ g_print ("===================================================\n");
+
ret = true;
break;
+ }
+
+ case GDK_V:
+ if (bc->_vpdrag) {
+ bc->_vpdrag->printDraggers();
+ ret = true;
+ } else {
+ g_print ("No VPDrag in Box3DContext.\n");
+ }
+ break;
+ case GDK_x:
+ if (MOD__ALT_ONLY) {
+ desktop->setToolboxFocusTo ("altx-box3d");
+ ret = TRUE;
+ }
+ break;
case GDK_X:
- {
if (MOD__CTRL) break; // Don't catch Ctrl+X ('cut') and Ctrl+Shift+X ('open XML editor')
- Inkscape::Selection *selection = sp_desktop_selection (inkscape_active_desktop());
- for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
- if (!SP_IS_3DBOX (i->data)) continue;
- sp_3dbox_switch_front_face (SP_3DBOX (i->data), Box3D::X);
- }
- bc->_vpdrag->updateLines();
+ persp3d_toggle_VPs(persp3d_currently_selected(bc), Proj::X);
+ bc->_vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically?
ret = true;
break;
- }
-
+
case GDK_Y:
{
if (MOD__CTRL) break; // Don't catch Ctrl+Y ("redo")
- Inkscape::Selection *selection = sp_desktop_selection (inkscape_active_desktop());
- for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
- if (!SP_IS_3DBOX (i->data)) continue;
- sp_3dbox_switch_front_face (SP_3DBOX (i->data), Box3D::Y);
- }
- bc->_vpdrag->updateLines();
+ persp3d_toggle_VPs(persp3d_currently_selected(bc), Proj::Y);
+ bc->_vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically?
ret = true;
break;
}
@@ -532,12 +555,8 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
case GDK_Z:
{
if (MOD__CTRL) break; // Don't catch Ctrl+Z ("undo")
- Inkscape::Selection *selection = sp_desktop_selection (inkscape_active_desktop());
- for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
- if (!SP_IS_3DBOX (i->data)) continue;
- sp_3dbox_switch_front_face (SP_3DBOX (i->data), Box3D::Z);
- }
- bc->_vpdrag->updateLines();
+ persp3d_toggle_VPs(persp3d_currently_selected(bc), Proj::Z);
+ bc->_vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically?
ret = true;
break;
}
@@ -554,7 +573,7 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
dragging = false;
if (!event_context->within_tolerance) {
// we've been dragging, finish the box
- sp_3dbox_finish(bc);
+ sp_box3d_finish(bc);
}
// do not return true, so that space would work switching to selector
}
@@ -593,7 +612,7 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
return ret;
}
-static void sp_3dbox_drag(SP3DBoxContext &bc, guint state)
+static void sp_box3d_drag(Box3DContext &bc, guint state)
{
SPDesktop *desktop = SP_EVENT_CONTEXT(&bc)->desktop;
/* Create object */
Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(&bc));
Inkscape::XML::Node *repr = xml_doc->createElement("svg:g");
- repr->setAttribute("sodipodi:type", "inkscape:3dbox");
+ repr->setAttribute("sodipodi:type", "inkscape:box3d");
/* Set style */
sp_desktop_apply_style_tool (desktop, repr, "tools.shapes.3dbox", false);
bc.item = (SPItem *) desktop->currentLayer()->appendChildRepr(repr);
Inkscape::GC::release(repr);
- bc.item->transform = SP_ITEM(desktop->currentRoot())->getRelativeTransform(desktop->currentLayer());
-
- /* Hook paths to the faces of the box (applies last used style if necessary) */
+ /**** bc.item->transform = SP_ITEM(desktop->currentRoot())->getRelativeTransform(desktop->currentLayer()); ****/
+ Inkscape::XML::Node *repr_side;
for (int i = 0; i < 6; ++i) {
- SP_3DBOX(bc.item)->faces[i]->hook_path_to_3dbox();
+ repr_side = xml_doc->createElement("svg:path");
+ repr_side->setAttribute("sodipodi:type", "inkscape:box3dside");
+ repr->addChild(repr_side, NULL);
+
+ Box3DSide *side = SP_BOX3D_SIDE(inkscape_active_document()->getObjectByRepr (repr_side));
+
+ guint desc = Box3D::int_to_face(i);
+
+ Box3D::Axis plane = (Box3D::Axis) (desc & 0x7);
+ plane = (Box3D::is_plane(plane) ? plane : Box3D::orth_plane_or_axis(plane));
+ side->dir1 = Box3D::extract_first_axis_direction(plane);
+ side->dir2 = Box3D::extract_second_axis_direction(plane);
+ side->front_or_rear = (Box3D::FrontOrRear) (desc & 0x8);
+
+ SP_OBJECT(side)->updateRepr(); // calls box3d_side_write() and updates, e.g., the axes string description
}
- // make rear face invisible in the beginning to avoid "flashing"
- SP_3DBOX (bc.item)->faces[5]->set_style (NULL, false);
+ box3d_set_z_orders(SP_BOX3D(bc.item));
bc.item->updateRepr();
- sp_3dbox_set_z_orders_in_the_first_place (SP_3DBOX (bc.item));
// TODO: It would be nice to show the VPs during dragging, but since there is no selection
// at this point (only after finishing the box), we must do this "manually"
- bc._vpdrag->updateDraggers();
+ /**** bc._vpdrag->updateDraggers(); ****/
sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
}
- // FIXME: remove these extra points
- NR::Point pt = bc.drag_ptB;
- NR::Point shift_pt = bc.drag_ptC;
+ g_assert(bc.item);
- NR::Rect r;
- if (!(state & GDK_SHIFT_MASK)) {
- r = Inkscape::snap_rectangular_box(desktop, bc.item, pt, bc.center, state);
- } else {
- r = Inkscape::snap_rectangular_box(desktop, bc.item, shift_pt, bc.center, state);
- }
+ SPBox3D *box = SP_BOX3D(bc.item);
+
+ box->orig_corner0 = bc.drag_origin_proj;
+ box->orig_corner7 = bc.drag_ptC_proj;
- SPEventContext *ec = SP_EVENT_CONTEXT(&bc);
- NR::Point origin_w(ec->xp, ec->yp);
- NR::Point origin(desktop->w2d(origin_w));
- sp_3dbox_position_set(bc);
- sp_3dbox_set_z_orders_in_the_first_place (SP_3DBOX (bc.item));
+ /* we need to call this from here (instead of from box3d_position_set(), for example)
+ because z-order setting must not interfere with display updates during undo/redo */
+ box3d_set_z_orders (box);
+
+ box3d_position_set(SP_BOX3D(bc.item));
// status text
//GString *Ax = SP_PX_TO_METRIC_STRING(origin[NR::X], desktop->namedview->getDefaultMetric());
//g_string_free(Ay, FALSE);
}
-static void sp_3dbox_finish(SP3DBoxContext *bc)
+static void sp_box3d_finish(Box3DContext *bc)
{
bc->_message_context->clear();
+ g_assert (SP_ACTIVE_DOCUMENT->current_persp3d);
+ //Persp3D *cur_persp = SP_ACTIVE_DOCUMENT->current_persp3d;
if ( bc->item != NULL ) {
- SPDesktop * desktop;
+ SPDesktop * desktop = SP_EVENT_CONTEXT_DESKTOP(bc);
+
+ SPBox3D *box = SP_BOX3D(bc->item);
+
+ box->orig_corner0 = bc->drag_origin_proj;
+ box->orig_corner7 = bc->drag_ptC_proj;
- desktop = SP_EVENT_CONTEXT_DESKTOP(bc);
+ box->updateRepr();
- SP_OBJECT(bc->item)->updateRepr();
- sp_3dbox_set_ratios(SP_3DBOX(bc->item));
+ box3d_relabel_corners(box);
sp_canvas_end_forced_full_redraws(desktop->canvas);
diff --git a/src/box3d-context.h b/src/box3d-context.h
index 33176ae84565316464af1292e75803e24375acca..1817aa180ff251256e1b0d584aa1f4813c2fc303 100644 (file)
--- a/src/box3d-context.h
+++ b/src/box3d-context.h
-#ifndef __SP_3DBOX_CONTEXT_H__
-#define __SP_3DBOX_CONTEXT_H__
+#ifndef __SP_BOX3D_CONTEXT_H__
+#define __SP_BOX3D_CONTEXT_H__
/*
* 3D box drawing context
#include <sigc++/sigc++.h>
#include "event-context.h"
-#include "perspective3d.h"
+#include "proj_pt.h"
+#include "vanishing-point.h"
struct SPKnotHolder;
-#define SP_TYPE_3DBOX_CONTEXT (sp_3dbox_context_get_type ())
-#define SP_3DBOX_CONTEXT(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_3DBOX_CONTEXT, SP3DBoxContext))
-#define SP_3DBOX_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_3DBOX_CONTEXT, SP3DBoxContextClass))
-#define SP_IS_3DBOX_CONTEXT(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_3DBOX_CONTEXT))
-#define SP_IS_3DBOX_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_3DBOX_CONTEXT))
+#define SP_TYPE_BOX3D_CONTEXT (sp_box3d_context_get_type ())
+#define SP_BOX3D_CONTEXT(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_BOX3D_CONTEXT, Box3DContext))
+#define SP_BOX3D_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_BOX3D_CONTEXT, Box3DContextClass))
+#define SP_IS_BOX3D_CONTEXT(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_BOX3D_CONTEXT))
+#define SP_IS_BOX3D_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_BOX3D_CONTEXT))
-class SP3DBoxContext;
-class SP3DBoxContextClass;
+class Box3DContext;
+class Box3DContextClass;
-struct SP3DBoxContext : public SPEventContext {
- SPItem *item;
- NR::Point center;
+struct Box3DContext : public SPEventContext {
+ SPItem *item;
+ NR::Point center;
/**
* save three corners while dragging:
NR::Point drag_origin;
NR::Point drag_ptB;
NR::Point drag_ptC;
+
+ Proj::Pt3 drag_origin_proj;
+ Proj::Pt3 drag_ptB_proj;
+ Proj::Pt3 drag_ptC_proj;
+
bool ctrl_dragged; /* whether we are ctrl-dragging */
bool extruded; /* whether shift-dragging already occured (i.e. the box is already extruded) */
Box3D::VPDrag * _vpdrag;
- sigc::connection sel_changed_connection;
+ sigc::connection sel_changed_connection;
- Inkscape::MessageContext *_message_context;
+ Inkscape::MessageContext *_message_context;
};
-struct SP3DBoxContextClass {
- SPEventContextClass parent_class;
+struct Box3DContextClass {
+ SPEventContextClass parent_class;
};
/* Standard Gtk function */
-GtkType sp_3dbox_context_get_type (void);
+GtkType sp_box3d_context_get_type (void);
-#endif
+#endif /* __SP_BOX3D_CONTEXT_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/box3d-face.cpp b/src/box3d-face.cpp
--- a/src/box3d-face.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-#define __SP_3DBOX_FACE_C__
-
-/*
- * Face of a 3D box ('perspectivic rectangle')
- *
- * Authors:
- * Maximilian Albert <Anhalter42@gmx.de>
- *
- * Copyright (C) 2007 authors
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "svg/svg.h"
-#include "box3d-face.h"
-#include "prefs-utils.h"
-
-// FIXME: It's quite redundant to pass the box plus the corners plus the axes. At least the corners can
-// theoretically be reconstructed from the box and the axes, but in order to do this we need
-// access to box->corners, which is not possible if we only have a forward declaration of SP3DBox
-// in box3d-face.h. (But we can't include box3d.h itself because the latter already includes
-// box3d-face.h).
-Box3DFace::Box3DFace(SP3DBox *box, NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D,
- Box3D::Axis plane, Box3D::FrontOrRear rel_pos)
- : front_or_rear (rel_pos), path (NULL), parent_box3d (box)
- {
- dir1 = extract_first_axis_direction (plane);
- dir2 = extract_second_axis_direction (plane);
- /*
- Box3D::Axis axis = (rel_pos == Box3D::FRONT ? Box3D::NONE : Box3D::third_axis_direction (plane));
- set_corners (box->corners[axis],
- box->corners[axis ^ dir1],
- box->corners[axis ^ dir1 ^ dir2],
- box->corners[axis ^ dir2]);
- */
- set_corners (A, B, C, D);
-}
-
-Box3DFace::~Box3DFace()
-{
- for (int i = 0; i < 4; ++i) {
- if (this->corners[i]) {
- //delete this->corners[i];
- this->corners[i] = NULL;
- }
- }
-}
-
-void Box3DFace::set_corners(NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D)
-{
- corners[0] = &A;
- corners[1] = &B;
- corners[2] = &C;
- corners[3] = &D;
-}
-
-/***
-void Box3DFace::set_shape(NR::Point const ul, NR::Point const lr,
- Box3D::Axis const dir1, Box3D::Axis const dir2,
- unsigned int shift_count, NR::Maybe<NR::Point> pt_align, bool align_along_PL)
-{
- corners[0] = ul;
- if (!pt_align) {
- corners[2] = lr;
- } else {
- if (align_along_PL) {
- Box3D::Axis dir3 = Box3D::third_axis_direction (dir1, dir2);
- Box3D::Line line1(*Box3D::Perspective3D::current_perspective->get_vanishing_point(dir1), lr);
- Box3D::Line line2(*pt_align, *Box3D::Perspective3D::current_perspective->get_vanishing_point(dir3));
- corners[2] = *line1.intersect(line2);
- } else {
- corners[2] = Box3D::Line(*pt_align, *Box3D::Perspective3D::current_perspective->get_vanishing_point(dir1)).closest_to(lr);
- }
- }
-
- Box3D::PerspectiveLine first_line (corners[0], dir1);
- Box3D::PerspectiveLine second_line (corners[2], dir2);
- NR::Maybe<NR::Point> ur = first_line.intersect(second_line);
-
- Box3D::PerspectiveLine third_line (corners[0], dir2);
- Box3D::PerspectiveLine fourth_line (corners[2], dir1);
- NR::Maybe<NR::Point> ll = third_line.intersect(fourth_line);
-
- // FIXME: How to handle the case if one of the intersections doesn't exist?
- // Maybe set them equal to the corresponding VPs?
- if (!ur) ur = NR::Point(0.0, 0.0);
- if (!ll) ll = NR::Point(0.0, 0.0);
-
- corners[1] = *ll;
- corners[3] = *ur;
-
- this->dir1 = dir1;
- this->dir2 = dir2;
-
- // FIXME: Can be made more concise
- NR::Point tmp_pt;
- for (unsigned int i=0; i < shift_count; i++) {
- tmp_pt = corners[3];
- corners[1] = corners[0];
- corners[2] = corners[1];
- corners[3] = corners[2];
- corners[0] = tmp_pt;
- }
-}
-***/
-
-Box3DFace::Box3DFace(Box3DFace const &box3dface)
-{
- for (int i = 0; i < 4; ++i) {
- this->corners[i] = box3dface.corners[i];
- }
- this->dir1 = box3dface.dir1;
- this->dir2 = box3dface.dir2;
-}
-
-/**
- * Construct a 3D box face with opposite corners A and C whose sides are directed
- * along axis1 and axis2. The corners have the following order:
- *
- * A = corners[0] --> along axis1 --> B = corners[1] --> along axis2 --> C = corners[2]
- * --> along axis1 --> D = corners[3] --> along axis2 --> D = corners[0].
- *
- * Note that several other functions rely on this precise order.
- */
-/***
-void
-Box3DFace::set_face (NR::Point const A, NR::Point const C, Box3D::Axis const axis1, Box3D::Axis const axis2)
-{
- *corners[0] = A;
- *corners[2] = C;
- if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
- return;
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
-
- Box3D::PerspectiveLine line1 (A, axis1, Box3D::Perspective3D::current_perspective);
- Box3D::PerspectiveLine line2 (C, axis2, Box3D::Perspective3D::current_perspective);
- NR::Maybe<NR::Point> B = line1.intersect(line2);
-
- Box3D::PerspectiveLine line3 (*corners[0], axis2, Box3D::Perspective3D::current_perspective);
- Box3D::PerspectiveLine line4 (*corners[2], axis1, Box3D::Perspective3D::current_perspective);
- NR::Maybe<NR::Point> D = line3.intersect(line4);
-
- // FIXME: How to handle the case if one of the intersections doesn't exist?
- // Maybe set them equal to the corresponding VPs?
- if (!D) D = NR::Point(0.0, 0.0);
- if (!B) B = NR::Point(0.0, 0.0);
-
- *corners[1] = *B;
- *corners[3] = *D;
-
- this->dir1 = axis1;
- this->dir2 = axis2;
-}
-***/
-
-NR::Point Box3DFace::operator[](unsigned int i)
-{
- return *corners[i % 4];
-}
-
-
-
-/**
- * Append the curve's path as a child to the given 3D box (since SP3DBox
- * is derived from SPGroup, so we can append children to its svg representation)
- */
-void Box3DFace::hook_path_to_3dbox(SPPath * existing_path)
-{
- if (this->path) {
- //g_print ("Path already exists. Returning ...\n");
- return;
- }
-
- if (existing_path != NULL) {
- // no need to create a new path
- this->path = existing_path;
- return;
- }
-
- /* create new path for face */
- Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(SP_OBJECT(parent_box3d)));
-
- Inkscape::XML::Node *repr_face = xml_doc->createElement("svg:path");
- repr_face->setAttribute("inkscape:box3dface", this->axes_string());
- this->path = SP_PATH(SP_OBJECT(parent_box3d)->appendChildRepr(repr_face));
- Inkscape::GC::release(repr_face);
-
- /* set the correct style */
- this->set_style (repr_face);
-}
-
-void Box3DFace::set_style(Inkscape::XML::Node *repr_face, bool extruded)
-{
- if (repr_face == NULL) {
- repr_face = SP_OBJECT_REPR (this->path);
- }
-
- if (!extruded && !strcmp (axes_string (), "XYrear")) {
- // to avoid "flashing" during the initial dragging process, we make the rear face invisible in this case
- repr_face->setAttribute("style", "fill:none");
- return;
- }
-
- gchar *descr = g_strconcat ("desktop.", axes_string (), NULL);
- const gchar * cur_style = prefs_get_string_attribute(descr, "style");
- g_free (descr);
-
- SPDesktop *desktop = inkscape_active_desktop();
- bool use_current = prefs_get_int_attribute("tools.shapes.3dbox", "usecurrent", 0);
- if (use_current && cur_style !=NULL) {
- /* use last used style */
- repr_face->setAttribute("style", cur_style);
- } else {
- /* use default style */
- GString *pstring = g_string_new("");
- g_string_printf (pstring, "tools.shapes.3dbox.%s", axes_string());
- sp_desktop_apply_style_tool (desktop, repr_face, pstring->str, false);
- }
-}
-
-/**
- * Write the path's "d" attribute to the SVG representation.
- */
-void Box3DFace::set_path_repr()
-{
- NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (this->parent_box3d)));
- SPCurve * curve = sp_curve_new();
- sp_curve_moveto (curve, ((*corners[0]) * i2d)[NR::X], ((*corners[0]) * i2d)[NR::Y]);
- sp_curve_lineto (curve, ((*corners[1]) * i2d)[NR::X], ((*corners[1]) * i2d)[NR::Y]);
- sp_curve_lineto (curve, ((*corners[2]) * i2d)[NR::X], ((*corners[2]) * i2d)[NR::Y]);
- sp_curve_lineto (curve, ((*corners[3]) * i2d)[NR::X], ((*corners[3]) * i2d)[NR::Y]);
- sp_curve_closepath (curve);
- SP_OBJECT(this->path)->repr->setAttribute("d", sp_svg_write_path (SP_CURVE_BPATH(curve)));
-}
-
-void Box3DFace::set_curve()
-{
- if (this->path == NULL) {
- return;
- }
- NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (this->parent_box3d)));
- SPCurve *curve = sp_curve_new();
- sp_curve_moveto(curve, (*corners[0]) * i2d);
- sp_curve_lineto(curve, (*corners[1]) * i2d);
- sp_curve_lineto(curve, (*corners[2]) * i2d);
- sp_curve_lineto(curve, (*corners[3]) * i2d);
- sp_curve_closepath(curve);
- sp_shape_set_curve(SP_SHAPE(this->path), curve, true);
- sp_curve_unref(curve);
-}
-
-gchar * Box3DFace::axes_string()
-{
- GString *pstring = g_string_new("");
- g_string_printf (pstring, "%s", Box3D::string_from_axes ((Box3D::Axis) (dir1 ^ dir2)));
- switch ((Box3D::Axis) (dir1 ^ dir2)) {
- case Box3D::XY:
- g_string_append_printf (pstring, (front_or_rear == Box3D::FRONT) ? "front" : "rear");
- break;
- case Box3D::XZ:
- g_string_append_printf (pstring, (front_or_rear == Box3D::FRONT) ? "top" : "bottom");
- break;
- case Box3D::YZ:
- g_string_append_printf (pstring, (front_or_rear == Box3D::FRONT) ? "right" : "left");
- break;
- default:
- break;
- }
- return pstring->str;
-}
-
-gint Box3DFace::descr_to_id (gchar const *descr)
-{
- if (!strcmp (descr, "XYrear")) { return 5; }
- if (!strcmp (descr, "XYfront")) { return 4; }
- if (!strcmp (descr, "XZbottom")) { return 3; }
- if (!strcmp (descr, "XZtop")) { return 2; }
- if (!strcmp (descr, "YZleft")) { return 1; }
- if (!strcmp (descr, "YZright")) { return 0; }
-
- g_warning ("Invalid description of 3D box face.\n");
- return -1;
-}
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/box3d-face.h b/src/box3d-face.h
--- a/src/box3d-face.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef __SP_3DBOX_FACE_H__
-#define __SP_3DBOX_FACE_H__
-
-/*
- * Face of a 3D box ('perspectivic rectangle')
- *
- * Authors:
- * Maximilian Albert <Anhalter42@gmx.de>
- *
- * Copyright (C) 2007 Authors
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "perspective-line.h"
-#include "display/curve.h"
-#include "sp-path.h"
-#include "sp-object.h"
-#include "inkscape.h"
-#include "desktop-style.h"
-#include "desktop.h"
-#include "xml/document.h"
-
-class SP3DBox;
-
-class Box3DFace {
-public:
- Box3DFace(SP3DBox *box, NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D,
- Box3D::Axis plane, Box3D::FrontOrRear rel_pos);
- Box3DFace(Box3DFace const &box3dface);
- virtual ~Box3DFace();
-
- NR::Point operator[](unsigned int i);
- void draw(SP3DBox *box3d, SPCurve *c);
-
- /***
- void set_shape(NR::Point const ul, NR::Point const lr,
- Box3D::Axis const dir1, Box3D::Axis const dir2,
- unsigned int shift_count = 0, NR::Maybe<NR::Point> pt_align = NR::Nothing(),
- bool align_along_PL = false);
- ***/
- void set_corners (NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D);
- //void set_face (NR::Point const A, NR::Point const C, Box3D::Axis const dir1, Box3D::Axis const dir2);
-
- void hook_path_to_3dbox(SPPath * existing_path = NULL);
- void set_style(Inkscape::XML::Node *repr_face = NULL, bool extruded = true);
- void set_path_repr();
- void set_curve();
- inline void lower_to_bottom() { SP_ITEM (path)->lowerToBottom(); }
- inline void raise_to_top() { SP_ITEM (path)->raiseToTop(); }
- gchar * axes_string();
- gchar * svg_repr_string();
- static gint descr_to_id (gchar const *descr);
-
-private:
- NR::Point *corners[4];
-
- Box3D::Axis dir1;
- Box3D::Axis dir2;
-
- Box3D::FrontOrRear front_or_rear;
-
- SPPath *path;
- SP3DBox *parent_box3d;
-};
-
-#endif
diff --git a/src/box3d-side.cpp b/src/box3d-side.cpp
--- /dev/null
+++ b/src/box3d-side.cpp
@@ -0,0 +1,424 @@
+#define __BOX3D_SIDE_C__
+
+/*
+ * 3D box face implementation
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "box3d-side.h"
+#include "document.h"
+#include "xml/document.h"
+#include "xml/repr.h"
+#include "display/curve.h"
+#include "svg/svg.h"
+#include "attributes.h"
+#include "inkscape.h"
+#include "persp3d.h"
+#include "box3d-context.h"
+#include "prefs-utils.h"
+#include "desktop-style.h"
+#include "box3d.h"
+
+static void box3d_side_class_init (Box3DSideClass *klass);
+static void box3d_side_init (Box3DSide *side);
+
+static void box3d_side_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static Inkscape::XML::Node *box3d_side_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
+static void box3d_side_set (SPObject *object, unsigned int key, const gchar *value);
+static void box3d_side_update (SPObject *object, SPCtx *ctx, guint flags);
+
+//static gchar * box3d_side_description (SPItem * item);
+//static void box3d_side_snappoints(SPItem const *item, SnapPointsIter p);
+
+//static void box3d_side_set_shape (SPShape *shape);
+//static void box3d_side_update_patheffect (SPShape *shape, bool write);
+
+static void box3d_side_apply_style (Box3DSide *side);
+static Proj::Pt3 box3d_side_corner (Box3DSide *side, guint index);
+static std::vector<Proj::Pt3> box3d_side_corners (Box3DSide *side);
+static gint box3d_side_descr_to_id (gchar const *descr);
+
+static SPShapeClass *parent_class;
+
+GType
+box3d_side_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ GTypeInfo info = {
+ sizeof (Box3DSideClass),
+ NULL, NULL,
+ (GClassInitFunc) box3d_side_class_init,
+ NULL, NULL,
+ sizeof (Box3DSide),
+ 16,
+ (GInstanceInitFunc) box3d_side_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static (SP_TYPE_SHAPE, "Box3DSide", &info, (GTypeFlags)0);
+ }
+ return type;
+}
+
+static void
+box3d_side_class_init (Box3DSideClass *klass)
+{
+ GObjectClass * gobject_class;
+ SPObjectClass * sp_object_class;
+ SPItemClass * item_class;
+ SPPathClass * path_class;
+ SPShapeClass * shape_class;
+
+ gobject_class = (GObjectClass *) klass;
+ sp_object_class = (SPObjectClass *) klass;
+ item_class = (SPItemClass *) klass;
+ path_class = (SPPathClass *) klass;
+ shape_class = (SPShapeClass *) klass;
+
+ parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE);
+
+ sp_object_class->build = box3d_side_build;
+ sp_object_class->write = box3d_side_write;
+ sp_object_class->set = box3d_side_set;
+ sp_object_class->update = box3d_side_update;
+
+ //item_class->description = box3d_side_description;
+ //item_class->snappoints = box3d_side_snappoints;
+
+ shape_class->set_shape = box3d_side_set_shape;
+ //shape_class->update_patheffect = box3d_side_update_patheffect;
+}
+
+static void
+box3d_side_init (Box3DSide * side)
+{
+ side->dir1 = Box3D::NONE;
+ side->dir2 = Box3D::NONE;
+ side->front_or_rear = Box3D::FRONT;
+}
+
+static void
+box3d_side_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr)
+{
+ if (((SPObjectClass *) parent_class)->build)
+ ((SPObjectClass *) parent_class)->build (object, document, repr);
+
+ sp_object_read_attr (object, "inkscape:box3dsidetype");
+}
+
+static Inkscape::XML::Node *
+box3d_side_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+ Box3DSide *side = SP_BOX3D_SIDE (object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ g_print ("Do we ever end up here?\n");
+ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+ repr = xml_doc->createElement("svg:path");
+ repr->setAttribute("sodipodi:type", "inkscape:box3dside"); // FIXME: Does this double the
+ }
+
+ if (flags & SP_OBJECT_WRITE_EXT) {
+ sp_repr_set_int(repr, "inkscape:box3dsidetype", side->dir1 ^ side->dir2 ^ side->front_or_rear);
+ }
+
+ sp_shape_set_shape ((SPShape *) object); // FIXME: necessary? YES!
+
+ /* Duplicate the path */
+ SPCurve *curve = ((SPShape *) object)->curve;
+ //Nulls might be possible if this called iteratively
+ if ( !curve ) {
+ return NULL;
+ }
+ NArtBpath *bpath = SP_CURVE_BPATH(curve);
+ if ( !bpath ) {
+ return NULL;
+ }
+ char *d = sp_svg_write_path ( bpath );
+ repr->setAttribute("d", d);
+ g_free (d);
+
+ box3d_side_apply_style (side);
+
+ if (((SPObjectClass *) (parent_class))->write)
+ ((SPObjectClass *) (parent_class))->write (object, repr, flags);
+
+ return repr;
+}
+
+static void
+box3d_side_set (SPObject *object, unsigned int key, const gchar *value)
+{
+ Box3DSide *side = SP_BOX3D_SIDE (object);
+
+ // TODO: In case the box was recreated (by undo, e.g.) we need to recreate the path
+ // (along with other info?) from the parent box.
+
+ /* fixme: we should really collect updates */
+ switch (key) {
+ case SP_ATTR_INKSCAPE_BOX3D_SIDE_TYPE:
+ if (value) {
+ guint desc = atoi (value);
+
+ if (!Box3D::is_face_id(desc)) {
+ g_print ("desc is not a face id: =%s=\n", value);
+ }
+ g_return_if_fail (Box3D::is_face_id (desc));
+ Box3D::Axis plane = (Box3D::Axis) (desc & 0x7);
+ plane = (Box3D::is_plane(plane) ? plane : Box3D::orth_plane_or_axis(plane));
+ side->dir1 = Box3D::extract_first_axis_direction(plane);
+ side->dir2 = Box3D::extract_second_axis_direction(plane);
+ side->front_or_rear = (Box3D::FrontOrRear) (desc & 0x8);
+
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+ default:
+ if (((SPObjectClass *) parent_class)->set)
+ ((SPObjectClass *) parent_class)->set (object, key, value);
+ break;
+ }
+}
+
+static void
+box3d_side_update (SPObject *object, SPCtx *ctx, guint flags)
+{
+ //g_print ("box3d_side_update\n");
+ if (flags & (SP_OBJECT_MODIFIED_FLAG |
+ //SP_OBJECT_CHILD_MODIFIED_FLAG |
+ SP_OBJECT_STYLE_MODIFIED_FLAG |
+ SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
+ /***
+ g_print ("\n\nIn box3d_side_update: ");
+ if (flags & SP_OBJECT_MODIFIED_FLAG) g_print ("SP_OBJECT_MODIFIED_FLAG ");
+ if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) g_print ("SP_OBJECT_CHILD_MODIFIED_FLAG ");
+ if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) g_print ("SP_OBJECT_STYLE_MODIFIED_FLAG ");
+ if (flags & SP_OBJECT_VIEWPORT_MODIFIED_FLAG) g_print ("SP_OBJECT_VIEWPORT_MODIFIED_FLAG ");
+ g_print ("\n");
+ ***/
+ sp_shape_set_shape ((SPShape *) object);
+ }
+
+ if (((SPObjectClass *) parent_class)->update)
+ ((SPObjectClass *) parent_class)->update (object, ctx, flags);
+}
+
+/***
+static void
+box3d_side_update_patheffect(SPShape *shape, bool write)
+{
+ box3d_side_set_shape(shape);
+
+ if (write) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+ if ( shape->curve != NULL ) {
+ NArtBpath *abp = sp_curve_first_bpath(shape->curve);
+ if (abp) {
+ gchar *str = sp_svg_write_path(abp);
+ repr->setAttribute("d", str);
+ g_free(str);
+ } else {
+ repr->setAttribute("d", "");
+ }
+ } else {
+ repr->setAttribute("d", NULL);
+ }
+ }
+
+ ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+***/
+
+/***
+static gchar *
+box3d_side_description (SPItem *item)
+{
+ Box3DSide *side = SP_BOX3D_SIDE (item);
+
+ // while there will never be less than 3 vertices, we still need to
+ // make calls to ngettext because the pluralization may be different
+ // for various numbers >=3. The singular form is used as the index.
+ if (side->flatsided == false )
+ return g_strdup_printf (ngettext("<b>Star</b> with %d vertex",
+ "<b>Star</b> with %d vertices",
+ star->sides), star->sides);
+ else
+ return g_strdup_printf (ngettext("<b>Polygon</b> with %d vertex",
+ "<b>Polygon</b> with %d vertices",
+ star->sides), star->sides);
+}
+***/
+
+void
+box3d_side_position_set (Box3DSide *side) {
+ box3d_side_set_shape (SP_SHAPE (side));
+
+ /* This call is responsible for live update of the sides during the initial drag */
+ SP_OBJECT(side)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+void
+box3d_side_set_shape (SPShape *shape)
+{
+ //g_print ("box3d_side_set_shape\n");
+ Box3DSide *side = SP_BOX3D_SIDE (shape);
+ if (!SP_OBJECT_DOCUMENT(side)->root) {
+ // avoid a warning caused by sp_document_height() (which is called from sp_item_i2d_affine() below)
+ // when reading a file containing 3D boxes
+ return;
+ }
+
+ if (!SP_IS_BOX3D(SP_OBJECT(side)->parent)) {
+ g_warning ("Parent of 3D box side is not a 3D box.\n");
+ /**
+ g_print ("Removing the inkscape:box3dside attribute and returning from box3d_side_set_shape().\n");
+ SP_OBJECT_REPR (shape)->setAttribute("sodipodi:type", NULL);
+ SP_OBJECT_REPR (shape)->setAttribute("inkscape:box3dside", NULL);
+ **/
+ return;
+ }
+
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR (shape);
+ Persp3D *persp = box3d_side_perspective(side);
+ //g_return_if_fail (persp != NULL);
+ if (!persp) {
+ //g_warning ("persp != NULL in box3d_side_set_shape failed!\n");
+ //persp = SP_OBJECT_DOCUMENT(side)->current_persp3d;
+ return;
+ }
+
+ SPCurve *c = sp_curve_new ();
+ // TODO: Draw the correct quadrangle here
+ // To do this, determine the perspective of the box, the orientation of the side (e.g., XY-FRONT)
+ // compute the coordinates of the corners in P^3, project them onto the canvas, and draw the
+ // resulting path.
+
+ std::vector<Proj::Pt3> corners = box3d_side_corners (side);
+
+ NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM(shape)));
+
+ // FIXME: This can better be implemented by using box3d_get_corner
+ sp_curve_moveto (c, persp->tmat.image(corners[0]).affine() * i2d);
+ sp_curve_lineto (c, persp->tmat.image(corners[1]).affine() * i2d);
+ sp_curve_lineto (c, persp->tmat.image(corners[2]).affine() * i2d);
+ sp_curve_lineto (c, persp->tmat.image(corners[3]).affine() * i2d);
+
+ sp_curve_closepath (c);
+ //sp_shape_perform_path_effect(c, SP_SHAPE (side));
+ sp_shape_set_curve_insync (SP_SHAPE (side), c, TRUE);
+ sp_curve_unref (c);
+}
+
+static void
+//box3d_side_apply_style (SPBox3D *box, bool extruded) {
+box3d_side_apply_style (Box3DSide *side) {
+ Inkscape::XML::Node *repr_face = SP_OBJECT_REPR(SP_OBJECT(side));
+
+ /**
+ if (!extruded && !strcmp (box3d_side_axes_string (), "XYrear")) {
+ // to avoid "flashing" during the initial dragging process, we make the rear face invisible in this case
+ repr_face->setAttribute("style", "fill:none");
+ return;
+ }
+ **/
+
+ gchar *descr = g_strconcat ("desktop.", box3d_side_axes_string (side), NULL);
+ const gchar * cur_style = prefs_get_string_attribute(descr, "style");
+ g_free (descr);
+
+ SPDesktop *desktop = inkscape_active_desktop();
+ bool use_current = prefs_get_int_attribute("tools.shapes.3dbox", "usecurrent", 0);
+ if (use_current && cur_style !=NULL) {
+ /* use last used style */
+ repr_face->setAttribute("style", cur_style);
+ } else {
+ /* use default style */
+ GString *pstring = g_string_new("");
+ g_string_printf (pstring, "tools.shapes.3dbox.%s", box3d_side_axes_string(side));
+ sp_desktop_apply_style_tool (desktop, repr_face, pstring->str, false);
+ }
+}
+
+gchar *
+box3d_side_axes_string(Box3DSide *side)
+{
+ GString *pstring = g_string_new("");
+ g_string_printf (pstring, "%s", Box3D::string_from_axes ((Box3D::Axis) (side->dir1 ^ side->dir2)));
+ switch ((Box3D::Axis) (side->dir1 ^ side->dir2)) {
+ case Box3D::XY:
+ g_string_append_printf (pstring, (side->front_or_rear == Box3D::FRONT) ? "front" : "rear");
+ break;
+ case Box3D::XZ:
+ g_string_append_printf (pstring, (side->front_or_rear == Box3D::FRONT) ? "top" : "bottom");
+ break;
+ case Box3D::YZ:
+ g_string_append_printf (pstring, (side->front_or_rear == Box3D::FRONT) ? "right" : "left");
+ break;
+ default:
+ break;
+ }
+ return pstring->str;
+}
+
+static Proj::Pt3
+box3d_side_corner (Box3DSide *side, guint index) {
+ SPBox3D *box = SP_BOX3D(SP_OBJECT_PARENT(side));
+ return Proj::Pt3 ((index & 0x1) ? box->orig_corner7[Proj::X] : box->orig_corner0[Proj::X],
+ (index & 0x2) ? box->orig_corner7[Proj::Y] : box->orig_corner0[Proj::Y],
+ (index & 0x4) ? box->orig_corner7[Proj::Z] : box->orig_corner0[Proj::Z],
+ 1.0);
+}
+
+static std::vector<Proj::Pt3>
+box3d_side_corners (Box3DSide *side) {
+ std::vector<Proj::Pt3> corners;
+ Box3D::Axis orth = Box3D::third_axis_direction (side->dir1, side->dir2);
+ unsigned int i0 = (side->front_or_rear ? orth : 0);
+ unsigned int i1 = i0 ^ side->dir1;
+ unsigned int i2 = i0 ^ side->dir1 ^ side->dir2;
+ unsigned int i3 = i0 ^ side->dir2;
+
+ corners.push_back (box3d_side_corner (side, i0));
+ corners.push_back (box3d_side_corner (side, i1));
+ corners.push_back (box3d_side_corner (side, i2));
+ corners.push_back (box3d_side_corner (side, i3));
+ return corners;
+}
+
+static gint
+box3d_side_descr_to_id (gchar const *descr)
+{
+ if (!strcmp (descr, "XYrear")) { return 5; }
+ if (!strcmp (descr, "XYfront")) { return 2; }
+ if (!strcmp (descr, "XZbottom")) { return 1; }
+ if (!strcmp (descr, "XZtop")) { return 4; }
+ if (!strcmp (descr, "YZleft")) { return 3; }
+ if (!strcmp (descr, "YZright")) { return 0; }
+
+ g_warning ("Invalid description of 3D box face.\n");
+ g_print (" (description is: %s)\n", descr);
+ return -1;
+}
+
+Persp3D *
+box3d_side_perspective(Box3DSide *side) {
+ return SP_BOX3D(SP_OBJECT(side)->parent)->persp_ref->getObject();
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/box3d-side.h b/src/box3d-side.h
--- /dev/null
+++ b/src/box3d-side.h
@@ -0,0 +1,59 @@
+#ifndef __BOX3D_SIDE_H__
+#define __BOX3D_SIDE_H__
+
+/*
+ * 3D box face implementation
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-polygon.h"
+#include "axis-manip.h"
+
+#define SP_TYPE_BOX3D_SIDE (box3d_side_get_type ())
+#define SP_BOX3D_SIDE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_BOX3D_SIDE, Box3DSide))
+#define SP_BOX3D_SIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_BOX3D_SIDE, Box3DSideClass))
+#define SP_IS_BOX3D_SIDE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_BOX3D_SIDE))
+#define SP_IS_BOX3D_SIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_BOX3D_SIDE))
+
+class SPBox3D;
+class Box3DSide;
+class Box3DSideClass;
+class Persp3D;
+
+// FIXME: Would it be better to inherit from SPPath instead?
+struct Box3DSide : public SPPolygon {
+ Box3D::Axis dir1;
+ Box3D::Axis dir2;
+ Box3D::FrontOrRear front_or_rear;
+};
+
+struct Box3DSideClass {
+ SPPolygonClass parent_class;
+};
+
+GType box3d_side_get_type (void);
+
+//void sp_box3d_side_position_set (Box3DSide *side, NR::Point corner1, NR::Point corner2);
+void box3d_side_set_shape (SPShape *shape);
+void box3d_side_position_set (Box3DSide *side); // FIXME: Replace this by box3d_side_set_shape??
+gchar *box3d_side_axes_string(Box3DSide *side);
+Persp3D *box3d_side_perspective(Box3DSide *side);
+
+#endif /* __BOX3D_SIDE_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/box3d.cpp b/src/box3d.cpp
index ff00a795c57505d50c8475fa83ed835cd16e4740..c9f3bb7d296b63b2533f1cdad606838d2caf921e 100644 (file)
--- a/src/box3d.cpp
+++ b/src/box3d.cpp
-#define __SP_3DBOX_C__
+#define __SP_BOX3D_C__
/*
* SVG <box3d> implementation
*
* Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak <buliabyak@users.sf.net>
- * Maximilian Albert <Anhalter42@gmx.de>
*
* Copyright (C) 2007 Authors
* Copyright (C) 1999-2002 Lauris Kaplinski
#include <glibmm/i18n.h>
#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);
-
-static void sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
-static void sp_3dbox_release (SPObject *object);
-static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value);
-static void sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags);
-static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
-
-static gchar *sp_3dbox_description(SPItem *item);
-
-//static void sp_3dbox_set_shape(SPShape *shape);
-//static void sp_3dbox_set_shape(SP3DBox *box3d);
+#include "xml/document.h"
+#include "xml/repr.h"
-static void sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value);
-static void sp_3dbox_update_perspective (Box3D::Perspective3D *persp, const gchar *value);
-static gchar * sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id);
-static std::pair<gdouble, gdouble> sp_3dbox_get_coord_pair_from_string (const gchar *);
-static gchar * sp_3dbox_get_perspective_string (SP3DBox *box);
+#include "box3d.h"
+#include "box3d-side.h"
+#include "box3d-context.h"
+#include "proj_pt.h"
+#include "transf_mat_3x4.h"
+#include "perspective-line.h"
+#include "inkscape.h"
+#include "persp3d.h"
+#include "line-geometry.h"
+#include "persp3d-reference.h"
+#include "uri.h"
+#include "2geom/geom.h"
+
+#include "desktop.h"
+#include "macros.h"
+
+static void box3d_class_init(SPBox3DClass *klass);
+static void box3d_init(SPBox3D *box3d);
+
+static void box3d_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void box3d_release(SPObject *object);
+static void box3d_set(SPObject *object, unsigned int key, const gchar *value);
+static void box3d_update(SPObject *object, SPCtx *ctx, guint flags);
+static Inkscape::XML::Node *box3d_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+
+static gchar *box3d_description(SPItem *item);
+static NR::Matrix box3d_set_transform(SPItem *item, NR::Matrix const &xform);
+
+static void box3d_ref_changed(SPObject *old_ref, SPObject *ref, SPBox3D *box);
+static void box3d_ref_modified(SPObject *href, guint flags, SPBox3D *box);
+//static void box3d_ref_changed(SPObject *old_ref, SPObject *ref, Persp3D *persp);
+//static void box3d_ref_modified(SPObject *href, guint flags, Persp3D *persp);
static SPGroupClass *parent_class;
static gint counter = 0;
GType
-sp_3dbox_get_type(void)
+box3d_get_type(void)
{
static GType type = 0;
if (!type) {
GTypeInfo info = {
- sizeof(SP3DBoxClass),
+ sizeof(SPBox3DClass),
NULL, /* base_init */
NULL, /* base_finalize */
- (GClassInitFunc) sp_3dbox_class_init,
+ (GClassInitFunc) box3d_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
- sizeof(SP3DBox),
+ sizeof(SPBox3D),
16, /* n_preallocs */
- (GInstanceInitFunc) sp_3dbox_init,
+ (GInstanceInitFunc) box3d_init,
NULL, /* value_table */
};
- type = g_type_register_static(SP_TYPE_GROUP, "SP3DBox", &info, (GTypeFlags) 0);
+ type = g_type_register_static(SP_TYPE_GROUP, "SPBox3D", &info, (GTypeFlags) 0);
}
return type;
}
static void
-sp_3dbox_class_init(SP3DBoxClass *klass)
+box3d_class_init(SPBox3DClass *klass)
{
SPObjectClass *sp_object_class = (SPObjectClass *) klass;
SPItemClass *item_class = (SPItemClass *) klass;
parent_class = (SPGroupClass *) g_type_class_ref(SP_TYPE_GROUP);
- sp_object_class->build = sp_3dbox_build;
- sp_object_class->set = sp_3dbox_set;
- sp_object_class->write = sp_3dbox_write;
- sp_object_class->update = sp_3dbox_update;
- sp_object_class->release = sp_3dbox_release;
+ sp_object_class->build = box3d_build;
+ sp_object_class->release = box3d_release;
+ sp_object_class->set = box3d_set;
+ sp_object_class->write = box3d_write;
+ sp_object_class->update = box3d_update;
- item_class->description = sp_3dbox_description;
+ item_class->description = box3d_description;
+ item_class->set_transform = box3d_set_transform;
}
static void
-sp_3dbox_init(SP3DBox *box)
+box3d_init(SPBox3D *box)
{
- for (int i = 0; i < 8; ++i) box->corners[i] = NR::Point(0,0);
- for (int i = 0; i < 6; ++i) box->faces[i] = NULL;
+ box->persp_href = NULL;
+ box->persp_ref = new Persp3DReference(SP_OBJECT(box));
+ new (&box->modified_connection) sigc::connection();
}
static void
-sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+box3d_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
{
if (((SPObjectClass *) (parent_class))->build) {
((SPObjectClass *) (parent_class))->build(object, document, repr);
}
- SP3DBox *box = SP_3DBOX (object);
-
+ SPBox3D *box = SP_BOX3D (object);
box->my_counter = counter++;
/* we initialize the z-orders to zero so that they are updated during dragging */
@@ -109,197 +122,199 @@ sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr
box->z_orders[i] = 0;
}
- box->front_bits = 0x0;
+ // TODO: Create/link to the correct perspective
-
- if (repr->attribute ("inkscape:perspective") == NULL) {
- // we are creating a new box; link it to the current perspective
- document->current_perspective->add_box (box);
- } else {
- // create a new perspective that we can compare with existing ones
- Box3D::Perspective3D *persp = new Box3D::Perspective3D (Box3D::VanishingPoint (0,0),
- Box3D::VanishingPoint (0,0),
- Box3D::VanishingPoint (0,0),
- document);
- sp_3dbox_update_perspective (persp, repr->attribute ("inkscape:perspective"));
- Box3D::Perspective3D *comp = document->find_perspective (persp);
- if (comp == NULL) {
- // perspective doesn't exist yet
- document->add_perspective (persp);
- persp->add_box (box);
- } else {
- // link the box to the existing perspective and delete the temporary one
- comp->add_box (box);
- delete persp;
- //g_assert (Box3D::get_persp_of_box (box) == comp);
-
- // FIXME: If the paths of the box's faces do not correspond to the svg representation of the perspective
- // the box is shown with a "wrong" initial shape that is only corrected after dragging.
- // Should we "repair" this by updating the paths at the end of sp_3dbox_build()?
- // Maybe it would be better to simply destroy and rebuild them in sp_3dbox_link_to_existing_paths().
- }
+ SPDocument *doc = SP_OBJECT_DOCUMENT(box);
+ if (!doc) {
+ g_print ("No document for the box!!!!\n");
+ return;
}
-
- sp_object_read_attr(object, "inkscape:box3dcornerA");
- sp_object_read_attr(object, "inkscape:box3dcornerB");
- sp_object_read_attr(object, "inkscape:box3dcornerC");
-
- // TODO: We create all faces in the beginning, but only the non-degenerate ones
- // should be written to the svg representation later in sp_3dbox_write.
- Box3D::Axis cur_plane, axis, dir1, dir2;
- Box3D::FrontOrRear cur_pos;
- for (int i = 0; i < 3; ++i) {
- for (int j = 0; j < 2; ++j) {
- cur_plane = Box3D::planes[i];
- cur_pos = Box3D::face_positions[j];
- // FIXME: The following code could theoretically be moved to
- // the constructor of Box3DFace (but see the comment there).
- axis = (cur_pos == Box3D::FRONT ? Box3D::NONE : Box3D::third_axis_direction (cur_plane));
- dir1 = extract_first_axis_direction (cur_plane);
- dir2 = extract_second_axis_direction (cur_plane);
-
- box->faces[Box3D::face_to_int(cur_plane ^ cur_pos)] =
- new Box3DFace (box, box->corners[axis], box->corners[axis ^ dir1],
- box->corners[axis ^ dir1 ^ dir2], box->corners[axis ^ dir2],
- cur_plane, cur_pos);
- }
+ /**
+ if (!box->persp3d) {
+ g_print ("Box seems to be newly created since no perspective is referenced yet. We reference the current perspective.\n");
+ box->persp3d = doc->current_persp3d;
}
+ **/
- // Check whether the paths of the faces of the box need to be linked to existing paths in the
- // document (e.g., after a 'redo' operation or after opening a file) and do so if necessary.
- sp_3dbox_link_to_existing_paths (box, repr);
-
- sp_3dbox_set_ratios (box, Box3D::XYZ);
+ box->persp_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(box3d_ref_changed), box));
- // 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];
- box->old_corner7 = box->corners[7];
+ sp_object_read_attr(object, "inkscape:perspectiveID");
+ sp_object_read_attr(object, "inkscape:corner0");
+ sp_object_read_attr(object, "inkscape:corner7");
}
+/**
+ * Virtual release of SPBox3D members before destruction.
+ */
static void
-sp_3dbox_release (SPObject *object)
+box3d_release(SPObject *object)
{
- SP3DBox *box = SP_3DBOX(object);
- for (int i = 0; i < 6; ++i) {
- if (box->faces[i]) {
- delete box->faces[i]; // FIXME: Anything else to do? Do we need to clean up the face first?
- }
- }
+ SPBox3D *box = (SPBox3D *) object;
+
+ if (box->persp_href) {
+ g_free(box->persp_href);
+ }
+ if (box->persp_ref) {
+ box->persp_ref->detach();
+ delete box->persp_ref;
+ box->persp_ref = NULL;
+ }
- // FIXME: We do not duplicate perspectives if they are the same for several boxes.
- // Thus, don't delete the perspective when deleting a box but rather unlink the box from it.
- SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->remove_box (box);
+ box->modified_connection.disconnect();
+ box->modified_connection.~connection();
- if (((SPObjectClass *) parent_class)->release) {
- ((SPObjectClass *) parent_class)->release (object);
- }
+ //persp3d_remove_box (box->persp_ref->getObject(), box);
+
+ if (((SPObjectClass *) parent_class)->release)
+ ((SPObjectClass *) parent_class)->release(object);
}
-static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
+static void
+box3d_set(SPObject *object, unsigned int key, const gchar *value)
{
+ SPBox3D *box = SP_BOX3D(object);
+
switch (key) {
- case SP_ATTR_INKSCAPE_3DBOX_CORNER_A:
- sp_3dbox_update_corner_with_value_from_svg (object, 2, value);
- break;
- case SP_ATTR_INKSCAPE_3DBOX_CORNER_B:
- sp_3dbox_update_corner_with_value_from_svg (object, 1, value);
+ case SP_ATTR_INKSCAPE_BOX3D_PERSPECTIVE_ID:
+ if ( value && box->persp_href && ( strcmp(value, box->persp_href) == 0 ) ) {
+ /* No change, do nothing. */
+ } else {
+ if (box->persp_href) {
+ g_free(box->persp_href);
+ box->persp_href = NULL;
+ }
+ if (value) {
+ box->persp_href = g_strdup(value);
+
+ // Now do the attaching, which emits the changed signal.
+ try {
+ box->persp_ref->attach(Inkscape::URI(value));
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s", e.what());
+ box->persp_ref->detach();
+ }
+ } else {
+ // Detach, which emits the changed signal.
+ box->persp_ref->detach();
+ // TODO: Clean this up (also w.r.t the surrounding if construct)
+ /***
+ g_print ("No perspective given. Attaching to current perspective instead.\n");
+ g_free(box->persp_href);
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(inkscape_active_document()->current_persp3d);
+ box->persp_href = g_strdup(repr->attribute("id"));
+ box->persp_ref->attach(Inkscape::URI(box->persp_href));
+ ***/
+ }
+ }
+
+ // FIXME: Is the following update doubled by some call in either persp3d.cpp or vanishing_point_new.cpp?
+ box3d_position_set(box);
break;
- case SP_ATTR_INKSCAPE_3DBOX_CORNER_C:
- sp_3dbox_update_corner_with_value_from_svg (object, 5, value);
+ case SP_ATTR_INKSCAPE_BOX3D_CORNER0:
+ if (value && strcmp(value, "0 : 0 : 0 : 0")) {
+ box->orig_corner0 = Proj::Pt3(value);
+ box->save_corner0 = box->orig_corner0;
+ box3d_position_set(box);
+ }
break;
- case SP_ATTR_INKSCAPE_3DBOX_PERSPECTIVE:
- {
- SP3DBox *box = SP_3DBOX (object);
- sp_3dbox_update_perspective (SP_OBJECT_DOCUMENT (object)->get_persp_of_box (box), value);
+ case SP_ATTR_INKSCAPE_BOX3D_CORNER7:
+ if (value && strcmp(value, "0 : 0 : 0 : 0")) {
+ box->orig_corner7 = Proj::Pt3(value);
+ box->save_corner7 = box->orig_corner7;
+ box3d_position_set(box);
+ }
break;
- }
default:
if (((SPObjectClass *) (parent_class))->set) {
((SPObjectClass *) (parent_class))->set(object, key, value);
}
break;
}
+ //object->updateRepr(); // This ensures correct update of the box after undo/redo. FIXME: Why is this not present in sp-rect.cpp and similar files?
}
+/**
+ * Gets called when (re)attached to another perspective.
+ */
static void
-sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags)
+box3d_ref_changed(SPObject *old_ref, SPObject *ref, SPBox3D *box)
+{
+ if (old_ref) {
+ sp_signal_disconnect_by_data(old_ref, box);
+ persp3d_remove_box (SP_PERSP3D(old_ref), box);
+ }
+ if ( SP_IS_PERSP3D(ref) && ref != box ) // FIXME: Comparisons sane?
+ {
+ box->modified_connection.disconnect();
+ box->modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&box3d_ref_modified), box));
+ box3d_ref_modified(ref, 0, box);
+ persp3d_add_box (SP_PERSP3D(ref), box);
+ }
+}
+
+static void
+box3d_update(SPObject *object, SPCtx *ctx, guint flags)
{
if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
- SP3DBox *box = SP_3DBOX(object);
- Inkscape::XML::Node *repr = SP_OBJECT_REPR(object);
- sp_3dbox_link_to_existing_paths (box, repr);
- SPEventContext *ec = inkscape_active_event_context();
- if (SP_IS_3DBOX_CONTEXT (ec)) {
- SP_3DBOX_CONTEXT (ec)->_vpdrag->updateDraggers();
- // FIXME: Should we update the corners here, too? Maybe this is the reason why the handles
- // are off after an undo/redo! On the other hand, if we do so we get warnings about
- // updates occuring while other updats are in progress ...
- }
+
+ /* FIXME?: Perhaps the display updates of box sides should be instantiated from here, but this
+ causes evil update loops so it's all done from box3d_position_set, which is called from
+ various other places (like the handlers in object-edit.cpp, vanishing-point.cpp, etc. */
+
}
- /* Invoke parent method */
+ // Invoke parent method
if (((SPObjectClass *) (parent_class))->update)
((SPObjectClass *) (parent_class))->update(object, ctx, flags);
}
-static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+
+static Inkscape::XML::Node *box3d_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
{
- SP3DBox *box = SP_3DBOX(object);
- // 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 repr;
+ SPBox3D *box = SP_BOX3D(object);
if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ g_print ("Do we ever end up here?\n");
Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
repr = xml_doc->createElement("svg:g");
- repr->setAttribute("sodipodi:type", "inkscape:3dbox");
- /* Hook paths to the faces of the box */
- for (int i = 0; i < 6; ++i) {
- box->faces[i]->hook_path_to_3dbox();
- }
- }
-
- for (int i = 0; i < 6; ++i) {
- box->faces[i]->set_path_repr();
+ repr->setAttribute("sodipodi:type", "inkscape:box3d");
}
if (flags & SP_OBJECT_WRITE_EXT) {
- gchar *str;
- str = sp_3dbox_get_corner_coords_string (box, 2);
- repr->setAttribute("inkscape:box3dcornerA", str);
-
- str = sp_3dbox_get_corner_coords_string (box, 1);
- repr->setAttribute("inkscape:box3dcornerB", str);
- str = sp_3dbox_get_corner_coords_string (box, 5);
- repr->setAttribute("inkscape:box3dcornerC", str);
+ if (box->persp_href) {
+ repr->setAttribute("inkscape:perspectiveID", box->persp_href);
+ } else {
+ /* box is not yet linked to a perspective; use the document's current perspective */
+ SPDocument *doc = inkscape_active_document();
+ if (box->persp_ref->getURI()) {
+ gchar *uri_string = box->persp_ref->getURI()->toString();
+ repr->setAttribute("inkscape:perspectiveID", uri_string);
+ g_free(uri_string);
+ } else if (doc) {
+ //persp3d_add_box (doc->current_persp3d, box);
+ Inkscape::XML::Node *persp_repr = SP_OBJECT_REPR(doc->current_persp3d);
+ const gchar *persp_id = persp_repr->attribute("id");
+ gchar *href = g_strdup_printf("#%s", persp_id);
+ repr->setAttribute("inkscape:perspectiveID", href);
+ g_free(href);
+ } else {
+ g_print ("No active document while creating perspective!!!\n");
+ }
+ }
- str = sp_3dbox_get_perspective_string (box);
- repr->setAttribute("inkscape:perspective", str);
- sp_3dbox_set_ratios (box);
+ gchar *coordstr0 = box->orig_corner0.coord_string();
+ gchar *coordstr7 = box->orig_corner7.coord_string();
+ repr->setAttribute("inkscape:corner0", coordstr0);
+ repr->setAttribute("inkscape:corner7", coordstr7);
+ g_free(coordstr0);
+ g_free(coordstr7);
- g_free ((void *) str);
+ box->orig_corner0.normalize();
+ box->orig_corner7.normalize();
- /* 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];
- box->old_corner7 = box->corners[7];
+ box->save_corner0 = box->orig_corner0;
+ box->save_corner7 = box->orig_corner7;
}
if (((SPObjectClass *) (parent_class))->write) {
@@ -310,988 +325,1003 @@ static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node
}
static gchar *
-sp_3dbox_description(SPItem *item)
+box3d_description(SPItem *item)
{
- g_return_val_if_fail(SP_IS_3DBOX(item), NULL);
+ g_return_val_if_fail(SP_IS_BOX3D(item), NULL);
return g_strdup(_("<b>3D Box</b>"));
}
-void sp_3dbox_set_ratios (SP3DBox *box, Box3D::Axis axes)
+void
+box3d_position_set (SPBox3D *box)
{
- Box3D::Perspective3D *persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
- NR::Point pt;
-
- if (axes & Box3D::X) {
- pt = persp->get_vanishing_point (Box3D::X)->get_pos();
- box->ratio_x = NR::L2 (pt - box->corners[2]) / NR::L2 (pt - box->corners[3]);
- }
-
- if (axes & Box3D::Y) {
- pt = persp->get_vanishing_point (Box3D::Y)->get_pos();
- box->ratio_y = NR::L2 (pt - box->corners[2]) / NR::L2 (pt - box->corners[0]);
- }
-
- if (axes & Box3D::Z) {
- pt = persp->get_vanishing_point (Box3D::Z)->get_pos();
- box->ratio_z = NR::L2 (pt - box->corners[4]) / NR::L2 (pt - box->corners[0]);
+ /* This draws the curve and calls requestDisplayUpdate() for each side (the latter is done in
+ box3d_side_position_set() to avoid update conflicts with the parent box) */
+ for (SPObject *child = sp_object_first_child(SP_OBJECT (box)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ box3d_side_position_set (SP_BOX3D_SIDE (child));
}
}
-void
-sp_3dbox_switch_front_face (SP3DBox *box, Box3D::Axis axis)
+static NR::Matrix
+box3d_set_transform(SPItem *item, NR::Matrix const &xform)
{
- if (SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->get_vanishing_point (axis)->is_finite()) {
- box->front_bits = box->front_bits ^ axis;
- }
-}
+ SPBox3D *box = SP_BOX3D(item);
+ Persp3D *persp = box->persp_ref->getObject();
-void
-sp_3dbox_position_set (SP3DBoxContext &bc)
-{
- SP3DBox *box3d = SP_3DBOX(bc.item);
+ persp3d_apply_affine_transformation(persp, xform); // also triggers repr updates
- sp_3dbox_set_shape(box3d);
+ /***
+ // FIXME: We somehow have to apply the transformation to strokes, patterns, and gradients. How?
+ NR::Matrix ret(NR::transform(xform));
+ gdouble const sw = hypot(ret[0], ret[1]);
+ gdouble const sh = hypot(ret[2], ret[3]);
- // FIXME: Why does the following call not automatically update the children
- // of box3d (which is an SPGroup, which should do this)?
- //SP_OBJECT(box3d)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ SPItem *sideitem = NULL;
+ for (SPObject *side = sp_object_first_child(box); side != NULL; side = SP_OBJECT_NEXT(side)) {
+ sideitem = SP_ITEM(side);
- /**
- SP_OBJECT(box3d->path_face1)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- SP_OBJECT(box3d->path_face2)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- SP_OBJECT(box3d->path_face3)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- SP_OBJECT(box3d->path_face4)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- SP_OBJECT(box3d->path_face5)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- SP_OBJECT(box3d->path_face6)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- ***/
-}
+ // Adjust stroke width
+ sp_item_adjust_stroke(sideitem, sqrt(fabs(sw * sh)));
-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);
+ // Adjust pattern fill
+ sp_item_adjust_pattern(sideitem, xform);
- // 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]);
+ // Adjust gradient fill
+ sp_item_adjust_gradient(sideitem, xform);
}
- box->faces[4]->set_corners (box->corners[0], box->corners[1], box->corners[3], box->corners[2]);
+ ***/
- sp_3dbox_update_curves (box);
+ return NR::identity();
}
-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 *box, bool use_previous_corners)
-{
- if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
- return;
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
- if (!use_previous_corners) {
- sp_3dbox_set_shape_from_points (box, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
- } else {
- sp_3dbox_set_shape_from_points (box, box->corners[2], box->corners[1], box->corners[5]);
- }
+/**
+ * Gets called when persp(?) repr contents change: i.e. parameter change.
+ */
+static void
+box3d_ref_modified(SPObject *href, guint flags, SPBox3D *box)
+{
+ /***
+ g_print ("FIXME: box3d_ref_modified was called. What should we do?\n");
+ g_print ("Here is at least the the href's id: %s\n", SP_OBJECT_REPR(href)->attribute("id"));
+ g_print (" ... and the box's, too: %s\n", SP_OBJECT_REPR(box)->attribute("id"));
+ ***/
+
}
+Proj::Pt3
+box3d_get_proj_corner (guint id, Proj::Pt3 const &c0, Proj::Pt3 const &c7) {
+ return Proj::Pt3 ((id & Box3D::X) ? c7[Proj::X] : c0[Proj::X],
+ (id & Box3D::Y) ? c7[Proj::Y] : c0[Proj::Y],
+ (id & Box3D::Z) ? c7[Proj::Z] : c0[Proj::Z],
+ 1.0);
+}
-void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const A, NR::Point const B, NR::Point const C)
-{
- sp_3dbox_move_corner_in_XY_plane (box, 2, A);
- sp_3dbox_move_corner_in_XY_plane (box, 1, B);
- sp_3dbox_move_corner_in_Z_direction (box, 5, C);
+Proj::Pt3
+box3d_get_proj_corner (SPBox3D const *box, guint id) {
+ return Proj::Pt3 ((id & Box3D::X) ? box->orig_corner7[Proj::X] : box->orig_corner0[Proj::X],
+ (id & Box3D::Y) ? box->orig_corner7[Proj::Y] : box->orig_corner0[Proj::Y],
+ (id & Box3D::Z) ? box->orig_corner7[Proj::Z] : box->orig_corner0[Proj::Z],
+ 1.0);
}
-inline static double
-normalized_angle (double angle) {
- if (angle < -M_PI) {
- return angle + 2*M_PI;
- } else if (angle > M_PI) {
- return angle - 2*M_PI;
+NR::Point
+box3d_get_corner_screen (SPBox3D const *box, guint id) {
+ Proj::Pt3 proj_corner (box3d_get_proj_corner (box, id));
+ if (!box->persp_ref->getObject()) {
+ //g_print ("No perspective present in box!! Should we simply use the currently active perspective?\n");
+ return NR::Point (NR_HUGE, NR_HUGE);
}
- return angle;
+ return box->persp_ref->getObject()->tmat.image(proj_corner).affine();
}
-static gdouble
-sp_3dbox_corner_angle_to_VP (SP3DBox *box, Box3D::Axis axis, guint extreme_corner)
-{
- Box3D::VanishingPoint *vp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->get_vanishing_point (axis);
- NR::Point dir;
+Proj::Pt3
+box3d_get_proj_center (SPBox3D *box) {
+ box->orig_corner0.normalize();
+ box->orig_corner7.normalize();
+ return Proj::Pt3 ((box->orig_corner0[Proj::X] + box->orig_corner7[Proj::X]) / 2,
+ (box->orig_corner0[Proj::Y] + box->orig_corner7[Proj::Y]) / 2,
+ (box->orig_corner0[Proj::Z] + box->orig_corner7[Proj::Z]) / 2,
+ 1.0);
+}
- if (vp->is_finite()) {
- dir = NR::unit_vector (vp->get_pos() - box->corners[extreme_corner]);
- } else {
- dir = NR::unit_vector (vp->v_dir);
+NR::Point
+box3d_get_center_screen (SPBox3D *box) {
+ Proj::Pt3 proj_center (box3d_get_proj_center (box));
+ if (!box->persp_ref->getObject()) {
+ //g_print ("No perspective present in box!! Should we simply use the currently active perspective?\n");
+ return NR::Point (NR_HUGE, NR_HUGE);
}
-
- return atan2 (dir[NR::Y], dir[NR::X]);
+ return box->persp_ref->getObject()->tmat.image(proj_center).affine();
}
+/*
+ * To keep the snappoint from jumping randomly between the two lines when the mouse pointer is close to
+ * their intersection, we remember the last snapped line and keep snapping to this specific line as long
+ * as the distance from the intersection to the mouse pointer is less than remember_snap_threshold.
+ */
-bool sp_3dbox_recompute_z_orders (SP3DBox *box)
-{
- gint new_z_orders[6];
-
- // TODO: Determine the front corner depending on the distance from VPs and/or the user presets
- guint front_corner = sp_3dbox_get_front_corner_id (box);
-
- gdouble dir_1x = sp_3dbox_corner_angle_to_VP (box, Box3D::X, front_corner);
- gdouble dir_3x = sp_3dbox_corner_angle_to_VP (box, Box3D::X, front_corner ^ Box3D::Y);
+// Should we make the threshold settable in the preferences?
+static double remember_snap_threshold = 30;
+//static guint remember_snap_index = 0;
+static guint remember_snap_index_center = 0;
+
+static Proj::Pt3
+box3d_snap (SPBox3D *box, int id, Proj::Pt3 const &pt_proj, Proj::Pt3 const &start_pt) {
+ double z_coord = start_pt[Proj::Z];
+ double diff_x = box->save_corner7[Proj::X] - box->save_corner0[Proj::X];
+ double diff_y = box->save_corner7[Proj::Y] - box->save_corner0[Proj::Y];
+ double x_coord = start_pt[Proj::X];
+ double y_coord = start_pt[Proj::Y];
+ Proj::Pt3 A_proj (x_coord, y_coord, z_coord, 1.0);
+ Proj::Pt3 B_proj (x_coord + diff_x, y_coord, z_coord, 1.0);
+ Proj::Pt3 C_proj (x_coord + diff_x, y_coord + diff_y, z_coord, 1.0);
+ Proj::Pt3 D_proj (x_coord, y_coord + diff_y, z_coord, 1.0);
+ Proj::Pt3 E_proj (x_coord - diff_x, y_coord + diff_y, z_coord, 1.0);
+
+ NR::Point A = box->persp_ref->getObject()->tmat.image(A_proj).affine();
+ NR::Point B = box->persp_ref->getObject()->tmat.image(B_proj).affine();
+ NR::Point C = box->persp_ref->getObject()->tmat.image(C_proj).affine();
+ NR::Point D = box->persp_ref->getObject()->tmat.image(D_proj).affine();
+ NR::Point E = box->persp_ref->getObject()->tmat.image(E_proj).affine();
+ NR::Point pt = box->persp_ref->getObject()->tmat.image(pt_proj).affine();
+
+ // TODO: Replace these lines between corners with lines from a corner to a vanishing point
+ // (this might help to prevent rounding errors if the box is small)
+ Box3D::Line pl1(A, B);
+ Box3D::Line pl2(A, D);
+ Box3D::Line diag1(A, (id == -1 || (!(id & Box3D::X) == !(id & Box3D::Y))) ? C : E);
+ Box3D::Line diag2(A, E); // diag2 is only taken into account if id equals -1, i.e., if we are snapping the center
+
+ int num_snap_lines = (id != -1) ? 3 : 4;
+ NR::Point snap_pts[num_snap_lines];
+
+ snap_pts[0] = pl1.closest_to (pt);
+ snap_pts[1] = pl2.closest_to (pt);
+ snap_pts[2] = diag1.closest_to (pt);
+ if (id == -1) {
+ snap_pts[3] = diag2.closest_to (pt);
+ }
- gdouble dir_1y = sp_3dbox_corner_angle_to_VP (box, Box3D::Y, front_corner);
- //gdouble dir_0y = sp_3dbox_corner_angle_to_VP (box, Box3D::Y, front_corner ^ Box3D::X);
+ gdouble const zoom = inkscape_active_desktop()->current_zoom();
- gdouble dir_1z = sp_3dbox_corner_angle_to_VP (box, Box3D::Z, front_corner);
- gdouble dir_3z = sp_3dbox_corner_angle_to_VP (box, Box3D::Z, front_corner ^ Box3D::Y);
+ // determine the distances to all potential snapping points
+ double snap_dists[num_snap_lines];
+ for (int i = 0; i < num_snap_lines; ++i) {
+ snap_dists[i] = NR::L2 (snap_pts[i] - pt) * zoom;
+ }
- // Still not perfect, but only fails in some rather degenerate cases.
- // I suspect that there is a more elegant model, though. :)
- new_z_orders[0] = Box3D::face_containing_corner (Box3D::XY, front_corner);
- if (normalized_angle (dir_1y - dir_1z) > 0) {
- new_z_orders[1] = Box3D::face_containing_corner (Box3D::YZ, front_corner);
- if (normalized_angle (dir_1x - dir_1z) > 0) {
- new_z_orders[2] = Box3D::face_containing_corner (Box3D::XZ, front_corner ^ Box3D::Y);
- } else {
- new_z_orders[2] = Box3D::face_containing_corner (Box3D::XZ, front_corner);
- }
- } else {
- if (normalized_angle (dir_3x - dir_3z) > 0) {
- new_z_orders[1] = Box3D::face_containing_corner (Box3D::XZ, front_corner ^ Box3D::Y);
- new_z_orders[2] = Box3D::face_containing_corner (Box3D::YZ, front_corner ^ Box3D::X);
- } else {
- if (normalized_angle (dir_1x - dir_1z) > 0) {
- new_z_orders[1] = Box3D::face_containing_corner (Box3D::YZ, front_corner ^ Box3D::X);
- new_z_orders[2] = Box3D::face_containing_corner (Box3D::XZ, front_corner);
- } else {
- new_z_orders[1] = Box3D::face_containing_corner (Box3D::XZ, front_corner);
- new_z_orders[2] = Box3D::face_containing_corner (Box3D::YZ, front_corner ^ Box3D::X);
- }
+ // while we are within a given tolerance of the starting point,
+ // keep snapping to the same point to avoid jumping
+ bool within_tolerance = true;
+ for (int i = 0; i < num_snap_lines; ++i) {
+ if (snap_dists[i] > remember_snap_threshold) {
+ within_tolerance = false;
+ break;
}
}
- new_z_orders[3] = Box3D::opposite_face (new_z_orders[2]);
- new_z_orders[4] = Box3D::opposite_face (new_z_orders[1]);
- new_z_orders[5] = Box3D::opposite_face (new_z_orders[0]);
-
- /* We only need to look for changes among the topmost three faces because the order
- of the other ones is just inverted. */
- if ((box->z_orders[0] != new_z_orders[0]) ||
- (box->z_orders[1] != new_z_orders[1]) ||
- (box->z_orders[2] != new_z_orders[2]))
- {
- for (int i = 0; i < 6; ++i) {
- box->z_orders[i] = new_z_orders[i];
+ // find the closest snapping point
+ int snap_index = -1;
+ double snap_dist = NR_HUGE;
+ for (int i = 0; i < num_snap_lines; ++i) {
+ if (snap_dists[i] < snap_dist) {
+ snap_index = i;
+ snap_dist = snap_dists[i];
}
- return true;
}
- return false;
-}
-
-// convenience
-static bool sp_3dbox_is_subset_or_superset (std::vector<gint> const &list1, std::vector<gint> const &list2)
-{
- return (std::includes (list1.begin(), list1.end(), list2.begin(), list2.end()) ||
- std::includes (list2.begin(), list2.end(), list1.begin(), list1.end()));
+ // snap to the closest point (or the previously remembered one
+ // if we are within tolerance of the starting point)
+ NR::Point result;
+ if (within_tolerance) {
+ result = snap_pts[remember_snap_index_center];
+ } else {
+ remember_snap_index_center = snap_index;
+ result = snap_pts[snap_index];
+ }
+ return box->persp_ref->getObject()->tmat.preimage (result, z_coord, Proj::Z);
}
-static bool sp_3dbox_differ_by_opposite_faces (std::vector<gint> const &list1, std::vector<gint> const &list2)
-{
- std::vector<gint> diff1;
- std::vector<gint> diff2;
- std::set_difference (list1.begin(), list1.end(), list2.begin(), list2.end(),
- std::insert_iterator<std::vector<gint> >(diff1, diff1.begin()));
- std::set_difference (list2.begin(), list2.end(), list1.begin(), list1.end(),
- std::insert_iterator<std::vector<gint> >(diff2, diff2.begin()));
-
- if (diff1.size() == 3 || diff1.size() != diff2.size())
- return false;
-
- for (guint i = 0; i < diff1.size(); ++i) {
- if (std::find (diff2.begin(), diff2.end(), Box3D::opposite_face (diff1[i])) == diff2.end()) {
- return false;
+void
+box3d_set_corner (SPBox3D *box, const guint id, NR::Point const &new_pos, const Box3D::Axis movement, bool constrained) {
+ g_return_if_fail ((movement != Box3D::NONE) && (movement != Box3D::XYZ));
+
+ box->orig_corner0.normalize();
+ box->orig_corner7.normalize();
+
+ /* update corners 0 and 7 according to which handle was moved and to the axes of movement */
+ if (!(movement & Box3D::Z)) {
+ Proj::Pt3 pt_proj (box->persp_ref->getObject()->tmat.preimage (new_pos, (id < 4) ? box->orig_corner0[Proj::Z] :
+ box->orig_corner7[Proj::Z],
+ Proj::Z));
+ if (constrained) {
+ pt_proj = box3d_snap (box, id, pt_proj, box3d_get_proj_corner (id, box->save_corner0, box->save_corner7));
}
- }
- return true;
-}
-static gint
-sp_3dbox_face_containing_diagonal_corners (guint corner1, guint corner2)
-{
- Box3D::Axis plane = (Box3D::Axis) (corner1 ^ corner2);
- if (!Box3D::is_plane (plane)) {
- g_warning ("Corners %d and %d should span a plane.\n", corner1, corner2);
- return 0;
+ // normalizing pt_proj is essential because we want to mingle affine coordinates
+ pt_proj.normalize();
+ box->orig_corner0 = Proj::Pt3 ((id & Box3D::X) ? box->save_corner0[Proj::X] : pt_proj[Proj::X],
+ (id & Box3D::Y) ? box->save_corner0[Proj::Y] : pt_proj[Proj::Y],
+ box->save_corner0[Proj::Z],
+ 1.0);
+ box->orig_corner7 = Proj::Pt3 ((id & Box3D::X) ? pt_proj[Proj::X] : box->save_corner7[Proj::X],
+ (id & Box3D::Y) ? pt_proj[Proj::Y] : box->save_corner7[Proj::Y],
+ box->save_corner7[Proj::Z],
+ 1.0);
+ } else {
+ Box3D::PerspectiveLine pl(box->persp_ref->getObject()->tmat.image(
+ box3d_get_proj_corner (id, box->save_corner0, box->save_corner7)).affine(),
+ Proj::Z, box->persp_ref->getObject());
+ NR::Point new_pos_snapped(pl.closest_to(new_pos));
+ Proj::Pt3 pt_proj (box->persp_ref->getObject()->
+ tmat.preimage (new_pos_snapped,
+ box3d_get_proj_corner (box, id)[(movement & Box3D::Y) ? Proj::X : Proj::Y],
+ (movement & Box3D::Y) ? Proj::X : Proj::Y));
+ bool corner0_move_x = !(id & Box3D::X) && (movement & Box3D::X);
+ bool corner0_move_y = !(id & Box3D::Y) && (movement & Box3D::Y);
+ bool corner7_move_x = (id & Box3D::X) && (movement & Box3D::X);
+ bool corner7_move_y = (id & Box3D::Y) && (movement & Box3D::Y);
+ // normalizing pt_proj is essential because we want to mingle affine coordinates
+ pt_proj.normalize();
+ box->orig_corner0 = Proj::Pt3 (corner0_move_x ? pt_proj[Proj::X] : box->orig_corner0[Proj::X],
+ corner0_move_y ? pt_proj[Proj::Y] : box->orig_corner0[Proj::Y],
+ (id & Box3D::Z) ? box->orig_corner0[Proj::Z] : pt_proj[Proj::Z],
+ 1.0);
+ box->orig_corner7 = Proj::Pt3 (corner7_move_x ? pt_proj[Proj::X] : box->orig_corner7[Proj::X],
+ corner7_move_y ? pt_proj[Proj::Y] : box->orig_corner7[Proj::Y],
+ (id & Box3D::Z) ? pt_proj[Proj::Z] : box->orig_corner7[Proj::Z],
+ 1.0);
}
-
- return Box3D::face_containing_corner (plane, corner1);
+ // FIXME: Should we update the box here? If so, how?
}
-static std::vector<gint> sp_3dbox_adjacent_faces_of_edge (guint corner1, guint corner2) {
- std::vector<gint> adj_faces;
- Box3D::Axis edge = (Box3D::Axis) (corner1 ^ corner2);
- if (!Box3D::is_single_axis_direction (edge)) {
- return adj_faces;
- }
-
- Box3D::Axis plane = Box3D::orth_plane_or_axis (edge);
- Box3D::Axis axis1 = Box3D::extract_first_axis_direction (plane);
- Box3D::Axis axis2 = Box3D::extract_second_axis_direction (plane);
- adj_faces.push_back (Box3D::face_containing_corner ((Box3D::Axis) (edge ^ axis1), corner1));
- adj_faces.push_back (Box3D::face_containing_corner ((Box3D::Axis) (edge ^ axis2), corner1));
- return adj_faces;
-}
+void box3d_set_center (SPBox3D *box, NR::Point const &new_pos, NR::Point const &old_pos, const Box3D::Axis movement, bool constrained) {
+ g_return_if_fail ((movement != Box3D::NONE) && (movement != Box3D::XYZ));
-static std::vector<gint> sp_3dbox_faces_meeting_in_corner (guint corner) {
- std::vector<gint> faces;
- for (int i = 0; i < 3; ++i) {
- faces.push_back (sp_3dbox_face_containing_diagonal_corners (corner, corner ^ Box3D::planes[i]));
- }
- return faces;
-}
+ if (!(movement & Box3D::Z)) {
+ double coord = (box->orig_corner0[Proj::Z] + box->orig_corner7[Proj::Z]) / 2;
+ double radx = (box->orig_corner7[Proj::X] - box->orig_corner0[Proj::X]) / 2;
+ double rady = (box->orig_corner7[Proj::Y] - box->orig_corner0[Proj::Y]) / 2;
-static void sp_3dbox_remaining_faces (std::vector<gint> const &faces, std::vector<gint> &rem_faces)
-{
- rem_faces.clear();
- for (gint i = 0; i < 6; ++i) {
- if (std::find (faces.begin(), faces.end(), i) == faces.end()) {
- rem_faces.push_back (i);
+ Proj::Pt3 pt_proj (box->persp_ref->getObject()->tmat.preimage (new_pos, coord, Proj::Z));
+ if (constrained) {
+ Proj::Pt3 old_pos_proj (box->persp_ref->getObject()->tmat.preimage (old_pos, coord, Proj::Z));
+ pt_proj = box3d_snap (box, -1, pt_proj, old_pos_proj);
}
+ // normalizing pt_proj is essential because we want to mingle affine coordinates
+ pt_proj.normalize();
+ box->orig_corner0 = Proj::Pt3 ((movement & Box3D::X) ? pt_proj[Proj::X] - radx : box->orig_corner0[Proj::X],
+ (movement & Box3D::Y) ? pt_proj[Proj::Y] - rady : box->orig_corner0[Proj::Y],
+ box->orig_corner0[Proj::Z],
+ 1.0);
+ box->orig_corner7 = Proj::Pt3 ((movement & Box3D::X) ? pt_proj[Proj::X] + radx : box->orig_corner7[Proj::X],
+ (movement & Box3D::Y) ? pt_proj[Proj::Y] + rady : box->orig_corner7[Proj::Y],
+ box->orig_corner7[Proj::Z],
+ 1.0);
+ } else {
+ double coord = (box->orig_corner0[Proj::X] + box->orig_corner7[Proj::X]) / 2;
+ double radz = (box->orig_corner7[Proj::Z] - box->orig_corner0[Proj::Z]) / 2;
+
+ Box3D::PerspectiveLine pl(old_pos, Proj::Z, box->persp_ref->getObject());
+ NR::Point new_pos_snapped(pl.closest_to(new_pos));
+ Proj::Pt3 pt_proj (box->persp_ref->getObject()->tmat.preimage (new_pos_snapped, coord, Proj::X));
+
+ /* normalizing pt_proj is essential because we want to mingle affine coordinates */
+ pt_proj.normalize();
+ box->orig_corner0 = Proj::Pt3 (box->orig_corner0[Proj::X],
+ box->orig_corner0[Proj::Y],
+ pt_proj[Proj::Z] - radz,
+ 1.0);
+ box->orig_corner7 = Proj::Pt3 (box->orig_corner7[Proj::X],
+ box->orig_corner7[Proj::Y],
+ pt_proj[Proj::Z] + radz,
+ 1.0);
}
}
/*
- * Given two adjacent edges (\a c2,\a c1) and (\a c2, \a c3) of \a box (with common corner \a c2),
- * check whether both lie on the convex hull of the point configuration given by \a box's corners.
+ * Manipulates corner1 through corner4 to contain the indices of the corners
+ * from which the perspective lines in the direction of 'axis' emerge
*/
-static bool
-sp_3dbox_is_border_edge_pair (SP3DBox *box, guint const c1, guint const c2, guint const c3)
+void box3d_corners_for_PLs (const SPBox3D * box, Proj::Axis axis,
+ NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4)
{
- Box3D::Axis edge21 = (Box3D::Axis) (c2 ^ c1);
- Box3D::Axis edge23 = (Box3D::Axis) (c2 ^ c3);
- Box3D::Axis rear_axis = Box3D::orth_plane_or_axis ((Box3D::Axis) (edge21 ^ edge23));
-
- NR::Point corner2 = box->corners[c2];
- NR::Point dir21 = box->corners[c1] - corner2;
- NR::Point dir23 = box->corners[c3] - corner2;
-
- if (!Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ edge21 ^ edge23] - corner2) ||
- !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis] - corner2) ||
- !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis ^ edge21] - corner2) ||
- !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis ^ edge21 ^ edge23] - corner2) ||
- !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis ^ edge23] - corner2)) {
- // corner triple c1, c2, c3 doesn't bound the convex hull
- return false;
+ g_return_if_fail (box->persp_ref->getObject());
+ //box->orig_corner0.normalize();
+ //box->orig_corner7.normalize();
+ double coord = (box->orig_corner0[axis] > box->orig_corner7[axis]) ?
+ box->orig_corner0[axis] :
+ box->orig_corner7[axis];
+
+ Proj::Pt3 c1, c2, c3, c4;
+ // FIXME: This can certainly be done more elegantly/efficiently than by a case-by-case analysis.
+ switch (axis) {
+ case Proj::X:
+ c1 = Proj::Pt3 (coord, box->orig_corner0[Proj::Y], box->orig_corner0[Proj::Z], 1.0);
+ c2 = Proj::Pt3 (coord, box->orig_corner7[Proj::Y], box->orig_corner0[Proj::Z], 1.0);
+ c3 = Proj::Pt3 (coord, box->orig_corner7[Proj::Y], box->orig_corner7[Proj::Z], 1.0);
+ c4 = Proj::Pt3 (coord, box->orig_corner0[Proj::Y], box->orig_corner7[Proj::Z], 1.0);
+ break;
+ case Proj::Y:
+ c1 = Proj::Pt3 (box->orig_corner0[Proj::X], coord, box->orig_corner0[Proj::Z], 1.0);
+ c2 = Proj::Pt3 (box->orig_corner7[Proj::X], coord, box->orig_corner0[Proj::Z], 1.0);
+ c3 = Proj::Pt3 (box->orig_corner7[Proj::X], coord, box->orig_corner7[Proj::Z], 1.0);
+ c4 = Proj::Pt3 (box->orig_corner0[Proj::X], coord, box->orig_corner7[Proj::Z], 1.0);
+ break;
+ case Proj::Z:
+ c1 = Proj::Pt3 (box->orig_corner7[Proj::X], box->orig_corner7[Proj::Y], coord, 1.0);
+ c2 = Proj::Pt3 (box->orig_corner7[Proj::X], box->orig_corner0[Proj::Y], coord, 1.0);
+ c3 = Proj::Pt3 (box->orig_corner0[Proj::X], box->orig_corner0[Proj::Y], coord, 1.0);
+ c4 = Proj::Pt3 (box->orig_corner0[Proj::X], box->orig_corner7[Proj::Y], coord, 1.0);
+ break;
+ default:
+ return;
}
- // corner triple c1, c2, c3 bounds the convex hull
- return true;
+ corner1 = box->persp_ref->getObject()->tmat.image(c1).affine();
+ corner2 = box->persp_ref->getObject()->tmat.image(c2).affine();
+ corner3 = box->persp_ref->getObject()->tmat.image(c3).affine();
+ corner4 = box->persp_ref->getObject()->tmat.image(c4).affine();
}
-/*
- * Test whether there are any adjacent corners of \a corner (i.e., connected with it along one of the axes)
- * such that the corresponding edges bound the convex hull of the box (as a point configuration in the plane)
- * If this is the case, return the corresponding two adjacent corners; otherwise return (-1, -1).
- */
-static Box3D::Axis
-sp_3dbox_axis_pair_bounding_convex_hull (SP3DBox *box, guint corner)
- {
- guint adj1 = corner ^ Box3D::X;
- guint adj2 = corner ^ Box3D::Y;
- guint adj3 = corner ^ Box3D::Z;
-
- if (sp_3dbox_is_border_edge_pair (box, adj1, corner, adj2)) {
- return Box3D::XY;
+/* Auxiliary function: Checks whether the half-line from A to B crosses the line segment joining C and D */
+static bool
+box3d_half_line_crosses_joining_line (Geom::Point const &A, Geom::Point const &B,
+ Geom::Point const &C, Geom::Point const &D) {
+ Geom::Point E; // the point of intersection
+ Geom::Point n0 = (B - A).ccw();
+ double d0 = dot(n0,A);
+
+ Geom::Point n1 = (D - C).ccw();
+ double d1 = dot(n1,C);
+ Geom::IntersectorKind intersects = Geom::line_intersection(n0, d0, n1, d1, E);
+ if (intersects == Geom::coincident || intersects == Geom::parallel) {
+ return false;
}
- if (sp_3dbox_is_border_edge_pair (box, adj1, corner, adj3)) {
- return Box3D::XZ;
+
+ if ((dot(C,n0) < d0) == (dot(D,n0) < d0)) {
+ // C and D lie on the same side of the line AB
+ return false;
}
- if (sp_3dbox_is_border_edge_pair (box, adj2, corner, adj3)) {
- return Box3D::YZ;
+ if ((dot(A,n1) < d1) != (dot(B,n1) < d1)) {
+ // A and B lie on different sides of the line CD
+ return true;
+ } else if (Geom::distance(E,A) < Geom::distance(E,B)) {
+ // The line CD passes on the "wrong" side of A
+ return false;
}
- return Box3D::NONE;
+
+ // The line CD passes on the "correct" side of A
+ return true;
}
-// inside_hull is modified 'in place' by the following function
-static void sp_3dbox_corner_configuration (SP3DBox *box, std::vector<gint> &on_hull, std::vector<gint> &inside_hull)
-{
- for (int i = 0; i < 8; ++i) {
- Box3D::Axis bounding_edges = sp_3dbox_axis_pair_bounding_convex_hull (box, i);
- if (bounding_edges != Box3D::NONE) {
- on_hull.push_back (i);
- } else {
- inside_hull.push_back (i);
- }
+static bool
+box3d_XY_axes_are_swapped (SPBox3D *box) {
+ Persp3D *persp = box->persp_ref->getObject();
+ g_return_val_if_fail(persp, false);
+ Box3D::PerspectiveLine l1(box3d_get_corner_screen(box, 3), Proj::X, persp);
+ Box3D::PerspectiveLine l2(box3d_get_corner_screen(box, 3), Proj::Y, persp);
+ NR::Point v1(l1.direction());
+ NR::Point v2(l2.direction());
+ v1.normalize();
+ v2.normalize();
+
+ return (v1[NR::X]*v2[NR::Y] - v1[NR::Y]*v2[NR::X] > 0);
+}
+
+static inline void
+box3d_aux_set_z_orders (int z_orders[6], int a, int b, int c, int d, int e, int f) {
+ z_orders[0] = a;
+ z_orders[1] = b;
+ z_orders[2] = c;
+ z_orders[3] = d;
+ z_orders[4] = e;
+ z_orders[5] = f;
+}
+
+static inline void
+box3d_swap_z_orders (int z_orders[6]) {
+ int tmp;
+ for (int i = 0; i < 3; ++i) {
+ tmp = z_orders[i];
+ z_orders[i] = z_orders[5-i];
+ z_orders[5-i] = tmp;
}
}
-/* returns true if there was a change in the z-orders (which triggers an update of the repr) */
-static bool sp_3dbox_recompute_z_orders_by_corner_configuration (SP3DBox *box)
-{
- gint new_z_orders[6];
- Box3D::Axis front_rear_axis = Box3D::Z;
+/*
+ * In der Standard-Perspektive:
+ * 2 = vorne
+ * 1 = oben
+ * 0 = links
+ * 3 = rechts
+ * 4 = unten
+ * 5 = hinten
+ */
- std::vector<gint> on_hull;
- std::vector<gint> inside_hull;
- std::vector<gint> visible_faces;
+/* All VPs infinite */
+static void
+box3d_set_new_z_orders_case0 (SPBox3D *box, int z_orders[6], Box3D::Axis central_axis) {
+ Persp3D *persp = box->persp_ref->getObject();
+ NR::Point xdir(persp3d_get_infinite_dir(persp, Proj::X));
+ NR::Point ydir(persp3d_get_infinite_dir(persp, Proj::Y));
+ NR::Point zdir(persp3d_get_infinite_dir(persp, Proj::Z));
- sp_3dbox_corner_configuration (box, on_hull, inside_hull);
+ bool swapped = box3d_XY_axes_are_swapped(box);
- switch (on_hull.size()) {
- case 4:
- {
- // the following works because on_hull is sorted
- gint front_face = sp_3dbox_face_containing_diagonal_corners (on_hull[0], on_hull[3]);
- visible_faces.push_back (front_face);
+ //g_print ("3 infinite VPs; ");
+ switch(central_axis) {
+ case Box3D::X:
+ if (!swapped) {
+ //g_print ("central axis X (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 0, 4, 1, 3, 5);
+ } else {
+ //g_print ("central axis X (case b)");
+ box3d_aux_set_z_orders (z_orders, 3, 1, 5, 2, 4, 0);
}
break;
-
- case 6:
- {
- guint c1 = inside_hull[0] ^ Box3D::XYZ;
- guint c2 = inside_hull[1] ^ Box3D::XYZ;
- Box3D::Axis edge = (Box3D::Axis) (c1 ^ c2);
- if (Box3D::is_single_axis_direction (edge)) {
- visible_faces = sp_3dbox_adjacent_faces_of_edge (c1, c2);
- } else if (c1 == c2 ^ Box3D::XYZ) {
- guint c_cmp = sp_3dbox_get_corner_id_along_edge (box, 0, front_rear_axis, Box3D::FRONT);
- guint visible_front_corner = (((c_cmp & front_rear_axis) == (c1 & front_rear_axis)) ? c1 : c2);
- visible_faces = sp_3dbox_faces_meeting_in_corner (visible_front_corner);
+ case Box3D::Y:
+ if (!swapped) {
+ //g_print ("central axis Y (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 1, 4, 0, 5);
} else {
- /* Under what conditions do we end up here? Can we safely ignore this case? */
- return false;
+ //g_print ("central axis Y (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 0, 4, 1, 3, 2);
}
break;
- }
-
- default:
- /* Under what conditions do we end up here? Can we safely ignore this case? */
- return false;
- }
-
- /* catch weird corner configurations; these should be theoretically impossible, but maybe
- occur in (almost) degenerate cases due to rounding errors, for example */
- if (std::find (visible_faces.begin(), visible_faces.end(), -1) != visible_faces.end()) {
- return false;
- }
-
- /* sort the list of visible faces for later use (although it may be already sorted anyway) */
- std::sort (visible_faces.begin(), visible_faces.end());
-
- std::vector<gint> invisible_faces;
- sp_3dbox_remaining_faces (visible_faces, invisible_faces);
-
-
- if (!sp_3dbox_is_subset_or_superset (visible_faces, box->currently_visible_faces) &&
- !sp_3dbox_differ_by_opposite_faces (visible_faces, box->currently_visible_faces)) {
- std::swap (visible_faces, invisible_faces);
- if (!sp_3dbox_is_subset_or_superset (visible_faces, box->currently_visible_faces) &&
- !sp_3dbox_differ_by_opposite_faces (visible_faces, box->currently_visible_faces)) {
- /* Hopefully this case is only caused by rounding errors or something similar;
- does it need further investigation? */
- return false;
- }
- }
-
- box->currently_visible_faces = visible_faces;
-
- // set new z-orders according to the visible/invisible faces
- guint vis_size = visible_faces.size();
- for (guint i = 0; i < vis_size; ++i) {
- new_z_orders[i] = visible_faces[i];
- }
- for (guint i = 0; i < invisible_faces.size(); ++i) {
- new_z_orders[vis_size + i] = invisible_faces[i];
- }
-
- // test whether any z-orders actually changed and indicate this in the return status
- for (int i = 0; i < 6; ++i) {
- if (box->z_orders[i] != new_z_orders[i]) {
- // we update the z-orders starting from the index where the change occurs
- for (int j = i; j < 6; ++j) {
- box->z_orders[j] = new_z_orders[j];
+ case Box3D::Z:
+ if (!swapped) {
+ //g_print ("central axis Z (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 0, 1, 4, 3, 5);
+ } else {
+ //g_print ("central axis Z (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 3, 4, 1, 0, 2);
}
- return true;
- }
- }
- return false;
-}
-
-// FIXME: Can we unify this and the next function for setting the z-orders?
-void sp_3dbox_set_z_orders_in_the_first_place (SP3DBox *box)
-{
- // For efficiency reasons, we only set the new z-orders if something really changed
- if (sp_3dbox_recompute_z_orders (box)) {
- box->faces[box->z_orders[0]]->lower_to_bottom ();
- box->faces[box->z_orders[1]]->lower_to_bottom ();
- box->faces[box->z_orders[2]]->lower_to_bottom ();
- box->faces[box->z_orders[3]]->lower_to_bottom ();
- box->faces[box->z_orders[4]]->lower_to_bottom ();
- box->faces[box->z_orders[5]]->lower_to_bottom ();
- }
-}
-
-void sp_3dbox_set_z_orders_later_on (SP3DBox *box)
-{
- // For efficiency reasons, we only set the new z-orders if something really changed
- if (sp_3dbox_recompute_z_orders_by_corner_configuration (box)) {
- box->faces[box->z_orders[0]]->lower_to_bottom ();
- box->faces[box->z_orders[1]]->lower_to_bottom ();
- box->faces[box->z_orders[2]]->lower_to_bottom ();
- box->faces[box->z_orders[3]]->lower_to_bottom ();
- box->faces[box->z_orders[4]]->lower_to_bottom ();
- box->faces[box->z_orders[5]]->lower_to_bottom ();
+ break;
+ case Box3D::NONE:
+ if (!swapped) {
+ //g_print ("central axis NONE (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 4, 1, 0, 5);
+ } else {
+ //g_print ("central axis NONE (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 0, 1, 4, 3, 2);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ break;
}
-}
-
-void
-sp_3dbox_update_curves (SP3DBox *box) {
- for (int i = 0; i < 6; ++i) {
- if (box->faces[i]) box->faces[i]->set_curve();
+ /**
+ if (swapped) {
+ g_print ("; swapped");
}
+ g_print ("\n");
+ **/
}
-/**
- * In some situations (e.g., after cloning boxes, undo & redo, or reading boxes from a file) there are
- * paths already present in the document which correspond to the faces of newly created boxes, but their
- * 'path' members don't link to them yet. The following function corrects this if necessary.
- */
-void
-sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr) {
- // TODO: We should probably destroy the existing paths and recreate them because we don't know
- // precisely which path corresponds to which face. Does this make a difference?
- // In sp_3dbox_write we write the correct paths anyway, don't we? But we could get into
- // trouble at a later stage when we only write single faces for degenerate boxes.
-
- SPDocument *document = SP_OBJECT_DOCUMENT(box);
- guint face_id = 0;
-
- for (Inkscape::XML::Node *i = sp_repr_children(repr); i != NULL; i = sp_repr_next(i)) {
- if (face_id > 5) {
- g_warning ("SVG representation of 3D boxes must contain 6 paths or less.\n");
+/* Precisely one finite VP */
+static void
+box3d_set_new_z_orders_case1 (SPBox3D *box, int z_orders[6], Box3D::Axis central_axis, Box3D::Axis fin_axis) {
+ Persp3D *persp = box->persp_ref->getObject();
+ NR::Point vp(persp3d_get_VP(persp, Box3D::toProj(fin_axis)).affine());
+
+ // note: in some of the case distinctions below we rely upon the fact that oaxis1 and oaxis2 are ordered
+ Box3D::Axis oaxis1 = Box3D::get_remaining_axes(fin_axis).first;
+ Box3D::Axis oaxis2 = Box3D::get_remaining_axes(fin_axis).second;
+ //g_print ("oaxis1 = %s, oaxis2 = %s\n", Box3D::string_from_axes(oaxis1), Box3D::string_from_axes(oaxis2));
+ int inside1 = 0;
+ int inside2 = 0;
+ inside1 = box3d_pt_lies_in_PL_sector (box, vp, 3, 3 ^ oaxis2, oaxis1);
+ inside2 = box3d_pt_lies_in_PL_sector (box, vp, 3, 3 ^ oaxis1, oaxis2);
+ //g_print ("inside1 = %d, inside2 = %d\n", inside1, inside2);
+
+ bool swapped = box3d_XY_axes_are_swapped(box);
+
+ //g_print ("2 infinite VPs; ");
+ //g_print ("finite axis: %s; ", Box3D::string_from_axes(fin_axis));
+ switch(central_axis) {
+ case Box3D::X:
+ if (!swapped) {
+ //g_print ("central axis X (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 4, 0, 1, 3, 5);
+ } else {
+ //if (inside2) {
+ //g_print ("central axis X (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 3, 1, 0, 2, 4);
+ //} else {
+ //g_print ("central axis X (case c)");
+ //box3d_aux_set_z_orders (z_orders, 5, 3, 1, 2, 0, 4);
+ //}
+ }
break;
- }
-
- SPObject *face_object = document->getObjectByRepr((Inkscape::XML::Node *) i);
- if (!SP_IS_PATH(face_object)) {
- g_warning ("SVG representation of 3D boxes should only contain paths.\n");
- continue;
- }
- // TODO: Currently we don't check whether all paths are being linked to different faces.
- // This is no problem with valid SVG files. It may lead to crashes, however,
- // in case a file is corrupt (e.g., two or more faces have identical descriptions).
- gint id = Box3DFace::descr_to_id (i->attribute ("inkscape:box3dface"));
- box->faces[id]->hook_path_to_3dbox(SP_PATH(face_object));
- ++face_id;
+ case Box3D::Y:
+ if (inside2 > 0) {
+ //g_print ("central axis Y (case a)");
+ box3d_aux_set_z_orders (z_orders, 1, 2, 3, 0, 5, 4);
+ } else if (inside2 < 0) {
+ //g_print ("central axis Y (case b)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 1, 4, 0, 5);
+ } else {
+ if (!swapped) {
+ //g_print ("central axis Y (case c1)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 1, 5, 0, 4);
+ } else {
+ //g_print ("central axis Y (case c2)");
+ box3d_aux_set_z_orders (z_orders, 5, 0, 4, 1, 3, 2);
+ }
+ }
+ break;
+ case Box3D::Z:
+ if (inside2) {
+ if (!swapped) {
+ //g_print ("central axis Z (case a1)");
+ box3d_aux_set_z_orders (z_orders, 2, 1, 3, 0, 4, 5);
+ } else {
+ //g_print ("central axis Z (case a2)");
+ box3d_aux_set_z_orders (z_orders, 5, 3, 4, 0, 1, 2);
+ }
+ } else if (inside1) {
+ if (!swapped) {
+ //g_print ("central axis Z (case b1)");
+ box3d_aux_set_z_orders (z_orders, 2, 0, 1, 4, 3, 5);
+ } else {
+ //g_print ("central axis Z (case b2)");
+ box3d_aux_set_z_orders (z_orders, 5, 3, 4, 1, 0, 2);
+ //box3d_aux_set_z_orders (z_orders, 5, 3, 0, 1, 2, 4);
+ }
+ } else {
+ // "regular" case
+ if (!swapped) {
+ //g_print ("central axis Z (case c1)");
+ box3d_aux_set_z_orders (z_orders, 0, 1, 2, 5, 4, 3);
+ } else {
+ //g_print ("central axis Z (case c2)");
+ box3d_aux_set_z_orders (z_orders, 5, 3, 4, 0, 2, 1);
+ //box3d_aux_set_z_orders (z_orders, 5, 3, 4, 0, 2, 1);
+ }
+ }
+ break;
+ case Box3D::NONE:
+ if (!swapped) {
+ //g_print ("central axis NONE (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 4, 5, 0, 1);
+ } else {
+ //g_print ("central axis NONE (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 0, 1, 3, 2, 4);
+ //box3d_aux_set_z_orders (z_orders, 2, 3, 4, 1, 0, 5);
+ }
+ break;
+ default:
+ g_assert_not_reached();
}
- if (face_id < 6) {
- //g_warning ("SVG representation of 3D boxes should contain exactly 6 paths (degenerate boxes are not yet supported).\n");
- // TODO: Check whether it is safe to add the remaining paths to the box and do so in case it is.
- // (But we also land here for newly created boxes where we shouldn't add any paths because
- // This is done in sp_3dbox_write later on.
+ /**
+ if (swapped) {
+ g_print ("; swapped");
}
+ g_print ("\n");
+ **/
}
-void
-sp_3dbox_reshape_after_VP_rotation (SP3DBox *box, Box3D::Axis axis)
-{
- Box3D::Perspective3D *persp = inkscape_active_document()->get_persp_of_box (box);
- Box3D::VanishingPoint *vp = persp->get_vanishing_point (axis);
-
- guint c1 = (axis == Box3D::Z) ? 1 : sp_3dbox_get_front_corner_id (box); // hack
- guint c2 = c1 ^ axis;
- NR::Point v = box->corners[c1] - box->corners[c2];
- double dist = NR::L2 (v) * ((NR::dot (v, vp->v_dir) < 0) ? 1 : -1); // "directed" distance
-
- Box3D::PerspectiveLine pline (box->corners[c1], axis, persp);
- NR::Point pt = pline.point_from_lambda (dist);
-
- sp_3dbox_move_corner_in_Z_direction (box, c2, pt, axis == Box3D::Z);
-}
-
-void
-sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes)
-{
- Box3D::Perspective3D * persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
-
- NR::Point A (box->corners[id ^ Box3D::XY]);
- if (Box3D::is_single_axis_direction (axes)) {
- pt = Box3D::PerspectiveLine (box->corners[id], axes, persp).closest_to(pt);
- }
+/* Precisely 2 finite VPs */
+static void
+box3d_set_new_z_orders_case2 (SPBox3D *box, int z_orders[6], Box3D::Axis central_axis, Box3D::Axis infinite_axis) {
+ Persp3D *persp = box->persp_ref->getObject();
- /* set the 'front' corners */
- box->corners[id] = pt;
+ NR::Point c3(box3d_get_corner_screen(box, 3));
+ NR::Point xdir(persp3d_get_PL_dir_from_pt(persp, c3, Proj::X));
+ NR::Point ydir(persp3d_get_PL_dir_from_pt(persp, c3, Proj::Y));
+ NR::Point zdir(persp3d_get_PL_dir_from_pt(persp, c3, Proj::Z));
- Box3D::PerspectiveLine pl_one (A, Box3D::Y, persp);
- Box3D::PerspectiveLine pl_two (pt, Box3D::X, persp);
- box->corners[id ^ Box3D::X] = pl_one.meet(pl_two);
+ bool swapped = box3d_XY_axes_are_swapped(box);
- pl_one = Box3D::PerspectiveLine (A, Box3D::X, persp);
- pl_two = Box3D::PerspectiveLine (pt, Box3D::Y, persp);
- box->corners[id ^ Box3D::Y] = pl_one.meet(pl_two);
+ int insidexy = box3d_VP_lies_in_PL_sector (box, Proj::X, 3, 3 ^ Box3D::Z, Box3D::Y);
+ int insidexz = box3d_VP_lies_in_PL_sector (box, Proj::X, 3, 3 ^ Box3D::Y, Box3D::Z);
- /* set the 'rear' corners */
- NR::Point B (box->corners[id ^ Box3D::XYZ]);
+ int insideyx = box3d_VP_lies_in_PL_sector (box, Proj::Y, 3, 3 ^ Box3D::Z, Box3D::X);
+ int insideyz = box3d_VP_lies_in_PL_sector (box, Proj::Y, 3, 3 ^ Box3D::X, Box3D::Z);
- pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::X], Box3D::Z, persp);
- pl_two = Box3D::PerspectiveLine (B, Box3D::Y, persp);
- box->corners[id ^ Box3D::XZ] = pl_one.meet(pl_two);
+ int insidezx = box3d_VP_lies_in_PL_sector (box, Proj::Z, 3, 3 ^ Box3D::Y, Box3D::X);
+ int insidezy = box3d_VP_lies_in_PL_sector (box, Proj::Z, 3, 3 ^ Box3D::X, Box3D::Y);
- pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::XZ], Box3D::X, persp);
- pl_two = Box3D::PerspectiveLine (pt, Box3D::Z, persp);
- box->corners[id ^ Box3D::Z] = pl_one.meet(pl_two);
+ //g_print ("Insides: xy = %d, xz = %d, yx = %d, yz = %d, zx = %d, zy = %d\n",
+ // insidexy, insidexz, insideyx, insideyz, insidezx, insidezy);
- pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::Z], Box3D::Y, persp);
- pl_two = Box3D::PerspectiveLine (B, Box3D::X, persp);
- box->corners[id ^ Box3D::YZ] = pl_one.meet(pl_two);
-
+ //g_print ("1 infinite VP; ");
+ switch(central_axis) {
+ case Box3D::X:
+ if (!swapped) {
+ if (insidezy == -1) {
+ //g_print ("central axis X (case a1)");
+ box3d_aux_set_z_orders (z_orders, 2, 4, 0, 1, 3, 5);
+ } else if (insidexy == 1) {
+ //g_print ("central axis X (case a2)");
+ box3d_aux_set_z_orders (z_orders, 2, 4, 0, 5, 1, 3);
+ } else {
+ //g_print ("central axis X (case a3)");
+ box3d_aux_set_z_orders (z_orders, 2, 4, 0, 1, 3, 5);
+ }
+ } else {
+ if (insideyz == -1) {
+ //g_print ("central axis X (case b1)");
+ box3d_aux_set_z_orders (z_orders, 3, 1, 5, 0, 2, 4);
+ } else {
+ if (!swapped) {
+ //g_print ("central axis X (case b2)");
+ box3d_aux_set_z_orders (z_orders, 3, 1, 5, 2, 4, 0);
+ } else {
+ //g_print ("central axis X (case b3)");
+ box3d_aux_set_z_orders (z_orders, 1, 3, 5, 0, 2, 4);
+ }
+ }
+ }
+ break;
+ case Box3D::Y:
+ if (!swapped) {
+ if (insideyz == 1) {
+ //g_print ("central axis Y (case a1)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 1, 0, 5, 4);
+ } else {
+ //g_print ("central axis Y (case a2)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 1, 5, 0, 4);
+ }
+ } else {
+ //g_print ("central axis Y (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 0, 4, 1, 3, 2);
+ }
+ break;
+ case Box3D::Z:
+ if (!swapped) {
+ if (insidezy == 1) {
+ //g_print ("central axis Z (case a1)");
+ box3d_aux_set_z_orders (z_orders, 2, 1, 0, 4, 3, 5);
+ } else if (insidexy == -1) {
+ //g_print ("central axis Z (case a2)");
+ box3d_aux_set_z_orders (z_orders, 2, 1, 0, 5, 4, 3);
+ } else {
+ //g_print ("central axis Z (case a3)");
+ box3d_aux_set_z_orders (z_orders, 2, 0, 1, 5, 3, 4);
+ }
+ } else {
+ //g_print ("central axis Z (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 3, 4, 1, 0, 2);
+ }
+ break;
+ case Box3D::NONE:
+ if (!swapped) {
+ //g_print ("central axis NONE (case a)");
+ box3d_aux_set_z_orders (z_orders, 2, 3, 4, 1, 0, 5);
+ } else {
+ //g_print ("central axis NONE (case b)");
+ box3d_aux_set_z_orders (z_orders, 5, 0, 1, 4, 3, 2);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ /**
+ if (swapped) {
+ g_print ("; swapped");
+ }
+ g_print ("\n");
+ **/
}
-void
-sp_3dbox_move_corner_in_Z_direction (SP3DBox *box, guint id, NR::Point pt, bool constrained)
-{
- if (!constrained) sp_3dbox_move_corner_in_XY_plane (box, id, pt, Box3D::XY);
-
- Box3D::Perspective3D * persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
-
- /* set the four corners of the face containing corners[id] */
- box->corners[id] = Box3D::PerspectiveLine (box->corners[id], Box3D::Z, persp).closest_to(pt);
+/*
+ * It can happen that during dragging the box is everted.
+ * In this case the opposite sides in this direction need to be swapped
+ */
+static Box3D::Axis
+box3d_everted_directions (SPBox3D *box) {
+ Box3D::Axis ev = Box3D::NONE;
- Box3D::PerspectiveLine pl_one (box->corners[id], Box3D::X, persp);
- Box3D::PerspectiveLine pl_two (box->corners[id ^ Box3D::XZ], Box3D::Z, persp);
- box->corners[id ^ Box3D::X] = pl_one.meet(pl_two);
+ box->orig_corner0.normalize();
+ box->orig_corner7.normalize();
- pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::X], Box3D::Y, persp);
- pl_two = Box3D::PerspectiveLine (box->corners[id ^ Box3D::XYZ], Box3D::Z, persp);
- box->corners[id ^ Box3D::XY] = pl_one.meet(pl_two);
+ if (box->orig_corner0[Proj::X] < box->orig_corner7[Proj::X])
+ ev = (Box3D::Axis) (ev ^ Box3D::X);
+ if (box->orig_corner0[Proj::Y] < box->orig_corner7[Proj::Y])
+ ev = (Box3D::Axis) (ev ^ Box3D::Y);
+ if (box->orig_corner0[Proj::Z] > box->orig_corner7[Proj::Z]) // FIXME: Remove the need to distinguish signs among the cases
+ ev = (Box3D::Axis) (ev ^ Box3D::Z);
- pl_one = Box3D::PerspectiveLine (box->corners[id], Box3D::Y, persp);
- pl_two = Box3D::PerspectiveLine (box->corners[id ^ Box3D::YZ], Box3D::Z, persp);
- box->corners[id ^ Box3D::Y] = pl_one.meet(pl_two);
+ return ev;
}
static void
-sp_3dbox_reshape_edge_after_VP_toggling (SP3DBox *box, const guint corner, const Box3D::Axis axis, Box3D::Perspective3D *persp)
-{
- /* Hmm, perhaps we should simply use one of the corners as the pivot point.
- But this way we minimize the amount of reshaping.
- On second thought, we need to find a way to ensure that all boxes sharing the same
- perspective are updated consistently _as a group_. That is, they should also retain
- their relative positions towards each other. */
- NR::Maybe<NR::Point> pt = sp_3dbox_get_midpoint_between_corners (box, corner, corner ^ axis);
- g_return_if_fail (pt);
-
- Box3D::Axis axis2 = ((axis == Box3D::Y) ? Box3D::X : Box3D::Y);
-
- Box3D::PerspectiveLine line1 (box->corners[corner], axis2, persp);
- Box3D::PerspectiveLine line2 (box->corners[corner ^ axis], axis2, persp);
-
- Box3D::PerspectiveLine line3 (*pt, axis, persp);
-
- NR::Point new_corner1 = line1.meet (line3);
- NR::Point new_corner2 = line2.meet (line3);
-
- box->corners[corner] = new_corner1;
- box->corners[corner ^ axis] = new_corner2;
-}
+box3d_swap_sides(int z_orders[6], Box3D::Axis axis) {
+ int pos1 = -1;
+ int pos2 = -1;
-void
-sp_3dbox_reshape_after_VP_toggling (SP3DBox *box, Box3D::Axis axis)
-{
- Box3D::Perspective3D *persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
- std::pair<Box3D::Axis, Box3D::Axis> dirs = Box3D::get_remaining_axes (axis);
-
- sp_3dbox_reshape_edge_after_VP_toggling (box, 0, axis, persp);
- sp_3dbox_reshape_edge_after_VP_toggling (box, 0 ^ dirs.first, axis, persp);
- sp_3dbox_reshape_edge_after_VP_toggling (box, 0 ^ dirs.first ^ dirs.second, axis, persp);
- sp_3dbox_reshape_edge_after_VP_toggling (box, 0 ^ dirs.second, axis, persp);
-}
-
-NR::Maybe<NR::Point>
-sp_3dbox_get_center (SP3DBox *box)
-{
- return sp_3dbox_get_midpoint_between_corners (box, 0, 7);
-}
+ for (int i = 0; i < 6; ++i) {
+ if (!(Box3D::int_to_face(z_orders[i]) & axis)) {
+ if (pos1 == -1) {
+ pos1 = i;
+ } else {
+ pos2 = i;
+ break;
+ }
+ }
+ }
-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);
+ int tmp = z_orders[pos1];
+ z_orders[pos1] = z_orders[pos2];
+ z_orders[pos2] = tmp;
}
-// 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>
-sp_3dbox_get_midpoint_between_corners (SP3DBox *box, guint id_corner1, guint id_corner2)
-{
- Box3D::Axis corner_axes = (Box3D::Axis) (id_corner1 ^ id_corner2);
- // Is all this sufficiently precise also for degenerate cases?
- if (sp_3dbox_corners_are_adjacent (id_corner1, id_corner2)) {
- Box3D::Axis orth_dir = get_perpendicular_axis_direction (corner_axes);
+bool
+box3d_recompute_z_orders (SPBox3D *box) {
+ Persp3D *persp = box->persp_ref->getObject();
- Box3D::Line diag1 (box->corners[id_corner1], box->corners[id_corner2 ^ orth_dir]);
- Box3D::Line diag2 (box->corners[id_corner1 ^ orth_dir], box->corners[id_corner2]);
- NR::Maybe<NR::Point> adjacent_face_center = diag1.intersect(diag2);
+ //g_return_val_if_fail(persp, false);
+ if (!persp)
+ return false;
- if (!adjacent_face_center) return NR::Nothing();
+ int z_orders[6];
- Box3D::Perspective3D * persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
+ NR::Point c3(box3d_get_corner_screen(box, 3));
- Box3D::PerspectiveLine pl (*adjacent_face_center, orth_dir, persp);
- return pl.intersect(Box3D::PerspectiveLine(box->corners[id_corner1], corner_axes, persp));
- } else {
- Box3D::Axis dir = Box3D::extract_first_axis_direction (corner_axes);
- Box3D::Line diag1 (box->corners[id_corner1], box->corners[id_corner2]);
- Box3D::Line diag2 (box->corners[id_corner1 ^ dir], box->corners[id_corner2 ^ dir]);
- return diag1.intersect(diag2);
+ // determine directions from corner3 to the VPs
+ int num_finite = 0;
+ Box3D::Axis axis_finite = Box3D::NONE;
+ Box3D::Axis axis_infinite = Box3D::NONE;
+ NR::Point dirs[3];
+ for (int i = 0; i < 3; ++i) {
+ dirs[i] = persp3d_get_PL_dir_from_pt(persp, c3, Box3D::toProj(Box3D::axes[i]));
+ if (persp3d_VP_is_finite(persp, Proj::axes[i])) {
+ num_finite++;
+ axis_finite = Box3D::axes[i];
+ } else {
+ axis_infinite = Box3D::axes[i];
+ }
}
-}
-
-static gchar *
-sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id)
-{
- id = id % 8;
- Inkscape::SVGOStringStream os;
- os << box->corners[id][NR::X] << "," << box->corners[id][NR::Y];
- return g_strdup(os.str().c_str());
-}
-static std::pair<gdouble, gdouble>
-sp_3dbox_get_coord_pair_from_string (const gchar *coords)
-{
- gchar **coordpair = g_strsplit( coords, ",", 0);
- // We might as well rely on g_ascii_strtod to convert the NULL pointer to 0.0,
- // but we include the following test anyway
- if (coordpair[0] == NULL || coordpair[1] == NULL) {
- g_strfreev (coordpair);
- g_warning ("Coordinate conversion failed.\n");
- return std::make_pair(0.0, 0.0);
+ // determine the "central" axis (if there is one)
+ Box3D::Axis central_axis = Box3D::NONE;
+ if(Box3D::lies_in_sector(dirs[0], dirs[1], dirs[2])) {
+ central_axis = Box3D::Z;
+ } else if(Box3D::lies_in_sector(dirs[1], dirs[2], dirs[0])) {
+ central_axis = Box3D::X;
+ } else if(Box3D::lies_in_sector(dirs[2], dirs[0], dirs[1])) {
+ central_axis = Box3D::Y;
}
- gdouble coord1 = g_ascii_strtod(coordpair[0], NULL);
- gdouble coord2 = g_ascii_strtod(coordpair[1], NULL);
- g_strfreev (coordpair);
+ switch (num_finite) {
+ case 0:
+ // TODO: Remark: In this case (and maybe one of the others, too) the z-orders for all boxes
+ // coincide, hence only need to be computed once in a more central location.
+ box3d_set_new_z_orders_case0(box, z_orders, central_axis);
+ break;
+ case 1:
+ box3d_set_new_z_orders_case1(box, z_orders, central_axis, axis_finite);
+ break;
+ case 2:
+ case 3:
+ box3d_set_new_z_orders_case2(box, z_orders, central_axis, axis_infinite);
+ break;
+ default:
+ /*
+ * For each VP F, check wether the half-line from the corner3 to F crosses the line segment
+ * joining the other two VPs. If this is the case, it determines the "central" corner from
+ * which the visible sides can be deduced. Otherwise, corner3 is the central corner.
+ */
+ // FIXME: We should eliminate the use of NR::Point altogether
+ Box3D::Axis central_axis = Box3D::NONE;
+ NR::Point vp_x = persp3d_get_VP(persp, Proj::X).affine();
+ NR::Point vp_y = persp3d_get_VP(persp, Proj::Y).affine();
+ NR::Point vp_z = persp3d_get_VP(persp, Proj::Z).affine();
+ Geom::Point vpx(vp_x[NR::X], vp_x[NR::Y]);
+ Geom::Point vpy(vp_y[NR::X], vp_y[NR::Y]);
+ Geom::Point vpz(vp_z[NR::X], vp_z[NR::Y]);
+
+ NR::Point c3 = box3d_get_corner_screen(box, 3);
+ Geom::Point corner3(c3[NR::X], c3[NR::Y]);
+
+ if (box3d_half_line_crosses_joining_line (corner3, vpx, vpy, vpz)) {
+ central_axis = Box3D::X;
+ } else if (box3d_half_line_crosses_joining_line (corner3, vpy, vpz, vpx)) {
+ central_axis = Box3D::Y;
+ } else if (box3d_half_line_crosses_joining_line (corner3, vpz, vpx, vpy)) {
+ central_axis = Box3D::Z;
+ }
+ //g_print ("Crossing: %s\n", Box3D::string_from_axes(central_axis));
- return std::make_pair(coord1, coord2);
-}
+ unsigned int central_corner = 3 ^ central_axis;
+ if (central_axis == Box3D::Z) {
+ central_corner = central_corner ^ Box3D::XYZ;
+ }
+ if (box3d_XY_axes_are_swapped(box)) {
+ //g_print ("Axes X and Y are swapped\n");
+ central_corner = central_corner ^ Box3D::XYZ;
+ }
-static gchar *
-sp_3dbox_get_perspective_string (SP3DBox *box)
-{
-
- return sp_3dbox_get_svg_descr_of_persp (SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box));
-}
-
-gchar *
-sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp)
-{
- // FIXME: We should move this code to perspective3d.cpp, but this yields compiler errors. Why?
- Inkscape::SVGOStringStream os;
-
- Box3D::VanishingPoint vp = *(persp->get_vanishing_point (Box3D::X));
- os << vp[NR::X] << "," << vp[NR::Y] << ",";
- os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
- if (vp.is_finite()) {
- os << "finite,";
- } else {
- os << "infinite,";
+ NR::Point c1(box3d_get_corner_screen(box, 1));
+ NR::Point c2(box3d_get_corner_screen(box, 2));
+ NR::Point c7(box3d_get_corner_screen(box, 7));
+
+ Geom::Point corner1(c1[NR::X], c1[NR::Y]);
+ Geom::Point corner2(c2[NR::X], c2[NR::Y]);
+ Geom::Point corner7(c7[NR::X], c7[NR::Y]);
+ // FIXME: At present we don't use the information about central_corner computed above.
+ switch (central_axis) {
+ case Box3D::Y:
+ if (!box3d_half_line_crosses_joining_line(vpz, vpy, corner3, corner2)) {
+ box3d_aux_set_z_orders (z_orders, 2, 3, 1, 5, 0, 4);
+ } else {
+ // degenerate case
+ //g_print ("Degenerate case #1\n");
+ box3d_aux_set_z_orders (z_orders, 2, 1, 3, 0, 5, 4);
+ }
+ break;
+
+ case Box3D::Z:
+ if (box3d_half_line_crosses_joining_line(vpx, vpz, corner3, corner1)) {
+ // degenerate case
+ //g_print ("Degenerate case #2\n");
+ box3d_aux_set_z_orders (z_orders, 2, 0, 1, 4, 3, 5);
+ } else if (box3d_half_line_crosses_joining_line(vpx, vpy, corner3, corner7)) {
+ // degenerate case
+ //g_print ("Degenerate case #3\n");
+ box3d_aux_set_z_orders (z_orders, 2, 1, 0, 5, 3, 4);
+ } else {
+ box3d_aux_set_z_orders (z_orders, 2, 1, 0, 3, 4, 5);
+ }
+ break;
+
+ case Box3D::X:
+ if (box3d_half_line_crosses_joining_line(vpz, vpx, corner3, corner1)) {
+ // degenerate case
+ //g_print ("Degenerate case #4\n");
+ box3d_aux_set_z_orders (z_orders, 2, 1, 0, 4, 5, 3);
+ } else {
+ box3d_aux_set_z_orders (z_orders, 2, 4, 0, 5, 1, 3);
+ }
+ break;
+
+ case Box3D::NONE:
+ box3d_aux_set_z_orders (z_orders, 2, 3, 4, 1, 0, 5);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ } // end default case
}
- vp = *(persp->get_vanishing_point (Box3D::Y));
- os << vp[NR::X] << "," << vp[NR::Y] << ",";
- os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
- if (vp.is_finite()) {
- os << "finite,";
- } else {
- os << "infinite,";
+ // TODO: If there are still errors in z-orders of everted boxes, we need to choose a variable corner
+ // instead of the hard-coded corner #3 in the computations above
+ Box3D::Axis ev = box3d_everted_directions(box);
+ for (int i = 0; i < 3; ++i) {
+ if (ev & Box3D::axes[i]) {
+ box3d_swap_sides(z_orders, Box3D::axes[i]);
+ }
}
- vp = *(persp->get_vanishing_point (Box3D::Z));
- os << vp[NR::X] << "," << vp[NR::Y] << ",";
- os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
- if (vp.is_finite()) {
- os << "finite";
- } else {
- os << "infinite";
+ // Check whether anything actually changed
+ for (int i = 0; i < 6; ++i) {
+ if (box->z_orders[i] != z_orders[i]) {
+ for (int j = i; j < 6; ++j) {
+ box->z_orders[j] = z_orders[j];
+ }
+ return true;
+ }
}
-
- return g_strdup(os.str().c_str());
+ return false;
}
-// 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));
- }
- if (cr1 == NR_HUGE) {
- return std::make_pair (A, B);
+static std::map<int, Box3DSide *>
+box3d_get_sides (SPBox3D *box) {
+ std::map<int, Box3DSide *> sides;
+ for (SPObject *side = sp_object_first_child(box); side != NULL; side = SP_OBJECT_NEXT(side)) {
+ sides[Box3D::face_to_int(sp_repr_get_int_attribute(SP_OBJECT_REPR(side),
+ "inkscape:box3dsidetype", -1))] = SP_BOX3D_SIDE(side);
}
- 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);
+ sides.erase(-1);
+ return sides;
}
-void sp_3dbox_recompute_Z_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;
-
- Box3D::PerspectiveLine aux_line1 (old_center, Box3D::Z, persp);
- Box3D::PerspectiveLine aux_line2 (new_center, Box3D::Y, persp);
- NR::Point Z1 = aux_line1.meet (aux_line2);
-
- 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_corner7, box->old_corner5, Box3D::Y, persp));
- Box3D::PerspectiveLine aux_line3 (A0, Box3D::X, persp);
- Box3D::PerspectiveLine aux_line4 (B0, Box3D::X, persp);
-
- NR::Point C0 = aux_line3.meet (aux_line1);
- NR::Point D0 = aux_line4.meet (aux_line1);
-
- std::pair<NR::Point, NR::Point> new_midpts = sp_3dbox_new_midpoints (persp, Box3D::Z, old_center, Z1, C0, D0);
- NR::Point C1 (new_midpts.first);
- NR::Point D1 (new_midpts.second);
- Box3D::PerspectiveLine aux_line5 (C1, Box3D::X, persp);
- Box3D::PerspectiveLine aux_line6 (D1, Box3D::X, persp);
-
- Box3D::PerspectiveLine aux_line7 (A0, Box3D::Z, persp);
- Box3D::PerspectiveLine aux_line8 (B0, Box3D::Z, persp);
-
- NR::Point A1 = aux_line5.meet (aux_line7);
- NR::Point B1 = aux_line6.meet (aux_line8);
-
- Box3D::PerspectiveLine aux_line9 (box->old_corner2, Box3D::Z, persp);
- Box3D::PerspectiveLine aux_line10 (box->old_corner5, Box3D::Z, persp);
-
- Box3D::PerspectiveLine aux_line11 (A1, Box3D::Y, persp);
- Box3D::PerspectiveLine aux_line12 (B1, Box3D::Y, persp);
-
- NR::Point new_corner2 = aux_line9.meet (aux_line11);
- NR::Point new_corner5 = aux_line10.meet (aux_line12);
-
- Box3D::PerspectiveLine aux_line13 (A1, Box3D::X, persp);
- NR::Point E1 = aux_line13.meet (aux_line8);
- Box3D::PerspectiveLine aux_line14 (E1, Box3D::Y, persp);
-
- NR::Point new_corner1 = aux_line10.meet (aux_line14);
-
- sp_3dbox_set_shape_from_points (box, new_corner2, new_corner1, new_corner5);
-}
-
-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);
-
- 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();
- if (!SP_IS_3DBOX_CONTEXT (ec))
- return;
-
- SP_3DBOX_CONTEXT (ec)->_vpdrag->updateLines();
+// TODO: Check whether the box is everted in any direction and swap the sides opposite to this direction
+void
+box3d_set_z_orders (SPBox3D *box) {
+ // For efficiency reasons, we only set the new z-orders if something really changed
+ if (box3d_recompute_z_orders (box)) {
+ std::map<int, Box3DSide *> sides = box3d_get_sides(box);
+ std::map<int, Box3DSide *>::iterator side;
+ for (unsigned int i = 0; i < 6; ++i) {
+ side = sides.find(box->z_orders[i]);
+ if (side != sides.end()) {
+ SP_ITEM((*side).second)->lowerToBottom();
+ }
+ }
+ /**
+ g_print ("Resetting z-orders: ");
+ for (int i = 0; i < 6; ++i) {
+ g_print ("%d ", box->z_orders[i]);
+ }
+ g_print ("\n");
+ **/
+ }
}
/*
- * Manipulates corner1 through corner4 to contain the indices of the corners
- * from which the perspective lines in the direction of 'axis' emerge
+ * Auxiliary function for z-order recomputing:
+ * Determines whether \a pt lies in the sector formed by the two PLs from the corners with IDs
+ * \a i21 and \a id2 to the VP in direction \a axis. If the VP is infinite, we say that \a pt
+ * lies in the sector if it lies between the two (parallel) PLs.
+ * \ret * 0 if \a pt doesn't lie in the sector
+ * * 1 if \a pt lies in the sector and either VP is finite of VP is infinite and the direction
+ * from the edge between the two corners to \a pt points towards the VP
+ * * -1 otherwise
*/
-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)
-{
- // along which axis to switch when takint
- Box3D::Axis switch_axis;
- if (axis == Box3D::X || axis == Box3D::Y) {
- switch_axis = (box->front_bits & axis) ? Box3D::Z : Box3D::NONE;
+// TODO: Maybe it would be useful to have a similar method for projective points pt because then we
+// can use it for VPs and perhaps merge the case distinctions during z-order recomputation.
+int
+box3d_pt_lies_in_PL_sector (SPBox3D const *box, NR::Point const &pt, int id1, int id2, Box3D::Axis axis) {
+ Persp3D *persp = box->persp_ref->getObject();
+
+ // the two corners
+ NR::Point c1(box3d_get_corner_screen(box, id1));
+ NR::Point c2(box3d_get_corner_screen(box, id2));
+
+ int ret = 0;
+ if (persp3d_VP_is_finite(persp, Box3D::toProj(axis))) {
+ NR::Point vp(persp3d_get_VP(persp, Box3D::toProj(axis)).affine());
+ NR::Point v1(c1 - vp);
+ NR::Point v2(c2 - vp);
+ NR::Point w(pt - vp);
+ ret = static_cast<int>(Box3D::lies_in_sector(v1, v2, w));
+ //g_print ("Case 0 - returning %d\n", ret);
} else {
- switch_axis = (box->front_bits & axis) ? Box3D::X : Box3D::NONE;
+ Box3D::PerspectiveLine pl1(c1, Box3D::toProj(axis), persp);
+ Box3D::PerspectiveLine pl2(c2, Box3D::toProj(axis), persp);
+ if (pl1.lie_on_same_side(pt, c2) && pl2.lie_on_same_side(pt, c1)) {
+ // test whether pt lies "towards" or "away from" the VP
+ Box3D::Line edge(c1,c2);
+ NR::Point c3(box3d_get_corner_screen(box, id1 ^ axis));
+ if (edge.lie_on_same_side(pt, c3)) {
+ ret = 1;
+ } else {
+ ret = -1;
+ }
+ }
+ //g_print ("Case 1 - returning %d\n", ret);
}
-
- switch (axis) {
- case Box3D::X:
- corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
- corner2 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
- corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
- corner4 = sp_3dbox_get_corner_along_edge (box, 6 ^ switch_axis, axis, Box3D::REAR);
- break;
- case Box3D::Y:
- corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
- corner2 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
- corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
- corner4 = sp_3dbox_get_corner_along_edge (box, 5 ^ switch_axis, axis, Box3D::REAR);
- break;
- case Box3D::Z:
- corner1 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
- corner2 = sp_3dbox_get_corner_along_edge (box, 3 ^ switch_axis, axis, Box3D::REAR);
- corner3 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
- corner4 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
- break;
- default:
- // do nothing
- break;
- }
+ return ret;
}
-/**
- * Returns the id of the corner on the edge along 'axis' and passing through 'corner' that
- * lies on the front/rear face in this direction.
- */
-guint
-sp_3dbox_get_corner_id_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
-{
- guint result;
- guint other_corner = corner ^ axis;
- Box3D::VanishingPoint *vp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->get_vanishing_point(axis);
- if (vp->is_finite()) {
- result = ( NR::L2 (vp->get_pos() - box->corners[corner])
- < NR::L2 (vp->get_pos() - box->corners[other_corner]) ? other_corner : corner);
- } else {
- // clear the axis bit and switch to the appropriate corner along axis, depending on the value of front_bits
- result = ((corner & (0xF ^ axis)) ^ (box->front_bits & axis));
- }
+int
+box3d_VP_lies_in_PL_sector (SPBox3D const *box, Proj::Axis vpdir, int id1, int id2, Box3D::Axis axis) {
+ Persp3D *persp = box->persp_ref->getObject();
- if (rel_pos == Box3D::FRONT) {
- return result;
+ if (!persp3d_VP_is_finite(persp, vpdir)) {
+ return 0;
} else {
- return result ^ axis;
+ return box3d_pt_lies_in_PL_sector(box, persp3d_get_VP(persp, vpdir).affine(), id1, id2, axis);
}
}
-NR::Point
-sp_3dbox_get_corner_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
-{
- return box->corners[sp_3dbox_get_corner_id_along_edge (box, corner, axis, rel_pos)];
-}
-
-guint
-sp_3dbox_get_front_corner_id (const SP3DBox *box)
-{
- guint front_corner = 1; // this could in fact be any corner, but we choose the one that is normally in front
- front_corner = sp_3dbox_get_corner_id_along_edge (box, front_corner, Box3D::X, Box3D::FRONT);
- front_corner = sp_3dbox_get_corner_id_along_edge (box, front_corner, Box3D::Y, Box3D::FRONT);
- front_corner = sp_3dbox_get_corner_id_along_edge (box, front_corner, Box3D::Z, Box3D::FRONT);
- return front_corner;
-}
-
-// auxiliary functions
+/* swap the coordinates of corner0 and corner7 along the specified axis */
static void
-sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value)
-{
- if (value == NULL) return;
- SP3DBox *box = SP_3DBOX(object);
-
- std::pair<gdouble, gdouble> coord_pair = sp_3dbox_get_coord_pair_from_string (value);
- box->corners[corner_id] = NR::Point (coord_pair.first, coord_pair.second);
- sp_3dbox_recompute_corners (box, box->corners[2], box->corners[1], box->corners[5]);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-}
-
-static void
-sp_3dbox_update_perspective (Box3D::Perspective3D *persp, const gchar *value)
-{
- // WARNING! This function changes the perspective associated to 'box'. Since there may be
- // many other boxes linked to the same perspective, their perspective is also changed.
- // If this behaviour is not desired in all cases, we need a different function.
- if (value == NULL) return;
-
- gchar **vps = g_strsplit( value, ",", 0);
- for (int i = 0; i < 15; ++i) {
- if (vps[i] == NULL) {
- g_warning ("Malformed svg attribute 'perspective'\n");
- return;
- }
+box3d_swap_coords(SPBox3D *box, Proj::Axis axis, bool smaller = true) {
+ box->orig_corner0.normalize();
+ box->orig_corner7.normalize();
+ if ((box->orig_corner0[axis] < box->orig_corner7[axis]) != smaller) {
+ double tmp = box->orig_corner0[axis];
+ box->orig_corner0[axis] = box->orig_corner7[axis];
+ box->orig_corner7[axis] = tmp;
}
+ // FIXME: Should we also swap the coordinates of save_corner0 and save_corner7?
+}
- persp->set_vanishing_point (Box3D::X, g_ascii_strtod (vps[0], NULL), g_ascii_strtod (vps[1], NULL),
- g_ascii_strtod (vps[2], NULL), g_ascii_strtod (vps[3], NULL),
- strcmp (vps[4], "finite") == 0 ? Box3D::VP_FINITE : Box3D::VP_INFINITE);
- persp->set_vanishing_point (Box3D::Y, g_ascii_strtod (vps[5], NULL), g_ascii_strtod (vps[6], NULL),
- g_ascii_strtod (vps[7], NULL), g_ascii_strtod (vps[8], NULL),
- strcmp (vps[9], "finite") == 0 ? Box3D::VP_FINITE : Box3D::VP_INFINITE);
- persp->set_vanishing_point (Box3D::Z, g_ascii_strtod (vps[10], NULL), g_ascii_strtod (vps[11], NULL),
- g_ascii_strtod (vps[12], NULL), g_ascii_strtod (vps[13], NULL),
- strcmp (vps[14], "finite") == 0 ? Box3D::VP_FINITE : Box3D::VP_INFINITE);
-
- // update the other boxes linked to the same perspective
- persp->reshape_boxes (Box3D::XYZ);
+/* ensure that the coordinates of corner0 and corner7 are in the correct order (to prevent everted boxes) */
+void
+box3d_relabel_corners(SPBox3D *box) {
+ box3d_swap_coords(box, Proj::X, false);
+ box3d_swap_coords(box, Proj::Y, false);
+ box3d_swap_coords(box, Proj::Z, true);
}
/*
diff --git a/src/box3d.h b/src/box3d.h
index 1e567ded95d2e1751d73cda2e8fb7bff9d9691f1..c676696e99e978b32caba2f7b58f45a55fab1837 100644 (file)
--- a/src/box3d.h
+++ b/src/box3d.h
-#ifndef __SP_3DBOX_H__
-#define __SP_3DBOX_H__
+#ifndef __SP_BOX3D_H__
+#define __SP_BOX3D_H__
/*
* SVG <box3d> implementation
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include "inkscape.h"
-#include "perspective-line.h"
-
#include "sp-item-group.h"
-#include "sp-path.h"
-#include "xml/document.h"
-#include "xml/repr.h"
-#include "line-geometry.h"
-#include "box3d-face.h"
-
+#include "proj_pt.h"
+#include "axis-manip.h"
-#define SP_TYPE_3DBOX (sp_3dbox_get_type ())
-#define SP_3DBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_3DBOX, SP3DBox))
-#define SP_3DBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_3DBOX, SP3DBoxClass))
-#define SP_IS_3DBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_3DBOX))
-#define SP_IS_3DBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_3DBOX))
+#define SP_TYPE_BOX3D (box3d_get_type ())
+#define SP_BOX3D(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_BOX3D, SPBox3D))
+#define SP_BOX3D_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_BOX3D, Box3DClass))
+#define SP_IS_BOX3D(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_BOX3D))
+#define SP_IS_BOX3D_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_BOX3D))
+class Box3DSide;
+class Persp3D;
+class Persp3DReference;
-struct SP3DBox : public SPGroup {
- NR::Point corners[8];
- Box3DFace *faces[6];
+struct SPBox3D : public SPGroup {
gint z_orders[6]; // z_orders[i] holds the ID of the face at position #i in the group (from top to bottom)
- std::vector<gint> currently_visible_faces;
+ gchar *persp_href;
+ Persp3DReference *persp_ref;
- // TODO: Keeping/updating the ratios works reasonably well but is still an ad hoc implementation.
- // Use a mathematically correct model to update the boxes.
- double ratio_x;
- double ratio_y;
- double ratio_z;
+ sigc::connection modified_connection;
- guint front_bits; /* used internally to determine which of two parallel faces is supposed to be the front face */
+ Proj::Pt3 orig_corner0;
+ Proj::Pt3 orig_corner7;
- // 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)
- // Also, it may be better not to store the old corners but rather the old lines to which we want to snap
- NR::Point old_center;
- NR::Point old_corner2;
- NR::Point old_corner1;
- NR::Point old_corner0;
- NR::Point old_corner3;
- NR::Point old_corner5;
- NR::Point old_corner7;
+ Proj::Pt3 save_corner0;
+ Proj::Pt3 save_corner7;
- gint my_counter; // for testing only
+ gint my_counter; // for debugging only
};
-struct SP3DBoxClass {
- SPGroupClass parent_class;
+struct SPBox3DClass {
+ SPGroupClass parent_class;
};
-GType sp_3dbox_get_type (void);
-
-void sp_3dbox_position_set (SP3DBoxContext &bc);
-void sp_3dbox_set_shape(SP3DBox *box3d, bool use_previous_corners = false);
-void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const pt1, NR::Point const pt2, NR::Point const pt3);
-void sp_3dbox_set_z_orders_in_the_first_place (SP3DBox *box);
-void sp_3dbox_set_z_orders_later_on (SP3DBox *box);
-void sp_3dbox_update_curves (SP3DBox *box);
-void sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr);
-void sp_3dbox_set_ratios (SP3DBox *box, Box3D::Axis axes = Box3D::XYZ);
-void sp_3dbox_switch_front_face (SP3DBox *box, Box3D::Axis axis);
-void sp_3dbox_reshape_after_VP_rotation (SP3DBox *box, Box3D::Axis axis);
-void sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes = Box3D::XY);
-void sp_3dbox_move_corner_in_Z_direction (SP3DBox *box, guint id, NR::Point pt, bool constrained = true);
-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);
-void sp_3dbox_recompute_Z_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);
-guint sp_3dbox_get_corner_id_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos);
-NR::Point sp_3dbox_get_corner_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos);
-guint sp_3dbox_get_front_corner_id (const SP3DBox *box);
-
-
-gchar * sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp);
-
-inline NR::Point sp_3dbox_get_corner (SP3DBox *box, guint id) { return box->corners[id]; }
-inline bool sp_3dbox_corners_are_adjacent (guint id_corner1, guint id_corner2) {
- return Box3D::is_single_axis_direction ((Box3D::Axis) (id_corner1 ^ id_corner2));
-}
-
-#endif
+GType box3d_get_type (void);
+
+void box3d_position_set (SPBox3D *box);
+Proj::Pt3 box3d_get_proj_corner (SPBox3D const *box, guint id);
+NR::Point box3d_get_corner_screen (SPBox3D const *box, guint id);
+Proj::Pt3 box3d_get_proj_center (SPBox3D *box);
+NR::Point box3d_get_center_screen (SPBox3D *box);
+
+void box3d_set_corner (SPBox3D *box, guint id, NR::Point const &new_pos, Box3D::Axis movement, bool constrained);
+void box3d_set_center (SPBox3D *box, NR::Point const &new_pos, NR::Point const &old_pos, Box3D::Axis movement, bool constrained);
+void box3d_corners_for_PLs (const SPBox3D * box, Proj::Axis axis, NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4);
+bool box3d_recompute_z_orders (SPBox3D *box);
+void box3d_set_z_orders (SPBox3D *box);
+
+int box3d_pt_lies_in_PL_sector (SPBox3D const *box, NR::Point const &pt, int id1, int id2, Box3D::Axis axis);
+int box3d_VP_lies_in_PL_sector (SPBox3D const *box, Proj::Axis vpdir, int id1, int id2, Box3D::Axis axis);
+
+/* ensures that the coordinates of corner0 and corner7 are in the correct order (to prevent everted boxes) */
+void box3d_relabel_corners(SPBox3D *box);
+
+
+#endif /* __SP_BOX3D_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp
index 236130173fec7f4536e6429f23c2f540ddcd920a..eaa4ee6a7a5be8473f13d636a40e9e6867e3d59d 100644 (file)
--- a/src/desktop-style.cpp
+++ b/src/desktop-style.cpp
#include "desktop-style.h"
#include "svg/svg-icc-color.h"
+#include "box3d-side.h"
/**
* Set color on selection on desktop.
@@ -165,9 +166,11 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write
sp_repr_css_change(inkscape_get_repr(INKSCAPE, "desktop"), css_write, "style");
for (const GSList *i = desktop->selection->itemList(); i != NULL; i = i->next) {
/* last used styles for 3D box faces are stored separately */
- if (SP_IS_PATH (i->data)) {
- const char * descr = SP_OBJECT_REPR (G_OBJECT (i->data))->attribute ("inkscape:box3dface");
+ if (SP_IS_BOX3D_SIDE (i->data)) {
+ //const char * descr = SP_OBJECT_REPR (G_OBJECT (i->data))->attribute ("inkscape:box3dside");
+ const char * descr = box3d_side_axes_string(SP_BOX3D_SIDE(i->data));
if (descr != NULL) {
+ g_print ("################ Box3DSide description found.\n");
gchar *style_grp = g_strconcat ("desktop.", descr, NULL);
sp_repr_css_change(inkscape_get_repr(INKSCAPE, style_grp), css_write, "style");
g_free (style_grp);
index efc4fd112a34fe4257aee30d8d4403453de65263..6fca902cef03c95bd07a9b8e73539a99179a324b 100644 (file)
@@ -2118,10 +2118,10 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear,
/* 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)) {
+ if (SP_IS_BOX3D_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);
+ Box3DContext *bc = SP_BOX3D_CONTEXT (ec);
bc->_vpdrag->updateLines();
}
}
diff --git a/src/document.cpp b/src/document.cpp
index 0207fe5979c3e6c5824b7dbddac303e935b05117..4b71576096b3ce85f633fa0b7dd76053d980f2c3 100644 (file)
--- a/src/document.cpp
+++ b/src/document.cpp
#include "libavoid/router.h"
#include "libnr/nr-rect.h"
#include "sp-item-group.h"
-#include "perspective3d.h"
#include "profile-manager.h"
+#include "persp3d.h"
#include "display/nr-arena-item.h"
#include "dialogs/rdf.h"
+#include "transf_mat_3x4.h"
+
#define A4_WIDTH_STR "210mm"
#define A4_HEIGHT_STR "297mm"
perspectives = NULL;
- /* Create an initial perspective, make it current and append it to the list of existing perspectives */
- current_perspective = new Box3D::Perspective3D (
- // VP in x-direction
- Box3D::VanishingPoint( NR::Point(-50.0, 600.0),
- NR::Point( -1.0, 0.0), Box3D::VP_FINITE),
- // VP in y-direction
- Box3D::VanishingPoint( NR::Point(500.0,1000.0),
- NR::Point( 0.0, 1.0), Box3D::VP_INFINITE),
- // VP in z-direction
- Box3D::VanishingPoint( NR::Point(700.0, 600.0),
- NR::Point(sqrt(3.0),1.0), Box3D::VP_FINITE),
- this);
-
- add_perspective (current_perspective);
-
p = new SPDocumentPrivate();
p->serial = next_serial++;
//delete this->_whiteboard_session_manager;
- current_perspective = NULL;
- // TODO: Do we have to delete the perspectives?
- /***
- for (GSList *i = perspectives; i != NULL; ++i) {
- delete ((Box3D::Perspective3D *) i->data);
- }
- g_slist_free (perspectives);
- ***/
}
-void SPDocument::add_perspective (Box3D::Perspective3D * const persp)
+void SPDocument::add_persp3d (Persp3D * const persp)
{
- // FIXME: Should we handle the case that the perspectives have equal VPs but are not identical?
- // If so, we need to take care of relinking the boxes, etc.
- if (persp == NULL || g_slist_find (perspectives, persp)) return;
- perspectives = g_slist_prepend (perspectives, persp);
-}
-
-void SPDocument::remove_perspective (Box3D::Perspective3D * const persp)
-{
- if (persp == NULL || !g_slist_find (perspectives, persp)) return;
- perspectives = g_slist_remove (perspectives, persp);
-}
-
-// find an existing perspective whose VPs are equal to those of persp
-Box3D::Perspective3D * SPDocument::find_perspective (const Box3D::Perspective3D * persp)
-{
- for (GSList *p = perspectives; p != NULL; p = p->next) {
- if (*((Box3D::Perspective3D *) p->data) == *persp) {
- return ((Box3D::Perspective3D *) p->data);
+ SPDefs *defs = SP_ROOT(this->root)->defs;
+ for (SPObject *i = sp_object_first_child(SP_OBJECT(defs)); i != NULL; i = SP_OBJECT_NEXT(i) ) {
+ if (SP_IS_PERSP3D(i)) {
+ g_print ("Encountered a Persp3D in defs\n");
}
}
- return NULL; // perspective was not found
-}
-Box3D::Perspective3D * SPDocument::get_persp_of_box (const SP3DBox *box)
-{
- for (GSList *p = perspectives; p != NULL; p = p->next) {
- if (((Box3D::Perspective3D *) p->data)->has_box (box))
- return (Box3D::Perspective3D *) p->data;
- }
- g_warning ("Stray 3D box!\n");
- g_assert_not_reached();
+ g_print ("Adding Persp3D to defs\n");
+ persp3d_create_xml_element (this);
}
-Box3D::Perspective3D * SPDocument::get_persp_of_VP (const Box3D::VanishingPoint *vp)
+void SPDocument::remove_persp3d (Persp3D * const persp)
{
- Box3D::Perspective3D *persp;
- for (GSList *p = perspectives; p != NULL; p = p->next) {
- persp = (Box3D::Perspective3D *) p->data;
- // we compare the pointers, not the position/state of the VPs; is this correct?
- if (persp->get_vanishing_point (Box3D::X) == vp ||
- persp->get_vanishing_point (Box3D::Y) == vp ||
- persp->get_vanishing_point (Box3D::Z) == vp)
- return persp;
- }
-
- g_warning ("Stray vanishing point!\n");
- g_assert_not_reached();
+ // TODO: Delete the repr, maybe perform a check if any boxes are still linked to the perspective.
+ // Anything else?
+ g_print ("Please implement deletion of perspectives here.\n");
}
unsigned long SPDocument::serial() const {
inkscape_ref();
}
+ /* Create an initial perspective, make it current and append it to the list of existing perspectives */
+ /***
+ document->current_perspective = new Box3D::Perspective3D (
+ // VP in x-direction
+ Box3D::VanishingPoint( NR::Point(-50.0, 600.0),
+ NR::Point( -1.0, 0.0), Box3D::VP_FINITE),
+ // VP in y-direction
+ Box3D::VanishingPoint( NR::Point(500.0,1000.0),
+ NR::Point( 0.0, 1.0), Box3D::VP_INFINITE),
+ // VP in z-direction
+ Box3D::VanishingPoint( NR::Point(700.0, 600.0),
+ NR::Point(sqrt(3.0),1.0), Box3D::VP_FINITE),
+ document);
+
+ document->add_perspective (document->current_perspective);
+ ***/
+
+ // Remark: Here, we used to create a "currentpersp3d" element in the document defs.
+ // But this is probably a bad idea since we need to adapt it for every change of selection, which will
+ // completely clutter the undo history. Maybe rather save it to prefs on exit and re-read it on startup?
+
+ Proj::Pt2 proj_vp_x = Proj::Pt2 (-50.0, 600.0, 1.0);
+ Proj::Pt2 proj_vp_y = Proj::Pt2 ( 0.0,1000.0, 0.0);
+ Proj::Pt2 proj_vp_z = Proj::Pt2 (700.0, 600.0, 1.0);
+ Proj::Pt2 proj_origin = Proj::Pt2 (300.0, 400.0, 1.0);
+
+ document->current_persp3d = (Persp3D *) persp3d_create_xml_element (document);
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(document->current_persp3d);
+
+ gchar *str = NULL;
+ str = proj_vp_x.coord_string();
+ repr->setAttribute("inkscape:vp_x", str);
+ g_free (str);
+ str = proj_vp_y.coord_string();
+ repr->setAttribute("inkscape:vp_y", str);
+ g_free (str);
+ str = proj_vp_z.coord_string();
+ repr->setAttribute("inkscape:vp_z", str);
+ g_free (str);
+ str = proj_origin.coord_string();
+ repr->setAttribute("inkscape:persp3d-origin", str);
+ g_free (str);
+ Inkscape::GC::release(repr);
+
+ /***
+ document->current_persp3d = (Persp3D *) sp_object_get_child_by_repr (SP_OBJECT(defs), repr);
+ g_assert (document->current_persp3d != NULL);
+ persp3d_update_with_point (document->current_persp3d, Proj::X, proj_vp_x);
+ persp3d_update_with_point (document->current_persp3d, Proj::Y, proj_vp_y);
+ persp3d_update_with_point (document->current_persp3d, Proj::Z, proj_vp_z);
+ persp3d_update_with_point (document->current_persp3d, Proj::W, proj_origin);
+ ***/
+
sp_document_set_undo_sensitive(document, true);
// reset undo key when selection changes, so that same-key actions on different objects are not coalesced
diff --git a/src/document.h b/src/document.h
index e1b405f1879fba37c0ce84ad9bb41fab9f81021c..6e2693aedc53fc827360b2dba15a48f01fdd1223 100644 (file)
--- a/src/document.h
+++ b/src/document.h
#include <glibmm/ustring.h>
#include "verbs.h"
#include <vector>
+#include <set>
namespace Avoid {
class Router;
}
class SP3DBox;
+class Persp3D;
-namespace Box3D {
- class Perspective3D;
- class VanishingPoint;
+namespace Proj {
+ class TransfMat3x4;
}
class SPDocumentPrivate;
Avoid::Router *router;
GSList *perspectives;
- Box3D::Perspective3D *current_perspective;
- // FIXME: Perspectives should be linked to the list of existing ones automatically in the constructor
- // and removed in the destructor!
- void add_perspective (Box3D::Perspective3D * const persp);
- void remove_perspective (Box3D::Perspective3D * const persp);
- /* find an existing perspective whose VPs are equal to those of persp */
- Box3D::Perspective3D * find_perspective (const Box3D::Perspective3D * persp);
+ Persp3D *current_persp3d; // "currently active" perspective (e.g., newly created boxes are attached to this one)
+ std::set<Persp3D *> persps_sel; // perspectives associated to currently selected boxes
- Box3D::Perspective3D * get_persp_of_box (const SP3DBox *box);
- Box3D::Perspective3D * get_persp_of_VP (const Box3D::VanishingPoint *vp);
+ void add_persp3d (Persp3D * const persp);
+ void remove_persp3d (Persp3D * const persp);
sigc::connection connectModified(ModifiedSignal::slot_type slot);
sigc::connection connectURISet(URISetSignal::slot_type slot);
@@ -266,3 +262,14 @@ void sp_document_resized_signal_emit (SPDocument *doc, gdouble width, gdouble he
unsigned int vacuum_document (SPDocument *document);
#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/gc-anchored.cpp b/src/gc-anchored.cpp
index 3f4cfc12dcf8cbe1a3022882495dbfe451f480f5..91055c968259493cd9ca562c6b9207bf07308b51 100644 (file)
--- a/src/gc-anchored.cpp
+++ b/src/gc-anchored.cpp
void Anchored::release() const {
Debug::EventTracker<ReleaseEvent> tracker(this);
+ g_return_if_fail(_anchor);
if (!--_anchor->refcount) {
_free_anchor(_anchor);
_anchor = NULL;
diff --git a/src/knotholder.cpp b/src/knotholder.cpp
index 3c61e980be70222842975495220a309ccde7424d..161e779d1baf97b7134dec2375baeae60c80667b 100644 (file)
--- a/src/knotholder.cpp
+++ b/src/knotholder.cpp
if (SP_IS_RECT(item))
object_verb = SP_VERB_CONTEXT_RECT;
- else if (SP_IS_3DBOX(item))
+ else if (SP_IS_BOX3D(item))
object_verb = SP_VERB_CONTEXT_3DBOX;
else if (SP_IS_GENERICELLIPSE(item))
object_verb = SP_VERB_CONTEXT_ARC;
@@ -293,7 +293,7 @@ static void knot_ungrabbed_handler(SPKnot */*knot*/, unsigned int /*state*/, SPK
if (SP_IS_RECT(object))
object_verb = SP_VERB_CONTEXT_RECT;
- else if (SP_IS_3DBOX(object))
+ else if (SP_IS_BOX3D(object))
object_verb = SP_VERB_CONTEXT_3DBOX;
else if (SP_IS_GENERICELLIPSE(object))
object_verb = SP_VERB_CONTEXT_ARC;
diff --git a/src/knotholder.h b/src/knotholder.h
index 18b6c4165241b38fa6de694a62a238bb2d95f871..fd09c7b23fb4ee2802fffd181087e0718a7405f9 100644 (file)
--- a/src/knotholder.h
+++ b/src/knotholder.h
GType sp_knot_holder_get_type();
-// For testing. What is the right way to update the knots from Perspective3D::reshape_boxes() ?
+// FIXME: This is an ugly hack! What is the right way to update the knots from VPDrag::updateBoxHandles() ?
void knotholder_update_knots(SPKnotHolder *knot_holder, SPItem *item);
#define SP_TYPE_KNOT_HOLDER (sp_knot_holder_get_type())
diff --git a/src/line-geometry.cpp b/src/line-geometry.cpp
index 872e9ed6b7f58ba47d9f6160ac168d299c09a064..d050ec458c266605d4142a65e09e4593080508f5 100644 (file)
--- a/src/line-geometry.cpp
+++ b/src/line-geometry.cpp
#include "line-geometry.h"
#include "inkscape.h"
+#include "desktop.h"
#include "desktop-style.h"
#include "desktop-handles.h"
#include "display/sp-canvas.h"
#include "display/sodipodi-ctrl.h"
-//#include "display/curve.cpp"
namespace Box3D {
@@ -178,57 +178,21 @@ 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)
+NR::Maybe<NR::Point> Line::intersection_with_viewbox (SPDesktop *desktop)
{
- 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) {
- // We return NR_HUGE so that we can catch this case in the calling functions
- return NR_HUGE;
+ 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();
}
- 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 {
- if (B == D) {
- // catch this case so that the line BD below is non-degenerate
- return 0;
- }
- 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) {
- // We return NR_HUGE so that we can catch this case in the calling functions
- 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));
+ Line line (e.first, e.second);
+ return this->intersect (line);
}
void create_canvas_point(NR::Point const &pos, double size, guint32 rgba)
diff --git a/src/line-geometry.h b/src/line-geometry.h
index e678c40312a958dfc6e97c16c910578720b84719..5e3152c03c97e44e9f4be6b32cdf559af56db5d6 100644 (file)
--- a/src/line-geometry.h
+++ b/src/line-geometry.h
#include "libnr/nr-maybe.h"
#include "glib.h"
#include "display/sp-ctrlline.h"
-#include "vanishing-point.h"
+#include "axis-manip.h" // FIXME: This is only for Box3D::epsilon; move that to a better location
#include "document.h"
#include "ui/view/view.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);
- friend NR::Point fourth_pt_with_given_cross_ratio (NR::Point const &A, NR::Point const &C, NR::Point const &D, double gamma);
+ NR::Maybe<NR::Point> intersection_with_viewbox (SPDesktop *desktop);
+ inline bool lie_on_same_side (NR::Point const &A, NR::Point const &B) {
+ /* If A is a point in the plane and n is the normal vector of the line then
+ the sign of dot(A, n) specifies the half-plane in which A lies.
+ Thus A and B lie on the same side if the dot products have equal sign. */
+ return ((NR::dot(A, normal) - d0) * (NR::dot(B, normal) - d0)) > 0;
+ }
double lambda (NR::Point const pt);
inline NR::Point point_from_lambda (double const lambda) {
@@ -66,14 +72,10 @@ std::pair<NR::Point, NR::Point> side_of_intersection (NR::Point const &A, NR::Po
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 ***/
+/*** For debugging 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);
-/*** For testing purposes: Draw a line between the specified points ***/
+/*** For debugging purposes: Draw a line between the specified points ***/
void create_canvas_line(NR::Point const &p1, NR::Point const &p2, guint32 rgba = 0xff00007f);
diff --git a/src/object-edit.cpp b/src/object-edit.cpp
index 1ef02672f4c04b222350b25f42691d8ec58301be..9c8db0936210a1130434fcc5dbe9867c1d18c7af 100644 (file)
--- a/src/object-edit.cpp
+++ b/src/object-edit.cpp
#include <libnr/nr-scale-ops.h>
-
#include "xml/repr.h"
#include "isnan.h"
#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
static SPKnotHolder *sp_rect_knot_holder(SPItem *item, SPDesktop *desktop);
-//static
-SPKnotHolder *sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop);
+static SPKnotHolder *box3d_knot_holder(SPItem *item, SPDesktop *desktop);
static SPKnotHolder *sp_arc_knot_holder(SPItem *item, SPDesktop *desktop);
static SPKnotHolder *sp_star_knot_holder(SPItem *item, SPDesktop *desktop);
static SPKnotHolder *sp_spiral_knot_holder(SPItem *item, SPDesktop *desktop);
{
if (SP_IS_RECT(item)) {
return sp_rect_knot_holder(item, desktop);
- } else if (SP_IS_3DBOX(item)) {
- return sp_3dbox_knot_holder(item, desktop);
+ } else if (SP_IS_BOX3D(item)) {
+ return box3d_knot_holder(item, desktop);
} else if (SP_IS_ARC(item)) {
return sp_arc_knot_holder(item, desktop);
} else if (SP_IS_STAR(item)) {
return knot_holder;
}
-/* 3D Box */
+/* Box3D (= the new 3D box structure) */
-static inline Box3D::Axis movement_axis_of_3dbox_corner (guint corner, guint state)
+static NR::Point box3d_knot_get(SPItem *item, guint knot_id)
{
- // this function has the purpose to simplify a change in the resizing behaviour of boxes
- switch (corner) {
- case 0:
- case 1:
- case 2:
- case 3:
- return ((state & GDK_SHIFT_MASK) ? Box3D::Z : Box3D::XY);
- case 4:
- case 5:
- case 6:
- case 7:
- return ((state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z);
- }
- return Box3D::NONE;
-}
-
-/*
- * To keep the snappoint from jumping randomly between the two lines when the mouse pointer is close to
- * their intersection, we remember the last snapped line and keep snapping to this specific line as long
- * as the distance from the intersection to the mouse pointer is less than remember_snap_threshold.
- */
+ g_assert(item != NULL);
+ SPBox3D *box = SP_BOX3D(item);
-// Should we make the threshold settable in the preferences?
-static double remember_snap_threshold = 30;
-static guint remember_snap_index = 0;
-static guint remember_snap_index_center = 0;
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
+ return box3d_get_corner_screen(box, knot_id) * i2d;
+}
-static NR::Point snap_knot_position_3dbox (SP3DBox *box, guint corner, Box3D::Axis direction, NR::Point const &origin, NR::Point const &p, guint /*state*/)
+static void box3d_knot_set(SPItem *item, guint knot_id, NR::Point const &new_pos, NR::Point const &origin, guint state)
{
- SPDesktop * desktop = inkscape_active_desktop();
- Box3D::Perspective3D *persp = sp_desktop_document (desktop)->get_persp_of_box (box);
-
- if (is_single_axis_direction (direction)) return p;
-
- Box3D::Axis axis1 = Box3D::extract_first_axis_direction (direction);
- Box3D::Axis axis2 = Box3D::extract_second_axis_direction (direction);
-
- NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (box)));
- NR::Point origin_dt = origin * i2d;
- NR::Point p_dt = p * i2d;
-
- Box3D::PerspectiveLine pl1 (origin_dt, axis1, persp);
- Box3D::PerspectiveLine pl2 (origin_dt, axis2, persp);
- Box3D::Line diag1 (origin_dt, box->corners[corner ^ Box3D::XY]);
-
- int num_snap_lines = 3;
- NR::Point snap_pts[num_snap_lines];
-
- snap_pts[0] = pl1.closest_to (p_dt);
- snap_pts[1] = pl2.closest_to (p_dt);
- snap_pts[2] = diag1.closest_to (p_dt);
-
- gdouble const zoom = desktop->current_zoom();
-
- double snap_dists[num_snap_lines];
-
- for (int i = 0; i < num_snap_lines; ++i) {
- snap_dists[i] = NR::L2 (snap_pts[i] - p_dt) * zoom;
- }
-
- bool within_tolerance = true;
- for (int i = 0; i < num_snap_lines; ++i) {
- if (snap_dists[i] > remember_snap_threshold) {
- within_tolerance = false;
- break;
- }
- }
-
- int snap_index = -1;
- double snap_dist = NR_HUGE;
- for (int i = 0; i < num_snap_lines; ++i) {
- if (snap_dists[i] < snap_dist) {
- snap_index = i;
- snap_dist = snap_dists[i];
- }
- }
+ g_assert(item != NULL);
+ SPBox3D *box = SP_BOX3D(item);
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
- if (within_tolerance) {
- return snap_pts[remember_snap_index] * i2d.inverse();
+ Box3D::Axis movement;
+ if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) {
+ movement = Box3D::XY;
} else {
- remember_snap_index = snap_index;
- return snap_pts[snap_index] * i2d.inverse();
+ movement = Box3D::Z;
}
+
+ box3d_set_corner (box, knot_id, new_pos * i2d, movement, (state & GDK_CONTROL_MASK));
+ box3d_set_z_orders(box);
+ box3d_position_set(box);
}
-static NR::Point snap_center_position_3dbox (SP3DBox *box, NR::Point const &origin, NR::Point const &p)
+static NR::Point box3d_knot_center_get (SPItem *item)
{
- SPDesktop * desktop = inkscape_active_desktop();
- Box3D::Perspective3D *persp = sp_desktop_document (desktop)->get_persp_of_box (box);
-
- Box3D::Axis axis1 = Box3D::X;
- Box3D::Axis axis2 = Box3D::Y;
-
- NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (box)));
- NR::Point origin_dt = origin * i2d;
- NR::Point p_dt = p * i2d;
-
- Box3D::PerspectiveLine pl1 (origin_dt, axis1, persp);
- Box3D::PerspectiveLine pl2 (origin_dt, axis2, persp);
- NR::Point midpt1 = sp_3dbox_get_midpoint_in_axis_direction (box->old_corner1, box->old_corner5, Box3D::Z, persp);
- NR::Point midpt2 = sp_3dbox_get_midpoint_in_axis_direction (box->old_corner3, box->old_corner7, Box3D::Z, persp);
- Box3D::Line diag1 (origin_dt, midpt1);
- Box3D::Line diag2 (origin_dt, midpt2);
-
- int num_snap_lines = 4;
- NR::Point snap_pts[num_snap_lines];
-
- // should we snap to the closest point or to the projection along perspective lines?
- snap_pts[0] = pl1.closest_to (p_dt);
- snap_pts[1] = pl2.closest_to (p_dt);
- snap_pts[2] = diag1.closest_to (p_dt);
- snap_pts[3] = diag2.closest_to (p_dt);
-
- gdouble const zoom = desktop->current_zoom();
-
- double snap_dists[num_snap_lines];
-
- for (int i = 0; i < num_snap_lines; ++i) {
- snap_dists[i] = NR::L2 (snap_pts[i] - p_dt) * zoom;
- }
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
+ return box3d_get_center_screen (SP_BOX3D(item)) * i2d;
+}
- bool within_tolerance = true;
- for (int i = 0; i < num_snap_lines; ++i) {
- if (snap_dists[i] > remember_snap_threshold) {
- within_tolerance = false;
- break;
- }
- }
+static void box3d_knot_center_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ SPBox3D *box = SP_BOX3D(item);
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
- int snap_index = -1;
- double snap_dist = NR_HUGE;
- for (int i = 0; i < num_snap_lines; ++i) {
- if (snap_dists[i] < snap_dist) {
- snap_index = i;
- snap_dist = snap_dists[i];
- }
- }
+ box3d_set_center (SP_BOX3D(item), new_pos * i2d, origin * i2d, !(state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z,
+ state & GDK_CONTROL_MASK);
- if (within_tolerance) {
- return snap_pts[remember_snap_index_center] * i2d.inverse();
- } else {
- remember_snap_index_center = snap_index;
- return snap_pts[snap_index] * i2d.inverse();
- }
+ box3d_set_z_orders(box);
+ box3d_position_set(box);
}
-static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id)
+static void box3d_knot0_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
{
- g_assert(item != NULL);
- SP3DBox *box = SP_3DBOX(item);
-
- NR::Matrix const i2d (sp_item_i2d_affine (item));
- return sp_3dbox_get_corner(box, knot_id) * i2d;
+ box3d_knot_set(item, 0, new_pos, origin, state);
}
-static void sp_3dbox_knot_set(SPItem *item, guint knot_id, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static void box3d_knot1_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
{
- g_assert(item != NULL);
- SP3DBox *box = SP_3DBOX(item);
+ box3d_knot_set(item, 1, new_pos, origin, state);
+}
- NR::Matrix const i2d (sp_item_i2d_affine (item));
- Box3D::Axis direction = movement_axis_of_3dbox_corner (knot_id, state);
- if ((state & GDK_CONTROL_MASK) && !is_single_axis_direction (direction)) {
- // snap if Ctrl is pressed and movement isn't already constrained to a single axis
- NR::Point const s = snap_knot_position_3dbox (box, knot_id, direction, origin, new_pos, state);
- sp_3dbox_move_corner_in_Z_direction (box, knot_id, s * i2d, false);
- } else {
- if (direction == Box3D::Z) {
- sp_3dbox_move_corner_in_Z_direction (box, knot_id, new_pos * i2d, true);
- } else {
- sp_3dbox_move_corner_in_Z_direction (box, knot_id, new_pos * i2d, false);
- }
- }
- sp_3dbox_update_curves (box);
- sp_3dbox_set_ratios (box);
- sp_3dbox_update_perspective_lines ();
- sp_3dbox_set_z_orders_later_on (box);
+static void box3d_knot2_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ box3d_knot_set(item, 2, new_pos, origin, state);
}
-static void sp_3dbox_knot_center_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static void box3d_knot3_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
{
- SP3DBox *box = SP_3DBOX(item);
+ box3d_knot_set(item, 3, new_pos, origin, state);
+}
- NR::Matrix const i2d (sp_item_i2d_affine (item));
- NR::Point new_pt (new_pos);
+static void box3d_knot4_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ box3d_knot_set(item, 4, new_pos, origin, state);
+}
- if ((state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK)) {
- // snap if Ctrl is pressed and movement isn't already constrained to a single axis
- new_pt = snap_center_position_3dbox (box, origin, new_pos);
- }
+static void box3d_knot5_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ box3d_knot_set(item, 5, new_pos, origin, state);
+}
- if (state & GDK_SHIFT_MASK) {
- sp_3dbox_recompute_Z_corners_from_new_center (box, new_pt * i2d);
- } else {
- sp_3dbox_recompute_XY_corners_from_new_center (box, new_pt * i2d);
- }
+static void box3d_knot6_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ box3d_knot_set(item, 6, new_pos, origin, state);
+}
- sp_3dbox_update_curves (box);
- sp_3dbox_set_z_orders_later_on (box);
+static void box3d_knot7_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+{
+ box3d_knot_set(item, 7, new_pos, origin, state);
}
-static NR::Point sp_3dbox_knot_center_get(SPItem *item)
+static NR::Point box3d_knot0_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;
+ return box3d_knot_get(item, 0);
}
-static void sp_3dbox_knot0_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot1_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 0, new_pos, origin, state);
+ return box3d_knot_get(item, 1);
}
-static void sp_3dbox_knot1_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot2_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 1, new_pos, origin, state);
+ return box3d_knot_get(item, 2);
}
-static void sp_3dbox_knot2_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot3_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 2, new_pos, origin, state);
+ return box3d_knot_get(item, 3);
}
-static void sp_3dbox_knot3_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot4_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 3, new_pos, origin, state);
+ return box3d_knot_get(item, 4);
}
-static void sp_3dbox_knot4_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot5_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 4, new_pos, origin, state);
+ return box3d_knot_get(item, 5);
}
-static void sp_3dbox_knot5_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot6_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 5, new_pos, origin, state);
+ return box3d_knot_get(item, 6);
}
-static void sp_3dbox_knot6_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static NR::Point box3d_knot7_get(SPItem *item)
{
- sp_3dbox_knot_set(item, 6, new_pos, origin, state);
+ return box3d_knot_get(item, 7);
}
-static void sp_3dbox_knot7_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
+static void box3d_knot_click(SPItem *item, guint state, guint id)
{
- sp_3dbox_knot_set(item, 7, new_pos, origin, state);
+ g_print ("Corner %d was clicked\n", id);
}
-static NR::Point sp_3dbox_knot0_get(SPItem *item)
+static void box3d_knot0_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 0);
+ box3d_knot_click(item, state, 0);
}
-static NR::Point sp_3dbox_knot1_get(SPItem *item)
+static void box3d_knot1_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 1);
+ box3d_knot_click(item, state, 1);
}
-static NR::Point sp_3dbox_knot2_get(SPItem *item)
+static void box3d_knot2_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 2);
+ box3d_knot_click(item, state, 2);
}
-static NR::Point sp_3dbox_knot3_get(SPItem *item)
+static void box3d_knot3_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 3);
+ box3d_knot_click(item, state, 3);
}
-static NR::Point sp_3dbox_knot4_get(SPItem *item)
+static void box3d_knot4_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 4);
+ box3d_knot_click(item, state, 4);
}
-static NR::Point sp_3dbox_knot5_get(SPItem *item)
+static void box3d_knot5_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 5);
+ box3d_knot_click(item, state, 5);
}
-static NR::Point sp_3dbox_knot6_get(SPItem *item)
+static void box3d_knot6_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 6);
+ box3d_knot_click(item, state, 6);
}
-static NR::Point sp_3dbox_knot7_get(SPItem *item)
+static void box3d_knot7_click(SPItem *item, guint state)
{
- return sp_3dbox_knot_get(item, 7);
+ box3d_knot_click(item, state, 7);
}
-//static
SPKnotHolder *
-sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop)
+box3d_knot_holder(SPItem *item, SPDesktop *desktop)
{
g_assert(item != NULL);
SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL);
- sp_knot_holder_add(knot_holder, sp_3dbox_knot0_set, sp_3dbox_knot0_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot0_set, box3d_knot0_get, box3d_knot0_click,
_("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
- sp_knot_holder_add(knot_holder, sp_3dbox_knot1_set, sp_3dbox_knot1_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot1_set, box3d_knot1_get, box3d_knot1_click,
_("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
- sp_knot_holder_add(knot_holder, sp_3dbox_knot2_set, sp_3dbox_knot2_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot2_set, box3d_knot2_get, box3d_knot2_click,
_("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
- sp_knot_holder_add(knot_holder, sp_3dbox_knot3_set, sp_3dbox_knot3_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot3_set, box3d_knot3_get, box3d_knot3_click,
_("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
- sp_knot_holder_add(knot_holder, sp_3dbox_knot4_set, sp_3dbox_knot4_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot4_set, box3d_knot4_get, box3d_knot4_click,
_("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_knot5_set, sp_3dbox_knot5_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot5_set, box3d_knot5_get, box3d_knot5_click,
_("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_knot6_set, sp_3dbox_knot6_get, NULL,
+ sp_knot_holder_add(knot_holder, box3d_knot6_set, box3d_knot6_get, box3d_knot6_click,
_("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,
+ sp_knot_holder_add(knot_holder, box3d_knot7_set, box3d_knot7_get, box3d_knot7_click,
_("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_holder_add_full(knot_holder, box3d_knot_center_set, box3d_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;
}
+
+
/* SPArc */
/*
diff --git a/src/pencil-context.cpp b/src/pencil-context.cpp
index 32ea8aafa36adb200e7198c0884d890589d832d5..1ee39d5309f9427ccc14007eb6a1fb3b1e7783b5 100644 (file)
--- a/src/pencil-context.cpp
+++ b/src/pencil-context.cpp
#include "libnr/n-art-bpath.h"
#include "context-fns.h"
#include "sp-namedview.h"
+#include "xml/repr.h"
+#include "document.h"
+#include "desktop-style.h"
static void sp_pencil_context_class_init(SPPencilContextClass *klass);
static void sp_pencil_context_init(SPPencilContext *pc);
@@ -223,6 +226,30 @@ pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &beve
break;
default:
/* Set first point of sequence */
+ if (bevent.state & GDK_CONTROL_MASK) {
+ /* Create object */
+ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
+ repr->setAttribute("sodipodi:type", "arc");
+ SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
+ Inkscape::GC::release(repr);
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
+ NR::Point pp = p * i2d;
+ sp_repr_set_svg_double (repr, "sodipodi:cx", pp[NR::X]);
+ sp_repr_set_svg_double (repr, "sodipodi:cy", pp[NR::Y]);
+ sp_repr_set_int (repr, "sodipodi:rx", 10);
+ sp_repr_set_int (repr, "sodipodi:ry", 10);
+
+ /* Set style */
+ sp_desktop_apply_style_tool(desktop, repr, "tools.shapes.arc", false);
+
+ item->updateRepr();
+ desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating single point"));
+ sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_PENCIL, _("Create single point"));
+ ret = true;
+ break;
+
+ }
if (anchor) {
p = anchor->dp;
desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path"));
@@ -255,6 +282,11 @@ pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &beve
static gint
pencil_handle_motion_notify(SPPencilContext *const pc, GdkEventMotion const &mevent)
{
+ if (mevent.state & GDK_CONTROL_MASK) {
+ // mouse was accidentally moved during Ctrl+click;
+ // ignore the motion and create a single point
+ return TRUE;
+ }
gint ret = FALSE;
SPDesktop *const dt = pc->desktop;
@@ -359,7 +391,10 @@ pencil_handle_button_release(SPPencilContext *const pc, GdkEventButton const &re
case SP_PENCIL_CONTEXT_IDLE:
/* Releasing button in idle mode means single click */
/* We have already set up start point/anchor in button_press */
- pc->state = SP_PENCIL_CONTEXT_ADDLINE;
+ if (!(revent.state & GDK_CONTROL_MASK)) {
+ // Ctrl+click creates a single point so only set context in ADDLINE mode when Ctrl isn't pressed
+ pc->state = SP_PENCIL_CONTEXT_ADDLINE;
+ }
ret = TRUE;
break;
case SP_PENCIL_CONTEXT_ADDLINE:
diff --git a/src/persp3d-reference.cpp b/src/persp3d-reference.cpp
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * The reference corresponding to the inkscape:perspectiveID attribute
+ *
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 2007 Maximilian Albert
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include "persp3d-reference.h"
+#include "persp3d.h"
+#include "uri.h"
+
+// for testing:
+#include "xml/repr.h"
+#include "box3d.h"
+
+static void persp3dreference_href_changed(SPObject *old_ref, SPObject *ref, Persp3DReference *persp3dref);
+static void persp3dreference_delete_self(SPObject *deleted, Persp3DReference *persp3dref);
+static void persp3dreference_source_modified(SPObject *iSource, guint flags, Persp3DReference *persp3dref);
+
+Persp3DReference::Persp3DReference(SPObject* i_owner) : URIReference(i_owner)
+{
+ owner=i_owner;
+ /**
+ if (owner) {
+ g_print ("Owner of newly created Persp3DReference is box #%d ", SP_BOX3D(owner)->my_counter);
+ g_print ("(no ID yet because we are calling from box3d_init()...\n");
+ }
+ **/
+ persp_href = NULL;
+ persp_repr = NULL;
+ persp = NULL;
+ _changed_connection = changedSignal().connect(sigc::bind(sigc::ptr_fun(persp3dreference_href_changed), this)); // listening to myself, this should be virtual instead
+}
+
+Persp3DReference::~Persp3DReference(void)
+{
+ _changed_connection.disconnect(); // to do before unlinking
+
+ quit_listening();
+ unlink();
+}
+
+bool
+Persp3DReference::_acceptObject(SPObject *obj) const
+{
+ return SP_IS_PERSP3D(obj);
+ /* effic: Don't bother making this an inline function: _acceptObject is a virtual function,
+ typically called from a context where the runtime type is not known at compile time. */
+}
+
+/***
+void
+Persp3DReference::link(char *to)
+{
+ if ( to == NULL ) {
+ quit_listening();
+ unlink();
+ } else {
+ if ( !persp_href || ( strcmp(to, persp_href) != 0 ) ) {
+ g_free(persp_href);
+ persp_href = g_strdup(to);
+ try {
+ attach(Inkscape::URI(to));
+ } catch (Inkscape::BadURIException &e) {
+ // TODO: Proper error handling as per
+ // http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
+ //
+ g_warning("%s", e.what());
+ detach();
+ }
+ }
+ }
+}
+***/
+
+void
+Persp3DReference::unlink(void)
+{
+ g_free(persp_href);
+ persp_href = NULL;
+ detach();
+}
+
+void
+Persp3DReference::start_listening(Persp3D* to)
+{
+ if ( to == NULL ) {
+ return;
+ }
+ persp = to;
+ persp_repr = SP_OBJECT_REPR(to);
+ _delete_connection = to->connectDelete(sigc::bind(sigc::ptr_fun(&persp3dreference_delete_self), this));
+ _modified_connection = to->connectModified(sigc::bind<2>(sigc::ptr_fun(&persp3dreference_source_modified), this));
+ //box3d_start_listening_to_persp_change (SP_BOX3D(this->owner), to);
+}
+
+void
+Persp3DReference::quit_listening(void)
+{
+ if ( persp == NULL ) {
+ return;
+ }
+ _modified_connection.disconnect();
+ _delete_connection.disconnect();
+ persp_repr = NULL;
+ persp = NULL;
+}
+
+static void
+persp3dreference_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, Persp3DReference *persp3dref)
+{
+ //g_print ("persp3dreference_href_changed:\n");
+ persp3dref->quit_listening();
+ /**
+ if (SP_IS_PERSP3D(persp3dref->getObject())){
+ g_print ("referenced object is a perspective\n");
+ } else {
+ g_print ("referenced object is NOT a perspective!!!!\n");
+ }
+ **/
+ Persp3D *refobj = SP_PERSP3D(persp3dref->getObject());
+ if ( refobj ) {
+ persp3dref->start_listening(refobj);
+ //g_print (" start listening to %s\n", SP_OBJECT_REPR(refobj)->attribute("id"));
+ }
+
+ /**
+ if (persp3dref->owner) {
+ g_print ("Requesting display update of owner box #%d (%s) from persp3dreference_href_changed()\n",
+ SP_BOX3D(persp3dref->owner)->my_counter,
+ SP_OBJECT_REPR(persp3dref->owner)->attribute("id"));
+ }
+ **/
+ persp3dref->owner->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+static void
+persp3dreference_delete_self(SPObject */*deleted*/, Persp3DReference *persp3dref)
+{
+ g_print ("persp3dreference_delete_self; FIXME: Can we leave this to the parent URIReference?\n");
+ if (persp3dref->owner) {
+ g_print ("Deleting box #%d (%s) (?) from Persp3DReference\n",
+ SP_BOX3D(persp3dref->owner)->my_counter,
+ SP_OBJECT_REPR(persp3dref->owner)->attribute("id"));
+ }
+ persp3dref->owner->deleteObject();
+}
+
+static void
+persp3dreference_source_modified(SPObject *iSource, guint flags, Persp3DReference *persp3dref)
+{
+ /**
+ g_print ("persp3dreference_source_modified; FIXME: Can we leave this to the parent URIReference?\n");
+ if (persp3dref->owner) {
+ g_print ("Requesting display update of box #%d (%s) from persp3dreference_source_modified\n",
+ SP_BOX3D(persp3dref->owner)->my_counter,
+ SP_OBJECT_REPR(persp3dref->owner)->attribute("id"));
+ }
+ **/
+ persp3dref->owner->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/persp3d-reference.h b/src/persp3d-reference.h
--- /dev/null
+++ b/src/persp3d-reference.h
@@ -0,0 +1,66 @@
+#ifndef SEEN_PERSP3D_REFERENCE_H
+#define SEEN_PERSP3D_REFERENCE_H
+
+/*
+ * The reference corresponding to the inkscape:perspectiveID attribute
+ *
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 2007 Maximilian Albert
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include "uri-references.h"
+#include <sigc++/sigc++.h>
+
+class SPObject;
+class Persp3D;
+
+namespace Inkscape {
+namespace XML {
+struct Node;
+}
+}
+
+class Persp3DReference : public Inkscape::URIReference {
+public:
+ Persp3DReference(SPObject *obj);
+ ~Persp3DReference();
+
+ Persp3D *getObject() const {
+ return (Persp3D *)URIReference::getObject();
+ }
+
+ SPObject *owner;
+
+ // concerning the Persp3D (we only use SPBox3D) that is refered to:
+ gchar *persp_href;
+ Inkscape::XML::Node *persp_repr;
+ Persp3D *persp;
+
+ sigc::connection _changed_connection;
+ sigc::connection _modified_connection;
+ sigc::connection _delete_connection;
+
+ void link(char* to);
+ void unlink(void);
+ void start_listening(Persp3D* to);
+ void quit_listening(void);
+
+protected:
+ virtual bool _acceptObject(SPObject *obj) const;
+};
+
+
+#endif /* !SEEN_PERSP3D_REFERENCE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/persp3d.cpp b/src/persp3d.cpp
--- /dev/null
+++ b/src/persp3d.cpp
@@ -0,0 +1,546 @@
+#define __PERSP3D_C__
+
+/*
+ * Class modelling a 3D perspective as an SPObject
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "persp3d.h"
+#include "perspective-line.h"
+#include "attributes.h"
+#include "document-private.h"
+#include "vanishing-point.h"
+#include "box3d-context.h"
+#include "box3d.h"
+#include "xml/document.h"
+#include "xml/node-event-vector.h"
+#include "desktop-handles.h"
+#include <glibmm/i18n.h>
+
+static void persp3d_class_init(Persp3DClass *klass);
+static void persp3d_init(Persp3D *stop);
+
+static void persp3d_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void persp3d_release(SPObject *object);
+static void persp3d_set(SPObject *object, unsigned key, gchar const *value);
+static void persp3d_update(SPObject *object, SPCtx *ctx, guint flags);
+static Inkscape::XML::Node *persp3d_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+
+static void persp3d_on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data);
+
+static SPObjectClass *persp3d_parent_class;
+
+static int global_counter = 0;
+
+/**
+ * Registers Persp3d class and returns its type.
+ */
+GType
+persp3d_get_type()
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(Persp3DClass),
+ NULL, NULL,
+ (GClassInitFunc) persp3d_class_init,
+ NULL, NULL,
+ sizeof(Persp3D),
+ 16,
+ (GInstanceInitFunc) persp3d_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static(SP_TYPE_OBJECT, "Persp3D", &info, (GTypeFlags)0);
+ }
+ return type;
+}
+
+static Inkscape::XML::NodeEventVector const persp3d_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ persp3d_on_repr_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+/**
+ * Callback to initialize Persp3D vtable.
+ */
+static void persp3d_class_init(Persp3DClass *klass)
+{
+ SPObjectClass *sp_object_class = (SPObjectClass *) klass;
+
+ persp3d_parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);
+
+ sp_object_class->build = persp3d_build;
+ sp_object_class->release = persp3d_release;
+ sp_object_class->set = persp3d_set;
+ sp_object_class->update = persp3d_update;
+ sp_object_class->write = persp3d_write;
+}
+
+/**
+ * Callback to initialize Persp3D object.
+ */
+static void
+persp3d_init(Persp3D *persp)
+{
+ persp->tmat = Proj::TransfMat3x4 ();
+
+ //persp->boxes = NULL;
+ persp->document = NULL;
+
+ persp->my_counter = global_counter++;
+}
+
+/**
+ * Virtual build: set persp3d attributes from its associated XML node.
+ */
+static void persp3d_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+ if (((SPObjectClass *) persp3d_parent_class)->build)
+ (* ((SPObjectClass *) persp3d_parent_class)->build)(object, document, repr);
+
+ /* calls sp_object_set for the respective attributes */
+ // The transformation matrix is updated according to the values we read for the VPs
+ sp_object_read_attr(object, "inkscape:vp_x");
+ sp_object_read_attr(object, "inkscape:vp_y");
+ sp_object_read_attr(object, "inkscape:vp_z");
+ sp_object_read_attr(object, "inkscape:persp3d-origin");
+
+ if (repr) {
+ repr->addListener (&persp3d_repr_events, object);
+ }
+
+ // FIXME: What precisely does this do and is it necessary for perspectives?
+ /* Register ourselves */
+ //sp_document_add_resource(document, "persp3d", object);
+}
+
+/**
+ * Virtual release of Persp3D members before destruction.
+ */
+static void persp3d_release(SPObject *object) {
+ //Persp3D *persp = (Persp3D *) object;
+
+ // FIXME: What precisely does this do and is it necessary for perspectives?
+ /**
+ if (SP_OBJECT_DOCUMENT(object)) {
+ // Unregister ourselves
+ sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "persp3d", SP_OBJECT(object));
+ }
+ **/
+}
+
+
+/**
+ * Virtual set: set attribute to value.
+ */
+// FIXME: Currently we only read the finite positions of vanishing points;
+// should we move VPs into their own repr (as it's done for SPStop, e.g.)?
+static void
+persp3d_set(SPObject *object, unsigned key, gchar const *value)
+{
+ Persp3D *persp = SP_PERSP3D (object);
+
+ switch (key) {
+ case SP_ATTR_INKSCAPE_PERSP3D_VP_X: {
+ if (value) {
+ Proj::Pt2 new_image (value);
+ persp3d_update_with_point (persp, Proj::X, new_image);
+ }
+ break;
+ }
+ case SP_ATTR_INKSCAPE_PERSP3D_VP_Y: {
+ if (value) {
+ Proj::Pt2 new_image (value);
+ persp3d_update_with_point (persp, Proj::Y, new_image);
+ break;
+ }
+ }
+ case SP_ATTR_INKSCAPE_PERSP3D_VP_Z: {
+ if (value) {
+ Proj::Pt2 new_image (value);
+ persp3d_update_with_point (persp, Proj::Z, new_image);
+ break;
+ }
+ }
+ case SP_ATTR_INKSCAPE_PERSP3D_ORIGIN: {
+ if (value) {
+ Proj::Pt2 new_image (value);
+ persp3d_update_with_point (persp, Proj::W, new_image);
+ break;
+ }
+ }
+ default: {
+ if (((SPObjectClass *) persp3d_parent_class)->set)
+ (* ((SPObjectClass *) persp3d_parent_class)->set)(object, key, value);
+ break;
+ }
+ }
+
+ // FIXME: Is this the right place for resetting the draggers?
+ SPEventContext *ec = inkscape_active_event_context();
+ if (SP_IS_BOX3D_CONTEXT(ec)) {
+ Box3DContext *bc = SP_BOX3D_CONTEXT(ec);
+ bc->_vpdrag->updateDraggers();
+ bc->_vpdrag->updateLines();
+ bc->_vpdrag->updateBoxHandles();
+ bc->_vpdrag->updateBoxReprs();
+ }
+}
+
+static void
+persp3d_update(SPObject *object, SPCtx *ctx, guint flags)
+{
+ if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
+
+ /* TODO: Should we update anything here? */
+
+ }
+
+ if (((SPObjectClass *) persp3d_parent_class)->update)
+ ((SPObjectClass *) persp3d_parent_class)->update(object, ctx, flags);
+}
+
+Persp3D *
+persp3d_create_xml_element (SPDocument *document, Persp3D *dup) {// if dup is given, copy the attributes over
+ SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
+ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
+ Inkscape::XML::Node *repr;
+ if (dup) {
+ repr = SP_OBJECT_REPR(dup)->duplicate (xml_doc);
+ } else {
+ repr = xml_doc->createElement("inkscape:perspective");
+ repr->setAttribute("sodipodi:type", "inkscape:persp3d");
+ }
+
+ /* Append the new persp3d to defs */
+ SP_OBJECT_REPR(defs)->addChild(repr, NULL);
+ Inkscape::GC::release(repr);
+
+ return (Persp3D *) sp_object_get_child_by_repr (SP_OBJECT(defs), repr);
+}
+
+/**
+ * Virtual write: write object attributes to repr.
+ */
+static Inkscape::XML::Node *
+persp3d_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+ SPDocument *document = SP_OBJECT_DOCUMENT(object);
+ Persp3D *persp = SP_PERSP3D(object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = SP_OBJECT_REPR(persp3d_create_xml_element (document));
+ }
+
+ if (flags & SP_OBJECT_WRITE_EXT) {
+ gchar *str = NULL; // FIXME: Should this be freed each time we set an attribute or only in the end or at all?
+ str = persp3d_pt_to_str (persp, Proj::X);
+ repr->setAttribute("inkscape:vp_x", str);
+
+ str = persp3d_pt_to_str (persp, Proj::Y);
+ repr->setAttribute("inkscape:vp_y", str);
+
+ str = persp3d_pt_to_str (persp, Proj::Z);
+ repr->setAttribute("inkscape:vp_z", str);
+
+ str = persp3d_pt_to_str (persp, Proj::W);
+ repr->setAttribute("inkscape:persp3d-origin", str);
+ }
+
+ if (((SPObjectClass *) persp3d_parent_class)->write)
+ (* ((SPObjectClass *) persp3d_parent_class)->write)(object, repr, flags);
+
+ return repr;
+}
+
+/* convenience wrapper around persp3d_get_finite_dir() and persp3d_get_infinite_dir() */
+NR::Point persp3d_get_PL_dir_from_pt (Persp3D *persp, NR::Point const &pt, Proj::Axis axis) {
+ if (persp3d_VP_is_finite(persp, axis)) {
+ return persp3d_get_finite_dir(persp, pt, axis);
+ } else {
+ return persp3d_get_infinite_dir(persp, axis);
+ }
+}
+
+NR::Point
+persp3d_get_finite_dir (Persp3D *persp, NR::Point const &pt, Proj::Axis axis) {
+ Box3D::PerspectiveLine pl(pt, axis, persp);
+ return pl.direction();
+}
+
+NR::Point
+persp3d_get_infinite_dir (Persp3D *persp, Proj::Axis axis) {
+ Proj::Pt2 vp(persp3d_get_VP(persp, axis));
+ if (vp[2] != 0.0) {
+ g_print ("VP should be infinite but is (%f : %f : %f)\n", vp[0], vp[1], vp[2]);
+ g_return_val_if_fail(vp[2] != 0.0, NR::Point(0.0, 0.0));
+ }
+ return NR::Point(vp[0], vp[1]);
+}
+
+double
+persp3d_get_infinite_angle (Persp3D *persp, Proj::Axis axis) {
+ return persp->tmat.get_infinite_angle(axis);
+}
+
+bool
+persp3d_VP_is_finite (Persp3D *persp, Proj::Axis axis) {
+ return persp->tmat.has_finite_image(axis);
+}
+
+void
+persp3d_toggle_VP (Persp3D *persp, Proj::Axis axis, bool set_undo) {
+ persp->tmat.toggle_finite(axis);
+ // FIXME: Remove this repr update and rely on vp_drag_sel_modified() to do this for us
+ // On the other hand, vp_drag_sel_modified() would update all boxes;
+ // here we can confine ourselves to the boxes of this particular perspective.
+ persp3d_update_box_reprs (persp);
+ persp3d_update_z_orders (persp);
+ SP_OBJECT(persp)->updateRepr(SP_OBJECT_WRITE_EXT);
+ if (set_undo) {
+ sp_document_done(sp_desktop_document(inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
+ _("Toggle vanishing point"));
+ }
+}
+
+/* toggle VPs for the same axis in all perspectives of a given list */
+void
+persp3d_toggle_VPs (std::set<Persp3D *> p, Proj::Axis axis) {
+ for (std::set<Persp3D *>::iterator i = p.begin(); i != p.end(); ++i) {
+ persp3d_toggle_VP((*i), axis, false);
+ }
+ sp_document_done(sp_desktop_document(inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
+ _("Toggle multiple vanishing points"));
+}
+
+void
+persp3d_set_VP_state (Persp3D *persp, Proj::Axis axis, Proj::VPState state) {
+ if (persp3d_VP_is_finite(persp, axis) != (state == Proj::FINITE)) {
+ persp3d_toggle_VP(persp, axis);
+ }
+}
+
+void
+persp3d_rotate_VP (Persp3D *persp, Proj::Axis axis, double angle, bool alt_pressed) { // angle is in degrees
+ // FIXME: Most of this functionality should be moved to trans_mat_3x4.(h|cpp)
+ if (persp->tmat.has_finite_image(axis)) {
+ // don't rotate anything for finite VPs
+ return;
+ }
+ Proj::Pt2 v_dir_proj (persp->tmat.column(axis));
+ NR::Point v_dir (v_dir_proj[0], v_dir_proj[1]);
+ double a = NR::atan2 (v_dir) * 180/M_PI;
+ a += alt_pressed ? 0.5 * ((angle > 0 ) - (angle < 0)) : angle; // the r.h.s. yields +/-0.5 or angle
+ persp->tmat.set_infinite_direction (axis, a);
+
+ persp3d_update_box_reprs (persp);
+ persp3d_update_z_orders (persp);
+ SP_OBJECT(persp)->updateRepr(SP_OBJECT_WRITE_EXT);
+}
+
+void
+persp3d_update_with_point (Persp3D *persp, Proj::Axis const axis, Proj::Pt2 const &new_image) {
+ persp->tmat.set_image_pt (axis, new_image);
+}
+
+void
+persp3d_apply_affine_transformation (Persp3D *persp, NR::Matrix const &xform) {
+ persp->tmat *= xform;
+ persp3d_update_box_reprs(persp);
+}
+
+gchar *
+persp3d_pt_to_str (Persp3D *persp, Proj::Axis const axis)
+{
+ return persp->tmat.pt_to_str(axis);
+}
+
+void
+persp3d_add_box (Persp3D *persp, SPBox3D *box) {
+ if (!box) {
+ //g_warning ("Trying to add NULL box to perspective.\n");
+ return;
+ }
+ if (std::find (persp->boxes.begin(), persp->boxes.end(), box) != persp->boxes.end()) {
+ //g_warning ("Attempting to add already existent box to perspective.\n");
+ return;
+ }
+ persp->boxes.push_back(box);
+ //SP_OBJECT_REPR(box)->setAttribute("inkscape:perspectiveID", SP_OBJECT_REPR(persp)->attribute("id"));
+}
+
+void
+persp3d_remove_box (Persp3D *persp, SPBox3D *box) {
+ std::vector<SPBox3D *>::iterator i = std::find (persp->boxes.begin(), persp->boxes.end(), box);
+ if (i != persp->boxes.end()) {
+ persp->boxes.erase(i);
+ }
+}
+
+bool
+persp3d_has_box (Persp3D *persp, SPBox3D *box) {
+ // FIXME: For some reason, std::find() does not seem to compare pointers "correctly" (or do we need to
+ // provide a proper comparison function?), so we manually traverse the list.
+ for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
+ if ((*i) == box) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+persp3d_update_box_displays (Persp3D *persp) {
+ if (persp->boxes.empty())
+ return;
+ //g_print ("Requesting display update for %d boxes in the perspective.\n", persp->boxes.size());
+ for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
+ box3d_position_set(*i);
+ }
+}
+
+void
+persp3d_update_box_reprs (Persp3D *persp) {
+ if (persp->boxes.empty())
+ return;
+ //g_print ("Requesting repr update for %d boxes in the perspective.\n", persp->boxes.size());
+ for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
+ SP_OBJECT(*i)->updateRepr(SP_OBJECT_WRITE_EXT);
+ }
+}
+
+void
+persp3d_update_z_orders (Persp3D *persp) {
+ if (persp->boxes.empty())
+ return;
+ for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
+ box3d_set_z_orders(*i);
+ }
+}
+
+// FIXME: For some reason we seem to require a vector instead of a list in Persp3D, but in vp_knot_moved_handler()
+// we need a list of boxes. If we can store a list in Persp3D right from the start, this function becomes
+// obsolete. We should do this.
+std::list<SPBox3D *>
+persp3d_list_of_boxes(Persp3D *persp) {
+ std::list<SPBox3D *> bx_lst;
+ for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
+ bx_lst.push_back(*i);
+ }
+ return bx_lst;
+}
+
+bool
+persp3d_perspectives_coincide(const Persp3D *lhs, const Persp3D *rhs)
+{
+ return lhs->tmat == rhs->tmat;
+}
+
+void
+persp3d_absorb(Persp3D *persp1, Persp3D *persp2) {
+ /* double check if we are called in sane situations */
+ g_return_if_fail (persp3d_perspectives_coincide(persp1, persp2) && persp1 != persp2);
+
+ std::vector<SPBox3D *>::iterator boxes;
+
+ // Note: We first need to copy the boxes of persp2 into a separate list;
+ // otherwise the loop below gets confused when perspectives are reattached.
+ std::list<SPBox3D *> boxes_of_persp2 = persp3d_list_of_boxes(persp2);
+
+ Inkscape::XML::Node *persp_repr = SP_OBJECT_REPR(persp1);
+ const gchar *persp_id = persp_repr->attribute("id");
+ gchar *href = g_strdup_printf("#%s", persp_id);
+
+ for (std::list<SPBox3D *>::iterator i = boxes_of_persp2.begin(); i != boxes_of_persp2.end(); ++i) {
+ SP_OBJECT_REPR(*i)->setAttribute("inkscape:perspectiveID", href);
+ }
+ g_free(href);
+
+ persp1->boxes.insert(persp1->boxes.begin(), persp2->boxes.begin(), persp2->boxes.end());
+}
+
+static void
+persp3d_on_repr_attr_changed ( Inkscape::XML::Node * repr,
+ const gchar *key,
+ const gchar *oldval,
+ const gchar *newval,
+ bool is_interactive,
+ void * data )
+{
+ //g_print("persp3d_on_repr_attr_changed!!!! TODO: Do we need to trigger any further updates than the box reprs?");
+
+ if (!data)
+ return;
+
+ Persp3D *persp = (Persp3D*) data;
+ persp3d_update_box_displays (persp);
+
+ //lpeobj->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+/* returns a std::set() of all perspectives of the currently selected boxes */
+std::set<Persp3D *>
+persp3d_currently_selected (Box3DContext *bc) {
+ Inkscape::Selection *selection = sp_desktop_selection (bc->desktop);
+
+ std::set<Persp3D *> p;
+ for (GSList *i = (GSList *) selection->itemList(); i != NULL; i = i->next) {
+ if (SP_IS_BOX3D (i->data)) {
+ p.insert(SP_BOX3D(i->data)->persp_ref->getObject());
+ }
+ }
+ return p;
+}
+
+void
+persp3d_print_debugging_info (Persp3D *persp) {
+ g_print ("=== Info for Persp3D %d ===\n", persp->my_counter);
+ gchar * cstr;
+ for (int i = 0; i < 4; ++i) {
+ cstr = persp3d_get_VP(persp, Proj::axes[i]).coord_string();
+ g_print (" VP %s: %s\n", Proj::string_from_axis(Proj::axes[i]), cstr);
+ g_free(cstr);
+ }
+ cstr = persp3d_get_VP(persp, Proj::W).coord_string();
+ g_print (" Origin: %s\n", cstr);
+ g_free(cstr);
+
+ g_print (" Boxes: ");
+ for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
+ g_print ("%d (%d)", (*i)->my_counter, (*i)->persp_ref->getObject()->my_counter);
+ }
+ g_print ("\n");
+ g_print ("========================\n");
+}
+
+void
+persp3d_print_debugging_info_all(SPDocument *document) {
+ SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
+ Inkscape::XML::Node *repr;
+ for (SPObject *child = sp_object_first_child(defs); child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ repr = SP_OBJECT_REPR(child);
+ if (SP_IS_PERSP3D(child)) {
+ persp3d_print_debugging_info(SP_PERSP3D(child));
+ }
+ }
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/persp3d.h b/src/persp3d.h
--- /dev/null
+++ b/src/persp3d.h
@@ -0,0 +1,94 @@
+#ifndef __PERSP3D_H__
+#define __PERSP3D_H__
+
+/*
+ * Implementation of 3D perspectives as SPObjects
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#define SP_TYPE_PERSP3D (persp3d_get_type ())
+#define SP_PERSP3D(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_PERSP3D, Persp3D))
+#define SP_PERSP3D_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_PERSP3D, Persp3DClass))
+#define SP_IS_PERSP3D(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_PERSP3D))
+#define SP_IS_PERSP3D_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_PERSP3D))
+
+#include <set>
+#include <vector>
+#include "sp-item.h"
+#include "transf_mat_3x4.h"
+
+class SPDocument;
+class SPBox3D;
+class Box3DContext;
+
+struct Persp3D : public SPObject {
+ Proj::TransfMat3x4 tmat;
+
+ // TODO: Also write the list of boxes into the xml repr and vice versa link boxes to their persp3d?
+ std::vector<SPBox3D *> boxes;
+ SPDocument *document; // FIXME: should this rather be the SPDesktop?
+
+ // for debugging only
+ int my_counter;
+};
+
+struct Persp3DClass {
+ SPItemClass parent_class;
+};
+
+
+/* Standard GType function */
+GType persp3d_get_type (void);
+
+// FIXME: Make more of these inline!
+inline Proj::Pt2 persp3d_get_VP (Persp3D *persp, Proj::Axis axis) { return persp->tmat.column(axis); }
+NR::Point persp3d_get_PL_dir_from_pt (Persp3D *persp, NR::Point const &pt, Proj::Axis axis); // convenience wrapper around the following two
+NR::Point persp3d_get_finite_dir (Persp3D *persp, NR::Point const &pt, Proj::Axis axis);
+NR::Point persp3d_get_infinite_dir (Persp3D *persp, Proj::Axis axis);
+double persp3d_get_infinite_angle (Persp3D *persp, Proj::Axis axis);
+bool persp3d_VP_is_finite (Persp3D *persp, Proj::Axis axis);
+void persp3d_toggle_VP (Persp3D *persp, Proj::Axis axis, bool set_undo = true);
+void persp3d_toggle_VPs (std::set<Persp3D *>, Proj::Axis axis);
+void persp3d_set_VP_state (Persp3D *persp, Proj::Axis axis, Proj::VPState state);
+void persp3d_rotate_VP (Persp3D *persp, Proj::Axis axis, double angle, bool alt_pressed); // angle is in degrees
+void persp3d_update_with_point (Persp3D *persp, Proj::Axis const axis, Proj::Pt2 const &new_image);
+void persp3d_apply_affine_transformation (Persp3D *persp, NR::Matrix const &xform);
+gchar * persp3d_pt_to_str (Persp3D *persp, Proj::Axis const axis);
+
+void persp3d_add_box (Persp3D *persp, SPBox3D *box);
+void persp3d_remove_box (Persp3D *persp, SPBox3D *box);
+bool persp3d_has_box (Persp3D *persp, SPBox3D *box);
+void persp3d_update_box_displays (Persp3D *persp);
+void persp3d_update_box_reprs (Persp3D *persp);
+void persp3d_update_z_orders (Persp3D *persp);
+inline unsigned int persp3d_num_boxes (Persp3D *persp) { return persp->boxes.size(); }
+std::list<SPBox3D *> persp3d_list_of_boxes(Persp3D *persp);
+
+bool persp3d_perspectives_coincide(const Persp3D *lhs, const Persp3D *rhs);
+void persp3d_absorb(Persp3D *persp1, Persp3D *persp2);
+
+Persp3D * persp3d_create_xml_element (SPDocument *document, Persp3D *dup = NULL);
+
+std::set<Persp3D *> persp3d_currently_selected (Box3DContext *bc);
+
+void persp3d_print_debugging_info (Persp3D *persp);
+void persp3d_print_debugging_info_all(SPDocument *doc);
+
+#endif /* __PERSP3D_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
index 90857e6d5ecd10fac2ee887f5db8dfda20068e66..051a1d94acb6d2cbcf7d53f24138560aac27aa44 100644 (file)
--- a/src/perspective-line.cpp
+++ b/src/perspective-line.cpp
*/
#include "perspective-line.h"
-#include "desktop.h"
+#include "persp3d.h"
namespace Box3D {
-PerspectiveLine::PerspectiveLine (NR::Point const &pt, Box3D::Axis const axis, Perspective3D *perspective) :
- Line (pt, *(perspective->get_vanishing_point(axis)), true)
+PerspectiveLine::PerspectiveLine (NR::Point const &pt, Proj::Axis const axis, Persp3D *persp) :
+ Line (pt, persp3d_get_VP(persp, axis).affine(), true)
{
- g_assert (perspective != NULL);
- g_assert (Box3D::is_single_axis_direction (axis));
+ g_assert (persp != NULL);
- if (perspective->get_vanishing_point(axis)->state == VP_INFINITE) {
- this->set_direction(perspective->get_vanishing_point(axis)->v_dir);
+ if (!persp3d_get_VP(persp, axis).is_finite()) {
+ Proj::Pt2 vp(persp3d_get_VP(persp, axis));
+ this->set_direction(NR::Point(vp[Proj::X], vp[Proj::Y]));
}
this->vp_dir = axis;
- this->persp = perspective;
-}
-
-// This function makes sure not to return NR::Nothing()
-// FIXME: How to gracefully handle parallel lines?
-NR::Maybe<NR::Point> PerspectiveLine::intersect (Line const &line)
-{
- NR::Maybe<NR::Point> pt = this->Line::intersect(line);
- if (!pt) {
- Box3D::VanishingPoint vp = *(persp->get_vanishing_point(vp_dir));
- if (vp.state == VP_INFINITE) {
- pt = vp;
- } else {
- pt = NR::Point (0.0, 0.0); // FIXME: Better solution needed
- }
- }
- return pt;
-}
-
-// FIXME: Do we really need two intersection methods?
-NR::Point PerspectiveLine::meet(Line const &line)
-{
- 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();
- /* 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);
+ this->persp = persp;
}
} // namespace Box3D
diff --git a/src/perspective-line.h b/src/perspective-line.h
index 90104ffdf963953e86d58efb690e6a896d9efc48..e0235aafc72f830f31512b1f3c86437879e33ea3 100644 (file)
--- a/src/perspective-line.h
+++ b/src/perspective-line.h
#ifndef SEEN_PERSPECTIVE_LINE_H
#define SEEN_PERSPECTIVE_LINE_H
-#include "vanishing-point.h"
#include "line-geometry.h"
-#include "box3d-context.h"
#include <glib.h>
class SPDesktop;
* PL runs through it; otherwise it has the direction specified by the v_dir vector
* of the VP.
*/
- 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);
+ PerspectiveLine (NR::Point const &pt, Proj::Axis const axis, Persp3D *persp);
private:
- Box3D::Axis vp_dir; // direction of the associated VP
- Perspective3D *persp;
+ Proj::Axis vp_dir; // direction of the associated VP
+ Persp3D *persp;
};
} // namespace Box3D
-/** A function to print out the VanishingPoint (prints the coordinates) **/
-/***
-inline std::ostream &operator<< (std::ostream &out_file, const VanishingPoint &vp) {
- out_file << vp;
- return out_file;
-}
-***/
-
-
#endif /* !SEEN_PERSPECTIVE_LINE_H */
/*
diff --git a/src/perspective3d.cpp b/src/perspective3d.cpp
--- a/src/perspective3d.cpp
+++ /dev/null
@@ -1,453 +0,0 @@
-#define __PERSPECTIVE3D_C__
-
-/*
- * Class modelling a 3D perspective
- *
- * Authors:
- * Maximilian Albert <Anhalter42@gmx.de>
- *
- * Copyright (C) 2007 authors
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "box3d.h"
-#include "box3d-context.h"
-#include "perspective-line.h"
-#include <iostream>
-#include "perspective3d.h"
-#include "desktop-handles.h"
-
-// can probably be removed later
-#include "inkscape.h"
-
-namespace Box3D {
-
-gint Perspective3D::counter = 0;
-
-/**
- * Computes the intersection of the two perspective lines from pt1 and pt2 to the respective
- * vanishing points in the given directions.
- */
-// FIXME: This has been moved to a virtual method inside PerspectiveLine; can probably be purged
-NR::Point
-perspective_intersection (NR::Point pt1, Box3D::Axis dir1, NR::Point pt2, Box3D::Axis dir2, Perspective3D *persp)
-{
- VanishingPoint const *vp1 = persp->get_vanishing_point(dir1);
- VanishingPoint const *vp2 = persp->get_vanishing_point(dir2);
- NR::Maybe<NR::Point> meet = Line(pt1, *vp1).intersect(Line(pt2, *vp2));
- // FIXME: How to handle parallel lines (also depends on the type of the VPs)?
- if (!meet) { meet = NR::Point (0.0, 0.0); }
- return *meet;
-}
-
-/**
- * Find the point on the perspective line from line_pt to the
- * vanishing point in direction dir that is closest to ext_pt.
- */
-NR::Point
-perspective_line_snap (NR::Point line_pt, Box3D::Axis dir, NR::Point ext_pt, Perspective3D *persp)
-{
- return PerspectiveLine(line_pt, dir, persp).closest_to(ext_pt);
-}
-
-Perspective3D::Perspective3D (VanishingPoint const &pt_x, VanishingPoint const &pt_y, VanishingPoint const &pt_z, SPDocument *doc)
- : boxes (NULL),
- document (doc)
-{
- vp_x = new VanishingPoint (pt_x);
- vp_y = new VanishingPoint (pt_y);
- vp_z = new VanishingPoint (pt_z);
-
- my_counter = Perspective3D::counter++;
-
- if (document == NULL) {
- g_warning ("What to do now?\n");
- }
-}
-
-Perspective3D::Perspective3D (Perspective3D &other)
- : boxes (NULL) // Should we add an option to copy the list of boxes?
-{
- vp_x = new VanishingPoint (*other.vp_x);
- vp_y = new VanishingPoint (*other.vp_y);
- vp_z = new VanishingPoint (*other.vp_z);
-
- my_counter = Perspective3D::counter++;
-
- document = other.document;
-}
-
-Perspective3D::~Perspective3D ()
-{
- if (document) {
- document->remove_perspective (this);
- } else {
- g_warning ("No document found!\n");
- }
-
- // Remove the VPs from their draggers
- SPEventContext *ec = inkscape_active_event_context();
- if (SP_IS_3DBOX_CONTEXT (ec)) {
- SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
- // we need to check if there are any draggers because the selection
- // is temporarily empty during duplication of boxes, e.g.
- if (bc->_vpdrag->draggers != NULL) {
- /***
- g_assert (bc->_vpdrag->getDraggerFor (*vp_x) != NULL);
- g_assert (bc->_vpdrag->getDraggerFor (*vp_y) != NULL);
- g_assert (bc->_vpdrag->getDraggerFor (*vp_z) != NULL);
- bc->_vpdrag->getDraggerFor (*vp_x)->removeVP (vp_x);
- bc->_vpdrag->getDraggerFor (*vp_y)->removeVP (vp_y);
- bc->_vpdrag->getDraggerFor (*vp_z)->removeVP (vp_z);
- ***/
- // TODO: the temporary perspective created when building boxes is not linked to any dragger, hence
- // we need to do the following checks. Maybe it would be better to not create a temporary
- // perspective at all but simply compare the VPs manually in sp_3dbox_build.
- VPDragger * dragger;
- dragger = bc->_vpdrag->getDraggerFor (*vp_x);
- if (dragger)
- dragger->removeVP (vp_x);
- dragger = bc->_vpdrag->getDraggerFor (*vp_y);
- if (dragger)
- dragger->removeVP (vp_y);
- dragger = bc->_vpdrag->getDraggerFor (*vp_z);
- if (dragger)
- dragger->removeVP (vp_z);
- }
- }
-
- delete vp_x;
- delete vp_y;
- delete vp_z;
-
- g_slist_free (boxes);
-}
-
-bool
-Perspective3D::operator==(Perspective3D const &other) const
-{
- // Two perspectives are equal iff their vanishing points coincide and have identical states
- return (*vp_x == *other.vp_x && *vp_y == *other.vp_y && *vp_z == *other.vp_z);
-}
-
-bool
-Perspective3D::has_vanishing_point (VanishingPoint *vp)
-{
- return (vp == vp_x || vp == vp_y || vp == vp_z);
-}
-
-VanishingPoint *
-Perspective3D::get_vanishing_point (Box3D::Axis const dir)
-{
- switch (dir) {
- case X:
- return vp_x;
- break;
- case Y:
- return vp_y;
- break;
- case Z:
- return vp_z;
- break;
- case NONE:
- g_warning ("Axis direction must be specified. As a workaround we return the VP in X direction.\n");
- return vp_x;
- break;
- default:
- g_warning ("Single axis direction needed to determine corresponding vanishing point.\n");
- return get_vanishing_point (extract_first_axis_direction(dir));
- break;
- }
-}
-
-void
-Perspective3D::set_vanishing_point (Box3D::Axis const dir, VanishingPoint const &pt)
-{
- switch (dir) {
- case X:
- (*vp_x) = pt;
- break;
- case Y:
- (*vp_y) = pt;
- break;
- case Z:
- (*vp_z) = pt;
- break;
- default:
- // no vanishing point to set
- break;
- }
-}
-
-void
-Perspective3D::set_infinite_direction (Box3D::Axis axis, NR::Point const dir)
-{
- Box3D::Axis axis1 = Box3D::get_remaining_axes (axis).first;
- Box3D::Axis axis2 = Box3D::get_remaining_axes (axis).second;
- Box3D::VanishingPoint *vp1 = get_vanishing_point (axis1);
- Box3D::VanishingPoint *vp2 = get_vanishing_point (axis2);
- if (fabs (Box3D::determinant (vp1->v_dir, dir)) < Box3D::epsilon ||
- fabs (Box3D::determinant (vp2->v_dir, dir)) < Box3D::epsilon) {
- // This is an ad-hoc correction; we should fix this more thoroughly
- double a = NR::atan2 (dir) + 0.01;
- this->set_infinite_direction (axis, NR::Point (cos (a), sin (a))); // we call this function again in case there is another conflict (which is unlikely, but possible)
- return;
- }
-
- get_vanishing_point (axis)->set_infinite_direction (dir);
- for (GSList *i = this->boxes; i != NULL; i = i->next) {
- sp_3dbox_reshape_after_VP_rotation (SP_3DBOX (i->data), axis);
- sp_3dbox_set_z_orders_later_on (SP_3DBOX (i->data));
- }
- update_box_reprs();
-}
-
-void
-Perspective3D::rotate (Box3D::Axis const axis, double const angle, bool const alt_pressed)
-{
- Box3D::VanishingPoint *vp = get_vanishing_point (axis);
- if (!vp->is_finite()) {
- //double add_value = angle;
- double a = NR::atan2 (vp->v_dir) * 180/M_PI;
- a += alt_pressed ? 0.5 * ((angle > 0 ) - (angle < 0)) : angle; // the r.h.s. yields +/-0.5 or angle
- a *= M_PI/180;
- this->set_infinite_direction (axis, NR::Point (cos (a), sin (a)));
- }
-}
-
-Axis
-Perspective3D::get_axis_of_VP (VanishingPoint *vp)
-{
- if (vp == vp_x) return X;
- if (vp == vp_y) return Y;
- if (vp == vp_z) return Z;
-
- g_warning ("Vanishing point not present in the perspective.\n");
- return NONE;
-}
-
-void
-Perspective3D::set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st)
-{
- VanishingPoint *vp;
- switch (dir) {
- case X:
- vp = vp_x;
- break;
- case Y:
- vp = vp_y;
- break;
- case Z:
- vp = vp_z;
- break;
- default:
- // no vanishing point to set
- return;
- }
-
- vp->set_pos (pt_x, pt_y);
- vp->v_dir = NR::Point (dir_x, dir_y);
- vp->state = st;
-}
-
-void
-Perspective3D::add_box (SP3DBox *box)
-{
- if (g_slist_find (this->boxes, box) != NULL) {
- // Don't add the same box twice
- g_warning ("Box already uses the current perspective. We don't add it again.\n");
- return;
- }
- this->boxes = g_slist_append (this->boxes, box);
-}
-
-void
-Perspective3D::remove_box (const SP3DBox *box)
-{
- if (!g_slist_find (this->boxes, box)) {
- g_warning ("Could not find box that is to be removed in the current perspective.\n");
- }
- this->boxes = g_slist_remove (this->boxes, box);
-}
-
-bool
-Perspective3D::has_box (const SP3DBox *box) const
-{
- return (g_slist_find (this->boxes, box) != NULL);
-}
-
-bool
-Perspective3D::all_boxes_occur_in_list (GSList *boxes_to_do)
-{
- for (GSList *i = boxes; i != NULL; i = i->next) {
- if (!g_slist_find (boxes_to_do, i->data)) {
- return false;
- }
- }
- return true;
-}
-
-GSList *
-Perspective3D::boxes_occurring_in_list (GSList * list_of_boxes)
-{
- GSList * result = NULL;
- for (GSList *i = list_of_boxes; i != NULL; i = i->next) {
- if (this->has_box (SP_3DBOX (i->data))) {
- result = g_slist_prepend (result, i->data);
- }
- }
- // we reverse so as to retain the same order as in list_of_boxes
- return g_slist_reverse (result);
-}
-
-/**
- * Update the shape of a box after a handle was dragged or a VP was changed, according to the stored ratios.
- */
-void
-Perspective3D::reshape_boxes (Box3D::Axis axes)
-{
- // TODO: Leave the "correct" corner fixed according to which face is supposed to be on front.
- NR::Point new_pt;
- VanishingPoint *vp;
- for (const GSList *i = this->boxes; i != NULL; i = i->next) {
- SP3DBox *box = SP_3DBOX (i->data);
- if (axes & Box3D::X) {
- vp = this->get_vanishing_point (Box3D::X);
- if (vp->is_finite()) {
- new_pt = vp->get_pos() + box->ratio_x * (box->corners[3] - vp->get_pos());
- sp_3dbox_move_corner_in_XY_plane (box, 2, new_pt);
- }
- }
- if (axes & Box3D::Y) {
- vp = this->get_vanishing_point (Box3D::Y);
- if (vp->is_finite()) {
- new_pt = vp->get_pos() + box->ratio_y * (box->corners[0] - vp->get_pos());
- sp_3dbox_move_corner_in_XY_plane (box, 2, new_pt);
- }
- }
- if (axes & Box3D::Z) {
- vp = this->get_vanishing_point (Box3D::Z);
- if (vp->is_finite()) {
- new_pt = vp->get_pos() + box->ratio_z * (box->corners[0] - vp->get_pos());
- sp_3dbox_move_corner_in_Z_direction (box, 4, new_pt);
- }
- }
-
- sp_3dbox_set_shape (box, true);
- }
-}
-
-void
-Perspective3D::toggle_boxes (Box3D::Axis axis)
-{
- get_vanishing_point (axis)->toggle_parallel();
- for (GSList *i = this->boxes; i != NULL; i = i->next) {
- 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
-Perspective3D::update_box_reprs ()
-{
- for (GSList *i = this->boxes; i != NULL; i = i->next) {
- SP_OBJECT(SP_3DBOX (i->data))->updateRepr(SP_OBJECT_WRITE_EXT);
- }
-}
-
-void
-Perspective3D::update_z_orders ()
-{
- for (GSList *i = this->boxes; i != NULL; i = i->next) {
- sp_3dbox_set_z_orders_later_on (SP_3DBOX (i->data));
- }
-}
-
-/* the direction from a point pt towards the specified vanishing point of the perspective */
-NR::Point
-Perspective3D::direction (NR::Point pt, Box3D::Axis axis)
-{
- Box3D::VanishingPoint *vp = this->get_vanishing_point (axis);
- if (!vp->is_finite()) {
- return vp->v_dir;
- }
- return (vp->get_pos() - pt);
-}
-
-// swallow the list of boxes from the other perspective and delete it
-void
-Perspective3D::absorb (Perspective3D *other)
-{
- g_return_if_fail (*this == *other);
-
- // FIXME: Is copying necessary? Is other->boxes invalidated when other is deleted below?
- this->boxes = g_slist_concat (this->boxes, g_slist_copy (other->boxes));
-
- // Should we delete the other perspective here or at the place from where absorb() is called?
- delete other;
- other = NULL;
-}
-
-// FIXME: We get compiler errors when we try to move the code from sp_3dbox_get_perspective_string to this function
-/***
-gchar *
-Perspective3D::svg_string ()
-{
-}
-***/
-
-void
-Perspective3D::print_debugging_info ()
-{
- g_print ("====================================================\n");
- for (GSList *i = sp_desktop_document (inkscape_active_desktop())->perspectives; i != NULL; i = i->next) {
- Perspective3D *persp = (Perspective3D *) i->data;
- g_print ("Perspective %d:\n", persp->my_counter);
-
- VanishingPoint * vp = persp->get_vanishing_point(Box3D::X);
- g_print (" VP X: (%f,%f) ", (*vp)[NR::X], (*vp)[NR::Y]);
- g_print ((vp->is_finite()) ? "(finite)\n" : "(infinite)\n");
-
- vp = persp->get_vanishing_point(Box3D::Y);
- g_print (" VP Y: (%f,%f) ", (*vp)[NR::X], (*vp)[NR::Y]);
- g_print ((vp->is_finite()) ? "(finite)\n" : "(infinite)\n");
-
- vp = persp->get_vanishing_point(Box3D::Z);
- g_print (" VP Z: (%f,%f) ", (*vp)[NR::X], (*vp)[NR::Y]);
- g_print ((vp->is_finite()) ? "(finite)\n" : "(infinite)\n");
-
- g_print ("\nBoxes: ");
- if (persp->boxes == NULL) {
- g_print ("none");
- } else {
- GSList *j;
- for (j = persp->boxes; j != NULL; j = j->next) {
- if (j->next == NULL) break;
- g_print ("%d, ", SP_3DBOX (j->data)->my_counter);
- }
- if (j != NULL) {
- g_print ("%d", SP_3DBOX (j->data)->my_counter);
- }
- g_print ("\n");
- }
- g_print ("\n");
- }
- g_print ("====================================================\n");
-}
-
-} // namespace Box3D
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/perspective3d.h b/src/perspective3d.h
--- a/src/perspective3d.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Class modelling a 3D perspective
- *
- * Authors:
- * Maximilian Albert <Anhalter42@gmx.de>
- *
- * Copyright (C) 2007 authors
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#ifndef SEEN_PERSPECTIVE3D_H
-#define SEEN_PERSPECTIVE3D_H
-
-#include "vanishing-point.h"
-#include "svg/stringstream.h"
-#include <glib.h>
-
-class SP3DBox;
-
-namespace Box3D {
-
-class PerspectiveLine;
-
-class Perspective3D {
-public:
- Perspective3D(VanishingPoint const &pt_x, VanishingPoint const &pt_y, VanishingPoint const &pt_z, SPDocument *document);
- Perspective3D(Perspective3D &other);
- ~Perspective3D();
-
- bool operator== (Perspective3D const &other) const;
-
- bool has_vanishing_point (VanishingPoint *vp);
- VanishingPoint *get_vanishing_point (Box3D::Axis const dir);
- Axis get_axis_of_VP (VanishingPoint *vp);
- void set_vanishing_point (Box3D::Axis const dir, VanishingPoint const &pt);
- void set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st);
- void set_infinite_direction (Box3D::Axis axis, NR::Point const dir);
- void rotate (Box3D::Axis const dir, double const angle, bool const alt_pressed = false);
- void add_box (SP3DBox *box);
- void remove_box (const SP3DBox *box);
- bool has_box (const SP3DBox *box) const;
- inline guint number_of_boxes () { return g_slist_length (boxes); }
- void reshape_boxes (Box3D::Axis axes);
- void toggle_boxes (Box3D::Axis axes); // update the shape of boxes after a VP's state was toggled
- void update_box_reprs ();
- void update_z_orders ();
-
- NR::Point direction (NR::Point pt, Box3D::Axis axis);
-
- /* convenience functions for interaction with dragging machinery: */
- bool all_boxes_occur_in_list (GSList *boxes_to_do);
- GSList * boxes_occurring_in_list (GSList * list_of_boxes);
-
- void absorb (Perspective3D *other); // swallow the other perspective if both coincide
-
- static gint counter; // for testing only
- gint my_counter; // for testing only
-
- static void print_debugging_info();
- static Perspective3D * current_perspective;
-
-private:
- VanishingPoint *vp_x;
- VanishingPoint *vp_y;
- VanishingPoint *vp_z;
- GSList * boxes; // holds a list of boxes sharing this specific perspective
- SPDocument * document;
-};
-
-NR::Point perspective_intersection (NR::Point pt1, Box3D::Axis dir1, NR::Point pt2, Box3D::Axis dir2, Perspective3D *persp);
-NR::Point perspective_line_snap (NR::Point pt, Box3D::Axis dir, NR::Point ext_pt, Perspective3D *persp);
-
-} // namespace Box3D
-
-
-/** A function to print out the VanishingPoint (prints the coordinates) **/
-/***
-inline std::ostream &operator<< (std::ostream &out_file, const VanishingPoint &vp) {
- out_file << vp;
- return out_file;
-}
-***/
-
-
-#endif /* !SEEN_PERSPECTIVE3D_H */
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/proj_pt.cpp b/src/proj_pt.cpp
--- /dev/null
+++ b/src/proj_pt.cpp
@@ -0,0 +1,119 @@
+#define __PROJ_PT_C__
+
+/*
+ * 3x4 transformation matrix to map points from projective 3-space into the projective plane
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "proj_pt.h"
+#include "svg/stringstream.h"
+
+namespace Proj {
+
+Pt2::Pt2(const gchar *coord_str) {
+ if (!coord_str) {
+ pt[0] = 0.0;
+ pt[1] = 0.0;
+ pt[2] = 1.0;
+ g_warning ("Coordinate string is empty. Creating default Pt2\n");
+ return;
+ }
+ gchar **coords = g_strsplit(coord_str, ":", 0);
+ if (coords[0] == NULL || coords[1] == NULL || coords[2] == NULL) {
+ g_strfreev (coords);
+ g_warning ("Malformed coordinate string.\n");
+ return;
+ }
+
+ pt[0] = g_ascii_strtod(coords[0], NULL);
+ pt[1] = g_ascii_strtod(coords[1], NULL);
+ pt[2] = g_ascii_strtod(coords[2], NULL);
+}
+
+void
+Pt2::normalize() {
+ if (fabs(pt[2]) < 1E-6 || pt[2] == 1.0)
+ return;
+ pt[0] /= pt[2];
+ pt[1] /= pt[2];
+ pt[2] = 1.0;
+}
+
+NR::Point
+Pt2::affine() {
+ if (fabs(pt[2]) < epsilon) {
+ return NR::Point (NR_HUGE, NR_HUGE);
+ }
+ return NR::Point (pt[0]/pt[2], pt[1]/pt[2]);
+}
+
+gchar *
+Pt2::coord_string() {
+ Inkscape::SVGOStringStream os;
+ os << pt[0] << " : "
+ << pt[1] << " : "
+ << pt[2];
+ return g_strdup(os.str().c_str());
+}
+
+Pt3::Pt3(const gchar *coord_str) {
+ if (!coord_str) {
+ pt[0] = 0.0;
+ pt[1] = 0.0;
+ pt[2] = 0.0;
+ pt[3] = 1.0;
+ g_warning ("Coordinate string is empty. Creating default Pt2\n");
+ return;
+ }
+ gchar **coords = g_strsplit(coord_str, ":", 0);
+ if (coords[0] == NULL || coords[1] == NULL ||
+ coords[2] == NULL || coords[3] == NULL) {
+ g_strfreev (coords);
+ g_warning ("Malformed coordinate string.\n");
+ return;
+ }
+
+ pt[0] = g_ascii_strtod(coords[0], NULL);
+ pt[1] = g_ascii_strtod(coords[1], NULL);
+ pt[2] = g_ascii_strtod(coords[2], NULL);
+ pt[3] = g_ascii_strtod(coords[3], NULL);
+}
+
+void
+Pt3::normalize() {
+ if (fabs(pt[3]) < 1E-6 || pt[3] == 1.0)
+ return;
+ pt[0] /= pt[3];
+ pt[1] /= pt[3];
+ pt[2] /= pt[3];
+ pt[3] = 1.0;
+}
+
+gchar *
+Pt3::coord_string() {
+ Inkscape::SVGOStringStream os;
+ os << pt[0] << " : "
+ << pt[1] << " : "
+ << pt[2] << " : "
+ << pt[3];
+ return g_strdup(os.str().c_str());
+}
+
+} // namespace Proj
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/proj_pt.h b/src/proj_pt.h
--- /dev/null
+++ b/src/proj_pt.h
@@ -0,0 +1,172 @@
+#ifndef __PROJ_PT_H__
+#define __PROJ_PT_H__
+
+/*
+ * 3x4 transformation matrix to map points from projective 3-space into the projective plane
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "libnr/nr-point.h"
+#include "libnr/nr-values.h"
+#include <gtk/gtk.h>
+
+namespace Proj {
+
+const double epsilon = 1E-6;
+
+// TODO: Catch the case when the constructors are called with only zeros
+class Pt2 {
+public:
+ Pt2 () { pt[0] = 0; pt[1] = 0; pt[2] = 1.0; } // we default to (0 : 0 : 1)
+ Pt2 (double x, double y, double w) { pt[0] = x; pt[1] = y; pt[2] = w; }
+ Pt2 (NR::Point const &point) { pt[0] = point[NR::X]; pt[1] = point[NR::Y]; pt[2] = 1; }
+ Pt2 (const gchar *coord_str);
+
+ inline double operator[] (unsigned int index) const {
+ if (index > 2) { return NR_HUGE; }
+ return pt[index];
+ }
+ inline double &operator[] (unsigned int index) {
+ // FIXME: How should we handle wrong indices?
+ //if (index > 2) { return NR_HUGE; }
+ return pt[index];
+ }
+ inline bool operator== (Pt2 &rhs) {
+ normalize();
+ rhs.normalize();
+ return (fabs(pt[0] - rhs.pt[0]) < epsilon &&
+ fabs(pt[1] - rhs.pt[1]) < epsilon &&
+ fabs(pt[2] - rhs.pt[2]) < epsilon);
+ }
+ inline bool operator!= (Pt2 &rhs) {
+ return !((*this) == rhs);
+ }
+
+ /*** For convenience, we define addition/subtraction etc. as "affine" operators (i.e.,
+ the result for finite points is the same as if the affine points were addes ***/
+ inline Pt2 &operator+(Pt2 &rhs) const {
+ Pt2 *result = new Pt2 (*this);
+ result->normalize();
+ rhs.normalize();
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ result->pt[i] += rhs.pt[i];
+ }
+ return *result;
+ }
+
+ inline Pt2 &operator-(Pt2 &rhs) const {
+ Pt2 *result = new Pt2 (*this);
+ result->normalize();
+ rhs.normalize();
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ result->pt[i] -= rhs.pt[i];
+ }
+ return *result;
+ }
+
+ inline Pt2 &operator*(double const s) const {
+ Pt2 *result = new Pt2 (*this);
+ result->normalize();
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ result->pt[i] *= s;
+ }
+ return *result;
+ }
+
+ void normalize();
+ NR::Point affine();
+ inline bool is_finite() { return pt[2] != 0; } // FIXME: Should we allow for some tolerance?
+ gchar *coord_string();
+ inline void print(gchar *s) const { g_print ("%s(%8.2f : %8.2f : %8.2f)\n", s, pt[0], pt[1], pt[2]); }
+
+private:
+ double pt[3];
+};
+
+
+class Pt3 {
+public:
+ Pt3 () { pt[0] = 0; pt[1] = 0; pt[2] = 0; pt[3] = 1.0; } // we default to (0 : 0 : 0 : 1)
+ Pt3 (double x, double y, double z, double w) { pt[0] = x; pt[1] = y; pt[2] = z; pt[3] = w; }
+ Pt3 (const gchar *coord_str);
+
+ inline bool operator== (Pt3 &rhs) {
+ normalize();
+ rhs.normalize();
+ return (fabs(pt[0] - rhs.pt[0]) < epsilon &&
+ fabs(pt[1] - rhs.pt[1]) < epsilon &&
+ fabs(pt[2] - rhs.pt[2]) < epsilon &&
+ fabs(pt[3] - rhs.pt[3]) < epsilon);
+ }
+
+ /*** For convenience, we define addition/subtraction etc. as "affine" operators (i.e.,
+ the result for finite points is the same as if the affine points were addes ***/
+ inline Pt3 &operator+(Pt3 &rhs) const {
+ Pt3 *result = new Pt3 (*this);
+ result->normalize();
+ rhs.normalize();
+ for ( unsigned i = 0 ; i < 3 ; ++i ) {
+ result->pt[i] += rhs.pt[i];
+ }
+ return *result;
+ }
+
+ inline Pt3 &operator-(Pt3 &rhs) const {
+ Pt3 *result = new Pt3 (*this);
+ result->normalize();
+ rhs.normalize();
+ for ( unsigned i = 0 ; i < 3 ; ++i ) {
+ result->pt[i] -= rhs.pt[i];
+ }
+ return *result;
+ }
+
+ inline Pt3 &operator*(double const s) const {
+ Pt3 *result = new Pt3 (*this);
+ result->normalize();
+ for ( unsigned i = 0 ; i < 3 ; ++i ) {
+ result->pt[i] *= s;
+ }
+ return *result;
+ }
+
+ inline double operator[] (unsigned int index) const {
+ if (index > 3) { return NR_HUGE; }
+ return pt[index];
+ }
+ inline double &operator[] (unsigned int index) {
+ // FIXME: How should we handle wrong indices?
+ //if (index > 3) { return NR_HUGE; }
+ return pt[index];
+ }
+ void normalize();
+ inline bool is_finite() { return pt[3] != 0; } // FIXME: Should we allow for some tolerance?
+ gchar *coord_string();
+ inline void print(gchar *s) const {
+ g_print ("%s(%8.2f : %8.2f : %8.2f : %8.2f)\n", s, pt[0], pt[1], pt[2], pt[3]);
+ }
+
+private:
+ double pt[4];
+};
+
+} // namespace Proj
+
+#endif /* __PROJ_PT_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/rect-context.cpp b/src/rect-context.cpp
index d069e052c977b7b9ce28e6248cce6046384cdea7..3efc8159611d9a7e142e58b2b54a28e5aa19fa61 100644 (file)
--- a/src/rect-context.cpp
+++ b/src/rect-context.cpp
*/
#include "config.h"
+#include "inkscape.h"
#include <gdk/gdkkeysyms.h>
@@ -397,6 +398,19 @@ static gint sp_rect_context_root_handler(SPEventContext *event_context, GdkEvent
}
break;
+ case GDK_T:
+ {
+ Inkscape::Selection *selection = sp_desktop_selection (inkscape_active_desktop());
+ SPItem *item = selection->singleItem();
+ if (item && SP_IS_RECT (item)) {
+ g_print ("Scaling transformation matrix\n");
+ SP_RECT (item)->transform = nr_matrix_set_scale(SP_RECT (item)->transform, 1.25, 1.5);
+ SP_OBJECT (item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ ret = TRUE;
+ }
+ break;
+
case GDK_Escape:
sp_desktop_selection(desktop)->clear();
//TODO: make dragging escapable by Esc
diff --git a/src/select-context.cpp b/src/select-context.cpp
index 2eff4297f3f78d6c7b28313c4aa733f82b8ebe17..cdf63785c2f33536b9b5acb715ddfec454ca2635 100644 (file)
--- a/src/select-context.cpp
+++ b/src/select-context.cpp
#include "message-stack.h"
#include "selection-describer.h"
#include "seltrans.h"
+#include "box3d.h"
static void sp_select_context_class_init(SPSelectContextClass *klass);
static void sp_select_context_init(SPSelectContext *select_context);
if (event->button.button == 1) {
if (!selection->isEmpty()) {
SPItem *clicked_item = (SPItem *) selection->itemList()->data;
- if (SP_IS_GROUP (clicked_item)) { // enter group
+ if (SP_IS_GROUP(clicked_item) && !SP_IS_BOX3D(clicked_item)) { // enter group if it's not a 3D box
desktop->setCurrentLayer(reinterpret_cast<SPObject *>(clicked_item));
sp_desktop_selection(desktop)->clear();
sc->dragging = false;
if (MOD__CTRL_ONLY) {
if (selection->singleItem()) {
SPItem *clicked_item = selection->singleItem();
- if (SP_IS_GROUP (clicked_item)) { // enter group
+ if ( SP_IS_GROUP(clicked_item) &&
+ !SP_IS_BOX3D(clicked_item)) { // enter group if it's not a 3D box
desktop->setCurrentLayer(reinterpret_cast<SPObject *>(clicked_item));
sp_desktop_selection(desktop)->clear();
} else {
index 01aab97b759f29efa95aa3d7401ea400cad58d65..1debd73e15e95206674d275db6b5b5fbfcf2940b 100644 (file)
{ return _("Polyline"); }
if (type == SP_TYPE_RECT)
{ return _("Rectangle"); }
- if (type == SP_TYPE_3DBOX)
+ if (type == SP_TYPE_BOX3D)
{ return _("3D Box"); }
if (type == SP_TYPE_TEXT)
{ return _("Text"); }
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp
index 7a1b0f33e0ef41cc118a6a9d862ed873263a2340..81a103cd7881faca80e9fee2723f2a336df651c8 100644 (file)
--- a/src/sp-ellipse.cpp
+++ b/src/sp-ellipse.cpp
static void
sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
{
+ g_print ("sp_ellipse_build\n");
if (((SPObjectClass *) ellipse_parent_class)->build)
(* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp
index cabc7b26a754e173cb6b9c2dee5eb37443e58052..410a7b37e4c898dc0ba1cce4378e1c2498ff6a39 100644 (file)
--- a/src/sp-item-group.cpp
+++ b/src/sp-item-group.cpp
#include "sp-clippath.h"
#include "sp-mask.h"
#include "sp-path.h"
+#include "box3d.h"
+#include "box3d-side.h"
static void sp_group_class_init (SPGroupClass *klass);
static void sp_group_init (SPGroup *group);
SPItem *pitem = SP_ITEM (SP_OBJECT_PARENT (gitem));
Inkscape::XML::Node *prepr = SP_OBJECT_REPR (pitem);
+ /* When ungrouping a 3D box, we must convert the sides to ordinary paths */
+ if (SP_IS_BOX3D (gitem)) {
+ g_print ("============== Ungrouping a 3D box ===============\n");
+ }
+
/* Step 1 - generate lists of children objects */
GSList *items = NULL;
GSList *objects = NULL;
SPItem *citem = SP_ITEM (child);
+ if (SP_IS_BOX3D_SIDE(child)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(child);
+ // FIXME: This doesn't remove the attribute "inkscape:box3dsidetype". Why?
+ repr->setAttribute("inkscape:box3dsidetype", NULL);
+ repr->setAttribute("sodipodi:type", NULL);
+ }
+
/* Merging of style */
// this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do
// it here _before_ the new transform is set, so as to use the pre-transform bbox
while (items) {
Inkscape::XML::Node *repr = (Inkscape::XML::Node *) items->data;
// add item
- prepr->appendChild(repr);
+ prepr->appendChild(repr);
// restore position; since the items list was prepended (i.e. reverse), we now add
// all children at the same pos, which inverts the order once again
repr->setPosition(pos > 0 ? pos : 0);
while (l) {
SPObject *child = SP_OBJECT (l->data);
l = g_slist_remove (l, child);
+ //g_print ("sp-item-group: onUpdate working on child with flags ");
if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
+ /***
+ g_print (" On parent object: ");
+ if (flags & SP_OBJECT_MODIFIED_FLAG) {
+ g_print("SP_OBJECT_MODIFIED_FLAG ");
+ }
+ if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
+ g_print("SP_OBJECT_CHILD_MODIFIED_FLAG ");
+ }
+ g_print ("\n");
+ g_print (" On child object: ");
+ if (child->uflags & SP_OBJECT_MODIFIED_FLAG) {
+ g_print("SP_OBJECT_MODIFIED_FLAG ");
+ }
+ if (child->uflags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
+ g_print("SP_OBJECT_CHILD_MODIFIED_FLAG ");
+ }
+ ***/
if (SP_IS_ITEM (child)) {
SPItem const &chi = *SP_ITEM(child);
cctx.i2doc = chi.transform * ictx->i2doc;
cctx.i2vp = chi.transform * ictx->i2vp;
+ /**
+ g_print ("case 1\n");
+ g_print ("\n On parent object: flags=%d ", flags);
+ g_print ("\n On child object: uflags=%d ", child->uflags);
+ if (flags & SP_OBJECT_MODIFIED_FLAG) g_print("SP_OBJECT_MODIFIED_FLAG ");
+ if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) g_print("SP_OBJECT_CHILD_MODIFIED_FLAG ");
+ g_print ("\n");
+ if (child->uflags & SP_OBJECT_MODIFIED_FLAG) {
+ g_print("SP_OBJECT_MODIFIED_FLAG ");
+ }
+ if (child->uflags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
+ g_print("SP_OBJECT_CHILD_MODIFIED_FLAG ");
+ }
+ g_print ("\n");
+ **/
+ //g_print ("Caution! The changed code applies! Does this change any behaviour?\n");
child->updateDisplay((SPCtx *)&cctx, flags);
+ //child->updateDisplay((SPCtx *)&cctx, child->uflags);
} else {
child->updateDisplay(ctx, flags);
+ //g_print ("case 2\n");
}
}
g_object_unref (G_OBJECT (child));
diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp
index 9338f101996372d0eb811f27f672e3ebe17dd955..5fa36d7b589884692431abe5fafe082ca66aab07 100644 (file)
--- a/src/sp-object-repr.cpp
+++ b/src/sp-object-repr.cpp
#include "sp-radial-gradient-fns.h"
#include "sp-rect.h"
#include "box3d.h"
+#include "box3d-side.h"
+#include "persp3d.h"
#include "sp-ellipse.h"
#include "sp-star.h"
#include "sp-stop-fns.h"
{ "inkscape:offset", SP_TYPE_OFFSET },
{ "spiral", SP_TYPE_SPIRAL },
{ "star", SP_TYPE_STAR },
- { "inkscape:3dbox", SP_TYPE_3DBOX }//,
- //{ "inkscape:3dboxface", SP_TYPE_3DBOX_FACE }
+ { "inkscape:box3d", SP_TYPE_BOX3D },
+ { "inkscape:box3dside", SP_TYPE_BOX3D_SIDE },
+ { "inkscape:persp3d", SP_TYPE_PERSP3D }
};
NameTypeEntry const *const t2entries[] = {
diff --git a/src/sp-rect.cpp b/src/sp-rect.cpp
index d9caebd5a00eaeae4464aec2536b30deaf79c57a..e5f1da05d24eb735e527ef04bfee4e60da0b57e0 100644 (file)
--- a/src/sp-rect.cpp
+++ b/src/sp-rect.cpp
static NR::Matrix
sp_rect_set_transform(SPItem *item, NR::Matrix const &xform)
{
+ g_print ("sp_rect_set_transform\n");
SPRect *rect = SP_RECT(item);
/* Calculate rect start in parent coords. */
diff --git a/src/syseq.h b/src/syseq.h
--- /dev/null
+++ b/src/syseq.h
@@ -0,0 +1,328 @@
+#ifndef SEEN_SYSEQ_H
+#define SEEN_SYSEQ_H
+
+/*
+ * Auxiliary routines to solve systems of linear equations in several variants and sizes.
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include "math.h"
+
+namespace SysEq {
+
+enum SolutionKind {
+ unique = 0,
+ ambiguous,
+ no_solution,
+ solution_exists // FIXME: remove this; does not yield enough information
+};
+
+inline void explain(SolutionKind sol) {
+ switch (sol) {
+ case SysEq::unique:
+ std::cout << "unique" << std::endl;
+ break;
+ case SysEq::ambiguous:
+ std::cout << "ambiguous" << std::endl;
+ break;
+ case SysEq::no_solution:
+ std::cout << "no solution" << std::endl;
+ break;
+ case SysEq::solution_exists:
+ std::cout << "solution exists" << std::endl;
+ break;
+ }
+}
+
+inline double
+determinant3x3 (double A[3][3]) {
+ return (A[0][0]*A[1][1]*A[2][2] +
+ A[0][1]*A[1][2]*A[2][0] +
+ A[0][2]*A[1][0]*A[2][1] -
+ A[0][0]*A[1][2]*A[2][1] -
+ A[0][1]*A[1][0]*A[2][2] -
+ A[0][2]*A[1][1]*A[2][0]);
+}
+
+/* Determinant of the 3x3 matrix having a, b, and c as columns */
+inline double
+determinant3v (const double a[3], const double b[3], const double c[3]) {
+ return (a[0]*b[1]*c[2] +
+ a[1]*b[2]*c[0] +
+ a[2]*b[0]*c[1] -
+ a[0]*b[2]*c[1] -
+ a[1]*b[0]*c[2] -
+ a[2]*b[1]*c[0]);
+}
+
+/* Fills the matrix A with random values between lower and upper */
+template <int S, int T>
+inline void fill_random (double A[S][T], double lower = 0.0, double upper = 1.0) {
+ srand(time(NULL));
+ double range = upper - lower;
+ for (int i = 0; i < S; ++i) {
+ for (int j = 0; j < T; ++j) {
+ A[i][j] = range*(random()/(RAND_MAX + 1.0)) - lower;
+ }
+ }
+}
+
+/* Copy the elements of A into B */
+template <int S, int T>
+inline void copy_mat(double A[S][T], double B[S][T]) {
+ for (int i = 0; i < S; ++i) {
+ for (int j = 0; j < T; ++j) {
+ B[i][j] = A[i][j];
+ }
+ }
+}
+
+template <int S, int T>
+inline void print_mat (const double A[S][T]) {
+ std::cout.setf(std::ios::left, std::ios::internal);
+ for (int i = 0; i < S; ++i) {
+ for (int j = 0; j < T; ++j) {
+ printf ("%8.2f ", A[i][j]);
+ }
+ std::cout << std::endl;;
+ }
+}
+
+/* Multiplication of two matrices */
+template <int S, int U, int T>
+inline void multiply(double A[S][U], double B[U][T], double res[S][T]) {
+ for (int i = 0; i < S; ++i) {
+ for (int j = 0; j < T; ++j) {
+ double sum = 0;
+ for (int k = 0; k < U; ++k) {
+ sum += A[i][k] * B[k][j];
+ }
+ res[i][j] = sum;
+ }
+ }
+}
+
+/*
+ * Multiplication of a matrix with a vector (for convenience, because with the previous
+ * multiplication function we would always have to write v[i][0] for elements of the vector.
+ */
+template <int S, int T>
+inline void multiply(double A[S][T], double v[T], double res[S]) {
+ for (int i = 0; i < S; ++i) {
+ double sum = 0;
+ for (int k = 0; k < T; ++k) {
+ sum += A[i][k] * v[k];
+ }
+ res[i] = sum;
+ }
+}
+
+// Remark: Since we are using templates, we cannot separate declarations from definitions (which would
+// result in linker errors but have to include the definitions here for the following functions.
+// FIXME: Maybe we should rework all this by using vector<vector<double> > structures for matrices
+// instead of double[S][T]. This would allow us to avoid templates. Would the performance degrade?
+
+/*
+ * Find the element of maximal absolute value in row i that
+ * does not lie in one of the columns given in avoid_cols.
+ */
+template <int S, int T>
+static int find_pivot(const double A[S][T], unsigned int i, std::vector<int> const &avoid_cols) {
+ if (i >= S) {
+ return -1;
+ }
+ int pos = -1;
+ double max = 0;
+ for (int j = 0; j < T; ++j) {
+ if (std::find(avoid_cols.begin(), avoid_cols.end(), j) != avoid_cols.end()) {
+ continue; // skip "forbidden" columns
+ }
+ if (fabs(A[i][j]) > max) {
+ pos = j;
+ max = fabs(A[i][j]);
+ }
+ }
+ return pos;
+}
+
+/*
+ * Performs a single 'exchange step' in the Gauss-Jordan algorithm (i.e., swapping variables in the
+ * two vectors).
+ */
+template <int S, int T>
+static void gauss_jordan_step (double A[S][T], int row, int col) {
+ double piv = A[row][col]; // pivot element
+ /* adapt the entries of the matrix, first outside the pivot row/column */
+ for (int k = 0; k < S; ++k) {
+ if (k == row) continue;
+ for (int l = 0; l < T; ++l) {
+ if (l == col) continue;
+ A[k][l] -= A[k][col] * A[row][l] / piv;
+ }
+ }
+ /* now adapt the pivot column ... */
+ for (int k = 0; k < S; ++k) {
+ if (k == row) continue;
+ A[k][col] /= piv;
+ }
+ /* and the pivot row */
+ for (int l = 0; l < T; ++l) {
+ if (l == col) continue;
+ A[row][l] /= -piv;
+ }
+ /* finally, set the element at the pivot position itself */
+ A[row][col] = 1/piv;
+}
+
+/*
+ * Perform Gauss-Jordan elimination on the matrix A, optionally avoiding a given column during pivot search
+ */
+template <int S, int T>
+static std::vector<int> gauss_jordan (double A[S][T], int avoid_col = -1) {
+ std::vector<int> cols_used;
+ if (avoid_col != -1) {
+ cols_used.push_back (avoid_col);
+ }
+ int col;
+ for (int i = 0; i < S; ++i) {
+ /* for each row find a pivot element of maximal absolute value, skipping the columns that were used before */
+ col = find_pivot<S,T>(A, i, cols_used);
+ cols_used.push_back(col);
+ if (col == -1) {
+ // no non-zero elements in the row
+ return cols_used;
+ }
+
+ /* if pivot search was successful we can perform a Gauss-Jordan step */
+ gauss_jordan_step<S,T> (A, i, col);
+ }
+ if (avoid_col != -1) {
+ // since the columns that were used will be needed later on, we need to clean up the column vector
+ cols_used.erase(cols_used.begin());
+ }
+ return cols_used;
+}
+
+/* compute the modified value that x[index] needs to assume so that in the end we have x[index]/x[T-1] = val */
+template <int S, int T>
+static double projectify (std::vector<int> const &cols, const double B[S][T], const double x[T],
+ const int index, const double val) {
+ double val_proj = 0.0;
+ if (index != -1) {
+ int c = -1;
+ for (int i = 0; i < S; ++i) {
+ if (cols[i] == T-1) {
+ c = i;
+ break;
+ }
+ }
+ if (c == -1) {
+ std::cout << "Something is wrong. Rethink!!" << std::endl;
+ return SysEq::no_solution;
+ }
+
+ double sp = 0;
+ for (int j = 0; j < T; ++j) {
+ if (j == index) continue;
+ sp += B[c][j] * x[j];
+ }
+ double mu = 1 - val * B[c][index];
+ if (fabs(mu) < 1E-6) {
+ std::cout << "No solution since adapted value is too close to zero" << std::endl;
+ return SysEq::no_solution;
+ }
+ val_proj = sp*val/mu;
+ } else {
+ val_proj = val; // FIXME: Is this correct?
+ }
+ return val_proj;
+}
+
+/**
+ * Solve the linear system of equations \a A * \a x = \a v where we additionally stipulate
+ * \a x[\a index] = \a val if \a index is not -1. The system is solved using Gauss-Jordan
+ * elimination so that we can gracefully handle the case that zero or infinitely many
+ * solutions exist.
+ *
+ * Since our application will be to finding preimages of projective mappings, we provide
+ * an additional argument \a proj. If this is true, we find a solution of
+ * \a x[\a index]/\a x[\T - 1] = \a val insted (i.e., we want the corresponding coordinate
+ * of the _affine image_ of the point with homogeneous coordinate vector \a x to be equal
+ * to \a val.
+ *
+ * Remark: We don't need this but it would be relatively simple to let the calling function
+ * prescripe the value of _multiple_ components of the solution vector instead of only a single one.
+ */
+template <int S, int T> SolutionKind gaussjord_solve (double A[S][T], double x[T], double v[S],
+ int index = -1, double val = 0.0, bool proj = false) {
+ double B[S][T];
+ //copy_mat<S,T>(A,B);
+ SysEq::copy_mat<S,T>(A,B);
+ std::vector<int> cols = gauss_jordan<S,T>(B, index);
+ if (std::find(cols.begin(), cols.end(), -1) != cols.end()) {
+ // pivot search failed for some row so the system is not solvable
+ return SysEq::no_solution;
+ }
+
+ /* the vector x is filled with the coefficients of the desired solution vector at appropriate places;
+ * the other components are set to zero, and we additionally set x[index] = val if applicable
+ */
+ std::vector<int>::iterator k;
+ for (int j = 0; j < S; ++j) {
+ x[cols[j]] = v[j];
+ }
+ for (int j = 0; j < T; ++j) {
+ k = std::find(cols.begin(), cols.end(), j);
+ if (k == cols.end()) {
+ x[j] = 0;
+ }
+ }
+
+ // we need to adapt the value if we we are in the "projective case" (see above)
+ double val_new = (proj ? projectify<S,T>(cols, B, x, index, val) : val);
+
+ if (index != -1 && index >= 0 && index < T) {
+ // we want the specified coefficient of the solution vector to have a given value
+ x[index] = val_new;
+ }
+
+ /* the final solution vector is now obtained as the product B*x, where B is the matrix
+ * obtained by Gauss-Jordan manipulation of A; we use w as an auxiliary vector and
+ * afterwards copy the result back to x
+ */
+ double w[S];
+ SysEq::multiply<S,T>(B,x,w);
+ for (int j = 0; j < S; ++j) {
+ x[cols[j]] = w[j];
+ }
+
+ if (S + (index == -1 ? 0 : 1) == T) {
+ return SysEq::unique;
+ } else {
+ return SysEq::ambiguous;
+ }
+}
+
+} // namespace SysEq
+
+#endif /* __SYSEQ_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/tools-switch.cpp b/src/tools-switch.cpp
index 1bc83d7a2bc09cdfde14eedc6ffbcd5b6ba80892..e4692d91b85e878242d2b51f0e4117816f36a5b2 100644 (file)
--- a/src/tools-switch.cpp
+++ b/src/tools-switch.cpp
dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag</b> to create a rectangle. <b>Drag controls</b> to round corners and resize. <b>Click</b> to select."));
break;
case TOOLS_SHAPES_3DBOX:
- dt->set_event_context(SP_TYPE_3DBOX_CONTEXT, tool_names[num]);
+ dt->set_event_context(SP_TYPE_BOX3D_CONTEXT, tool_names[num]);
dt->activate_guides(false);
inkscape_eventcontext_set(sp_desktop_event_context(dt));
dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag</b> to create a 3D box. <b>Drag controls</b> to resize in perspective. <b>Click</b> to select (with <b>Ctrl+Alt</b> for single faces)."));
{
if (SP_IS_RECT(item)) {
tools_switch(dt, TOOLS_SHAPES_RECT);
+ } else if (SP_IS_BOX3D(item)) {
+ tools_switch(dt, TOOLS_SHAPES_3DBOX);
} else if (SP_IS_GENERICELLIPSE(item)) {
tools_switch(dt, TOOLS_SHAPES_ARC);
} else if (SP_IS_STAR(item)) {
diff --git a/src/transf_mat_3x4.cpp b/src/transf_mat_3x4.cpp
--- /dev/null
+++ b/src/transf_mat_3x4.cpp
@@ -0,0 +1,200 @@
+#define SEEN_TRANSF_MAT_3x4_C
+
+/*
+ * 3x4 transformation matrix to map points from projective 3-space into the projective plane
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "transf_mat_3x4.h"
+#include <gtk/gtk.h>
+#include "svg/stringstream.h"
+#include "syseq.h"
+#include "libnr/nr-matrix.h"
+#include "document.h"
+#include "inkscape.h"
+
+namespace Proj {
+
+TransfMat3x4::TransfMat3x4 () {
+ for (unsigned int i = 0; i < 3; ++i) {
+ for (unsigned int j = 0; j < 4; ++j) {
+ tmat[i][j] = (i == j ? 1 : 0); // or should we initialize all values with 0? does it matter at all?
+ }
+ }
+}
+
+TransfMat3x4::TransfMat3x4 (Proj::Pt2 vp_x, Proj::Pt2 vp_y, Proj::Pt2 vp_z, Proj::Pt2 origin) {
+ for (unsigned int i = 0; i < 3; ++i) {
+ tmat[i][0] = vp_x[i];
+ tmat[i][1] = vp_y[i];
+ tmat[i][2] = vp_z[i];
+ tmat[i][3] = origin[i];
+ }
+}
+
+TransfMat3x4::TransfMat3x4(TransfMat3x4 const &rhs) {
+ for (unsigned int i = 0; i < 3; ++i) {
+ for (unsigned int j = 0; j < 4; ++j) {
+ tmat[i][j] = rhs.tmat[i][j];
+ }
+ }
+}
+
+Pt2
+TransfMat3x4::column (Proj::Axis axis) const {
+ return Proj::Pt2 (tmat[0][axis], tmat[1][axis], tmat[2][axis]);
+}
+
+Pt2
+TransfMat3x4::image (Pt3 const &point) {
+ double x = tmat[0][0] * point[0] + tmat[0][1] * point[1] + tmat[0][2] * point[2] + tmat[0][3] * point[3];
+ double y = tmat[1][0] * point[0] + tmat[1][1] * point[1] + tmat[1][2] * point[2] + tmat[1][3] * point[3];
+ double w = tmat[2][0] * point[0] + tmat[2][1] * point[1] + tmat[2][2] * point[2] + tmat[2][3] * point[3];
+
+ return Pt2 (x, y, w);
+}
+
+Pt3
+TransfMat3x4::preimage (NR::Point const &pt, double coord, Proj::Axis axis) {
+ double x[4];
+ double v[3];
+ v[0] = pt[NR::X];
+ v[1] = pt[NR::Y];
+ v[2] = 1.0;
+ int index = (int) axis;
+
+ SysEq::SolutionKind sol = SysEq::gaussjord_solve<3,4>(tmat, x, v, index, coord, true);
+
+ if (sol != SysEq::unique) {
+ if (sol == SysEq::no_solution) {
+ g_print ("No solution. Please investigate.\n");
+ } else {
+ g_print ("Infinitely many solutions. Please investigate.\n");
+ }
+ }
+ return Pt3(x[0], x[1], x[2], x[3]);
+}
+
+void
+TransfMat3x4::set_image_pt (Proj::Axis axis, Proj::Pt2 const &pt) {
+ // FIXME: Do we need to adapt the coordinates in any way or can we just use them as they are?
+ for (int i = 0; i < 3; ++i) {
+ tmat[i][axis] = pt[i];
+ }
+}
+
+void
+TransfMat3x4::toggle_finite (Proj::Axis axis) {
+ g_return_if_fail (axis != Proj::W);
+ if (has_finite_image(axis)) {
+ NR::Point dir (column(axis).affine());
+ NR::Point origin (column(Proj::W).affine());
+ dir -= origin;
+ set_column (axis, Proj::Pt2(dir[NR::X], dir[NR::Y], 0));
+ } else {
+ Proj::Pt2 dir (column(axis));
+ Proj::Pt2 origin (column(Proj::W).affine());
+ dir = dir + origin;
+ dir[2] = 1.0;
+ set_column (axis, dir);
+ }
+}
+
+gchar *
+TransfMat3x4::pt_to_str (Proj::Axis axis) {
+ Inkscape::SVGOStringStream os;
+ os << tmat[0][axis] << " : "
+ << tmat[1][axis] << " : "
+ << tmat[2][axis];
+ return g_strdup(os.str().c_str());
+}
+
+bool
+TransfMat3x4::operator==(const TransfMat3x4 &rhs) const
+{
+ // Should we allow a certain tolerance or "normalize" the matrices first?
+ for (int i = 0; i < 3; ++i) {
+ Proj::Pt2 pt1 = column(Proj::axes[i]);
+ Proj::Pt2 pt2 = rhs.column(Proj::axes[i]);
+ if (pt1 != pt2) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* multiply a projective matrix by an affine matrix */
+TransfMat3x4
+TransfMat3x4::operator*(NR::Matrix const &A) const {
+ TransfMat3x4 ret;
+
+ // Is it safe to always use the currently active document?
+ double h = sp_document_height(inkscape_active_document());
+
+ /*
+ * Note: The strange multiplication involving the document height is due to the buggy
+ * intertwining of SVG and document coordinates. Essentially, what we do is first
+ * convert from "real-world" to SVG coordinates, then apply the transformation A
+ * (by multiplying with the NR::Matrix) and then convert back from SVG to real-world
+ * coordinates. Maybe there is even a more Inkscape-ish way to achieve this?
+ * Once Inkscape has gotton rid of the two different coordiate systems, we can change
+ * this function to an ordinary matrix multiplication.
+ */
+ for (int j = 0; j < 4; ++j) {
+ ret.tmat[0][j] = A[0]*tmat[0][j] + A[2]*(h*tmat[2][j] - tmat[1][j]) + A[4]*tmat[2][j];
+ ret.tmat[1][j] = A[1]*tmat[0][j] + A[3]*(h*tmat[2][j] - tmat[1][j]) + A[5]*tmat[2][j];
+ ret.tmat[2][j] = tmat[2][j];
+
+ ret.tmat[1][j] = h*ret.tmat[2][j] - ret.tmat[1][j]; // switch back from SVG to desktop coordinates
+ }
+
+ return ret;
+}
+
+// FIXME: Shouldn't rather operator* call operator*= for efficiency? (Because in operator*=
+// there is in principle no need to create a temporary object, which happens in the assignment)
+TransfMat3x4 &
+TransfMat3x4::operator*=(NR::Matrix const &A) {
+ *this = *this * A;
+ return *this;
+}
+
+
+void
+TransfMat3x4::print () const {
+ g_print ("Transformation matrix:\n");
+ for (int i = 0; i < 3; ++i) {
+ g_print (" ");
+ for (int j = 0; j < 4; ++j) {
+ g_print ("%8.2f ", tmat[i][j]);
+ }
+ g_print ("\n");
+ }
+}
+
+void
+TransfMat3x4::normalize_column (Proj::Axis axis) {
+ Proj::Pt2 new_col(column(axis));
+ new_col.normalize();
+ set_image_pt(axis, new_col);
+}
+
+
+} // namespace Proj
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/transf_mat_3x4.h b/src/transf_mat_3x4.h
--- /dev/null
+++ b/src/transf_mat_3x4.h
@@ -0,0 +1,80 @@
+#ifndef SEEN_TRANSF_MAT_3x4_H
+#define SEEN_TRANSF_MAT_3x4_H
+
+/*
+ * 3x4 transformation matrix to map points from projective 3-space into the projective plane
+ *
+ * Authors:
+ * Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "proj_pt.h"
+#include "axis-manip.h"
+#include "libnr/nr-point-fns.h"
+
+namespace Proj {
+
+class TransfMat3x4 {
+public:
+ TransfMat3x4();
+ TransfMat3x4(Pt2 vp_x, Pt2 vp_y, Pt2 vp_z, Pt2 origin);
+ TransfMat3x4(TransfMat3x4 const &rhs);
+ Pt2 column (Proj::Axis axis) const;
+ Pt2 image (Pt3 const &point);
+ Pt3 preimage (NR::Point const &pt, double coord = 0, Axis = Z);
+ void set_image_pt (Proj::Axis axis, Proj::Pt2 const &pt);
+ void toggle_finite (Proj::Axis axis);
+ double get_infinite_angle (Proj::Axis axis) {
+ if (has_finite_image(axis)) {
+ return NR_HUGE;
+ }
+ Pt2 vp(column(axis));
+ return NR::atan2(NR::Point(vp[0], vp[1])) * 180.0/M_PI;
+ }
+ void set_infinite_direction (Proj::Axis axis, double angle) { // angle is in degrees
+ g_return_if_fail(tmat[2][axis] == 0); // don't set directions for finite VPs
+
+ double a = angle * M_PI/180;
+ NR::Point pt(tmat[0][axis], tmat[1][axis]);
+ double rad = NR::L2(pt);
+ set_image_pt(axis, Proj::Pt2(cos (a) * rad, sin (a) * rad, 0.0));
+ }
+ inline bool has_finite_image (Proj::Axis axis) { return (tmat[2][axis] != 0.0); }
+
+ gchar * pt_to_str (Proj::Axis axis);
+
+ bool operator==(const TransfMat3x4 &rhs) const;
+ TransfMat3x4 operator*(NR::Matrix const &A) const;
+ TransfMat3x4 &operator*=(NR::Matrix const &A);
+
+ void print() const;
+
+private:
+ // FIXME: Is changing a single column allowed when a projective coordinate system is specified!?!?!
+ void normalize_column (Proj::Axis axis);
+ inline void set_column (Proj::Axis axis, Proj::Pt2 pt) {
+ tmat[0][axis] = pt[0];
+ tmat[1][axis] = pt[1];
+ tmat[2][axis] = pt[2];
+ }
+ double tmat[3][4];
+};
+
+} // namespace Proj
+
+#endif /* __TRANSF_MAT_3x4_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
index 6f72a962140accd9ae0ee5c22845df39c6ac6772..0a8336102c290feaa5ac46f0c42e32c4563684ad 100644 (file)
--- a/src/vanishing-point.cpp
+++ b/src/vanishing-point.cpp
#include "vanishing-point.h"
#include "desktop-handles.h"
-#include "box3d.h"
+#include "desktop.h"
+#include "event-context.h"
+#include "xml/repr.h"
+#include "perspective-line.h"
#include "knotholder.h" // FIXME: can we avoid direct access to knotholder_update_knots?
SP_KNOT_SHAPE_CIRCLE //VP_INFINITE
};
-// FIXME: We should always require to have both the point (for finite VPs)
-// and the direction (for infinite VPs) set. Otherwise toggling
-// shows very unexpected behaviour.
-// Later on we can maybe infer the infinite direction from the finite point
-// and a suitable center of the scene. How to go in the other direction?
-VanishingPoint::VanishingPoint(NR::Point const &pt, NR::Point const &inf_dir, VPState st)
- : NR::Point (pt), state (st), v_dir (inf_dir) {}
-
-VanishingPoint::VanishingPoint(NR::Point const &pt)
- : NR::Point (pt), state (VP_FINITE), v_dir (0.0, 0.0) {}
-
-VanishingPoint::VanishingPoint(NR::Point const &pt, NR::Point const &direction)
- : NR::Point (pt), state (VP_INFINITE), v_dir (direction) {}
-
-VanishingPoint::VanishingPoint(NR::Coord x, NR::Coord y)
- : NR::Point(x, y), state(VP_FINITE), v_dir(0.0, 0.0) {}
-
-VanishingPoint::VanishingPoint(NR::Coord dir_x, NR::Coord dir_y, VPState st)
- : NR::Point(0.0, 0.0), state(st), v_dir(dir_x, dir_y) {}
-
-VanishingPoint::VanishingPoint(NR::Coord x, NR::Coord y, NR::Coord dir_x, NR::Coord dir_y)
- : NR::Point(x, y), state(VP_INFINITE), v_dir(dir_x, dir_y) {}
-
-VanishingPoint::VanishingPoint(VanishingPoint const &rhs) : NR::Point (rhs)
-{
- this->state = rhs.state;
- //this->ref_pt = rhs.ref_pt;
- this->v_dir = rhs.v_dir;
-}
-
-VanishingPoint::~VanishingPoint () {}
-
-bool VanishingPoint::operator== (VanishingPoint const &other)
-{
- // Should we compare the parent perspectives, too? Probably not.
- if ((*this)[NR::X] == other[NR::X] && (*this)[NR::Y] == other[NR::Y]
- && this->state == other.state && this->v_dir == other.v_dir) {
- return true;
- }
- return false;
-}
-
-bool VanishingPoint::is_finite() const
-{
- return this->state == VP_FINITE;
-}
-
-VPState VanishingPoint::toggle_parallel()
-{
- if (this->state == VP_FINITE) {
- this->state = VP_INFINITE;
- } else {
- this->state = VP_FINITE;
- }
-
- return this->state;
-}
-
-void VanishingPoint::draw(Box3D::Axis const axis)
-{
- switch (axis) {
- case X:
- if (state == VP_FINITE)
- create_canvas_point(*this, 6.0, 0xff000000);
- else
- create_canvas_point(*this, 6.0, 0xffffff00);
- break;
- case Y:
- if (state == VP_FINITE)
- create_canvas_point(*this, 6.0, 0x0000ff00);
- else
- create_canvas_point(*this, 6.0, 0xffffff00);
- break;
- case Z:
- if (state == VP_FINITE)
- create_canvas_point(*this, 6.0, 0x00770000);
- else
- create_canvas_point(*this, 6.0, 0xffffff00);
- break;
- default:
- g_assert_not_reached();
- break;
- }
-}
-
static void
-vp_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data)
+vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data)
{
VPDrag *drag = (VPDrag *) data;
- drag->updateDraggers ();
- drag->updateLines ();
+ drag->updateDraggers();
+ drag->updateLines();
+ drag->updateBoxReprs();
}
static void
-vp_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data)
+vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data)
{
VPDrag *drag = (VPDrag *) data;
- /***
- if (drag->local_change) {
- drag->local_change = false;
- } else {
- drag->updateDraggers ();
- }
- ***/
drag->updateLines ();
-}
-
-// auxiliary function
-static GSList *
-eliminate_remaining_boxes_of_persp_starting_from_list_position (GSList *boxes_to_do, const SP3DBox *start_box, const Perspective3D *persp)
-{
- GSList *i = g_slist_find (boxes_to_do, start_box);
- g_return_val_if_fail (i != NULL, boxes_to_do);
-
- SP3DBox *box;
- GSList *successor;
-
- i = i->next;
- while (i != NULL) {
- successor = i->next;
- box = SP_3DBOX (i->data);
- if (persp->has_box (box)) {
- boxes_to_do = g_slist_remove (boxes_to_do, box);
- }
- i = successor;
- }
-
- return boxes_to_do;
+ //drag->updateBoxReprs();
+ drag->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
+ drag->updateDraggers ();
}
static bool
have_VPs_of_same_perspective (VPDragger *dr1, VPDragger *dr2)
{
- Perspective3D *persp;
- for (GSList *i = dr1->vps; i != NULL; i = i->next) {
- persp = dr1->parent->document->get_persp_of_VP ((VanishingPoint *) i->data);
- if (dr2->hasPerspective (persp)) {
+ for (std::list<VanishingPoint>::iterator i = dr1->vps.begin(); i != dr1->vps.end(); ++i) {
+ if (dr2->hasPerspective ((*i).get_perspective())) {
return true;
}
}
}
static void
-vp_knot_moved_handler (SPKnot */*knot*/, NR::Point const *ppointer, guint state, gpointer data)
+vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpointer data)
{
VPDragger *dragger = (VPDragger *) data;
VPDrag *drag = dragger->parent;
@@ -197,6 +87,60 @@ vp_knot_moved_handler (SPKnot */*knot*/, NR::Point const *ppointer, guint state,
// FIXME: take from prefs
double snap_dist = SNAP_DIST / inkscape_active_desktop()->current_zoom();
+ /*
+ * We use dragging_started to indicate if we have already checked for the need to split Draggers up.
+ * This only has the purpose of avoiding costly checks in the routine below.
+ */
+ if (!dragger->dragging_started && (state & GDK_SHIFT_MASK)) {
+ /* with Shift; if there is more than one box linked to this VP
+ we need to split it and create a new perspective */
+ //g_print ("Number of boxes in dragger: %d\n", dragger->numberOfBoxes());
+ if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected
+ //g_print ("We need to split the VPDragger\n");
+ std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes();
+ /**
+ g_print ("===== VPs of selected boxes: ===========================\n");
+ for (std::set<VanishingPoint*, less_ptr>::iterator i = sel_vps.begin(); i != sel_vps.end(); ++i) {
+ (*i)->printPt();
+ }
+ g_print ("========================================================\n");
+ **/
+
+ std::list<SPBox3D *> sel_boxes;
+ for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) {
+ // for each VP that has selected boxes:
+ Persp3D *old_persp = (*vp)->get_perspective();
+ sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop()));
+
+ // we create a new perspective ...
+ Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp);
+
+ /* ... unlink the boxes from the old one and
+ FIXME: We need to unlink the _un_selected boxes of each VP so that
+ the correct boxes are kept with the VP being moved */
+ std::list<SPBox3D *> bx_lst = persp3d_list_of_boxes(old_persp);
+ for (std::list<SPBox3D *>::iterator i = bx_lst.begin(); i != bx_lst.end(); ++i) {
+ //g_print ("Iterating over box #%d\n", (*i)->my_counter);
+ if (std::find(sel_boxes.begin(), sel_boxes.end(), *i) == sel_boxes.end()) {
+ /* if a box in the VP is unselected, move it to the
+ newly created perspective so that it doesn't get dragged **/
+ //g_print (" switching box #%d to new perspective.\n", (*i)->my_counter);
+ persp3d_remove_box (old_persp, *i);
+ persp3d_add_box (new_persp, *i);
+ gchar *href = g_strdup_printf("#%s", SP_OBJECT_REPR(new_persp)->attribute("id"));
+ SP_OBJECT_REPR(*i)->setAttribute("inkscape:perspectiveID", href);
+ g_free(href);
+ }
+ }
+ }
+ // FIXME: Do we need to create a new dragger as well?
+ dragger->updateZOrders ();
+ sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
+ _("Split vanishing points"));
+ return;
+ }
+ }
+
if (!(state & GDK_SHIFT_MASK)) {
// without Shift; see if we need to snap to another dragger
for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
@@ -207,13 +151,14 @@ vp_knot_moved_handler (SPKnot */*knot*/, NR::Point const *ppointer, guint state,
continue;
}
- // update positions ...
- for (GSList *j = dragger->vps; j != NULL; j = j->next) {
- ((VanishingPoint *) j->data)->set_pos (d_new->point);
+ // update positions ... (this is needed so that the perspectives are detected as identical)
+ // FIXME: This is called a bit too often, isn't it?
+ for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
+ (*j).set_pos(d_new->point);
}
+
// ... join lists of VPs ...
- // FIXME: Do we have to copy the second list (i.e, is it invalidated when dragger is deleted below)?
- d_new->vps = g_slist_concat (d_new->vps, g_slist_copy (dragger->vps));
+ d_new->vps.merge(dragger->vps);
// ... delete old dragger ...
drag->draggers = g_list_remove (drag->draggers, dragger);
@@ -222,13 +167,12 @@ vp_knot_moved_handler (SPKnot */*knot*/, NR::Point const *ppointer, guint state,
// ... and merge any duplicate perspectives
d_new->mergePerspectives();
-
+
// TODO: Update the new merged dragger
//d_new->updateKnotShape ();
- d_new->updateTip ();
+ d_new->updateTip();
- d_new->reshapeBoxes (d_new->point, Box3D::XYZ);
- d_new->updateBoxReprs ();
+ d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger!
d_new->updateZOrders ();
drag->updateLines ();
@@ -237,120 +181,50 @@ vp_knot_moved_handler (SPKnot */*knot*/, NR::Point const *ppointer, guint state,
// deleted according to changes in the svg representation, not based on any user input
// as is currently the case.
- //sp_document_done (sp_desktop_document (drag->desktop), SP_VERB_CONTEXT_3DBOX,
- // _("Merge vanishing points"));
+ sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
+ _("Merge vanishing points"));
return;
}
}
}
- dragger->point = p;
- dragger->reshapeBoxes (p, Box3D::XYZ);
- dragger->updateBoxReprs ();
- dragger->updateZOrders ();
+ dragger->point = p; // FIXME: Brauchen wir dragger->point überhaupt?
- drag->updateLines ();
+ dragger->updateVPs(p);
+ dragger->updateBoxDisplays();
+ dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
+ dragger->updateZOrders();
+
+ drag->updateLines();
- //drag->local_change = false;
+ dragger->dragging_started = true;
}
-/***
+/* helpful for debugging */
static void
vp_knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
{
VPDragger *dragger = (VPDragger *) data;
+ g_print ("\nVPDragger contains the following VPs: ");
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ g_print("%d (%d) ", (*i).my_counter, (*i).get_perspective()->my_counter);
+ }
+ g_print("\n");
}
-***/
void
-vp_knot_grabbed_handler (SPKnot */*knot*/, unsigned int state, gpointer data)
+vp_knot_grabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
{
VPDragger *dragger = (VPDragger *) data;
VPDrag *drag = dragger->parent;
drag->dragging = true;
-
- //sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
-
- if ((state & GDK_SHIFT_MASK) && !drag->hasEmptySelection()) { // FIXME: Is the second check necessary?
-
- if (drag->allBoxesAreSelected (dragger)) {
- // if all of the boxes linked to dragger are selected, we don't need to split it
- return;
- }
-
- // we are Shift-dragging; unsnap if we carry more than one VP
-
- // FIXME: Should we distinguish between the following cases:
- // 1) there are several VPs in a dragger
- // 2) there is only a single VP but several boxes linked to it
- // ?
- // Or should we simply unlink all selected boxes? Currently we do the latter.
- if (dragger->numberOfBoxes() > 1) {
- // create a new dragger
- VPDragger *dr_new = new VPDragger (drag, dragger->point, NULL);
- drag->draggers = g_list_prepend (drag->draggers, dr_new);
-
- // move all the VPs from dragger to dr_new
- dr_new->vps = dragger->vps;
- dragger->vps = NULL;
-
- /* now we move all selected boxes back to the current dragger (splitting perspectives
- if they also have unselected boxes) so that they are further reshaped during dragging */
-
- GSList *boxes_to_do = drag->selectedBoxesWithVPinDragger (dr_new);
-
- for (GSList *i = boxes_to_do; i != NULL; i = i->next) {
- SP3DBox *box = SP_3DBOX (i->data);
- Perspective3D *persp = drag->document->get_persp_of_box (box);
- VanishingPoint *vp = dr_new->getVPofPerspective (persp);
- if (vp == NULL) {
- g_warning ("VP is NULL. We should be okay, though.\n");
- }
- if (persp->all_boxes_occur_in_list (boxes_to_do)) {
- // if all boxes of persp are selected, we can simply move the VP from dr_new back to dragger
- dr_new->removeVP (vp);
- dragger->addVP (vp);
-
- // some cleaning up for efficiency
- boxes_to_do = eliminate_remaining_boxes_of_persp_starting_from_list_position (boxes_to_do, box, persp);
- } else {
- /* otherwise the unselected boxes need to stay linked to dr_new; thus we
- create a new perspective and link the VPs to the correct draggers */
- Perspective3D *persp_new = new Perspective3D (*persp);
- drag->document->add_perspective (persp_new);
-
- Axis vp_axis = persp->get_axis_of_VP (vp);
- dragger->addVP (persp_new->get_vanishing_point (vp_axis));
- std::pair<Axis, Axis> rem_axes = get_remaining_axes (vp_axis);
- drag->addDragger (persp->get_vanishing_point (rem_axes.first));
- drag->addDragger (persp->get_vanishing_point (rem_axes.second));
-
- // now we move the selected boxes from persp to persp_new
- GSList * selected_boxes_of_perspective = persp->boxes_occurring_in_list (boxes_to_do);
- for (GSList *j = selected_boxes_of_perspective; j != NULL; j = j->next) {
- persp->remove_box (SP_3DBOX (j->data));
- persp_new->add_box (SP_3DBOX (j->data));
- }
-
- // cleaning up
- boxes_to_do = eliminate_remaining_boxes_of_persp_starting_from_list_position (boxes_to_do, box, persp);
- }
- }
-
- // TODO: Something is still wrong with updating the boxes' representations after snapping
- //dr_new->updateBoxReprs ();
-
- dragger->updateTip();
- dr_new->updateTip();
- }
- }
}
static void
-vp_knot_ungrabbed_handler (SPKnot *knot, guint /*state*/, gpointer data)
+vp_knot_ungrabbed_handler (SPKnot *knot, guint state, gpointer data)
{
VPDragger *dragger = (VPDragger *) data;
dragger->point_original = dragger->point = knot->pos;
- /***
- VanishingPoint *vp;
- for (GSList *i = dragger->vps; i != NULL; i = i->next) {
- vp = (VanishingPoint *) i->data;
- vp->set_pos (knot->pos);
+ dragger->dragging_started = false;
+
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ (*i).set_pos (knot->pos);
+ (*i).updateBoxReprs();
+ (*i).updatePerspRepr();
}
- ***/
dragger->parent->updateDraggers ();
- dragger->updateBoxReprs ();
+ //dragger->updateBoxReprs ();
+ dragger->parent->updateLines ();
+ dragger->parent->updateBoxHandles ();
// TODO: Update box's paths and svg representation
_("3D box: Move vanishing point"));
}
-VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp)
+unsigned int VanishingPoint::global_counter = 0;
+
+// FIXME: Rename to something more meaningful!
+void
+VanishingPoint::set_pos(Proj::Pt2 const &pt) {
+ g_return_if_fail (_persp);
+ _persp->tmat.set_image_pt (_axis, pt);
+}
+
+std::list<SPBox3D *>
+VanishingPoint::selectedBoxes(Inkscape::Selection *sel) {
+ std::list<SPBox3D *> sel_boxes;
+ for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
+ if (!SP_IS_BOX3D(i->data))
+ continue;
+ SPBox3D *box = SP_BOX3D(i->data);
+ if (this->hasBox(box)) {
+ sel_boxes.push_back (box);
+ }
+ }
+ return sel_boxes;
+}
+
+VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint &vp)
{
- this->vps = NULL;
+ //this->vps = NULL;
this->parent = parent;
this->point = p;
this->point_original = p;
- if (vp->is_finite()) {
+ this->dragging_started = false;
+
+ if (vp.is_finite()) {
// create the knot
this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
this->knot->setMode(SP_KNOT_MODE_XOR);
// 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);
/***
// disconnect signals
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
- /***
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_clicked_handler), this);
- ***/
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
/***
/* unref should call destroy */
g_object_unref (G_OBJECT (this->knot));
- g_slist_free (this->vps);
- this->vps = NULL;
+ //g_slist_free (this->vps);
+ //this->vps = NULL;
}
/**
}
guint num = this->numberOfBoxes();
- if (g_slist_length (this->vps) == 1) {
- VanishingPoint *vp = (VanishingPoint *) this->vps->data;
- switch (vp->state) {
- case VP_FINITE:
- this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
- "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
- num),
- num);
- break;
- case VP_INFINITE:
- // This won't make sense any more when infinite VPs are not shown on the canvas,
- // but currently we update the status message anyway
- this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
- "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
- num),
- num);
- break;
+ if (this->vps.size() == 1) {
+ if (this->vps.front().is_finite()) {
+ this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
+ "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
+ num),
+ num);
+ } else {
+ // This won't make sense any more when infinite VPs are not shown on the canvas,
+ // but currently we update the status message anyway
+ this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
+ "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
+ num),
+ num);
}
} else {
- int length = g_slist_length (this->vps);
+ int length = this->vps.size();
char *desc1 = g_strdup_printf ("Collection of <b>%d</b> vanishing points ", length);
char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)",
"shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
}
/**
- * Adds a vanishing point to the dragger (also updates the position)
+ * Adds a vanishing point to the dragger (also updates the position if necessary);
+ * the perspective is stored separately, too, for efficiency in updating boxes.
*/
void
-VPDragger::addVP (VanishingPoint *vp)
+VPDragger::addVP (VanishingPoint &vp, bool update_pos)
{
- if (vp == NULL) {
- return;
- }
- if (!vp->is_finite() || g_slist_find (this->vps, vp)) {
- // don't add infinite VPs, and don't add the same VP twice
+ //if (!vp.is_finite() || g_slist_find (this->vps, vp)) {
+ if (!vp.is_finite() || std::find (vps.begin(), vps.end(), vp) != vps.end()) {
+ // don't add infinite VPs; don't add the same VP twice
return;
}
- vp->set_pos (this->point);
- this->vps = g_slist_prepend (this->vps, vp);
+ if (update_pos) {
+ vp.set_pos (this->point);
+ }
+ //this->vps = g_slist_prepend (this->vps, vp);
+ this->vps.push_front (vp);
+ //this->persps.include (vp.get_perspective());
this->updateTip();
}
void
-VPDragger::removeVP (VanishingPoint *vp)
+VPDragger::removeVP (VanishingPoint const &vp)
{
- if (vp == NULL) {
- g_print ("NULL vanishing point will not be removed.\n");
- return;
+ std::list<VanishingPoint>::iterator i = std::find (this->vps.begin(), this->vps.end(), vp);
+ if (i != this->vps.end()) {
+ this->vps.erase (i);
}
- g_assert (this->vps != NULL);
- this->vps = g_slist_remove (this->vps, vp);
-
this->updateTip();
}
-// returns the VP contained in the dragger that belongs to persp
VanishingPoint *
-VPDragger::getVPofPerspective (Perspective3D *persp)
-{
- for (GSList *i = vps; i != NULL; i = i->next) {
- if (persp->has_vanishing_point ((VanishingPoint *) i->data)) {
- return ((VanishingPoint *) i->data);
+VPDragger::findVPWithBox (SPBox3D *box) {
+ for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
+ if ((*vp).hasBox(box)) {
+ return &(*vp);
}
}
return NULL;
}
-bool
-VPDragger::hasBox(const SP3DBox *box)
-{
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- if (parent->document->get_persp_of_VP ((VanishingPoint *) i->data)->has_box (box)) return true;
+std::set<VanishingPoint*, less_ptr>
+VPDragger::VPsOfSelectedBoxes() {
+ std::set<VanishingPoint*, less_ptr> sel_vps;
+ VanishingPoint *vp;
+ // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference.
+ Inkscape::Selection *sel = sp_desktop_selection(inkscape_active_desktop());
+ for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
+ if (!SP_IS_BOX3D(i->data))
+ continue;
+ SPBox3D *box = SP_BOX3D(i->data);
+ vp = this->findVPWithBox(box);
+ if (vp) {
+ sel_vps.insert (vp);
+ }
}
- return false;
+ return sel_vps;
}
guint
VPDragger::numberOfBoxes ()
{
guint num = 0;
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- num += parent->document->get_persp_of_VP ((VanishingPoint *) i->data)->number_of_boxes ();
+ for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
+ num += (*vp).numberOfBoxes();
}
return num;
}
bool
-VPDragger::hasPerspective (const Perspective3D *persp)
+VPDragger::hasPerspective (const Persp3D *persp)
{
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- if (*persp == *parent->document->get_persp_of_VP ((VanishingPoint *) i->data)) {
+ for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
+ if (persp3d_perspectives_coincide(persp, (*i).get_perspective())) {
return true;
- }
+ }
}
return false;
}
void
VPDragger::mergePerspectives ()
{
- Perspective3D *persp1, *persp2;
- GSList * successor = NULL;
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- persp1 = parent->document->get_persp_of_VP ((VanishingPoint *) i->data);
- for (GSList *j = i->next; j != NULL; j = successor) {
- // if the perspective is deleted, the VP is invalidated, too, so we must store its successor beforehand
- successor = j->next;
- persp2 = parent->document->get_persp_of_VP ((VanishingPoint *) j->data);
- if (*persp1 == *persp2) {
- persp1->absorb (persp2); // persp2 is deleted; hopefully this doesn't screw up the list of vanishing points and thus the loops
+ Persp3D *persp1, *persp2;
+ for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
+ persp1 = (*i).get_perspective();
+ for (std::list<VanishingPoint>::iterator j = i; j != vps.end(); ++j) {
+ persp2 = (*j).get_perspective();
+ if (persp1 == persp2) {
+ /* don't merge a perspective with itself */
+ continue;
+ }
+ if (persp3d_perspectives_coincide(persp1,persp2)) {
+ /* if perspectives coincide but are not the same, merge them */
+ persp3d_absorb(persp1, persp2);
+
+ this->parent->swap_perspectives_of_VPs(persp2, persp1);
+
+ SP_OBJECT(persp2)->deleteObject(false);
}
}
}
}
void
-VPDragger::reshapeBoxes (NR::Point const &p, Box3D::Axis /*axes*/)
+VPDragger::updateBoxDisplays ()
{
- Perspective3D *persp;
- for (GSList const* i = this->vps; i != NULL; i = i->next) {
- VanishingPoint *vp = (VanishingPoint *) i->data;
- // TODO: We can extract the VP directly from the box's perspective. Is that vanishing point identical to 'vp'?
- // Or is there duplicated information? If so, remove it and simplify the whole construction!
- vp->set_pos(p);
- persp = parent->document->get_persp_of_VP (vp);
- Box3D::Axis axis = persp->get_axis_of_VP (vp);
- parent->document->get_persp_of_VP (vp)->reshape_boxes (axis); // FIXME: we should only update the direction of the VP
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ (*i).updateBoxDisplays();
}
- parent->updateBoxHandles();
}
void
-VPDragger::updateBoxReprs ()
+VPDragger::updateVPs (NR::Point const &pt)
{
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- parent->document->get_persp_of_VP ((VanishingPoint *) i->data)->update_box_reprs ();
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ (*i).set_pos (pt);
}
}
void
VPDragger::updateZOrders ()
{
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- parent->document->get_persp_of_VP ((VanishingPoint *) i->data)->update_z_orders ();
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ persp3d_update_z_orders((*i).get_perspective());
+ }
+}
+
+void
+VPDragger::printVPs() {
+ g_print ("VPDragger at position (%f, %f):\n", point[NR::X], point[NR::Y]);
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ g_print (" VP %s\n", (*i).axisString());
}
}
this->front_or_rear_lines = 0x1;
//this->selected = NULL;
- this->local_change = false;
this->dragging = false;
this->sel_changed_connection = this->selection->connectChanged(
{
for (GList const* i = this->draggers; i != NULL; i = i->next) {
VPDragger *dragger = (VPDragger *) i->data;
- for (GSList const* j = dragger->vps; j != NULL; j = j->next) {
- VanishingPoint *vp2 = (VanishingPoint *) j->data;
- g_assert (vp2 != NULL);
-
+ for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
// TODO: Should we compare the pointers or the VPs themselves!?!?!?!
- //if ((*vp2) == vp) {
- if (vp2 == &vp) {
+ if (*j == vp) {
return (dragger);
}
}
return NULL;
}
+void
+VPDrag::printDraggers ()
+{
+ g_print ("=== VPDrag info: =================================\n");
+ for (GList const* i = this->draggers; i != NULL; i = i->next) {
+ ((VPDragger *) i->data)->printVPs();
+ g_print ("========\n");
+ }
+ g_print ("=================================================\n");
+}
+
/**
* Regenerates the draggers list from the current selection; is called when selection is changed or modified
*/
{
if (this->dragging)
return;
- /***
- while (selected) {
- selected = g_list_remove(selected, selected->data);
- }
- ***/
// delete old draggers
for (GList const* i = this->draggers; i != NULL; i = i->next) {
delete ((VPDragger *) i->data);
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
SPItem *item = SP_ITEM(i->data);
- //SPStyle *style = SP_OBJECT_STYLE (item);
+ if (!SP_IS_BOX3D (item)) continue;
+ SPBox3D *box = SP_BOX3D (item);
- if (!SP_IS_3DBOX (item)) continue;
- SP3DBox *box = SP_3DBOX (item);
-
- Box3D::Perspective3D *persp = document->get_persp_of_box (box);
- addDragger (persp->get_vanishing_point(Box3D::X));
- addDragger (persp->get_vanishing_point(Box3D::Y));
- addDragger (persp->get_vanishing_point(Box3D::Z));
+ VanishingPoint vp;
+ for (int i = 0; i < 3; ++i) {
+ vp.set (box->persp_ref->getObject(), Proj::axes[i]);
+ addDragger (vp);
+ }
}
}
g_return_if_fail (this->selection != NULL);
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
- if (!SP_IS_3DBOX(i->data)) continue;
- SP3DBox *box = SP_3DBOX (i->data);
+ if (!SP_IS_BOX3D(i->data)) continue;
+ SPBox3D *box = SP_BOX3D (i->data);
- this->drawLinesForFace (box, Box3D::X);
- this->drawLinesForFace (box, Box3D::Y);
- this->drawLinesForFace (box, Box3D::Z);
+ this->drawLinesForFace (box, Proj::X);
+ this->drawLinesForFace (box, Proj::Y);
+ this->drawLinesForFace (box, Proj::Z);
}
}
VPDrag::updateBoxHandles ()
{
// FIXME: Is there a way to update the knots without accessing the
- // statically linked function knotholder_update_knots?
+ // (previously) statically linked function knotholder_update_knots?
GSList *sel = (GSList *) selection->itemList();
+ if (!sel)
+ return; // no selection
+
if (g_slist_length (sel) > 1) {
// Currently we only show handles if a single box is selected
return;
}
- if (!SP_IS_3DBOX (sel->data))
- return;
-
SPEventContext *ec = inkscape_active_event_context();
g_assert (ec != NULL);
if (ec->shape_knot_holder != NULL) {
}
}
+void
+VPDrag::updateBoxReprs ()
+{
+ for (GList *i = this->draggers; i != NULL; i = i->next) {
+ VPDragger *dragger = (VPDragger *) i->data;
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ (*i).updateBoxReprs();
+ }
+ }
+}
+
+void
+VPDrag::updateBoxDisplays ()
+{
+ for (GList *i = this->draggers; i != NULL; i = i->next) {
+ VPDragger *dragger = (VPDragger *) i->data;
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ (*i).updateBoxDisplays();
+ }
+ }
+}
+
+
/**
* Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
*/
void
-VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
+VPDrag::drawLinesForFace (const SPBox3D *box, Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
{
guint color;
switch (axis) {
// TODO: Make color selectable by user
- case Box3D::X: color = VP_LINE_COLOR_STROKE_X; break;
- case Box3D::Y: color = VP_LINE_COLOR_STROKE_Y; break;
- case Box3D::Z: color = VP_LINE_COLOR_STROKE_Z; break;
+ case Proj::X: color = VP_LINE_COLOR_STROKE_X; break;
+ case Proj::Y: color = VP_LINE_COLOR_STROKE_Y; break;
+ case Proj::Z: color = VP_LINE_COLOR_STROKE_Z; break;
default: g_assert_not_reached();
}
NR::Point corner1, corner2, corner3, corner4;
- sp_3dbox_corners_for_perspective_lines (box, axis, corner1, corner2, corner3, corner4);
+ box3d_corners_for_PLs (box, axis, corner1, corner2, corner3, corner4);
- VanishingPoint *vp = document->get_persp_of_box (box)->get_vanishing_point (axis);
- if (vp->is_finite()) {
+ g_return_if_fail (box->persp_ref->getObject());
+ Proj::Pt2 vp = persp3d_get_VP (box->persp_ref->getObject(), axis);
+ if (vp.is_finite()) {
// draw perspective lines for finite VPs
- NR::Point pt = vp->get_pos();
+ NR::Point pt = vp.affine();
if (this->front_or_rear_lines & 0x1) {
// draw 'front' perspective lines
this->addLine (corner1, pt, color);
@@ -801,7 +731,7 @@ VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner
} else {
// draw perspective lines for infinite VPs
NR::Maybe<NR::Point> pt1, pt2, pt3, pt4;
- Box3D::Perspective3D *persp = this->document->get_persp_of_box (box);
+ Persp3D *persp = box->persp_ref->getObject();
SPDesktop *desktop = inkscape_active_desktop (); // FIXME: Store the desktop in VPDrag
Box3D::PerspectiveLine pl (corner1, axis, persp);
pt1 = pl.intersection_with_viewbox(desktop);
@@ -830,53 +760,21 @@ VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner
this->addLine (corner4, *pt4, color);
}
}
-
-}
-
-/**
- * Returns true if all boxes that are linked to a VP in the dragger are selected
- */
-bool
-VPDrag::allBoxesAreSelected (VPDragger *dragger) {
- GSList *selected_boxes = (GSList *) dragger->parent->selection->itemList();
- for (GSList *i = dragger->vps; i != NULL; i = i->next) {
- if (!document->get_persp_of_VP ((VanishingPoint *) i->data)->all_boxes_occur_in_list (selected_boxes)) {
- return false;
- }
- }
- return true;
-}
-
-GSList *
-VPDrag::selectedBoxesWithVPinDragger (VPDragger *dragger)
-{
- GSList *sel_boxes = g_slist_copy ((GSList *) dragger->parent->selection->itemList());
- for (GSList const *i = sel_boxes; i != NULL; i = i->next) {
- SP3DBox *box = SP_3DBOX (i->data);
- if (!dragger->hasBox (box)) {
- sel_boxes = g_slist_remove (sel_boxes, box);
- }
- }
- return sel_boxes;
}
-
/**
* If there already exists a dragger within MERGE_DIST of p, add the VP to it;
* otherwise create new dragger and add it to draggers list
+ * We also store the corresponding perspective in case it is not already present.
*/
void
-VPDrag::addDragger (VanishingPoint *vp)
+VPDrag::addDragger (VanishingPoint &vp)
{
- if (vp == NULL) {
- g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)");
- g_assert (vp != NULL);
- }
- if (!vp->is_finite()) {
+ if (!vp.is_finite()) {
// don't create draggers for infinite vanishing points
return;
}
- NR::Point p = vp->get_pos();
+ NR::Point p = vp.get_pos();
for (GList *i = this->draggers; i != NULL; i = i->next) {
VPDragger *dragger = (VPDragger *) i->data;
this->draggers = g_list_append (this->draggers, new_dragger);
}
+void
+VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1)
+{
+ // iterate over all VP in all draggers and replace persp2 with persp1
+ for (GList *i = this->draggers; i != NULL; i = i->next) {
+ for (std::list<VanishingPoint>::iterator j = ((VPDragger *) (i->data))->vps.begin();
+ j != ((VPDragger *) (i->data))->vps.end(); ++j) {
+ if ((*j).get_perspective() == persp2) {
+ (*j).set_perspective(persp1);
+ }
+ }
+ }
+}
+
/**
Create a line from p1 to p2 and add it to the lines list
*/
this->lines = g_slist_append (this->lines, line);
}
-} // namespace Box3D
-
+} // namespace Box3D
+
/*
Local Variables:
mode:c++
diff --git a/src/vanishing-point.h b/src/vanishing-point.h
index 3dde393855d99362e04aa55b45949e925ff009c2..47c11be186df74643e348ef43ac47ecb8152e3ea 100644 (file)
--- a/src/vanishing-point.h
+++ b/src/vanishing-point.h
#ifndef SEEN_VANISHING_POINT_H
#define SEEN_VANISHING_POINT_H
+#include <set>
#include "libnr/nr-point.h"
#include "knot.h"
#include "selection.h"
#include "axis-manip.h"
+#include "inkscape.h"
+#include "persp3d.h"
+#include "box3d.h"
+#include "persp3d-reference.h"
#include "line-geometry.h" // TODO: Remove this include as soon as we don't need create_canvas_(point|line) any more.
-class SP3DBox;
+class SPBox3D;
namespace Box3D {
VP_INFINITE // perspective lines are parallel
};
-// FIXME: Store the Axis of the VP inside the class
-class VanishingPoint : public NR::Point {
+/* VanishingPoint is a simple wrapper class to easily extract VP data from perspectives.
+ * A VanishingPoint represents a VP in a certain direction (X, Y, Z) of a single perspective.
+ * In particular, it can potentially have more than one box linked to it (although in facth they
+ * are rather linked to the parent perspective).
+ */
+// FIXME: Don't store the box in the VP but rather the perspective (and link the box to it)!!
+class VanishingPoint {
public:
- inline VanishingPoint() : NR::Point() {};
- /***
- inline VanishingPoint(NR::Point const &pt, NR::Point const &ref = NR::Point(0,0))
- : NR::Point (pt),
- ref_pt (ref),
- v_dir (pt[NR::X] - ref[NR::X], pt[NR::Y] - ref[NR::Y]) {}
- inline VanishingPoint(NR::Coord x, NR::Coord y, NR::Point const &ref = NR::Point(0,0))
- : NR::Point (x, y),
- ref_pt (ref),
- v_dir (x - ref[NR::X], y - ref[NR::Y]) {}
- ***/
- VanishingPoint(NR::Point const &pt, NR::Point const &inf_dir, VPState st);
- VanishingPoint(NR::Point const &pt);
- VanishingPoint(NR::Point const &dir, VPState const state);
- VanishingPoint(NR::Point const &pt, NR::Point const &direction);
- VanishingPoint(NR::Coord x, NR::Coord y);
- VanishingPoint(NR::Coord x, NR::Coord y, VPState const state);
- VanishingPoint(NR::Coord x, NR::Coord y, NR::Coord dir_x, NR::Coord dir_y);
- VanishingPoint(VanishingPoint const &rhs);
- ~VanishingPoint();
-
- bool operator== (VanishingPoint const &other);
-
- inline NR::Point get_pos() const { return NR::Point ((*this)[NR::X], (*this)[NR::Y]); }
- inline double get_angle() const { return NR::atan2 (this->v_dir) * 180/M_PI; } // return angle of infinite direction is in degrees
- inline void set_pos(NR::Point const &pt) { (*this)[NR::X] = pt[NR::X];
- (*this)[NR::Y] = pt[NR::Y]; }
- inline void set_pos(const double pt_x, const double pt_y) { (*this)[NR::X] = pt_x;
- (*this)[NR::Y] = pt_y; }
- inline void set_infinite_direction (const NR::Point dir) { v_dir = dir; }
- inline void set_infinite_direction (const double dir_x, const double dir_y) { v_dir = NR::Point (dir_x, dir_y); }
-
- bool is_finite() const;
- VPState toggle_parallel();
- void draw(Box3D::Axis const axis); // Draws a point on the canvas if state == VP_FINITE
- //inline VPState state() { return state; }
-
- VPState state;
- //NR::Point ref_pt; // point of reference to compute the direction of parallel lines
- NR::Point v_dir; // direction of perslective lines if the VP has state == VP_INFINITE
-
+ VanishingPoint() : my_counter(VanishingPoint::global_counter++), _persp(NULL), _axis(Proj::NONE) {}
+ VanishingPoint(Persp3D *persp, Proj::Axis axis) : my_counter(VanishingPoint::global_counter++), _persp(persp), _axis(axis) {}
+ VanishingPoint(const VanishingPoint &other) : my_counter(VanishingPoint::global_counter++), _persp(other._persp), _axis(other._axis) {}
+
+ inline VanishingPoint &operator=(VanishingPoint const &rhs) {
+ _persp = rhs._persp;
+ _axis = rhs._axis;
+ return *this;
+ }
+ inline bool operator==(VanishingPoint const &rhs) const {
+ /* vanishing points coincide if they belong to the same perspective */
+ return (_persp == rhs._persp && _axis == rhs._axis);
+ }
+
+ inline bool operator<(VanishingPoint const &rhs) const {
+ return my_counter < rhs.my_counter;
+ }
+
+ inline void set(Persp3D *persp, Proj::Axis axis) {
+ _persp = persp;
+ _axis = axis;
+ }
+ void set_pos(Proj::Pt2 const &pt);
+ inline bool is_finite() const {
+ g_return_val_if_fail (_persp, false);
+ return persp3d_get_VP (_persp, _axis).is_finite();
+ }
+ inline NR::Point get_pos() const {
+ g_return_val_if_fail (_persp, NR::Point (NR_HUGE, NR_HUGE));
+ return persp3d_get_VP (_persp,_axis).affine();
+ }
+ inline Persp3D * get_perspective() const {
+ return _persp;
+ }
+ inline Persp3D * set_perspective(Persp3D *persp) {
+ return _persp = persp;
+ }
+
+ inline bool hasBox (SPBox3D *box) {
+ return persp3d_has_box(_persp, box);
+ }
+ inline unsigned int numberOfBoxes() const {
+ return persp3d_num_boxes(_persp);
+ }
+
+ /* returns all selected boxes sharing this perspective */
+ std::list<SPBox3D *> selectedBoxes(Inkscape::Selection *sel);
+
+ inline void updateBoxDisplays() const {
+ g_return_if_fail (_persp);
+ persp3d_update_box_displays(_persp);
+ }
+ inline void updateBoxReprs() const {
+ g_return_if_fail (_persp);
+ persp3d_update_box_reprs(_persp);
+ }
+ inline void updatePerspRepr() const {
+ g_return_if_fail (_persp);
+ SP_OBJECT(_persp)->updateRepr(SP_OBJECT_WRITE_EXT);
+ }
+ inline void printPt() const {
+ g_return_if_fail (_persp);
+ persp3d_get_VP (_persp, _axis).print("");
+ }
+ inline gchar *axisString () { return Proj::string_from_axis (_axis); }
+
+ unsigned int my_counter;
+ static unsigned int global_counter; // FIXME: Only to implement operator< so that we can merge lists. Do this in a better way!!
private:
+ Persp3D *_persp;
+ Proj::Axis _axis;
};
-class Perspective3D;
class VPDrag;
+struct less_ptr : public std::binary_function<VanishingPoint *, VanishingPoint *, bool> {
+ bool operator()(VanishingPoint *vp1, VanishingPoint *vp2) {
+ return GPOINTER_TO_INT(vp1) < GPOINTER_TO_INT(vp2);
+ }
+};
+
struct VPDragger {
public:
- VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp);
+ VPDragger(VPDrag *parent, NR::Point p, VanishingPoint &vp);
~VPDragger();
VPDrag *parent;
// position of the knot before it began to drag; updated when released
NR::Point point_original;
- GSList *vps; // the list of vanishing points
+ bool dragging_started;
+
+ std::list<VanishingPoint> vps;
- void addVP(VanishingPoint *vp);
- void removeVP(VanishingPoint *vp);
- /* returns the VP of the dragger that belongs to the given perspective */
- VanishingPoint *getVPofPerspective (Perspective3D *persp);
+ void addVP(VanishingPoint &vp, bool update_pos = false);
+ void removeVP(const VanishingPoint &vp);
void updateTip();
- bool hasBox (const SP3DBox *box);
guint numberOfBoxes(); // the number of boxes linked to all VPs of the dragger
+ VanishingPoint *findVPWithBox(SPBox3D *box);
+ std::set<VanishingPoint*, less_ptr> VPsOfSelectedBoxes();
- bool hasPerspective (const Perspective3D *perps);
- void mergePerspectives (); // remove duplicate perspectives
+ bool hasPerspective(const Persp3D *persp);
+ void mergePerspectives(); // remove duplicate perspectives
- void reshapeBoxes(NR::Point const &p, Box3D::Axis axes);
- void updateBoxReprs();
+ void updateBoxDisplays();
+ void updateVPs(NR::Point const &pt);
void updateZOrders();
+
+ void printVPs();
};
struct VPDrag {
VPDragger *getDraggerFor (VanishingPoint const &vp);
- //void grabKnot (VanishingPoint const &vp, gint x, gint y, guint32 etime);
-
- bool local_change;
bool dragging;
SPDocument *document;
GList *draggers;
GSList *lines;
+ void printDraggers(); // convenience for debugging
+ /*
+ * FIXME: Should the following functions be merged?
+ * Also, they should make use of the info in a VanishingPoint structure (regarding boxes
+ * and perspectives) rather than each time iterating over the whole list of selected items?
+ */
void updateDraggers ();
void updateLines ();
void updateBoxHandles ();
- void drawLinesForFace (const SP3DBox *box, Box3D::Axis axis); //, guint corner1, guint corner2, guint corner3, guint corner4);
+ void updateBoxReprs ();
+ void updateBoxDisplays ();
+ void drawLinesForFace (const SPBox3D *box, Proj::Axis axis); //, guint corner1, guint corner2, guint corner3, guint corner4);
bool show_lines; /* whether perspective lines are drawn at all */
guint front_or_rear_lines; /* whether we draw perspective lines from all corners or only the
front/rear corners (indicated by the first/second bit, respectively */
// FIXME: Should this be private? (It's the case with the corresponding function in gradient-drag.h)
// But vp_knot_grabbed_handler
- void addDragger (VanishingPoint *vp);
+ void addDragger (VanishingPoint &vp);
+
+ void swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1);
private:
//void deselect_all();
} // namespace Box3D
-/** A function to print out the VanishingPoint (prints the coordinates) **/
-/***
-inline std::ostream &operator<< (std::ostream &out_file, const VanishingPoint &vp) {
- out_file << vp;
- return out_file;
-}
-***/
-
-
#endif /* !SEEN_VANISHING_POINT_H */
/*
diff --git a/src/verbs.h b/src/verbs.h
index be9405f9bac09e4cd4235094acfdbb37f07d0fd8..a9abc02ed613543ab0a2ff57f7ac4f9af91d4c26 100644 (file)
--- a/src/verbs.h
+++ b/src/verbs.h
SP_VERB_CONTEXT_TWEAK,
SP_VERB_CONTEXT_RECT,
SP_VERB_CONTEXT_3DBOX,
+ //SP_VERB_CONTEXT_BOX3D,
SP_VERB_CONTEXT_ARC,
SP_VERB_CONTEXT_STAR,
SP_VERB_CONTEXT_SPIRAL,
SP_VERB_CONTEXT_TWEAK_PREFS,
SP_VERB_CONTEXT_RECT_PREFS,
SP_VERB_CONTEXT_3DBOX_PREFS,
+ //SP_VERB_CONTEXT_BOX3D_PREFS,
SP_VERB_CONTEXT_ARC_PREFS,
SP_VERB_CONTEXT_STAR_PREFS,
SP_VERB_CONTEXT_SPIRAL_PREFS,
index 47cb78cb22d21ad91f6f158c5688f584bda645a2..9e9686e77763d36ce92d10c1428563c7bd63bcec 100644 (file)
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
-static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
+static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
{ "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
{ "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
{ "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
-// { "SP3DBoxContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
+ { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
{ "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
{ "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
{ "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
{ "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
- { "SP3DBoxContext", "3dbox_toolbox", 0, sp_3dbox_toolbox_prep, "3DBoxToolbar",
+ { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
{ "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
@@ -2343,26 +2343,35 @@ static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions
//## 3D Box ##
//########################
-static void sp_3dbox_toggle_vp_changed (GtkToggleAction */*act*/, GObject *dataKludge, Box3D::Axis axis)
+static void box3d_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Proj::Axis axis)
{
SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
SPDocument *document = sp_desktop_document (desktop);
- Box3D::Perspective3D *persp = document->current_perspective;
+ // FIXME: Make sure document->current_persp3d is set correctly!
+ Persp3D *persp = document->current_persp3d;
- g_return_if_fail (is_single_axis_direction (axis));
g_return_if_fail (persp);
- persp->toggle_boxes (axis);
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(dataKludge, "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
+
+ persp3d_set_VP_state(persp, axis, gtk_toggle_action_get_active(act) ? Proj::INFINITE : Proj::FINITE);
- gchar *str;
+ // FIXME: Can we merge this functionality with the one in box3d_persp_tb_event_attr_changed()?
+ gchar *str;
switch (axis) {
- case Box3D::X:
+ case Proj::X:
str = g_strdup ("box3d_angle_x_action");
break;
- case Box3D::Y:
+ case Proj::Y:
str = g_strdup ("box3d_angle_y_action");
break;
- case Box3D::Z:
+ case Proj::Z:
str = g_strdup ("box3d_angle_z_action");
break;
default:
@@ -2370,67 +2379,77 @@ static void sp_3dbox_toggle_vp_changed (GtkToggleAction */*act*/, GObject *dataK
}
GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
if (angle_action) {
- gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
+ gtk_action_set_sensitive (angle_action, !persp3d_VP_is_finite(persp, axis));
}
- // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
- // but without the if construct, we get continuous segfaults. Needs further investigation.
- if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
- sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
- _("3D Box: Change perspective"));
- }
+ sp_document_maybe_done(sp_desktop_document(desktop), "toggle_vp", SP_VERB_CONTEXT_3DBOX,
+ _("3D Box: Toggle VP"));
+ //sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,_("3D Box: Toggle VP"));
+
+ g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
}
-static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
+static void box3d_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
{
- sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
+ box3d_toggle_vp_changed (act, dataKludge, Proj::X);
}
-static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
+static void box3d_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
{
- sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
+ box3d_toggle_vp_changed (act, dataKludge, Proj::Y);
}
-static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
+static void box3d_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
{
- sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
+ box3d_toggle_vp_changed (act, dataKludge, Proj::Z);
}
-static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
+static void box3d_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis )
{
SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
- Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
+ Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
- if (persp) {
- double angle = adj->value * M_PI/180;
- persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(dataKludge, "freeze")) {
+ return;
+ }
- // FIXME: See comment above; without the if construct we get segfaults during undo.
- if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
- sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
- _("3D Box: Change perspective"));
+ // in turn, prevent listener from responding
+ g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
+
+ if (persp) {
+ double angle = adj->value;
+ // FIXME: Shouldn't we set the angle via the SVG attributes of the perspective instead of directly?
+ if (persp3d_VP_is_finite(persp, axis)) {
+ return;
}
+ persp->tmat.set_infinite_direction (axis, angle);
+ persp3d_update_box_reprs (persp);
+
+ sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
+ _("3D Box: Change perspective"));
}
- //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
+
+ g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
}
-static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
+static void box3d_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
{
- sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
+ box3d_vp_angle_changed (adj, dataKludge, Proj::X);
}
-static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
+static void box3d_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
{
- sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
+ box3d_vp_angle_changed (adj, dataKludge, Proj::Y);
}
-static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
+static void box3d_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
{
- sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
+ box3d_vp_angle_changed (adj, dataKludge, Proj::Z);
}
// normalize angle so that it lies in the interval [0,360]
-static double sp_3dbox_normalize_angle (double a) {
+static double box3d_normalize_angle (double a) {
double angle = a + ((int) (a/360.0))*360;
if (angle < 0) {
angle += 360.0;
return angle;
}
-static void sp_3dbox_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
- gchar const */*old_value*/, gchar const */*new_value*/,
- bool /*is_interactive*/, gpointer data)
+static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
+ gchar const *old_value, gchar const *new_value,
+ bool is_interactive, gpointer data)
{
GtkWidget *tbl = GTK_WIDGET(data);
// FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
- /***
// quit if run by the _changed callbacks
if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
- return;
+ //return;
}
// in turn, prevent callbacks from responding
- g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
- ***/
+ //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
- if (!strcmp(name, "inkscape:perspective")) {
- GtkAdjustment *adj = 0;
- double angle;
- SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
- Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
+ GtkAdjustment *adj = 0;
+ double angle;
+ SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
+ // FIXME: Get the persp from the box (should be the same, but ...)
+ Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
+ if (!strcmp(name, "inkscape:vp_x")) {
+ GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_x_action"));
+ GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_x_action"));
+ if (!persp3d_VP_is_finite(persp, Proj::X)) {
+ gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
+ gtk_toggle_action_set_active(tact, TRUE);
+ } else {
+ gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
+ gtk_toggle_action_set_active(tact, FALSE);
+ }
adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
- angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
- gtk_adjustment_set_value(adj, angle);
+ angle = persp3d_get_infinite_angle(persp, Proj::X);
+ if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
+ gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
+ }
+ }
+
+ if (!strcmp(name, "inkscape:vp_y")) {
+ GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_y_action"));
+ GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_y_action"));
+ if (!persp3d_VP_is_finite(persp, Proj::Y)) {
+ gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
+ gtk_toggle_action_set_active(tact, TRUE);
+ } else {
+ gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
+ gtk_toggle_action_set_active(tact, FALSE);
+ }
adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
- angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
- gtk_adjustment_set_value(adj, angle);
+ angle = persp3d_get_infinite_angle(persp, Proj::Y);
+ if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
+ gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
+ }
+ }
+
+ if (!strcmp(name, "inkscape:vp_z")) {
+ GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_z_action"));
+ GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_z_action"));
+ if (!persp3d_VP_is_finite(persp, Proj::Z)) {
+ gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
+ gtk_toggle_action_set_active(tact, TRUE);
+ } else {
+ gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
+ gtk_toggle_action_set_active(tact, FALSE);
+ }
adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
- angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
- gtk_adjustment_set_value(adj, angle);
+ angle = persp3d_get_infinite_angle(persp, Proj::Z);
+ if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
+ gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
+ }
}
+
+ //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
}
-static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
+static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
{
NULL, /* child_added */
NULL, /* child_removed */
- sp_3dbox_tb_event_attr_changed,
+ box3d_persp_tb_event_attr_changed,
NULL, /* content_changed */
NULL /* order_changed */
};
/**
* \param selection Should not be NULL.
*/
+// FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
+// Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
static void
-sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
+box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
{
Inkscape::XML::Node *repr = NULL;
purge_repr_listener(tbl, tbl);
SPItem *item = selection->singleItem();
- if (item) {
- repr = SP_OBJECT_REPR(item);
+ if (item && SP_IS_BOX3D(item)) {
+ //repr = SP_OBJECT_REPR(item);
+ repr = SP_OBJECT_REPR(SP_BOX3D(item)->persp_ref->getObject());
if (repr) {
g_object_set_data(tbl, "repr", repr);
Inkscape::GC::anchor(repr);
- sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
- sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
+ sp_repr_add_listener(repr, &box3d_persp_tb_repr_events, tbl);
+ sp_repr_synthesize_events(repr, &box3d_persp_tb_repr_events, tbl);
}
}
}
-static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
+static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
{
EgeAdjustmentAction* eact = 0;
SPDocument *document = sp_desktop_document (desktop);
- Box3D::Perspective3D *persp = document->current_perspective;
+ Persp3D *persp = document->current_persp3d;
bool toggled = false;
/* angle of VP in X direction */
eact = create_adjustment_action("3DBoxPosAngleXAction",
_("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
- "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
- GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
- 0.0, 360.0, 1.0, 10.0,
+ "tools.shapes.3dbox", "dir_vp_x", persp3d_get_infinite_angle(persp, Proj::X),
+ GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
+ -360.0, 360.0, 1.0, 10.0,
0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
- sp_3dbox_vpx_angle_changed,
+ box3d_vpx_angle_changed,
0.1, 1);
gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
g_object_set_data(holder, "box3d_angle_x_action", eact);
- if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
+ if (!persp3d_VP_is_finite(persp, Proj::X)) {
gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
} else {
gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
@@ -2533,30 +2595,28 @@ static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction
{
InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
_("Toggle VP in X direction"),
- _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
+ _("Toggle VP in X direction between 'finite' and 'infinite' (= parallel)"),
"toggle_vp_x",
Inkscape::ICON_SIZE_DECORATION);
gtk_action_group_add_action(mainActions, GTK_ACTION(act));
- if (persp) {
- toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
- }
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
+ g_object_set_data(holder, "toggle_vp_x_action", act);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::X));
/* we connect the signal after setting the state to avoid switching the state again */
- g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
+ g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_x_changed), holder);
}
/* angle of VP in Y direction */
eact = create_adjustment_action("3DBoxPosAngleYAction",
_("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
- "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
+ "tools.shapes.3dbox", "dir_vp_y", persp3d_get_infinite_angle(persp, Proj::Y),
GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
- 0.0, 360.0, 1.0, 10.0,
+ -360.0, 360.0, 1.0, 10.0,
0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
- sp_3dbox_vpy_angle_changed,
+ box3d_vpy_angle_changed,
0.1, 1);
gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
g_object_set_data(holder, "box3d_angle_y_action", eact);
- if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
+ if (!persp3d_VP_is_finite(persp, Proj::Y)) {
gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
} else {
gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
@@ -2566,31 +2626,29 @@ static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction
{
InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
_("Toggle VP in Y direction"),
- _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
+ _("Toggle VP in Y direction between 'finite' and 'infinite' (= parallel)"),
"toggle_vp_y",
Inkscape::ICON_SIZE_DECORATION);
gtk_action_group_add_action(mainActions, GTK_ACTION(act));
- if (persp) {
- toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
- }
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Y));
+ g_object_set_data(holder, "toggle_vp_y_action", act);
/* we connect the signal after setting the state to avoid switching the state again */
- g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
+ g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_y_changed), holder);
}
/* angle of VP in Z direction */
eact = create_adjustment_action("3DBoxPosAngleZAction",
_("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
- "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
+ "tools.shapes.3dbox", "dir_vp_z", persp3d_get_infinite_angle(persp, Proj::Z),
GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
- 0.0, 360.0, 1.0, 10.0,
+ -360.0, 360.0, 1.0, 10.0,
0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
- sp_3dbox_vpz_angle_changed,
+ box3d_vpz_angle_changed,
0.1, 1);
gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
g_object_set_data(holder, "box3d_angle_z_action", eact);
- if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
+ if (!persp3d_VP_is_finite(persp, Proj::Z)) {
gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
} else {
gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
@@ -2600,20 +2658,19 @@ static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction
{
InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
_("Toggle VP in Z direction"),
- _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
+ _("Toggle VP in Z direction between 'finite' and 'infinite' (= parallel)"),
"toggle_vp_z",
Inkscape::ICON_SIZE_DECORATION);
gtk_action_group_add_action(mainActions, GTK_ACTION(act));
- if (persp) {
- toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
- }
+
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Z));
+ g_object_set_data(holder, "toggle_vp_z_action", act);
/* we connect the signal after setting the state to avoid switching the state again */
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
- g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
+ g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_z_changed), holder);
}
sigc::connection *connection = new sigc::connection(
- sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
+ sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
);
g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);