From dd077ad5b18808e4d36956554368377cf25c2eb6 Mon Sep 17 00:00:00 2001 From: cilix42 Date: Sat, 14 Jul 2007 14:51:56 +0000 Subject: [PATCH] Write distinguished corners of 3D boxes to the svg representation from which the box can be recomputed. 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 | 5 ++ src/attributes.h | 5 ++ src/box3d-context.cpp | 10 ++- src/box3d-face.cpp | 16 +++- src/box3d-face.h | 2 +- src/box3d.cpp | 149 ++++++++++++++++++++++++++++++------ src/box3d.h | 1 + src/selection-describer.cpp | 3 + 8 files changed, 160 insertions(+), 31 deletions(-) diff --git a/src/attributes.cpp b/src/attributes.cpp index a8cd2e83d..968e38633 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -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"}, diff --git a/src/attributes.h b/src/attributes.h index fa492d978..9e658f562 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -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, diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp index 6eaedfb78..0bcf9ffa4 100644 --- a/src/box3d-context.cpp +++ b/src/box3d-context.cpp @@ -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; } diff --git a/src/box3d-face.cpp b/src/box3d-face.cpp index 02c84e22c..2cb510277 100644 --- a/src/box3d-face.cpp +++ b/src/box3d-face.cpp @@ -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); } diff --git a/src/box3d-face.h b/src/box3d-face.h index 04e012b41..61c13432d 100644 --- a/src/box3d-face.h +++ b/src/box3d-face.h @@ -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(); diff --git a/src/box3d.cpp b/src/box3d.cpp index 1e4cad8b3..101cce565 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -16,6 +16,7 @@ */ #include +#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 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 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 +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++ diff --git a/src/box3d.h b/src/box3d.h index 8fe90dc3b..ee0f30c81 100644 --- a/src/box3d.h +++ b/src/box3d.h @@ -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 sp_3dbox_get_center (SP3DBox *box); diff --git a/src/selection-describer.cpp b/src/selection-describer.cpp index 69e10904f..01aab97b7 100644 --- a/src/selection-describer.cpp +++ b/src/selection-describer.cpp @@ -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". -- 2.30.2