From 833612d1c1e43055c4428afa90dd2b112a439780 Mon Sep 17 00:00:00 2001 From: cilix42 Date: Mon, 6 Aug 2007 07:37:24 +0000 Subject: [PATCH] First stage of draggable vanishing points (no snapping/unsnapping yet) --- src/box3d-context.cpp | 10 ++ src/box3d-context.h | 2 + src/object-edit.cpp | 2 + src/perspective3d.cpp | 59 +++++++ src/perspective3d.h | 2 + src/vanishing-point.cpp | 339 ++++++++++++++++++++++++++++++++++++++++ src/vanishing-point.h | 55 +++++++ 7 files changed, 469 insertions(+) diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp index 5f1029576..3096ad179 100644 --- a/src/box3d-context.cpp +++ b/src/box3d-context.cpp @@ -113,6 +113,8 @@ static void sp_3dbox_context_init(SP3DBoxContext *box3d_context) box3d_context->ctrl_dragged = false; box3d_context->extruded = false; + box3d_context->_vpdrag = NULL; + new (&box3d_context->sel_changed_connection) sigc::connection(); } @@ -123,6 +125,9 @@ static void sp_3dbox_context_dispose(GObject *object) ec->enableGrDrag(false); + delete (bc->_vpdrag); + bc->_vpdrag = NULL; + bc->sel_changed_connection.disconnect(); bc->sel_changed_connection.~connection(); @@ -213,6 +218,8 @@ static void sp_3dbox_context_setup(SPEventContext *ec) sigc::bind(sigc::ptr_fun(&sp_3dbox_context_selection_changed), (gpointer)bc) ); + bc->_vpdrag = new Box3D::VPDrag(ec->desktop); + if (prefs_get_int_attribute("tools.shapes", "selcue", 0) != 0) { ec->enableSelectionCue(); } @@ -520,6 +527,9 @@ static void sp_3dbox_drag(SP3DBoxContext &bc, guint state) } bc.item->updateRepr(); + // TODO: It would be nice to show the VPs during dragging, but since there is no selection + // at this point (only after finishing the box), we must do this "manually" + bc._vpdrag->updateDraggers(); sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5); } diff --git a/src/box3d-context.h b/src/box3d-context.h index bf359ebe7..6d2e560d4 100644 --- a/src/box3d-context.h +++ b/src/box3d-context.h @@ -48,6 +48,8 @@ struct SP3DBoxContext : public SPEventContext { bool ctrl_dragged; /* whether we are ctrl-dragging */ bool extruded; /* whether shift-dragging already occured (i.e. the box is already extruded) */ + Box3D::VPDrag * _vpdrag; + /* temporary member until the precise behaviour is sorted out */ static guint number_of_handles; diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 6eff465f8..e8390da20 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -546,6 +546,7 @@ static void sp_3dbox_knot_set(SPItem *item, guint knot_id, Box3D::Axis direction sp_3dbox_move_corner_in_XY_plane (box, knot_id, new_pos * i2d, (state & GDK_SHIFT_MASK) ? direction : Box3D::XY); } sp_3dbox_update_curves (box); + sp_3dbox_set_ratios (box); } static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id) @@ -615,6 +616,7 @@ static void sp_3dbox_knot_set_uniformly(SPItem *item, guint knot_id, Box3D::Axis sp_3dbox_move_corner_in_Z_direction (box, knot_id, new_pos * i2d, (state & GDK_SHIFT_MASK)); } sp_3dbox_update_curves (box); + sp_3dbox_set_ratios (box); } static void sp_3dbox_knot0_set_uniformly(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) diff --git a/src/perspective3d.cpp b/src/perspective3d.cpp index dd05ec7c7..e5b1f6212 100644 --- a/src/perspective3d.cpp +++ b/src/perspective3d.cpp @@ -38,6 +38,23 @@ get_persp_of_box (const SP3DBox *box) g_assert_not_reached(); } +Perspective3D * +get_persp_of_VP (const VanishingPoint *vp) +{ + Perspective3D *persp; + for (GSList *p = Perspective3D::perspectives; p != NULL; p = p->next) { + persp = (Perspective3D *) p->data; + // we compare the pointers, not the position/state of the VPs; is this correct? + if (persp->get_vanishing_point (Box3D::X) == vp || + persp->get_vanishing_point (Box3D::Y) == vp || + persp->get_vanishing_point (Box3D::Z) == vp) + return persp; + } + + g_warning ("Stray vanishing point!\n"); + g_assert_not_reached(); +} + /** * Computes the intersection of the two perspective lines from pt1 and pt2 to the respective * vanishing points in the given directions. @@ -88,6 +105,37 @@ Perspective3D::~Perspective3D () { Perspective3D::remove_perspective (this); + // Remove the VPs from their draggers + SPEventContext *ec = inkscape_active_event_context(); + if (SP_IS_3DBOX_CONTEXT (ec)) { + SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec); + // we need to check if there are any draggers because the selection + // is temporarily empty during duplication of boxes, e.g. + if (bc->_vpdrag->draggers != NULL) { + /*** + g_assert (bc->_vpdrag->getDraggerFor (*vp_x) != NULL); + g_assert (bc->_vpdrag->getDraggerFor (*vp_y) != NULL); + g_assert (bc->_vpdrag->getDraggerFor (*vp_z) != NULL); + bc->_vpdrag->getDraggerFor (*vp_x)->removeVP (vp_x); + bc->_vpdrag->getDraggerFor (*vp_y)->removeVP (vp_y); + bc->_vpdrag->getDraggerFor (*vp_z)->removeVP (vp_z); + ***/ + // TODO: the temporary perspective created when building boxes is not linked to any dragger, hence + // we need to do the following checks. Maybe it would be better to not create a temporary + // perspective at all but simply compare the VPs manually in sp_3dbox_build. + VPDragger * dragger; + dragger = bc->_vpdrag->getDraggerFor (*vp_x); + if (dragger) + dragger->removeVP (vp_x); + dragger = bc->_vpdrag->getDraggerFor (*vp_y); + if (dragger) + dragger->removeVP (vp_y); + dragger = bc->_vpdrag->getDraggerFor (*vp_z); + if (dragger) + dragger->removeVP (vp_z); + } + } + delete vp_x; delete vp_y; delete vp_z; @@ -145,6 +193,17 @@ Perspective3D::set_vanishing_point (Box3D::Axis const dir, VanishingPoint const } } +Axis +Perspective3D::get_axis_of_VP (VanishingPoint *vp) +{ + if (vp == vp_x) return X; + if (vp == vp_y) return Y; + if (vp == vp_z) return Z; + + g_warning ("Vanishing point not present in the perspective.\n"); + return NONE; +} + void Perspective3D::set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st) { diff --git a/src/perspective3d.h b/src/perspective3d.h index 43b0ca3d3..c3e07b23b 100644 --- a/src/perspective3d.h +++ b/src/perspective3d.h @@ -30,6 +30,7 @@ public: bool operator== (Perspective3D const &other); VanishingPoint *get_vanishing_point (Box3D::Axis const dir); + Axis get_axis_of_VP (VanishingPoint *vp); void set_vanishing_point (Box3D::Axis const dir, VanishingPoint const &pt); void set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st); void add_box (SP3DBox *box); @@ -57,6 +58,7 @@ private: }; Perspective3D * get_persp_of_box (const SP3DBox *box); +Perspective3D * get_persp_of_VP (const VanishingPoint *vp); NR::Point perspective_intersection (NR::Point pt1, Box3D::Axis dir1, NR::Point pt2, Box3D::Axis dir2, Perspective3D *persp); NR::Point perspective_line_snap (NR::Point pt, Box3D::Axis dir, NR::Point ext_pt, Perspective3D *persp); diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp index 5f40936cf..edf769c17 100644 --- a/src/vanishing-point.cpp +++ b/src/vanishing-point.cpp @@ -12,9 +12,31 @@ */ #include "vanishing-point.h" +#include "desktop-handles.h" +#include "box3d.h" namespace Box3D { +#define VP_KNOT_COLOR_NORMAL 0xffffff00 +#define VP_KNOT_COLOR_SELECTED 0x0000ff00 + +#define VP_LINE_COLOR_FILL 0x0000ff7f +#define VP_LINE_COLOR_STROKE_X 0xff00007f +#define VP_LINE_COLOR_STROKE_Y 0x0000ff7f +#define VP_LINE_COLOR_STROKE_Z 0xffff007f + +// screen pixels between knots when they snap: +#define SNAP_DIST 5 + +// absolute distance between gradient points for them to become a single dragger when the drag is created: +#define MERGE_DIST 0.1 + +// knot shapes corresponding to GrPointType enum +SPKnotShapeType vp_knot_shapes [] = { + SP_KNOT_SHAPE_SQUARE, // VP_FINITE + SP_KNOT_SHAPE_CIRCLE //VP_INFINITE +}; + // 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. @@ -45,6 +67,8 @@ VanishingPoint::VanishingPoint(VanishingPoint const &rhs) : NR::Point (rhs) this->v_dir = rhs.v_dir; } +VanishingPoint::~VanishingPoint () {} + bool VanishingPoint::operator== (VanishingPoint const &other) { // Should we compare the parent perspectives, too? Probably not. @@ -98,6 +122,321 @@ void VanishingPoint::draw(Box3D::Axis const axis) } } +static void +vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data) +{ + VPDrag *drag = (VPDrag *) data; + drag->updateDraggers (); + //drag->updateLines (); +} + +static void +vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data) +{ + VPDrag *drag = (VPDrag *) data; + /*** + if (drag->local_change) { + drag->local_change = false; + } else { + drag->updateDraggers (); + } + ***/ + //drag->updateLines (); +} + +static void +vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpointer data) +{ + g_warning ("Please implement vp_knot_moved_handler.\n"); + VPDragger *dragger = (VPDragger *) data; + //VPDrag *drag = dragger->parent; + + NR::Point p = *ppointer; + + dragger->point = p; + + dragger->reshapeBoxes (p, Box3D::XYZ); + //dragger->parent->updateLines (); + + //drag->local_change = false; +} + +static void +vp_knot_grabbed_handler (SPKnot *knot, unsigned int state, gpointer data) +{ + VPDragger *dragger = (VPDragger *) data; + + //sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5); +} + +static void +vp_knot_ungrabbed_handler (SPKnot *knot, guint state, gpointer data) +{ + g_warning ("Please fully implement vp_knot_ungrabbed_handler.\n"); + + VPDragger *dragger = (VPDragger *) data; + //VPDrag *drag = dragger->parent; + + //sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas); + + dragger->point_original = dragger->point = knot->pos; + + /*** + VanishingPoint *vp; + for (GSList *i = dragger->vps; i != NULL; i = i->next) { + vp = (VanishingPoint *) i->data; + vp->set_pos (knot->pos); + } + ***/ + + dragger->parent->updateDraggers (); + dragger->updateBoxReprs (); + + // TODO: Update box's paths and svg representation + + // TODO: Undo machinery!! +} + +VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp) +{ + if (vp == NULL) { + g_print ("VP used to create the VPDragger is NULL. This can happen when shift-dragging knots.\n"); + g_print ("How to correctly handle this? Should we just ignore it, as we currently do?\n"); + //g_assert (vp != NULL); + } + this->vps = NULL; + + this->parent = parent; + + this->point = p; + this->point_original = p; + + // create the knot + this->knot = sp_knot_new (parent->desktop, NULL); + this->knot->setMode(SP_KNOT_MODE_XOR); + this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL); + this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff); + sp_knot_update_ctrl(this->knot); + + // move knot to the given point + sp_knot_set_position (this->knot, &this->point, SP_KNOT_STATE_NORMAL); + sp_knot_show (this->knot); + + // connect knot's signals + g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this); + /*** + g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this); + ***/ + g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this); + g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this); + /*** + g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this); + ***/ + + // add the initial VP (which may be NULL!) + this->addVP (vp); + //updateKnotShape(); +} + +VPDragger::~VPDragger() +{ + // unselect if it was selected + //this->parent->setDeselected(this); + + // disconnect signals + g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this); + /*** + g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_clicked_handler), this); + ***/ + g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this); + g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this); + /*** + g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_doubleclicked_handler), this); + ***/ + + /* unref should call destroy */ + g_object_unref (G_OBJECT (this->knot)); + + g_slist_free (this->vps); + this->vps = NULL; +} + +/** + * Adds a vanishing point to the dragger (also updates the position) + */ +void +VPDragger::addVP (VanishingPoint *vp) +{ + if (vp == NULL) { + g_print ("No VP present in addVP. We return without adding a new VP to the list.\n"); + return; + } + vp->set_pos (this->point); + this->vps = g_slist_prepend (this->vps, vp); + + //this->updateTip(); +} + +void +VPDragger::removeVP (VanishingPoint *vp) +{ + if (vp == NULL) { + g_print ("NULL vanishing point will not be removed.\n"); + return; + } + g_assert (this->vps != NULL); + this->vps = g_slist_remove (this->vps, vp); + + //this->updateTip(); +} + +void +VPDragger::reshapeBoxes (NR::Point const &p, Box3D::Axis axes) +{ + Perspective3D *persp; + for (GSList const* i = this->vps; i != NULL; i = i->next) { + VanishingPoint *vp = (VanishingPoint *) i->data; + // TODO: We can extract the VP directly from the box's perspective. Is that vanishing point identical to 'vp'? + // Or is there duplicated information? If so, remove it and simplify the whole construction! + vp->set_pos(p); + persp = get_persp_of_VP (vp); + Box3D::Axis axis = persp->get_axis_of_VP (vp); + get_persp_of_VP (vp)->reshape_boxes (axis); // FIXME: we should only update the direction of the VP + } +} + +void +VPDragger::updateBoxReprs () +{ + for (GSList *i = this->vps; i != NULL; i = i->next) { + Box3D::get_persp_of_VP ((VanishingPoint *) i->data)->update_box_reprs (); + } +} + +VPDrag::VPDrag (SPDesktop *desktop) +{ + this->desktop = desktop; + this->draggers = NULL; + this->selection = sp_desktop_selection(desktop); + + this->sel_changed_connection = this->selection->connectChanged( + sigc::bind ( + sigc::ptr_fun(&vp_drag_sel_changed), + (gpointer)this ) + + ); + this->sel_modified_connection = this->selection->connectModified( + sigc::bind( + sigc::ptr_fun(&vp_drag_sel_modified), + (gpointer)this ) + ); + + this->updateDraggers (); + //this->updateLines (); +} + +VPDrag::~VPDrag() +{ + this->sel_changed_connection.disconnect(); + this->sel_modified_connection.disconnect(); + + for (GList *l = this->draggers; l != NULL; l = l->next) { + delete ((VPDragger *) l->data); + } + g_list_free (this->draggers); + this->draggers = NULL; +} + +/** + * Select the dragger that has the given VP. + */ +VPDragger * +VPDrag::getDraggerFor (VanishingPoint const &vp) +{ + for (GList const* i = this->draggers; i != NULL; i = i->next) { + VPDragger *dragger = (VPDragger *) i->data; + for (GSList const* j = dragger->vps; j != NULL; j = j->next) { + VanishingPoint *vp2 = (VanishingPoint *) j->data; + g_assert (vp2 != NULL); + + // TODO: Should we compare the pointers or the VPs themselves!?!?!?! + //if ((*vp2) == vp) { + if (vp2 == &vp) { + return (dragger); + } + } + } + return NULL; +} + +/** + * Regenerates the draggers list from the current selection; is called when selection is changed or modified + */ +void +VPDrag::updateDraggers () +{ + /*** + while (selected) { + selected = g_list_remove(selected, selected->data); + } + ***/ + // delete old draggers + for (GList const* i = this->draggers; i != NULL; i = i->next) { + delete ((VPDragger *) i->data); + } + g_list_free (this->draggers); + this->draggers = NULL; + + g_return_if_fail (this->selection != NULL); + + for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { + SPItem *item = SP_ITEM(i->data); + //SPStyle *style = SP_OBJECT_STYLE (item); + + if (!SP_IS_3DBOX (item)) continue; + SP3DBox *box = SP_3DBOX (item); + + // FIXME: Get the VPs from the selection!!!! + //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::X)); + //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::Y)); + //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::Z)); + + //Box3D::Perspective3D *persp = box->perspective; + Box3D::Perspective3D *persp = Box3D::get_persp_of_box (box); + addDragger (persp->get_vanishing_point(Box3D::X)); + addDragger (persp->get_vanishing_point(Box3D::Y)); + addDragger (persp->get_vanishing_point(Box3D::Z)); + } +} + +/** + * If there already exists a dragger within MERGE_DIST of p, add the VP to it; + * otherwise create new dragger and add it to draggers list + */ +void +VPDrag::addDragger (VanishingPoint *vp) +{ + if (vp == NULL) { + g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)"); + g_assert (vp != NULL); + } + NR::Point p = vp->get_pos(); + + for (GList *i = this->draggers; i != NULL; i = i->next) { + VPDragger *dragger = (VPDragger *) i->data; + if (NR::L2 (dragger->point - p) < MERGE_DIST) { + // distance is small, merge this draggable into dragger, no need to create new dragger + dragger->addVP (vp); + //dragger->updateKnotShape(); + return; + } + } + + VPDragger *new_dragger = new VPDragger(this, p, vp); + // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end. + this->draggers = g_list_append (this->draggers, new_dragger); +} + } // namespace Box3D /* diff --git a/src/vanishing-point.h b/src/vanishing-point.h index 918b27fc7..58b3b1427 100644 --- a/src/vanishing-point.h +++ b/src/vanishing-point.h @@ -13,6 +13,8 @@ #define SEEN_VANISHING_POINT_H #include "libnr/nr-point.h" +#include "knot.h" +#include "selection.h" #include "axis-manip.h" #include "line-geometry.h" // TODO: Remove this include as soon as we don't need create_canvas_(point|line) any more. @@ -46,6 +48,7 @@ public: 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); + ~VanishingPoint(); bool operator== (VanishingPoint const &other); @@ -67,6 +70,58 @@ public: private: }; +class VPDrag; + +struct VPDragger { +public: + VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp); + ~VPDragger(); + + VPDrag *parent; + SPKnot *knot; + + // position of the knot, desktop coords + NR::Point point; + // position of the knot before it began to drag; updated when released + NR::Point point_original; + + GSList *vps; // the list of vanishing points + + void addVP(VanishingPoint *vp); + void removeVP(VanishingPoint *vp); + + void reshapeBoxes(NR::Point const &p, Box3D::Axis axes); + void updateBoxReprs(); +}; + +struct VPDrag { +public: + VPDrag(SPDesktop *desktop); + ~VPDrag(); + + VPDragger *getDraggerFor (VanishingPoint const &vp); + + //void grabKnot (VanishingPoint const &vp, gint x, gint y, guint32 etime); + + bool local_change; + + SPDesktop *desktop; + GList *draggers; + //GSList *lines; + + void updateDraggers (); + //void updateLines (); + +private: + //void deselect_all(); + + //void addLine (NR::Point p1, NR::Point p2, guint32 rgba); + void addDragger (VanishingPoint *vp); + + Inkscape::Selection *selection; + sigc::connection sel_changed_connection; + sigc::connection sel_modified_connection; +}; } // namespace Box3D -- 2.30.2