Code

First (very limited) version of the 3D box tool; allows for drawing of new boxes...
authorcilix42 <cilix42@users.sourceforge.net>
Thu, 21 Jun 2007 13:01:57 +0000 (13:01 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Thu, 21 Jun 2007 13:01:57 +0000 (13:01 +0000)
26 files changed:
share/icons/icons.svg
src/Makefile_insert
src/box3d-context.cpp [new file with mode: 0644]
src/box3d-context.h [new file with mode: 0644]
src/box3d-face.cpp [new file with mode: 0644]
src/box3d-face.h [new file with mode: 0644]
src/box3d.cpp [new file with mode: 0644]
src/box3d.h [new file with mode: 0644]
src/knotholder.cpp
src/line-geometry.cpp [new file with mode: 0644]
src/line-geometry.h [new file with mode: 0644]
src/perspective-line.cpp [new file with mode: 0644]
src/perspective-line.h [new file with mode: 0644]
src/perspective3d.cpp [new file with mode: 0644]
src/perspective3d.h [new file with mode: 0644]
src/preferences-skeleton.h
src/sp-object-repr.cpp
src/tools-switch.cpp
src/tools-switch.h
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h
src/vanishing-point.cpp [new file with mode: 0644]
src/vanishing-point.h [new file with mode: 0644]
src/verbs.cpp
src/verbs.h
src/widgets/toolbox.cpp

index b1e578200da156fd16d4961e8f7f896cfe92ab30..a579af7194c15da18e60dabd73eac4fa8ee845c8 100644 (file)
        y1="154.99997"
        x2="526.70886"
        y2="161.71585" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.13356,0,0,1.133368,199.5737,-131.407)"
+       x1="101.09393"
+       y1="221.06877"
+       x2="112.20101"
+       y2="234.00311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.809546,0,0,0.809417,202.9983,-4.125322)"
+       x1="144.08243"
+       y1="157.82079"
+       x2="176.86269"
+       y2="188.41277" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4362"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.13356,0,0,1.133368,195.5737,-135.407)"
+       x1="101.09393"
+       y1="221.06877"
+       x2="112.20101"
+       y2="234.00311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4364"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.809546,0,0,0.809417,198.9983,-8.125322)"
+       x1="144.08243"
+       y1="157.82079"
+       x2="176.86269"
+       y2="188.41277" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4952"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.13356,0,0,1.133368,339.5737,-131.407)"
+       x1="101.09393"
+       y1="221.06877"
+       x2="112.20101"
+       y2="234.00311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.809546,0,0,0.809417,342.9983,-4.125322)"
+       x1="144.08243"
+       y1="157.82079"
+       x2="176.86269"
+       y2="188.41277" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4978"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.13356,0,0,1.133368,195.5737,-135.407)"
+       x1="101.09393"
+       y1="221.06877"
+       x2="112.20101"
+       y2="234.00311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4980"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.809546,0,0,0.809417,198.9983,-8.125322)"
+       x1="144.08243"
+       y1="157.82079"
+       x2="176.86269"
+       y2="188.41277" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4982"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.13356,0,0,1.133368,339.5737,-131.407)"
+       x1="101.09393"
+       y1="221.06877"
+       x2="112.20101"
+       y2="234.00311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4985"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.809546,0,0,0.809417,342.9983,-4.125322)"
+       x1="144.08243"
+       y1="157.82079"
+       x2="176.86269"
+       y2="188.41277" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4991"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.13356,0,0,1.133368,339.5737,-131.407)"
+       x1="101.09393"
+       y1="221.06877"
+       x2="112.20101"
+       y2="234.00311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient4993"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.809546,0,0,0.809417,342.9983,-4.125322)"
+       x1="144.08243"
+       y1="157.82079"
+       x2="176.86269"
+       y2="188.41277" />
   </defs>
   <sodipodi:namedview
      inkscape:guide-bbox="true"
      inkscape:window-x="6"
      inkscape:window-height="764"
      inkscape:window-width="1021"
-     inkscape:cy="860.52734"
-     inkscape:cx="563.9888"
-     inkscape:zoom="0.71599583"
+     inkscape:cy="1119.1738"
+     inkscape:cx="696.90117"
+     inkscape:zoom="4.050284"
      gridtolerance="6"
      snaptogrid="false"
      showgrid="true"
      guidetolerance="0.50000000px"
      gridempspacing="2"
      fill="#8ab3de"
-     stroke="#646464" />
+     stroke="#646464"
+     inkscape:object-nodes="true"
+     objecttolerance="11" />
   <metadata
      id="metadata1810">
     <rdf:RDF>
@@ -14854,4 +14976,115 @@ http://www.inkscape.org/</dc:description>
        d="M 504.50005,157.49995 L 515.50005,157.49995 L 511.50005,152.49995 L 500.50005,152.49995 L 504.50005,157.49995 z"
        style="opacity:1;color:#000000;fill:url(#linearGradient9119);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient9121);stroke-width:1;stroke-linecap:square;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
   </g>
+  <g
+     id="draw_3dbox">
+    <g
+       transform="translate(144,4)"
+       id="g4354">
+      <rect
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="rect4356"
+         width="24"
+         height="24"
+         x="450"
+         y="115" />
+      <rect
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="rect4358"
+         width="18.991669"
+         height="18.991783"
+         x="452.50833"
+         y="117.50822" />
+      <rect
+         style="color:#000000;fill:url(#linearGradient4978);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4980);stroke-width:0.99999982;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="rect4360"
+         width="17"
+         height="17"
+         x="453.5"
+         y="118.5" />
+    </g>
+    <rect
+       y="115"
+       x="590"
+       height="24"
+       width="24"
+       id="rect4344"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+    <rect
+       y="117.50822"
+       x="592.5083"
+       height="18.991783"
+       width="18.991669"
+       id="rect4346"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+    <rect
+       y="118.5"
+       x="593.5"
+       height="17"
+       width="17"
+       id="rect4348"
+       style="color:#000000;fill:url(#linearGradient4991);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4993);stroke-width:0.99999982;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+    <path
+       sodipodi:nodetypes="cc"
+       id="path4960"
+       d="M 611.49997,117.50822 L 615.5,121.50822"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       sodipodi:nodetypes="cc"
+       id="path4962"
+       d="M 611.49997,117.50822 L 615.5,121.50822"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       sodipodi:nodetypes="cc"
+       id="path4964"
+       d="M 592.5083,136.5 L 596.50833,140.5"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       sodipodi:nodetypes="cc"
+       id="path5001"
+       d="M 611.49997,136.5 L 615.5,140.5"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+  </g>
+  <g
+     id="toggle_vp_x"
+     transform="matrix(1,0,0,1.0907248,0,-12.901177)">
+    <path
+       sodipodi:nodetypes="cc"
+       d="M 646.36929,119.70888 L 646.36929,140.20127"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.83003211;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4997" />
+    <path
+       sodipodi:nodetypes="cc"
+       d="M 653.38169,119.70888 L 653.38169,140.20127"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.83003211;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4979" />
+  </g>
+  <g
+     id="toggle_vp_y"
+     transform="matrix(1,0,0,1.0907248,40,-12.901177)">
+    <path
+       sodipodi:nodetypes="cc"
+       d="M 646.36929,119.70888 L 646.36929,140.20127"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.83003211;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4987" />
+    <path
+       sodipodi:nodetypes="cc"
+       d="M 653.38169,119.70888 L 653.38169,140.20127"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.83003211;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4989" />
+  </g>
+  <g
+     id="toggle_vp_z"
+     transform="matrix(1,0,0,1.0907248,80,-12.901177)">
+    <path
+       sodipodi:nodetypes="cc"
+       d="M 646.36929,119.70888 L 646.36929,140.20127"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.83003211;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4993" />
+    <path
+       sodipodi:nodetypes="cc"
+       d="M 653.38169,119.70888 L 653.38169,140.20127"
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.83003211;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4995" />
+  </g>
 </svg>
index 9ded1945f600ad9ae59eeac9e436159c5cda502a..f1ba21920d4b60e649f96ea99dd0483440471e13 100644 (file)
@@ -41,6 +41,9 @@ libinkpre_a_SOURCES = \
        arc-context.cpp arc-context.h   \
        attributes.cpp attributes.h     \
        bad-uri-exception.h     \
+       box3d.cpp box3d.h \
+       box3d-context.cpp box3d-context.h \
+       box3d-face.cpp box3d-face.h \
        brokenimage.xpm \
        color-rgba.h    \
        color-profile.cpp color-profile.h       \
@@ -90,6 +93,7 @@ libinkpre_a_SOURCES = \
        knotholder.cpp knotholder.h     \
        layer-fns.cpp layer-fns.h       \
        layer-manager.cpp layer-manager.h       \
+       line-geometry.cpp line-geometry.h       \
        macros.h        \
        main-cmdlineact.cpp main-cmdlineact.h \
        media.cpp media.h       \
@@ -110,6 +114,8 @@ libinkpre_a_SOURCES =       \
        pen-context.h   \
        pencil-context.cpp      \
        pencil-context.h        \
+       perspective-line.cpp perspective-line.h \
+       perspective3d.cpp perspective3d.h       \
        preferences.cpp preferences.h \
        preferences-skeleton.h  \
        menus-skeleton.h        \
@@ -257,6 +263,7 @@ libinkpre_a_SOURCES =       \
        text-tag-attributes.h   \
        tools-switch.cpp tools-switch.h\
        uri-references.cpp uri-references.h     \
+       vanishing-point.cpp vanishing-point.h   \
        verbs.cpp verbs.h       \
        version.cpp version.h   \
        zoom-context.cpp zoom-context.h
diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp
new file mode 100644 (file)
index 0000000..ead6486
--- /dev/null
@@ -0,0 +1,608 @@
+#define __SP_3DBOX_CONTEXT_C__
+
+/*
+ * 3D box drawing context
+ *
+ * Author:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2007      Maximilian Albert <Anhalter42@gmx.de>
+ * Copyright (C) 2006      Johan Engelen <johan@shouraizou.nl>
+ * Copyright (C) 2000-2005 authors
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "config.h"
+
+#include <gdk/gdkkeysyms.h>
+
+#include "macros.h"
+#include "display/sp-canvas.h"
+#include "document.h"
+#include "sp-namedview.h"
+#include "selection.h"
+#include "desktop-handles.h"
+#include "snap.h"
+#include "display/curve.h"
+#include "desktop.h"
+#include "message-context.h"
+#include "pixmaps/cursor-rect.xpm"
+#include "box3d.h"
+#include "box3d-context.h"
+#include "sp-metrics.h"
+#include <glibmm/i18n.h>
+#include "object-edit.h"
+#include "xml/repr.h"
+#include "xml/node-event-vector.h"
+#include "prefs-utils.h"
+#include "context-fns.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_3dbox_context_setup(SPEventContext *ec);
+static void sp_3dbox_context_set(SPEventContext *ec, gchar const *key, gchar const *val);
+
+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 void sp_3dbox_drag(SP3DBoxContext &rc, guint state);
+static void sp_3dbox_finish(SP3DBoxContext *rc);
+
+static SPEventContextClass *parent_class;
+
+
+GtkType sp_3dbox_context_get_type()
+{
+    static GType type = 0;
+    if (!type) {
+        GTypeInfo info = {
+            sizeof(SP3DBoxContextClass),
+            NULL, NULL,
+            (GClassInitFunc) sp_3dbox_context_class_init,
+            NULL, NULL,
+            sizeof(SP3DBoxContext),
+            4,
+            (GInstanceInitFunc) sp_3dbox_context_init,
+            NULL,    /* value_table */
+        };
+        type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SP3DBoxContext", &info, (GTypeFlags) 0);
+    }
+    return type;
+}
+
+static void sp_3dbox_context_class_init(SP3DBoxContextClass *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;
+
+    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;
+}
+
+Box3D::Perspective3D * SP3DBoxContext::current_perspective = NULL;
+
+static void sp_3dbox_context_init(SP3DBoxContext *box3d_context)
+{
+    SPEventContext *event_context = SP_EVENT_CONTEXT(box3d_context);
+
+    event_context->cursor_shape = cursor_rect_xpm;
+    event_context->hot_x = 4;
+    event_context->hot_y = 4;
+    event_context->xp = 0;
+    event_context->yp = 0;
+    event_context->tolerance = 0;
+    event_context->within_tolerance = false;
+    event_context->item_to_select = NULL;
+
+    event_context->shape_repr = NULL;
+    event_context->shape_knot_holder = NULL;
+
+    box3d_context->item = NULL;
+
+    box3d_context->rx = 0.0;
+    box3d_context->ry = 0.0;
+
+    box3d_context->ctrl_dragged = false;
+    box3d_context->extruded = false;
+
+    /* create an initial perspective */
+    if (!SP3DBoxContext::current_perspective) {
+        SP3DBoxContext::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_INFINITE),
+                                              // VP in y-direction
+                                              Box3D::VanishingPoint( NR::Point(700.0, 500.0),
+                                                                     NR::Point(sqrt(3.0),1.0), Box3D::VP_INFINITE),
+                                              // VP in z-direction
+                                              Box3D::VanishingPoint( NR::Point(500.0,1000.0),
+                                                                     NR::Point(  0.0,   1.0), Box3D::VP_INFINITE));
+    }
+    
+    new (&box3d_context->sel_changed_connection) sigc::connection();
+}
+
+static void sp_3dbox_context_dispose(GObject *object)
+{
+    SP3DBoxContext *rc = SP_3DBOX_CONTEXT(object);
+    SPEventContext *ec = SP_EVENT_CONTEXT(object);
+
+    ec->enableGrDrag(false);
+
+    rc->sel_changed_connection.disconnect();
+    rc->sel_changed_connection.~connection();
+
+    /* fixme: This is necessary because we do not grab */
+    if (rc->item) {
+        sp_3dbox_finish(rc);
+    }
+
+    if (ec->shape_knot_holder) {
+        sp_knot_holder_destroy(ec->shape_knot_holder);
+        ec->shape_knot_holder = NULL;
+    }
+
+    if (ec->shape_repr) { // remove old listener
+        sp_repr_remove_listener_by_data(ec->shape_repr, ec);
+        Inkscape::GC::release(ec->shape_repr);
+        ec->shape_repr = 0;
+    }
+
+    if (rc->_message_context) {
+        delete rc->_message_context;
+    }
+
+    G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+static Inkscape::XML::NodeEventVector ec_shape_repr_events = {
+    NULL, /* child_added */
+    NULL, /* child_removed */
+    ec_shape_event_attr_changed,
+    NULL, /* content_changed */
+    NULL  /* order_changed */
+};
+
+/**
+\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)
+{
+    SP3DBoxContext *rc = SP_3DBOX_CONTEXT(data);
+    SPEventContext *ec = SP_EVENT_CONTEXT(rc);
+
+    if (ec->shape_knot_holder) { // destroy knotholder
+        sp_knot_holder_destroy(ec->shape_knot_holder);
+        ec->shape_knot_holder = NULL;
+    }
+
+    if (ec->shape_repr) { // remove old listener
+        sp_repr_remove_listener_by_data(ec->shape_repr, ec);
+        Inkscape::GC::release(ec->shape_repr);
+        ec->shape_repr = 0;
+    }
+
+    SPItem *item = selection->singleItem();
+    if (item) {
+        ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop);
+        Inkscape::XML::Node *shape_repr = SP_OBJECT_REPR(item);
+        if (shape_repr) {
+            ec->shape_repr = shape_repr;
+            Inkscape::GC::anchor(shape_repr);
+            sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec);
+        }
+    }
+}
+
+static void sp_3dbox_context_setup(SPEventContext *ec)
+{
+    SP3DBoxContext *rc = SP_3DBOX_CONTEXT(ec);
+
+    if (((SPEventContextClass *) parent_class)->setup) {
+        ((SPEventContextClass *) parent_class)->setup(ec);
+    }
+
+    SPItem *item = sp_desktop_selection(ec->desktop)->singleItem();
+    if (item) {
+        ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop);
+        Inkscape::XML::Node *shape_repr = SP_OBJECT_REPR(item);
+        if (shape_repr) {
+            ec->shape_repr = shape_repr;
+            Inkscape::GC::anchor(shape_repr);
+            sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec);
+        }
+    }
+
+    rc->sel_changed_connection.disconnect();
+    rc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(
+        sigc::bind(sigc::ptr_fun(&sp_3dbox_context_selection_changed), (gpointer)rc)
+    );
+
+    sp_event_context_read(ec, "rx");
+    sp_event_context_read(ec, "ry");
+
+    if (prefs_get_int_attribute("tools.shapes", "selcue", 0) != 0) {
+        ec->enableSelectionCue();
+    }
+
+    if (prefs_get_int_attribute("tools.shapes", "gradientdrag", 0) != 0) {
+        ec->enableGrDrag();
+    }
+
+    rc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
+}
+
+static void sp_3dbox_context_set(SPEventContext *ec, gchar const *key, gchar const *val)
+{
+    SP3DBoxContext *rc = 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 ) {
+        rc->rx = ( val
+                         ? g_ascii_strtod (val, NULL)
+                         : 0.0 );
+    } else if ( strcmp(key, "ry") == 0 ) {
+        rc->ry = ( val
+                         ? g_ascii_strtod (val, NULL)
+                         : 0.0 );
+    }
+}
+
+static gint sp_3dbox_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
+{
+    SPDesktop *desktop = event_context->desktop;
+
+    gint ret = FALSE;
+
+    switch (event->type) {
+    case GDK_BUTTON_PRESS:
+        if ( event->button.button == 1 ) {
+            Inkscape::setup_for_drag_start(desktop, event_context, event);
+            ret = TRUE;
+        }
+        break;
+        // motion and release are always on root (why?)
+    default:
+        break;
+    }
+
+    if (((SPEventContextClass *) parent_class)->item_handler) {
+        ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
+    }
+
+    return ret;
+}
+
+static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEvent *event)
+{
+    static bool dragging;
+
+    SPDesktop *desktop = event_context->desktop;
+    Inkscape::Selection *selection = sp_desktop_selection (desktop);
+
+    SP3DBoxContext *rc = SP_3DBOX_CONTEXT(event_context);
+
+    event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
+
+    gint ret = FALSE;
+    switch (event->type) {
+    case GDK_BUTTON_PRESS:
+        if ( event->button.button == 1 ) {
+            NR::Point const button_w(event->button.x,
+                                     event->button.y);
+
+            // save drag origin
+            event_context->xp = (gint) button_w[NR::X];
+            event_context->yp = (gint) button_w[NR::Y];
+            event_context->within_tolerance = true;
+            
+            // remember clicked item, disregarding groups, honoring Alt
+            event_context->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE);
+
+            dragging = true;
+            
+            /* Position center */
+            NR::Point const button_dt(desktop->w2d(button_w));
+            rc->drag_origin = button_dt;
+            rc->drag_ptB = button_dt;
+            rc->drag_ptC = button_dt;
+
+            /* Snap center */
+            SnapManager const &m = desktop->namedview->snap_manager;
+            rc->center = m.freeSnap(Inkscape::Snapper::SNAP_POINT | Inkscape::Snapper::BBOX_POINT,
+                                    button_dt, rc->item).getPoint();
+
+            sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+                                ( GDK_KEY_PRESS_MASK |
+                                  GDK_BUTTON_RELEASE_MASK       |
+                                  GDK_POINTER_MOTION_MASK       |
+                                  GDK_BUTTON_PRESS_MASK ),
+                                NULL, event->button.time);
+            ret = TRUE;
+        }
+        break;
+    case GDK_MOTION_NOTIFY:
+        if ( dragging
+             && ( event->motion.state & GDK_BUTTON1_MASK ) )
+        {
+            if ( event_context->within_tolerance
+                 && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
+                 && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
+                break; // do not drag if we're within tolerance from origin
+            }
+            // Once the user has moved farther than tolerance from the original location
+            // (indicating they intend to draw, not click), then always process the
+            // motion notify coordinates as given (no snapping back to origin)
+            event_context->within_tolerance = false;
+
+            NR::Point const motion_w(event->motion.x,
+                                     event->motion.y);
+            NR::Point motion_dt(desktop->w2d(motion_w));
+
+            SnapManager const &m = desktop->namedview->snap_manager;
+            motion_dt = m.freeSnap(Inkscape::Snapper::BBOX_POINT | Inkscape::Snapper::SNAP_POINT, motion_dt, rc->item).getPoint();
+
+            rc->ctrl_dragged  = event->motion.state & GDK_CONTROL_MASK;
+
+            if (event->motion.state & GDK_SHIFT_MASK)
+                rc->extruded = true; // set rc->extruded once shift is pressed
+
+            if (!rc->extruded) {
+               rc->drag_ptB = motion_dt;
+            } else {
+                // Without Ctrl, motion of the extruded corner is constrained to the
+                // perspective line from drag_ptB to vanishing point Y.
+                if (!rc->ctrl_dragged) {
+                       rc->drag_ptC = Box3D::perspective_line_snap (rc->drag_ptB, Box3D::Y, motion_dt);
+                } else {
+                    rc->drag_ptC = motion_dt;
+                }
+                rc->drag_ptC = m.freeSnap(Inkscape::Snapper::BBOX_POINT | Inkscape::Snapper::SNAP_POINT, rc->drag_ptC, rc->item).getPoint();
+                if (rc->ctrl_dragged) {
+                       Box3D::PerspectiveLine pl1 (NR::Point (event_context->xp, event_context->yp), Box3D::Z);
+                       Box3D::PerspectiveLine pl2 (rc->drag_ptB, Box3D::X);
+                       NR::Point corner1 = pl1.meet(pl2);
+                       
+                       Box3D::PerspectiveLine pl3 (corner1, Box3D::X);
+                       Box3D::PerspectiveLine pl4 (rc->drag_ptC, Box3D::Y);
+                       rc->drag_ptB = pl3.meet(pl4);
+                }
+            }
+            
+            
+            sp_3dbox_drag(*rc, event->motion.state);
+            
+            ret = TRUE;
+        }
+        ret = TRUE;
+        break;
+    case GDK_BUTTON_RELEASE:
+        event_context->xp = event_context->yp = 0;
+        if ( event->button.button == 1 ) {
+            dragging = false;
+
+            if (!event_context->within_tolerance) {
+                // we've been dragging, finish the rect
+                sp_3dbox_finish(rc);
+            } else if (event_context->item_to_select) {
+                // no dragging, select clicked item if any
+                if (event->button.state & GDK_SHIFT_MASK) {
+                    selection->toggle(event_context->item_to_select);
+                } else {
+                    selection->set(event_context->item_to_select);
+                }
+            } else {
+                // click in an empty space
+                selection->clear();
+            }
+
+            event_context->item_to_select = NULL;
+            ret = TRUE;
+            sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+                                  event->button.time);
+        }
+        break;
+    case GDK_KEY_PRESS:
+        switch (get_group0_keyval (&event->key)) {
+        case GDK_Alt_L:
+        case GDK_Alt_R:
+        case GDK_Control_L:
+        case GDK_Control_R:
+        case GDK_Shift_L:
+        case GDK_Shift_R:
+        case GDK_Meta_L:  // Meta is when you press Shift+Alt (at least on my machine)
+        case GDK_Meta_R:
+            if (!dragging){
+                sp_event_show_modifier_tip (event_context->defaultMessageContext(), event,
+                                            _("<b>Ctrl</b>: make square or integer-ratio rect, lock a rounded corner circular"),
+                                            _("<b>Shift</b>: draw around the starting point"),
+                                            NULL);
+            }
+            break;
+        case GDK_Up:
+        case GDK_Down:
+        case GDK_KP_Up:
+        case GDK_KP_Down:
+            // prevent the zoom field from activation
+            if (!MOD__CTRL_ONLY)
+                ret = TRUE;
+            break;
+
+        case GDK_x:
+        case GDK_X:
+            if (MOD__ALT_ONLY) {
+                desktop->setToolboxFocusTo ("altx-rect");
+                ret = TRUE;
+            }
+            break;
+
+        case GDK_Escape:
+            sp_desktop_selection(desktop)->clear();
+            //TODO: make dragging escapable by Esc
+            break;
+
+        case GDK_space:
+            if (dragging) {
+                sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+                                      event->button.time);
+                dragging = false;
+                if (!event_context->within_tolerance) {
+                    // we've been dragging, finish the rect
+                    sp_3dbox_finish(rc);
+                }
+                // do not return true, so that space would work switching to selector
+            }
+            break;
+
+        default:
+            break;
+        }
+        break;
+    case GDK_KEY_RELEASE:
+        switch (get_group0_keyval (&event->key)) {
+        case GDK_Alt_L:
+        case GDK_Alt_R:
+        case GDK_Control_L:
+        case GDK_Control_R:
+        case GDK_Shift_L:
+        case GDK_Shift_R:
+        case GDK_Meta_L:  // Meta is when you press Shift+Alt
+        case GDK_Meta_R:
+            event_context->defaultMessageContext()->clear();
+            break;
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (!ret) {
+        if (((SPEventContextClass *) parent_class)->root_handler) {
+            ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
+        }
+    }
+
+    return ret;
+}
+
+static void sp_3dbox_drag(SP3DBoxContext &rc, guint state)
+{
+    SPDesktop *desktop = SP_EVENT_CONTEXT(&rc)->desktop;
+
+    if (!rc.item) {
+
+        if (Inkscape::have_viable_layer(desktop, rc._message_context) == false) {
+            return;
+        }
+
+        /* Create object */
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(&rc));
+        Inkscape::XML::Node *repr = xml_doc->createElement("svg:g");
+        repr->setAttribute("sodipodi:type", "inkscape:3dbox");
+
+        /* Set style */
+        //sp_desktop_apply_style_tool (desktop, repr, "tools.shapes.3dbox", false);
+
+        rc.item = (SPItem *) desktop->currentLayer()->appendChildRepr(repr);
+        Inkscape::GC::release(repr);
+        rc.item->transform = SP_ITEM(desktop->currentRoot())->getRelativeTransform(desktop->currentLayer());
+
+        /* The separate faces (created from rear to front) */
+        SP3DBox *box3d = SP_3DBOX(rc.item);
+        box3d->face3.hook_path_to_3dbox();
+        box3d->face4.hook_path_to_3dbox();
+        box3d->face5.hook_path_to_3dbox();
+        box3d->face6.hook_path_to_3dbox();
+        box3d->face1.hook_path_to_3dbox();
+        box3d->face2.hook_path_to_3dbox();
+
+        rc.item->updateRepr();
+
+        sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
+    }
+
+    // FIXME: remove these extra points
+    NR::Point pt = rc.drag_ptB;
+    NR::Point shift_pt = rc.drag_ptC;
+
+    NR::Rect r;
+    if (!(state & GDK_SHIFT_MASK)) {
+        r = Inkscape::snap_rectangular_box(desktop, rc.item, pt, rc.center, state);
+    } else {
+        r = Inkscape::snap_rectangular_box(desktop, rc.item, shift_pt, rc.center, state);
+    }
+
+    /*** artefacts of rect ***
+    if ( rc.rx != 0.0 ) {
+        sp_3dbox_set_rx (SP_3DBOX(rc.item), TRUE, rc.rx);
+    }
+    if ( rc.ry != 0.0 ) {
+        if (rc.rx == 0.0)
+            sp_3dbox_set_ry (SP_3DBOX(rc.item), TRUE, CLAMP(rc.ry, 0, MIN(r.dimensions()[NR::X], r.dimensions()[NR::Y])/2));
+        else
+            sp_3dbox_set_ry (SP_3DBOX(rc.item), TRUE, CLAMP(rc.ry, 0, r.dimensions()[NR::Y]));
+    }
+    ***/
+
+    SPEventContext *ec = SP_EVENT_CONTEXT(&rc);
+    NR::Point origin_w(ec->xp, ec->yp);
+    NR::Point origin(desktop->w2d(origin_w));
+    sp_3dbox_position_set(rc);
+
+    // status text
+    GString *xs = SP_PX_TO_METRIC_STRING(r.dimensions()[NR::X], desktop->namedview->getDefaultMetric());
+    GString *ys = SP_PX_TO_METRIC_STRING(r.dimensions()[NR::Y], desktop->namedview->getDefaultMetric());
+    rc._message_context->setF(Inkscape::NORMAL_MESSAGE, _("<b>Rectangle</b>: %s &#215; %s; with <b>Ctrl</b> to make square or integer-ratio rectangle; with <b>Shift</b> to draw around the starting point"), xs->str, ys->str);
+    g_string_free(xs, FALSE);
+    g_string_free(ys, FALSE);
+}
+
+static void sp_3dbox_finish(SP3DBoxContext *rc)
+{
+    rc->_message_context->clear();
+
+    if ( rc->item != NULL ) {
+        SPDesktop * desktop;
+
+        desktop = SP_EVENT_CONTEXT_DESKTOP(rc);
+
+        SP_OBJECT(rc->item)->updateRepr();
+
+        sp_canvas_end_forced_full_redraws(desktop->canvas);
+
+        sp_desktop_selection(desktop)->set(rc->item);
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                         _("Create 3d box"));
+
+        rc->item = NULL;
+    }
+
+    rc->ctrl_dragged = false;
+    rc->extruded = false;
+}
+
+/*
+  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-context.h b/src/box3d-context.h
new file mode 100644 (file)
index 0000000..bf1d0e5
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __SP_3DBOX_CONTEXT_H__
+#define __SP_3DBOX_CONTEXT_H__
+
+/*
+ * 3D box drawing context
+ *
+ * Author:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2000 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ * Copyright (C) 2002 Lauris Kaplinski
+ * Copyright (C) 2007 Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Released under GNU GPL
+ */
+
+#include <sigc++/sigc++.h>
+#include "event-context.h"
+#include "perspective3d.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))
+
+class SP3DBoxContext;
+class SP3DBoxContextClass;
+
+struct SP3DBoxContext : public SPEventContext {
+       SPItem *item;
+       NR::Point center;
+
+       gdouble rx;     /* roundness radius (x direction) */
+       gdouble ry;     /* roundness radius (y direction) */
+
+    /**
+     * save three corners while dragging:
+     * 1) the starting point (already done by the event_context)
+     * 2) drag_ptB --> the opposite corner of the front face (before pressing shift)
+     * 3) drag_ptC --> the "extruded corner" (which coincides with the mouse pointer location
+     *    if we are ctrl-dragging but is constrained to the perspective line from drag_ptC
+     *    to the vanishing point Y otherwise)
+     */
+    NR::Point drag_origin;
+    NR::Point drag_ptB;
+    NR::Point drag_ptC;
+    bool ctrl_dragged; /* whether we are ctrl-dragging */
+    bool extruded; /* whether shift-dragging already occured (i.e. the box is already extruded) */
+
+    static Box3D::Perspective3D *current_perspective;
+
+       sigc::connection sel_changed_connection;
+
+       Inkscape::MessageContext *_message_context;
+};
+
+struct SP3DBoxContextClass {
+       SPEventContextClass parent_class;
+};
+
+/* Standard Gtk function */
+
+GtkType sp_3dbox_context_get_type (void);
+
+#endif
diff --git a/src/box3d-face.cpp b/src/box3d-face.cpp
new file mode 100644 (file)
index 0000000..eb05b2c
--- /dev/null
@@ -0,0 +1,161 @@
+#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 "box3d-face.h"
+#include <iostream>
+
+Box3DFace::Box3DFace(SP3DBox *box3d) : corner1 (0, 0), corner2 (0, 0), corner3 (0, 0), corner4 (0, 0),
+                                       dir1 (Box3D::NONE), dir2 (Box3D::NONE), path (NULL), parent_box3d (box3d)
+{
+} 
+
+void Box3DFace::set_shape(NR::Point const ul, NR::Point const lr,
+                     Box3D::PerspDir const dir1, Box3D::PerspDir const dir2,
+                     unsigned int shift_count, NR::Maybe<NR::Point> pt_align, bool align_along_PL)
+{
+    corner1 = ul;
+    if (!pt_align) {
+        corner3 = lr;
+    } else {
+        if (align_along_PL) {
+               Box3D::PerspDir dir3;
+            if (dir1 == Box3D::X && dir2 == Box3D::Y) dir3 = Box3D::Z;
+            if (dir1 == Box3D::X && dir2 == Box3D::Z) dir3 = Box3D::Y;
+            if (dir1 == Box3D::Y && dir2 == Box3D::X) dir3 = Box3D::Z;
+            if (dir1 == Box3D::Y && dir2 == Box3D::Z) dir3 = Box3D::X;
+            if (dir1 == Box3D::Z && dir2 == Box3D::X) dir3 = Box3D::Y;
+            if (dir1 == Box3D::Z && dir2 == Box3D::Y) dir3 = Box3D::X;
+            Box3D::Line line1(*SP3DBoxContext::current_perspective->get_vanishing_point(dir1), lr);
+            Box3D::Line line2(*pt_align, *SP3DBoxContext::current_perspective->get_vanishing_point(dir3));
+            corner3 = *line1.intersect(line2);
+        } else {
+            corner3 = Box3D::Line(*pt_align, *SP3DBoxContext::current_perspective->get_vanishing_point(dir1)).closest_to(lr);
+        }
+    }
+
+    Box3D::PerspectiveLine first_line  (corner1, dir1);
+    Box3D::PerspectiveLine second_line (corner3, dir2);
+    NR::Maybe<NR::Point> ur = first_line.intersect(second_line);
+
+    Box3D::PerspectiveLine third_line  (corner1, dir2);
+    Box3D::PerspectiveLine fourth_line (corner3, 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);
+
+    corner2 = *ll;
+    corner4 = *ur;
+
+    this->dir1 = dir1;
+    this->dir2 = dir2;
+
+    // FIXME: More effective with array of corners
+    NR::Point tmp_pt;
+    for (unsigned int i=0; i < shift_count; i++) {
+       tmp_pt = corner4;
+       corner2 = corner1;
+       corner3 = corner2;
+       corner4 = corner3;
+       corner1 = tmp_pt;
+    }
+}
+
+Box3DFace::Box3DFace(Box3DFace const &box3dface)
+{
+       this->corner1 = box3dface.corner1;
+       this->corner2 = box3dface.corner2;
+       this->corner3 = box3dface.corner3;
+       this->corner4 = box3dface.corner4;
+       this->dir1 = box3dface.dir1;
+       this->dir2 = box3dface.dir2;
+}
+
+NR::Point Box3DFace::operator[](unsigned int i)
+{
+       unsigned int index = i % 4;
+    switch (index) {
+       case 0: return corner1; break;
+       case 1: return corner2; break;
+       case 2: return corner3; break;
+       case 3: return corner4; break;
+    }
+    // The following two lines are just to prevent a compiler warning ("control reaches
+    // end of non-void function); they can be removed if desired
+    g_message ("Error: This code line hould not be reached\n");
+    return NR::Point (0, 0);
+}
+
+/**
+ * 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()
+{
+    SPDesktop *desktop = inkscape_active_desktop();
+    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(inkscape_active_event_context()));
+    Inkscape::XML::Node *repr_face = xml_doc->createElement("svg:path");
+    sp_desktop_apply_style_tool (desktop, repr_face, "tools.shapes.3dbox", false);
+    this->path = SP_PATH(SP_OBJECT(parent_box3d)->appendChildRepr(repr_face));
+    Inkscape::GC::release(repr_face);
+}
+
+/**
+ * Write the path's "d" attribute to the SVG representation.
+ */
+void Box3DFace::set_path_repr()
+{
+    SP_OBJECT(this->path)->repr->setAttribute("d", svg_repr_string());
+}
+
+void Box3DFace::set_curve()
+{
+    SPDocument *doc = SP_OBJECT_DOCUMENT(this->parent_box3d);
+    gdouble height = sp_document_height(doc);
+
+    SPCurve *curve = sp_curve_new();
+    sp_curve_moveto(curve, corner1[NR::X], height - corner1[NR::Y]);
+    sp_curve_lineto(curve, corner2[NR::X], height - corner2[NR::Y]);
+    sp_curve_lineto(curve, corner3[NR::X], height - corner3[NR::Y]);
+    sp_curve_lineto(curve, corner4[NR::X], height - corner4[NR::Y]);
+    sp_curve_closepath(curve);
+    sp_shape_set_curve(SP_SHAPE(this->path), curve, true);
+    sp_curve_unref(curve);
+}
+
+gchar * Box3DFace::svg_repr_string()
+{
+    SPDocument *doc = SP_OBJECT_DOCUMENT(this->parent_box3d);
+    gdouble height = sp_document_height(doc);
+
+    GString *pstring = g_string_new("");
+    g_string_sprintf (pstring, "M %f,%f L %f,%f L %f,%f L %f,%f z",
+                               corner1[NR::X], height - corner1[NR::Y],
+                               corner2[NR::X], height - corner2[NR::Y],
+                               corner3[NR::X], height - corner3[NR::Y],
+                               corner4[NR::X], height - corner4[NR::Y]);
+    return pstring->str;
+}
+
+/*
+  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
new file mode 100644 (file)
index 0000000..123d4cb
--- /dev/null
@@ -0,0 +1,59 @@
+#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 *box3d);
+    //Box3DFace(SP3DBox *box3d, NR::Point const ul, NR::Point const lr,
+    //          Box3D::PerspDir const dir1, Box3D::PerspDir const dir2,
+    //          unsigned int shift_count = 0, NR::Maybe<NR::Point> pt_align = NR::Nothing(), bool align_along_PL = false);
+    Box3DFace(Box3DFace const &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::PerspDir const dir1, Box3D::PerspDir const dir2,
+                   unsigned int shift_count = 0, NR::Maybe<NR::Point> pt_align = NR::Nothing(),
+                   bool align_along_PL = false);
+    
+    void hook_path_to_3dbox();
+    void set_path_repr();
+    void set_curve();
+    gchar * svg_repr_string();
+
+private:
+    NR::Point corner1;
+    NR::Point corner2;
+    NR::Point corner3;
+    NR::Point corner4;
+
+    Box3D::PerspDir dir1;
+    Box3D::PerspDir dir2;
+    
+    SPPath *path;
+    SP3DBox *parent_box3d; // the parent box
+};
+
+#endif
diff --git a/src/box3d.cpp b/src/box3d.cpp
new file mode 100644 (file)
index 0000000..127ffa8
--- /dev/null
@@ -0,0 +1,257 @@
+#define __SP_3DBOX_C__
+
+/*
+ * SVG <box3d> implementation
+ *
+ * Authors:
+ *   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
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "box3d.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_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);
+
+static SPGroupClass *parent_class;
+
+GType
+sp_3dbox_get_type(void)
+{
+    static GType type = 0;
+
+    if (!type) {
+        GTypeInfo info = {
+            sizeof(SP3DBoxClass),
+            NULL,   /* base_init */
+            NULL,   /* base_finalize */
+            (GClassInitFunc) sp_3dbox_class_init,
+            NULL,   /* class_finalize */
+            NULL,   /* class_data */
+            sizeof(SP3DBox),
+            16,     /* n_preallocs */
+            (GInstanceInitFunc) sp_3dbox_init,
+            NULL,   /* value_table */
+        };
+        type = g_type_register_static(SP_TYPE_GROUP, "SP3DBox", &info, (GTypeFlags) 0);
+    }
+
+    return type;
+}
+
+static void
+sp_3dbox_class_init(SP3DBoxClass *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;
+
+    //item_class->description = sp_3dbox_description;
+}
+
+static void
+sp_3dbox_init(SP3DBox *box3d)
+{
+    box3d->face1 = Box3DFace (box3d);
+    box3d->face2 = Box3DFace (box3d);
+    box3d->face3 = Box3DFace (box3d);
+    box3d->face4 = Box3DFace (box3d);
+    box3d->face5 = Box3DFace (box3d);
+    box3d->face6 = Box3DFace (box3d);
+}
+
+static void
+sp_3dbox_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, "width");
+}
+
+static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
+{
+    //SP3DBox *box3d = SP_3DBOX(object);
+
+    switch (key) {
+        /***
+        case SP_ATTR_WIDTH:
+            rect->width.readOrUnset(value);
+            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+            break;
+        ***/
+       default:
+            if (((SPObjectClass *) (parent_class))->set) {
+                ((SPObjectClass *) (parent_class))->set(object, key, value);
+            }
+            break;
+    }
+}
+
+
+static void
+sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags)
+{
+    //SP3DBox *box3d = SP_3DBOX(object);
+
+    /* Invoke parent method */
+    if (((SPObjectClass *) (parent_class))->update)
+        ((SPObjectClass *) (parent_class))->update(object, ctx, flags);
+    
+    //sp_3dbox_set_shape (box3d);
+}
+
+
+
+static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+    SP3DBox *box3d = 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;
+    SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
+
+    if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+        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");
+    }
+
+
+    box3d->face1.set_path_repr();
+    if (bc->extruded) {
+        NR::Point corner1, corner2, corner3, corner4;
+        sp_3dbox_compute_specific_corners (bc, corner1, corner2, corner3, corner4);
+        box3d->face2.set_path_repr();
+        box3d->face3.set_path_repr();
+        box3d->face4.set_path_repr();
+        box3d->face5.set_path_repr();
+        box3d->face6.set_path_repr();
+    }
+    if (((SPObjectClass *) (parent_class))->write) {
+        ((SPObjectClass *) (parent_class))->write(object, repr, flags);
+    }
+
+    return repr;
+}
+
+void
+sp_3dbox_position_set (SP3DBoxContext &bc)
+{
+    SP3DBox *box3d = SP_3DBOX(bc.item);
+
+    sp_3dbox_set_shape(box3d);
+
+    // 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);
+
+    /**
+    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);
+    ***/
+}
+
+static void
+// FIXME: Note that this is _not_ the virtual set_shape() method inherited from SPShape,
+//        since SP3DBox is inherited from SPGroup. The following method is "artificially"
+//        called from sp_3dbox_update().
+//sp_3dbox_set_shape(SPShape *shape)
+sp_3dbox_set_shape(SP3DBox *box3d)
+{
+    // 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());
+
+    // FIXME: Why must the coordinates be flipped vertically???
+    //SPDocument *doc = SP_OBJECT_DOCUMENT(box3d);
+    //gdouble height = sp_document_height(doc);
+
+    /* Curve-adaption variant: */
+    NR::Point corner1, corner2, corner3, corner4;
+    corner1 = bc->drag_origin;
+
+    if (bc->extruded) {
+        sp_3dbox_compute_specific_corners (bc, corner1, corner2, corner3, corner4);
+        box3d->face3.set_shape(bc->drag_origin, corner4, Box3D::Y, Box3D::Z);
+        box3d->face3.set_curve();
+        box3d->face4.set_shape(corner2, corner4, Box3D::X, Box3D::Z);
+        box3d->face4.set_curve();
+        box3d->face5.set_shape(bc->drag_origin, corner2, Box3D::X, Box3D::Y);
+        box3d->face5.set_curve();
+        box3d->face6.set_shape(bc->drag_ptB, corner4, Box3D::X, Box3D::Y);
+        box3d->face6.set_curve();
+
+        box3d->face2.set_shape(corner1, bc->drag_ptC, Box3D::Y, Box3D::Z);
+        box3d->face2.set_curve();
+    }
+    box3d->face1.set_shape(bc->drag_origin, bc->drag_ptB, Box3D::X, Box3D::Z);
+    box3d->face1.set_curve();
+}
+
+
+void
+sp_3dbox_compute_specific_corners (SP3DBoxContext *box3d_context, NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4)
+{
+        // TODO: Check for numerical stability and handle "wrong" cases more gracefully.
+        //       (This now mostly applies to the intersection code in the PerspectiveLine class)
+        Box3D::PerspectiveLine pl1 (box3d_context->drag_origin, Box3D::X);
+        Box3D::PerspectiveLine pl2 (box3d_context->drag_ptB, Box3D::Z);
+        corner1 = pl1.meet(pl2);
+
+        Box3D::PerspectiveLine pl3 (corner1, Box3D::Y);
+        Box3D::PerspectiveLine pl4 (box3d_context->drag_ptC, Box3D::Z);
+        corner2 = pl3.meet(pl4);
+
+        Box3D::PerspectiveLine pl5 (corner2, Box3D::X);
+        Box3D::PerspectiveLine pl6 (box3d_context->drag_origin, Box3D::Y);
+        corner3 = pl5.meet(pl6);
+
+        Box3D::PerspectiveLine pl7 (box3d_context->drag_ptC, Box3D::X);
+        Box3D::PerspectiveLine pl8 (corner3, Box3D::Z);
+        corner4 = pl7.meet(pl8);
+}
+
+
+
+/*
+  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.h b/src/box3d.h
new file mode 100644 (file)
index 0000000..c83194f
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __SP_3DBOX_H__
+#define __SP_3DBOX_H__
+
+/*
+ * SVG <box3d> implementation
+ *
+ * Authors:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007      Authors
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "inkscape.h"
+#include "perspective-line.h"
+//#include "display/curve.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"
+
+
+#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))
+
+
+struct SP3DBox : public SPGroup {
+       Box3DFace face1;
+       Box3DFace face2;
+       Box3DFace face3;
+       Box3DFace face4;
+       Box3DFace face5;
+       Box3DFace face6;
+};
+
+struct SP3DBoxClass {
+       SPGroupClass parent_class;
+};
+
+GType sp_3dbox_get_type (void);
+
+void sp_3dbox_position_set (SP3DBoxContext &bc);
+void sp_3dbox_compute_specific_corners (SP3DBoxContext *box3d_context, NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4);
+
+#endif
index c67f22933f760ec298ecf2ea912b3e808bc5514e..a24bb16259d0bcf2b70d7459e0f9436e67448810 100644 (file)
@@ -29,6 +29,7 @@
 #include "spiral-context.h"
 #include "sp-spiral.h"
 #include "sp-offset.h"
+#include "box3d.h"
 
 #include <libnr/nr-matrix-div.h>
 #include <glibmm/i18n.h>
@@ -236,6 +237,8 @@ static void knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
 
     if (SP_IS_RECT(item))
         object_verb = SP_VERB_CONTEXT_RECT;
+    else if (SP_IS_3DBOX(item))
+        object_verb = SP_VERB_CONTEXT_3DBOX;
     else if (SP_IS_GENERICELLIPSE(item))
         object_verb = SP_VERB_CONTEXT_ARC;
     else if (SP_IS_STAR(item))
@@ -289,6 +292,8 @@ static void knot_ungrabbed_handler(SPKnot *knot, unsigned int state, SPKnotHolde
 
         if (SP_IS_RECT(object))
             object_verb = SP_VERB_CONTEXT_RECT;
+        else if (SP_IS_3DBOX(object))
+            object_verb = SP_VERB_CONTEXT_3DBOX;
         else if (SP_IS_GENERICELLIPSE(object))
             object_verb = SP_VERB_CONTEXT_ARC;
         else if (SP_IS_STAR(object))
diff --git a/src/line-geometry.cpp b/src/line-geometry.cpp
new file mode 100644 (file)
index 0000000..d36b1b6
--- /dev/null
@@ -0,0 +1,112 @@
+#define __LINE_GEOMETRY_C__
+
+/*
+ * Routines for dealing with lines (intersections, etc.)
+ *
+ * Authors:
+ *   Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "line-geometry.h"
+#include "inkscape.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 {
+
+/** 
+ * Draw a line beginning at 'start'. If is_endpoint is true, use 'vec' as the endpoint
+ * of the segment. Otherwise interpret it as the direction of the line.
+ * FIXME: Think of a better way to distinguish between the two constructors of lines.
+ */
+Line::Line(NR::Point const &start, NR::Point const &vec, bool is_endpoint) {
+    pt = start;
+    if (is_endpoint)
+        v_dir = vec - start;
+    else
+       v_dir = vec;
+    normal = v_dir.ccw();
+    d0 = NR::dot(normal, pt);
+}
+
+Line::Line(Line const &line) {
+    pt = line.pt;
+    v_dir = line.v_dir;
+    normal = line.normal;
+    d0 = line.d0;
+}
+
+Line &Line::operator=(Line const &line) {
+    pt = line.pt;
+    v_dir = line.v_dir;
+    normal = line.normal;
+    d0 = line.d0;
+
+    return *this;
+}
+
+NR::Maybe<NR::Point> Line::intersect(Line const &line) {
+    NR::Coord denom = NR::dot(v_dir, line.normal);
+    g_return_val_if_fail(fabs(denom) > 1e-6, NR::Nothing());
+
+    NR::Coord lambda = (line.d0 - NR::dot(pt, line.normal)) / denom;
+    return pt + lambda * v_dir;
+}
+
+void Line::set_direction(NR::Point const &dir)
+{
+    v_dir = dir;
+    normal = v_dir.ccw();
+    d0 = NR::dot(normal, pt);
+}
+
+NR::Point Line::closest_to(NR::Point const &pt)
+{
+       /* return the intersection of this line with a perpendicular line passing through pt */ 
+    NR::Maybe<NR::Point> result = this->intersect(Line(pt, (this->v_dir).ccw(), false));
+    g_return_val_if_fail (result, NR::Point (0.0, 0.0));
+    return *result;
+}
+
+void create_canvas_point(NR::Point const &pos, double size, guint32 rgba)
+{
+    SPDesktop *desktop = inkscape_active_desktop();
+    SPCanvasItem * canvas_pt = sp_canvas_item_new(sp_desktop_controls(desktop), SP_TYPE_CTRL,
+                          "size", size,
+                          "filled", 1,
+                          "fill_color", rgba,
+                          "stroked", 1,
+                          "stroke_color", 0x000000ff,
+                          NULL);
+    SP_CTRL(canvas_pt)->moveto(pos);
+}
+
+void create_canvas_line(NR::Point const &p1, NR::Point const &p2, guint32 rgba)
+{
+    SPDesktop *desktop = inkscape_active_desktop();
+    SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(desktop),
+                                                            SP_TYPE_CTRLLINE, NULL);
+    sp_ctrlline_set_coords(SP_CTRLLINE(line), p1, p2);
+    sp_ctrlline_set_rgba32 (SP_CTRLLINE(line), rgba);
+    sp_canvas_item_show (line);
+}
+
+} // 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/line-geometry.h b/src/line-geometry.h
new file mode 100644 (file)
index 0000000..53f39db
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Routines for dealing with lines (intersections, etc.)
+ *
+ * Authors:
+ *   Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_LINE_GEOMETRY_H
+#define SEEN_LINE_GEOMETRY_H
+
+#include "libnr/nr-point.h"
+#include "libnr/nr-point-fns.h"
+#include "libnr/nr-maybe.h"
+#include "glib.h"
+#include "display/sp-ctrlline.h"
+#include "vanishing-point.h"
+
+#include "document.h"
+#include "ui/view/view.h"
+
+namespace Box3D {
+
+class Line {
+public:
+    Line(NR::Point const &start, NR::Point const &vec, bool is_endpoint = true);
+    Line(Line const &line);
+    Line &operator=(Line const &line);
+    virtual NR::Maybe<NR::Point> intersect(Line const &line);
+    void set_direction(NR::Point const &dir); // FIXME: Can we avoid this explicit assignment?
+    
+    NR::Point closest_to(NR::Point const &pt); // returns the point on the line closest to pt 
+
+    friend inline std::ostream &operator<< (std::ostream &out_file, const Line &in_line);
+       
+private:
+    NR::Point pt;
+    NR::Point v_dir;
+    NR::Point normal;
+    NR::Coord d0;
+};
+
+/*** For testing purposes: Draw a knot/node of specified size and color at the given position ***/
+void create_canvas_point(NR::Point const &pos, double size = 4.0, guint32 rgba = 0xff00007f);
+
+/*** For testing purposes: Draw a line between the specified points ***/
+void create_canvas_line(NR::Point const &p1, NR::Point const &p2, guint32 rgba = 0xff00007f);
+
+
+/** A function to print out the Line.  It just prints out the coordinates of start end end point
+    on the given output stream */
+inline std::ostream &operator<< (std::ostream &out_file, const Line &in_line) {
+    out_file << "Start: " << in_line.pt << "  Direction: " << in_line.v_dir;
+    return out_file;
+}
+
+} // namespace Box3D
+
+
+#endif /* !SEEN_LINE_GEOMETRY_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/perspective-line.cpp b/src/perspective-line.cpp
new file mode 100644 (file)
index 0000000..b2e4d23
--- /dev/null
@@ -0,0 +1,63 @@
+#define __PERSPECTIVE_LINE_C__
+
+/*
+ * Perspective line for 3D perspectives
+ *
+ * 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"
+
+namespace Box3D {
+
+PerspectiveLine::PerspectiveLine (NR::Point const &pt, PerspDir const axis, Perspective3D *perspective) :
+        Line (pt, *(perspective->get_vanishing_point(axis)), true)
+{
+    g_assert (perspective != NULL);
+
+    if (perspective->get_vanishing_point(axis)->state == VP_INFINITE) {
+        this->set_direction(perspective->get_vanishing_point(axis)->v_dir);
+    }
+    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()
+}
+
+} // 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/perspective-line.h b/src/perspective-line.h
new file mode 100644 (file)
index 0000000..a1c61c5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Perspective line for 3D perspectives
+ *
+ * Authors:
+ *   Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_PERSPECTIVE_LINE_H
+#define SEEN_PERSPECTIVE_LINE_H
+
+#include "vanishing-point.h"
+#include "box3d-context.h" 
+#include <glib.h>
+
+namespace Box3D {
+
+class PerspectiveLine : public Box3D::Line {
+public:
+    /** 
+     * Create a perspective line starting at 'pt' and pointing in the direction of the
+     * vanishing point corresponding to 'axis'. If the VP has style VP_FINITE then the
+     * PL runs through it; otherwise it has the direction specified by the v_dir vector
+     * of the VP.
+     */
+    PerspectiveLine (NR::Point const &pt, PerspDir const axis,
+                     Perspective3D *perspective = SP3DBoxContext::current_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);
+       
+private:
+    PerspDir vp_dir; // direction of the associated VP
+    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_PERSPECTIVE_LINE_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/perspective3d.cpp b/src/perspective3d.cpp
new file mode 100644 (file)
index 0000000..678b695
--- /dev/null
@@ -0,0 +1,98 @@
+#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-context.h"
+#include "perspective-line.h"
+#include <iostream>
+
+namespace Box3D {
+
+/**
+ * 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::PerspDir dir1, NR::Point pt2, Box3D::PerspDir dir2)
+{
+    VanishingPoint const *vp1 = SP3DBoxContext::current_perspective->get_vanishing_point(dir1);
+    VanishingPoint const *vp2 = SP3DBoxContext::current_perspective->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, PerspDir dir, NR::Point ext_pt)
+{
+    return PerspectiveLine(line_pt, dir).closest_to(ext_pt);
+}  
+
+Perspective3D::Perspective3D (VanishingPoint const &pt_x, VanishingPoint const &pt_y, VanishingPoint const &pt_z)
+        : vp_x (pt_x),
+          vp_y (pt_y),
+          vp_z (pt_z)
+{
+       // Draw the three vanishing points
+    vp_x.draw(X);
+    vp_y.draw(Y);
+    vp_z.draw(Z);
+}
+
+VanishingPoint *Perspective3D::get_vanishing_point (PerspDir const dir)
+{
+    // FIXME: Also handle value 'NONE' in switch
+    switch (dir) {
+        case X:
+            return &vp_x;
+            break;
+        case Y:
+            return &vp_y;
+            break;
+        case Z:
+            return &vp_z;
+            break;
+    }
+}
+
+void Perspective3D::set_vanishing_point (PerspDir const dir, VanishingPoint const &pt)
+{
+    // FIXME: Also handle value 'NONE' in switch
+    switch (dir) {
+        case X:
+            vp_x = pt;
+            break;
+        case Y:
+            vp_y = pt;
+            break;
+        case Z:
+            vp_z = pt;
+            break;
+    }
+}
+
+} // 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
new file mode 100644 (file)
index 0000000..9f033f8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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"
+
+namespace Box3D {
+
+NR::Point perspective_intersection (NR::Point pt1, Box3D::PerspDir dir1, NR::Point pt2, Box3D::PerspDir dir2);
+NR::Point perspective_line_snap (NR::Point pt, PerspDir dir, NR::Point ext_pt);
+
+class PerspectiveLine;
+
+class Perspective3D {
+public:
+    Perspective3D(VanishingPoint const &pt_x, VanishingPoint const &pt_y, VanishingPoint const &pt_z);
+
+    VanishingPoint *get_vanishing_point (PerspDir const dir);
+    void set_vanishing_point (PerspDir const dir, VanishingPoint const &pt);
+
+private:
+    VanishingPoint vp_x;
+    VanishingPoint vp_y;
+    VanishingPoint vp_z;
+};
+
+
+} // 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 :
index 28b1fe01849f5988591500b58944aeb097bf8cb0..ead8e7a623f6b6fd9d0a28f1e5a698b94a3234d5 100644 (file)
@@ -51,6 +51,7 @@ static char const preferences_skeleton[] =
 "         style=\"fill:none;stroke:black;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;\">\n"
 "    <group id=\"shapes\" style=\"fill-rule:evenodd;\" selcue=\"1\" gradientdrag=\"1\">\n"
 "      <eventcontext id=\"rect\" style=\"fill:blue;\" usecurrent=\"1\"/>\n"
+"      <eventcontext id=\"3dbox\" style=\"fill:blue;\" usecurrent=\"1\"/>\n"
 "      <eventcontext id=\"arc\" style=\"fill:red;\" end=\"0\" start=\"0\" usecurrent=\"1\"/>\n"
 "      <eventcontext id=\"star\" magnitude=\"5\" style=\"fill:yellow;\" usecurrent=\"1\"/>\n"
 "      <eventcontext id=\"spiral\" style=\"fill:none;\" usecurrent=\"0\"/>\n"
index 43c946670397d515e2069f141305ce68191f3024..5a1554cbd1d59a14233e33068dac766f2f99856f 100644 (file)
@@ -21,6 +21,7 @@
 #include "sp-path.h"
 #include "sp-radial-gradient-fns.h"
 #include "sp-rect.h"
+#include "box3d.h"
 #include "sp-ellipse.h"
 #include "sp-star.h"
 #include "sp-stop-fns.h"
@@ -171,7 +172,9 @@ populate_dtables()
         { "arc", SP_TYPE_ARC },
         { "inkscape:offset", SP_TYPE_OFFSET },
         { "spiral", SP_TYPE_SPIRAL },
-        { "star", SP_TYPE_STAR }
+        { "star", SP_TYPE_STAR },
+        { "inkscape:3dbox", SP_TYPE_3DBOX }//,
+        //{ "inkscape:3dboxface", SP_TYPE_3DBOX_FACE }
     };
 
     NameTypeEntry const *const t2entries[] = {
index 3393263599c17c0f1348a647c723e5171b0b40a7..bc9655a83b0d201691c3a8cffabf065d7ba32596 100644 (file)
@@ -27,6 +27,8 @@
 #include "sp-path.h"
 #include "rect-context.h"
 #include "sp-rect.h"
+#include "box3d-context.h"
+#include "box3d.h"
 #include "arc-context.h"
 #include "sp-ellipse.h"
 #include "star-context.h"
@@ -54,6 +56,7 @@ static char const *const tool_names[] = {
     "tools.select",
     "tools.nodes",
     "tools.shapes.rect",
+    "tools.shapes.3dbox",
     "tools.shapes.arc",
     "tools.shapes.star",
     "tools.shapes.spiral",
@@ -74,6 +77,7 @@ static char const *const tool_ids[] = {
     "select",
     "nodes",
     "rect",
+    "3dbox",
     "arc",
     "star",
     "spiral",
@@ -143,6 +147,12 @@ tools_switch(SPDesktop *dt, int num)
             inkscape_eventcontext_set(sp_desktop_event_context(dt));
             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->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."));
+            break;
         case TOOLS_SHAPES_ARC:
             dt->set_event_context(SP_TYPE_ARC_CONTEXT, tool_names[num]);
             dt->activate_guides(false);
index 252add75855c932a9b8a3c0971fc0153241451ed..aac48d8343bdf3e55c71aeeac312ed50d67d0e55 100644 (file)
@@ -19,6 +19,7 @@ enum {
   TOOLS_SELECT,
   TOOLS_NODES,
   TOOLS_SHAPES_RECT,
+  TOOLS_SHAPES_3DBOX,
   TOOLS_SHAPES_ARC,
   TOOLS_SHAPES_STAR,
   TOOLS_SHAPES_SPIRAL,
index 5694a3ea3ddd1b3be777a7db061818e893f09f6a..f3429ce7b59de9964f5d78242115fd92858bc52d 100644 (file)
@@ -362,6 +362,9 @@ void InkscapePreferences::initPageTools()
     //Rectangle
     this->AddPage(_page_rectangle, _("Rectangle"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_RECT);
     this->AddNewObjectsStyle(_page_rectangle, "tools.shapes.rect");
+    //3D box
+    this->AddPage(_page_3dbox, _("3D Box"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_3DBOX);
+    this->AddNewObjectsStyle(_page_3dbox, "tools.shapes.3dbox");
     //ellipse
     this->AddPage(_page_ellipse, _("Ellipse"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_ELLIPSE);
     this->AddNewObjectsStyle(_page_ellipse, "tools.shapes.arc");
index f2a61d1ba8081e4415cb91caf95c28bd340b5676..91b7a83ec9679c7ffdd7fe6fcabc7272309aef17 100644 (file)
@@ -42,6 +42,7 @@ enum {
     PREFS_PAGE_TOOLS_ZOOM,
     PREFS_PAGE_TOOLS_SHAPES,
     PREFS_PAGE_TOOLS_SHAPES_RECT,
+    PREFS_PAGE_TOOLS_SHAPES_3DBOX,
     PREFS_PAGE_TOOLS_SHAPES_ELLIPSE,
     PREFS_PAGE_TOOLS_SHAPES_STAR,
     PREFS_PAGE_TOOLS_SHAPES_SPIRAL,
@@ -100,7 +101,7 @@ protected:
         _page_clones, _page_mask, _page_transforms, _page_filters, _page_select, _page_misc;
     DialogPage _page_selector, _page_node, _page_zoom, _page_shapes, _page_pencil, _page_pen,
                _page_calligraphy, _page_text, _page_gradient, _page_connector, _page_dropper;
-    DialogPage _page_rectangle, _page_ellipse, _page_star, _page_spiral, _page_paintbucket;
+    DialogPage _page_rectangle, _page_3dbox, _page_ellipse, _page_star, _page_spiral, _page_paintbucket;
 
     PrefSpinButton _mouse_sens, _mouse_thres;
     PrefCheckButton _mouse_use_ext_input;
diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp
new file mode 100644 (file)
index 0000000..7933fbe
--- /dev/null
@@ -0,0 +1,101 @@
+#define __VANISHING_POINT_C__
+
+/*
+ * Vanishing point for 3D perspectives
+ *
+ * Authors:
+ *   Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "vanishing-point.h"
+#include <iostream>
+
+namespace Box3D {
+
+// 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;
+}
+
+
+bool VanishingPoint::is_finite()
+{
+    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(PerspDir 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;
+    }
+}
+
+} // 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/vanishing-point.h b/src/vanishing-point.h
new file mode 100644 (file)
index 0000000..4770c74
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Vanishing point for 3D perspectives
+ *
+ * Authors:
+ *   Maximilian Albert <Anhalter42@gmx.de>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_VANISHING_POINT_H
+#define SEEN_VANISHING_POINT_H
+
+#include "libnr/nr-point.h"
+#include "line-geometry.h"
+
+namespace Box3D {
+
+enum VPState {
+    VP_FINITE = 0, // perspective lines meet in the VP
+    VP_INFINITE    // perspective lines are parallel
+};
+
+enum PerspDir {
+    X,
+    Y,
+    Z,
+    NONE
+};
+
+// FIXME: Store the PerspDir of the VP inside the class
+class VanishingPoint : public NR::Point {
+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);
+
+    bool is_finite();
+    VPState toggle_parallel();
+    void draw(PerspDir 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
+
+private:
+};
+
+
+} // 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 */
+
+/*
+  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 :
index 01172f1021e8587ccedc89b617a4e26136731b6b..04a49f37c4bd3a58d9ad03275d5e548d96a5f974 100644 (file)
@@ -1388,6 +1388,9 @@ ContextVerb::perform(SPAction *action, void *data, void *pdata)
         case SP_VERB_CONTEXT_RECT:
             tools_switch_current(TOOLS_SHAPES_RECT);
             break;
+        case SP_VERB_CONTEXT_3DBOX:
+            tools_switch_current(TOOLS_SHAPES_3DBOX);
+            break;
         case SP_VERB_CONTEXT_ARC:
             tools_switch_current(TOOLS_SHAPES_ARC);
             break;
@@ -1437,6 +1440,10 @@ ContextVerb::perform(SPAction *action, void *data, void *pdata)
             prefs_set_int_attribute("dialogs.preferences", "page", PREFS_PAGE_TOOLS_SHAPES_RECT);
             dt->_dlg_mgr->showDialog("InkscapePreferences");
             break;
+        case SP_VERB_CONTEXT_3DBOX_PREFS:
+            prefs_set_int_attribute("dialogs.preferences", "page", PREFS_PAGE_TOOLS_SHAPES_3DBOX);
+            dt->_dlg_mgr->showDialog("InkscapePreferences");
+            break;
         case SP_VERB_CONTEXT_ARC_PREFS:
             prefs_set_int_attribute("dialogs.preferences", "page", PREFS_PAGE_TOOLS_SHAPES_ELLIPSE);
             dt->_dlg_mgr->showDialog("InkscapePreferences");
@@ -2344,6 +2351,8 @@ Verb *Verb::_base_verbs[] = {
                     N_("Edit path nodes or control handles"), "draw_node"),
     new ContextVerb(SP_VERB_CONTEXT_RECT, "ToolRect", N_("Rectangle"),
                     N_("Create rectangles and squares"), "draw_rect"),
+    new ContextVerb(SP_VERB_CONTEXT_3DBOX, "Tool3DBox", N_("3D Box"),
+                    N_("Create 3D boxes"), "draw_3dbox"),
     new ContextVerb(SP_VERB_CONTEXT_ARC, "ToolArc", N_("Ellipse"),
                     N_("Create circles, ellipses, and arcs"), "draw_arc"),
     new ContextVerb(SP_VERB_CONTEXT_STAR, "ToolStar", N_("Star"),
@@ -2376,6 +2385,8 @@ Verb *Verb::_base_verbs[] = {
                     N_("Open Preferences for the Node tool"), NULL),
     new ContextVerb(SP_VERB_CONTEXT_RECT_PREFS, "RectPrefs", N_("Rectangle Preferences"),
                     N_("Open Preferences for the Rectangle tool"), NULL),
+    new ContextVerb(SP_VERB_CONTEXT_3DBOX_PREFS, "3DBoxPrefs", N_("3D Box Preferences"),
+                    N_("Open Preferences for the 3D Box tool"), NULL),
     new ContextVerb(SP_VERB_CONTEXT_ARC_PREFS, "ArcPrefs", N_("Ellipse Preferences"),
                     N_("Open Preferences for the Ellipse tool"), NULL),
     new ContextVerb(SP_VERB_CONTEXT_STAR_PREFS, "StarPrefs", N_("Star Preferences"),
index 3ebc129d16d41c7ebb88dcc2330c6f1d9c23d047..f8e25f94df78782cda5398f69500ba791778405c 100644 (file)
@@ -136,6 +136,7 @@ enum {
     SP_VERB_CONTEXT_SELECT,
     SP_VERB_CONTEXT_NODE,
     SP_VERB_CONTEXT_RECT,
+    SP_VERB_CONTEXT_3DBOX,
     SP_VERB_CONTEXT_ARC,
     SP_VERB_CONTEXT_STAR,
     SP_VERB_CONTEXT_SPIRAL,
@@ -152,6 +153,7 @@ enum {
     SP_VERB_CONTEXT_SELECT_PREFS,
     SP_VERB_CONTEXT_NODE_PREFS,
     SP_VERB_CONTEXT_RECT_PREFS,
+    SP_VERB_CONTEXT_3DBOX_PREFS,
     SP_VERB_CONTEXT_ARC_PREFS,
     SP_VERB_CONTEXT_STAR_PREFS,
     SP_VERB_CONTEXT_SPIRAL_PREFS,
index 9f8e675c2ab5a24502bb210cd5c0eb4aa47b466b..4c497134f5c4879323dbc3317e478d9a5aa4dd79 100644 (file)
@@ -64,6 +64,7 @@
 #include "node-context.h"
 #include "shape-editor.h"
 #include "sp-rect.h"
+#include "box3d-context.h"
 #include "sp-star.h"
 #include "sp-spiral.h"
 #include "sp-ellipse.h"
@@ -102,6 +103,7 @@ static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainA
 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       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);
@@ -124,6 +126,7 @@ static struct {
     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_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 },
     { "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 },
@@ -158,6 +161,8 @@ static struct {
       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",
+      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")},
     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
@@ -272,6 +277,12 @@ static gchar const * ui_descr =
         "    <toolitem action='RectResetAction' />"
         "  </toolbar>"
 
+        "  <toolbar name='3DBoxToolbar'>"
+        "    <toolitem action='3DBoxVPXAction' />"
+        "    <toolitem action='3DBoxVPYAction' />"
+        "    <toolitem action='3DBoxVPZAction' />"
+        "  </toolbar>"
+
         "  <toolbar name='SpiralToolbar'>"
         "    <toolitem action='SpiralStateAction' />"
         "    <toolitem action='SpiralRevolutionAction' />"
@@ -2089,6 +2100,89 @@ static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions
     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
 }
 
+//########################
+//##       3D Box       ##
+//########################
+
+static void sp_3dbox_toggle_vp_changed( GtkToggleAction *act, gpointer data )
+{
+    guint dir = (guint) data;
+    Box3D::PerspDir axis;// = (Box3D::PerspDir) data;
+
+    GString *pstring;
+    switch (dir) {
+        case 0:
+            pstring = g_string_new("togglevpx");
+            axis = Box3D::X;
+            break;
+        case 1:
+            pstring = g_string_new("togglevpy");
+            axis = Box3D::Y;
+            break;
+        case 2:
+            pstring = g_string_new("togglevpz");
+            axis = Box3D::Z;
+            break;
+    }
+    
+    if (SP3DBoxContext::current_perspective) {
+        Box3D::VanishingPoint *vp = SP3DBoxContext::current_perspective->get_vanishing_point(axis);
+        vp->toggle_parallel();
+        vp->draw(axis);
+        prefs_set_int_attribute( "tools.shapes.3dbox", pstring->str, vp->is_finite() ? 0 : 1);
+    }
+    
+}
+
+static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
+{
+    bool toggled = false;
+    /* toggle VP in X direction */
+    {
+    InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXAction",
+                                                  _("Toggle VP in X direction"),
+                                                  _("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 ) );
+    g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), (gpointer) 0);
+    if (SP3DBoxContext::current_perspective) {
+        toggled = SP3DBoxContext::current_perspective->get_vanishing_point(Box3D::X)->is_finite();
+    }
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
+    }
+
+    /* toggle VP in Y direction */
+    {
+    InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYAction",
+                                                  _("Toggle VP in Y direction"),
+                                                  _("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 ) );
+    g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), (gpointer) 1);
+    if (SP3DBoxContext::current_perspective) {
+        toggled = SP3DBoxContext::current_perspective->get_vanishing_point(Box3D::Y)->is_finite();
+    }
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
+    }
+
+    /* toggle VP in Z direction */
+    {
+    InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZAction",
+                                                  _("Toggle VP in Z direction"),
+                                                  _("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 ) );
+    g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), (gpointer) 2);
+    if (SP3DBoxContext::current_perspective) {
+        toggled = SP3DBoxContext::current_perspective->get_vanishing_point(Box3D::Z)->is_finite();
+    }
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
+    }
+}
+
 //########################
 //##       Spiral       ##
 //########################