diff options
| -rw-r--r-- | src/attributes.cpp | 5 | ||||
| -rw-r--r-- | src/attributes.h | 5 | ||||
| -rw-r--r-- | src/box3d-context.cpp | 10 | ||||
| -rw-r--r-- | src/box3d-face.cpp | 16 | ||||
| -rw-r--r-- | src/box3d-face.h | 2 | ||||
| -rw-r--r-- | src/box3d.cpp | 149 | ||||
| -rw-r--r-- | src/box3d.h | 1 | ||||
| -rw-r--r-- | 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 <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) { @@ -259,6 +300,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) { NR::Point A (box->corners[id ^ Box3D::XY]); @@ -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++ 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<NR::Point> 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". |
