From: cilix42 Date: Thu, 21 Jun 2007 13:01:57 +0000 (+0000) Subject: First (very limited) version of the 3D box tool; allows for drawing of new boxes... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=1d882e9533b20c0e0783e9d17fcd3f5466fa422f;p=inkscape.git First (very limited) version of the 3D box tool; allows for drawing of new boxes in a fixed perspective without any live interaction. --- diff --git a/share/icons/icons.svg b/share/icons/icons.svg index b1e578200..a579af719 100644 --- a/share/icons/icons.svg +++ b/share/icons/icons.svg @@ -4141,6 +4141,126 @@ y1="154.99997" x2="526.70886" y2="161.71585" /> + + + + + + + + + + + + + stroke="#646464" + inkscape:object-nodes="true" + objecttolerance="11" /> @@ -14854,4 +14976,115 @@ http://www.inkscape.org/ 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" /> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Makefile_insert b/src/Makefile_insert index 9ded1945f..f1ba21920 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -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 index 000000000..ead6486e5 --- /dev/null +++ b/src/box3d-context.cpp @@ -0,0 +1,608 @@ +#define __SP_3DBOX_CONTEXT_C__ + +/* + * 3D box drawing context + * + * Author: + * Lauris Kaplinski + * bulia byak + * + * Copyright (C) 2007 Maximilian Albert + * Copyright (C) 2006 Johan Engelen + * 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 + +#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 +#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, + _("Ctrl: make square or integer-ratio rect, lock a rounded corner circular"), + _("Shift: 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, _("Rectangle: %s × %s; with Ctrl to make square or integer-ratio rectangle; with Shift 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 index 000000000..bf1d0e5ab --- /dev/null +++ b/src/box3d-context.h @@ -0,0 +1,69 @@ +#ifndef __SP_3DBOX_CONTEXT_H__ +#define __SP_3DBOX_CONTEXT_H__ + +/* + * 3D box drawing context + * + * Author: + * Lauris Kaplinski + * + * Copyright (C) 2000 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2002 Lauris Kaplinski + * Copyright (C) 2007 Maximilian Albert + * + * Released under GNU GPL + */ + +#include +#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 index 000000000..eb05b2c04 --- /dev/null +++ b/src/box3d-face.cpp @@ -0,0 +1,161 @@ +#define __SP_3DBOX_FACE_C__ + +/* + * Face of a 3D box ('perspectivic rectangle') + * + * Authors: + * Maximilian Albert + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "box3d-face.h" +#include + +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 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 ur = first_line.intersect(second_line); + + Box3D::PerspectiveLine third_line (corner1, dir2); + Box3D::PerspectiveLine fourth_line (corner3, dir1); + NR::Maybe 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 index 000000000..123d4cbbc --- /dev/null +++ b/src/box3d-face.h @@ -0,0 +1,59 @@ +#ifndef __SP_3DBOX_FACE_H__ +#define __SP_3DBOX_FACE_H__ + +/* + * Face of a 3D box ('perspectivic rectangle') + * + * Authors: + * Maximilian Albert + * + * 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 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 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 index 000000000..127ffa848 --- /dev/null +++ b/src/box3d.cpp @@ -0,0 +1,257 @@ +#define __SP_3DBOX_C__ + +/* + * SVG implementation + * + * Authors: + * Lauris Kaplinski + * bulia byak + * Maximilian Albert + * + * 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 index 000000000..c83194fa0 --- /dev/null +++ b/src/box3d.h @@ -0,0 +1,55 @@ +#ifndef __SP_3DBOX_H__ +#define __SP_3DBOX_H__ + +/* + * SVG implementation + * + * Authors: + * Lauris Kaplinski + * Maximilian Albert + * + * 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 diff --git a/src/knotholder.cpp b/src/knotholder.cpp index c67f22933..a24bb1625 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -29,6 +29,7 @@ #include "spiral-context.h" #include "sp-spiral.h" #include "sp-offset.h" +#include "box3d.h" #include #include @@ -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 index 000000000..d36b1b63d --- /dev/null +++ b/src/line-geometry.cpp @@ -0,0 +1,112 @@ +#define __LINE_GEOMETRY_C__ + +/* + * Routines for dealing with lines (intersections, etc.) + * + * Authors: + * Maximilian Albert + * + * 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 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 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 index 000000000..53f39db39 --- /dev/null +++ b/src/line-geometry.h @@ -0,0 +1,74 @@ +/* + * Routines for dealing with lines (intersections, etc.) + * + * Authors: + * Maximilian Albert + * + * 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 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 index 000000000..b2e4d2340 --- /dev/null +++ b/src/perspective-line.cpp @@ -0,0 +1,63 @@ +#define __PERSPECTIVE_LINE_C__ + +/* + * Perspective line for 3D perspectives + * + * Authors: + * Maximilian Albert + * + * 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 PerspectiveLine::intersect (Line const &line) +{ + NR::Maybe 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 index 000000000..a1c61c522 --- /dev/null +++ b/src/perspective-line.h @@ -0,0 +1,63 @@ +/* + * Perspective line for 3D perspectives + * + * Authors: + * Maximilian Albert + * + * 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 + +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 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 index 000000000..678b695d0 --- /dev/null +++ b/src/perspective3d.cpp @@ -0,0 +1,98 @@ +#define __PERSPECTIVE3D_C__ + +/* + * Class modelling a 3D perspective + * + * Authors: + * Maximilian Albert + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "box3d-context.h" +#include "perspective-line.h" +#include + +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 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 index 000000000..9f033f80d --- /dev/null +++ b/src/perspective3d.h @@ -0,0 +1,61 @@ +/* + * Class modelling a 3D perspective + * + * Authors: + * Maximilian Albert + * + * 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 : diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 28b1fe018..ead8e7a62 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -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" " \n" " \n" +" \n" " \n" " \n" " \n" diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp index 43c946670..5a1554cbd 100644 --- a/src/sp-object-repr.cpp +++ b/src/sp-object-repr.cpp @@ -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[] = { diff --git a/src/tools-switch.cpp b/src/tools-switch.cpp index 339326359..bc9655a83 100644 --- a/src/tools-switch.cpp +++ b/src/tools-switch.cpp @@ -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, _("Drag to create a rectangle. Drag controls to round corners and resize. Click 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, _("Drag to create a 3D box. Drag controls to resize in perspective. Click to select.")); + break; case TOOLS_SHAPES_ARC: dt->set_event_context(SP_TYPE_ARC_CONTEXT, tool_names[num]); dt->activate_guides(false); diff --git a/src/tools-switch.h b/src/tools-switch.h index 252add758..aac48d834 100644 --- a/src/tools-switch.h +++ b/src/tools-switch.h @@ -19,6 +19,7 @@ enum { TOOLS_SELECT, TOOLS_NODES, TOOLS_SHAPES_RECT, + TOOLS_SHAPES_3DBOX, TOOLS_SHAPES_ARC, TOOLS_SHAPES_STAR, TOOLS_SHAPES_SPIRAL, diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 5694a3ea3..f3429ce7b 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -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"); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index f2a61d1ba..91b7a83ec 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -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 index 000000000..7933fbeba --- /dev/null +++ b/src/vanishing-point.cpp @@ -0,0 +1,101 @@ +#define __VANISHING_POINT_C__ + +/* + * Vanishing point for 3D perspectives + * + * Authors: + * Maximilian Albert + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "vanishing-point.h" +#include + +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 index 000000000..4770c74f1 --- /dev/null +++ b/src/vanishing-point.h @@ -0,0 +1,91 @@ +/* + * Vanishing point for 3D perspectives + * + * Authors: + * Maximilian Albert + * + * 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 : diff --git a/src/verbs.cpp b/src/verbs.cpp index 01172f102..04a49f37c 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -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"), diff --git a/src/verbs.h b/src/verbs.h index 3ebc129d1..f8e25f94d 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -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, diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 9f8e675c2..4c497134f 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -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 = " " " " + " " + " " + " " + " " + " " + " " " " " " @@ -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 ## //########################