Code

Write distinguished corners of 3D boxes to the svg representation from which the...
authorcilix42 <cilix42@users.sourceforge.net>
Sat, 14 Jul 2007 14:51:56 +0000 (14:51 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Sat, 14 Jul 2007 14:51:56 +0000 (14:51 +0000)
This is used to fix several bugs related to the editing of boxes resulting from cloning, undo operations or newly opened documents (we had stray pointers to SPPaths before that made Inkscape crash).

src/attributes.cpp
src/attributes.h
src/box3d-context.cpp
src/box3d-face.cpp
src/box3d-face.h
src/box3d.cpp
src/box3d.h
src/selection-describer.cpp

index a8cd2e83d0518185cf0fb9fb21e5e559afa649b7..968e3863373a74a5f2f7772cff593f76aabc3b10 100644 (file)
@@ -112,6 +112,11 @@ static SPStyleProp const props[] = {
     /* SPRect */
     {SP_ATTR_RX, "rx"},
     {SP_ATTR_RY, "ry"},
+    /* SP3DBox */
+    {SP_ATTR_INKSCAPE_3DBOX, "inkscape:3dbox"},
+    {SP_ATTR_INKSCAPE_3DBOX_CORNER_A, "inkscape:box3dcornerA"}, // "upper left front" corner
+    {SP_ATTR_INKSCAPE_3DBOX_CORNER_B, "inkscape:box3dcornerB"}, // "lower right front" corner
+    {SP_ATTR_INKSCAPE_3DBOX_CORNER_C, "inkscape:box3dcornerC"}, // "lower right rear" corner
     /* SPEllipse */
     {SP_ATTR_R, "r"},
     {SP_ATTR_CX, "cx"},
index fa492d978d72e5725d50406705fc1d95bb651d5c..9e658f562d4b2bfd6a0a0682898d659601a0a13b 100644 (file)
@@ -112,6 +112,11 @@ enum SPAttributeEnum {
     /* SPRect */
     SP_ATTR_RX,
     SP_ATTR_RY,
+    /* SP3DBox */
+    SP_ATTR_INKSCAPE_3DBOX,
+    SP_ATTR_INKSCAPE_3DBOX_CORNER_A, // "upper left front" corner
+    SP_ATTR_INKSCAPE_3DBOX_CORNER_B, // "lower right front" corner
+    SP_ATTR_INKSCAPE_3DBOX_CORNER_C, // "lower right rear" corner
     /* SPEllipse */
     SP_ATTR_R,
     SP_ATTR_CX,
index 6eaedfb7856d918ef368069c1a66a8a6905d81c0..0bcf9ffa46ccbbaaca3558fa189985b057778674 100644 (file)
@@ -203,6 +203,14 @@ void sp_3dbox_context_selection_changed(Inkscape::Selection *selection, gpointer
             ec->shape_repr = shape_repr;
             Inkscape::GC::anchor(shape_repr);
             sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec);
+
+            // FIXME: The following really belongs in sp_3dbox_build. But when undoing & redoing the
+            //        creation of a 3D box, we have no means of accessing the recreated paths, which
+            //        seem to be built after the box itself. Thus we need to check for untracked paths
+            //        here and hook them to the box if the latter was created by a redo operation.
+            if (SP_IS_3DBOX(item)) {
+                sp_3dbox_link_to_existing_paths (SP_3DBOX(item), shape_repr);
+            }
         }
     }
 }
@@ -577,7 +585,7 @@ static void sp_3dbox_finish(SP3DBoxContext *rc)
 
         sp_desktop_selection(desktop)->set(rc->item);
         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
-                         _("Create 3d box"));
+                         _("Create 3D box"));
 
         rc->item = NULL;
     }
index 02c84e22c6b76e62f36fc5d0ef4264b24066004f..2cb510277da99f2a730d1fad357d4748f551192b 100644 (file)
@@ -162,19 +162,27 @@ NR::Point Box3DFace::operator[](unsigned int i)
  * 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()
+void Box3DFace::hook_path_to_3dbox(SPPath * existing_path)
 {
-    if (this->path) return; // This test can probably be removed.
+    if (this->path) {
+        //g_print ("Path already exists. Returning ...\n");
+        return;
+    }
+
+    if (existing_path != NULL) {
+        // no need to create a new path
+        this->path = existing_path;
+        return;
+    }
 
     SPDesktop *desktop = inkscape_active_desktop();
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(inkscape_active_event_context()));
+    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(SP_OBJECT(parent_box3d)));
     GString *pstring = g_string_new("");
     g_string_printf (pstring, "tools.shapes.3dbox.%s", axes_string());
 
     Inkscape::XML::Node *repr_face = xml_doc->createElement("svg:path");
     sp_desktop_apply_style_tool (desktop, repr_face, pstring->str, false);
     this->path = SP_PATH(SP_OBJECT(parent_box3d)->appendChildRepr(repr_face));
-
     Inkscape::GC::release(repr_face);
 }
 
index 04e012b41d7f0306f958261cb3eee872f4cc63fa..61c13432d7bad100f1a7dfc498acdcbfe101e7b8 100644 (file)
@@ -42,7 +42,7 @@ public:
     void set_corners (NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D);
     void set_face (NR::Point const A, NR::Point const C, Box3D::Axis const dir1, Box3D::Axis const dir2);
     
-    void hook_path_to_3dbox();
+    void hook_path_to_3dbox(SPPath * existing_path = NULL);
     void set_path_repr();
     void set_curve();
     gchar * axes_string();
index 1e4cad8b3ccb09e0c29e2abff5b52baa7f1c5435..101cce5650bc8c718b5e88cc64248c4e45b3dc20 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <glibmm/i18n.h>
+#include "attributes.h"
 #include "box3d.h"
 
 static void sp_3dbox_class_init(SP3DBoxClass *klass);
@@ -32,6 +33,9 @@ static gchar *sp_3dbox_description(SPItem *item);
 //static void sp_3dbox_set_shape(SPShape *shape);
 static void sp_3dbox_set_shape(SP3DBox *box3d);
 
+static gchar * sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id);
+static std::pair<gdouble, gdouble> sp_3dbox_get_coord_pair_from_string (const gchar *);
+
 static SPGroupClass *parent_class;
 
 GType
@@ -78,8 +82,25 @@ sp_3dbox_class_init(SP3DBoxClass *klass)
 static void
 sp_3dbox_init(SP3DBox *box)
 {
-    // We create all faces in the beginning (but only the non-degenerate ones
-    // should be written to the svg representation later in sp_3dbox_write).
+    for (int i = 0; i < 8; ++i) box->corners[i] = NR::Point(0,0);
+    for (int i = 0; i < 6; ++i) box->faces[i] = NULL;
+}
+
+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);
+    }
+
+    SP3DBox *box = SP_3DBOX (object);
+
+    sp_object_read_attr(object, "inkscape:box3dcornerA");
+    sp_object_read_attr(object, "inkscape:box3dcornerB");
+    sp_object_read_attr(object, "inkscape:box3dcornerC");
+
+    // TODO: We create all faces in the beginning, but only the non-degenerate ones
+    //       should be written to the svg representation later in sp_3dbox_write.
     Box3D::Axis cur_plane, axis, dir1, dir2;
     Box3D::FrontOrRear cur_pos;
     for (int i = 0; i < 3; ++i) {
@@ -98,16 +119,10 @@ sp_3dbox_init(SP3DBox *box)
                                     cur_plane, cur_pos);
         }
     }
-}
 
-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");
+    // Check whether the paths of the faces of the box need to be linked to existing paths in the
+    // document (e.g., after a 'redo' operation or after opening a file) and do so if necessary.
+    sp_3dbox_link_to_existing_paths (box, repr);
 }
 
 /*
@@ -127,17 +142,30 @@ sp_3dbox_release (SPObject *object)
 }
 */
 
-static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
+static void
+sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value)
 {
-    //SP3DBox *box3d = SP_3DBOX(object);
+    if (value == NULL) return;
+    SP3DBox *box = SP_3DBOX(object);
 
+    std::pair<gdouble, gdouble> coord_pair = sp_3dbox_get_coord_pair_from_string (value);
+    box->corners[corner_id] = NR::Point (coord_pair.first, coord_pair.second);
+    sp_3dbox_recompute_corners (box, box->corners[2], box->corners[1], box->corners[5]);
+    object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
+{
     switch (key) {
-        /***
-        case SP_ATTR_WIDTH:
-            rect->width.readOrUnset(value);
-            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+        case SP_ATTR_INKSCAPE_3DBOX_CORNER_A:
+            sp_3dbox_update_corner_with_value_from_svg (object, 2, value);
+            break;
+        case SP_ATTR_INKSCAPE_3DBOX_CORNER_B:
+            sp_3dbox_update_corner_with_value_from_svg (object, 1, value);
+            break;
+        case SP_ATTR_INKSCAPE_3DBOX_CORNER_C:
+            sp_3dbox_update_corner_with_value_from_svg (object, 5, value);
             break;
-        ***/
        default:
             if (((SPObjectClass *) (parent_class))->set) {
                 ((SPObjectClass *) (parent_class))->set(object, key, value);
@@ -146,24 +174,19 @@ static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
     }
 }
 
-
 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);
+    SP3DBox *box = SP_3DBOX(object);
     // FIXME: How to handle other contexts???
     // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
     if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
@@ -173,10 +196,28 @@ static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node
         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
         repr = xml_doc->createElement("svg:g");
         repr->setAttribute("sodipodi:type", "inkscape:3dbox");
+        /* Hook paths to the faces of the box */
+        for (int i = 0; i < 6; ++i) {
+            box->faces[i]->hook_path_to_3dbox();
+        }
     }
 
     for (int i = 0; i < 6; ++i) {
-        box3d->faces[i]->set_path_repr();
+        box->faces[i]->set_path_repr();
+    }
+
+    if (flags & SP_OBJECT_WRITE_EXT) {
+        gchar *coords;
+        coords = sp_3dbox_get_corner_coords_string (box, 2);
+        repr->setAttribute("inkscape:box3dcornerA", coords);
+
+        coords = sp_3dbox_get_corner_coords_string (box, 1);
+        repr->setAttribute("inkscape:box3dcornerB", coords);
+
+        coords = sp_3dbox_get_corner_coords_string (box, 5);
+        repr->setAttribute("inkscape:box3dcornerC", coords);
+
+        g_free ((void *) coords);
     }
 
     if (((SPObjectClass *) (parent_class))->write) {
@@ -258,6 +299,38 @@ sp_3dbox_update_curves (SP3DBox *box) {
     }
 }
 
+void
+sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr) {
+    // TODO: We should probably destroy the existing paths and recreate them because we don't know
+    //       precisely which path corresponds to which face. Does this make a difference?
+    //       In sp_3dbox_write we write the correct paths anyway, don't we? But we could get into
+    //       trouble at a later stage when we only write single faces for degenerate boxes.
+
+    SPDocument *document = SP_OBJECT_DOCUMENT(box);
+    guint face_id = 0;
+
+    for (Inkscape::XML::Node *i = sp_repr_children(repr); i != NULL; i = sp_repr_next(i)) {
+        if (face_id > 5) {
+            g_warning ("SVG representation of 3D boxes must contain 6 paths or less.\n");
+            break;
+        }
+
+        SPObject *face_object = document->getObjectByRepr((Inkscape::XML::Node *) i);
+        if (!SP_IS_PATH(face_object)) {
+            g_warning ("SVG representation of 3D boxes should only contain paths.\n");
+            continue;
+        }
+        box->faces[face_id]->hook_path_to_3dbox(SP_PATH(face_object));
+        ++face_id;
+    }
+    if (face_id < 6) {
+        //g_warning ("SVG representation of 3D boxes should contain exactly 6 paths (degenerate boxes are not yet supported).\n");
+        // TODO: Check whether it is safe to add the remaining paths to the box and do so in case it is.
+        //       (But we also land here for newly created boxes where we shouldn't add any paths because
+        //       This is done in sp_3dbox_write later on.
+    }
+}
+
 void
 sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes)
 {
@@ -346,6 +419,32 @@ sp_3dbox_get_midpoint_between_corners (SP3DBox *box, guint id_corner1, guint id_
     }
 }
 
+static gchar *
+sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id)
+{
+    id = id % 8;
+    return g_strdup_printf ("%f,%f", box->corners[id][NR::X], box->corners[id][NR::Y]);
+}
+
+static std::pair<gdouble, gdouble>
+sp_3dbox_get_coord_pair_from_string (const gchar *coords)
+{
+    gchar **coordlist = g_strsplit( coords, ",", 0);
+    // We might as well rely on g_strtod to convert the NULL pointer to 0.0,
+    // but we include the following test anyway
+    if (coordlist[0] == NULL || coordlist[1] == NULL) {
+        g_strfreev (coordlist);
+        g_warning ("Coordinate conversion failed.\n");
+        return std::make_pair(0.0, 0.0);
+    }
+
+    gdouble coord1 = g_strtod(coordlist[0], NULL);
+    gdouble coord2 = g_strtod(coordlist[1], NULL);
+    g_strfreev (coordlist);
+
+    return std::make_pair(coord1, coord2);
+}
+
 /*
   Local Variables:
   mode:c++
index 8fe90dc3bdd5389319bf428c6a94d2cd453f928e..ee0f30c8169c7c5375595337bcf6926758285d13 100644 (file)
@@ -47,6 +47,7 @@ GType sp_3dbox_get_type (void);
 void sp_3dbox_position_set (SP3DBoxContext &bc);
 void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const pt1, NR::Point const pt2, NR::Point const pt3);
 void sp_3dbox_update_curves (SP3DBox *box);
+void sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr);
 void sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes = Box3D::XY);
 void sp_3dbox_move_corner_in_Z_direction (SP3DBox *box, guint id, NR::Point pt, bool constrained = true);
 NR::Maybe<NR::Point> sp_3dbox_get_center (SP3DBox *box);
index 69e10904ff2a84a3a1a040b56b25983aa8b69150..01aab97b759f29efa95aa3d7401ea400cad58d65 100644 (file)
@@ -24,6 +24,7 @@
 #include "sp-flowtext.h"
 #include "sp-use.h"
 #include "sp-rect.h"
+#include "box3d.h"
 #include "sp-ellipse.h"
 #include "sp-star.h"
 #include "sp-anchor.h"
@@ -59,6 +60,8 @@ type2term(GType type)
         { return _("Polyline"); }
     if (type == SP_TYPE_RECT)
         { return _("Rectangle"); }
+    if (type == SP_TYPE_3DBOX)
+        { return _("3D Box"); }
     if (type == SP_TYPE_TEXT)
         { return _("Text"); }
     // TRANSLATORS: only translate "string" in "context|string".