1 #define __VANISHING_POINT_C__
3 /*
4 * Vanishing point for 3D perspectives
5 *
6 * Authors:
7 * Maximilian Albert <Anhalter42@gmx.de>
8 *
9 * Copyright (C) 2007 authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #include "vanishing-point.h"
15 #include "desktop-handles.h"
16 #include "box3d.h"
18 namespace Box3D {
20 #define VP_KNOT_COLOR_NORMAL 0xffffff00
21 #define VP_KNOT_COLOR_SELECTED 0x0000ff00
23 #define VP_LINE_COLOR_FILL 0x0000ff7f
24 #define VP_LINE_COLOR_STROKE_X 0xff00007f
25 #define VP_LINE_COLOR_STROKE_Y 0x0000ff7f
26 #define VP_LINE_COLOR_STROKE_Z 0xffff007f
28 // screen pixels between knots when they snap:
29 #define SNAP_DIST 5
31 // absolute distance between gradient points for them to become a single dragger when the drag is created:
32 #define MERGE_DIST 0.1
34 // knot shapes corresponding to GrPointType enum
35 SPKnotShapeType vp_knot_shapes [] = {
36 SP_KNOT_SHAPE_SQUARE, // VP_FINITE
37 SP_KNOT_SHAPE_CIRCLE //VP_INFINITE
38 };
40 // FIXME: We should always require to have both the point (for finite VPs)
41 // and the direction (for infinite VPs) set. Otherwise toggling
42 // shows very unexpected behaviour.
43 // Later on we can maybe infer the infinite direction from the finite point
44 // and a suitable center of the scene. How to go in the other direction?
45 VanishingPoint::VanishingPoint(NR::Point const &pt, NR::Point const &inf_dir, VPState st)
46 : NR::Point (pt), state (st), v_dir (inf_dir) {}
48 VanishingPoint::VanishingPoint(NR::Point const &pt)
49 : NR::Point (pt), state (VP_FINITE), v_dir (0.0, 0.0) {}
51 VanishingPoint::VanishingPoint(NR::Point const &pt, NR::Point const &direction)
52 : NR::Point (pt), state (VP_INFINITE), v_dir (direction) {}
54 VanishingPoint::VanishingPoint(NR::Coord x, NR::Coord y)
55 : NR::Point(x, y), state(VP_FINITE), v_dir(0.0, 0.0) {}
57 VanishingPoint::VanishingPoint(NR::Coord dir_x, NR::Coord dir_y, VPState st)
58 : NR::Point(0.0, 0.0), state(st), v_dir(dir_x, dir_y) {}
60 VanishingPoint::VanishingPoint(NR::Coord x, NR::Coord y, NR::Coord dir_x, NR::Coord dir_y)
61 : NR::Point(x, y), state(VP_INFINITE), v_dir(dir_x, dir_y) {}
63 VanishingPoint::VanishingPoint(VanishingPoint const &rhs) : NR::Point (rhs)
64 {
65 this->state = rhs.state;
66 //this->ref_pt = rhs.ref_pt;
67 this->v_dir = rhs.v_dir;
68 }
70 VanishingPoint::~VanishingPoint () {}
72 bool VanishingPoint::operator== (VanishingPoint const &other)
73 {
74 // Should we compare the parent perspectives, too? Probably not.
75 if ((*this)[NR::X] == other[NR::X] && (*this)[NR::Y] == other[NR::Y]
76 && this->state == other.state && this->v_dir == other.v_dir) {
77 return true;
78 }
79 return false;
80 }
82 bool VanishingPoint::is_finite() const
83 {
84 return this->state == VP_FINITE;
85 }
87 VPState VanishingPoint::toggle_parallel()
88 {
89 if (this->state == VP_FINITE) {
90 this->state = VP_INFINITE;
91 } else {
92 this->state = VP_FINITE;
93 }
95 return this->state;
96 }
98 void VanishingPoint::draw(Box3D::Axis const axis)
99 {
100 switch (axis) {
101 case X:
102 if (state == VP_FINITE)
103 create_canvas_point(*this, 6.0, 0xff000000);
104 else
105 create_canvas_point(*this, 6.0, 0xffffff00);
106 break;
107 case Y:
108 if (state == VP_FINITE)
109 create_canvas_point(*this, 6.0, 0x0000ff00);
110 else
111 create_canvas_point(*this, 6.0, 0xffffff00);
112 break;
113 case Z:
114 if (state == VP_FINITE)
115 create_canvas_point(*this, 6.0, 0x00770000);
116 else
117 create_canvas_point(*this, 6.0, 0xffffff00);
118 break;
119 default:
120 g_assert_not_reached();
121 break;
122 }
123 }
125 static void
126 vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data)
127 {
128 VPDrag *drag = (VPDrag *) data;
129 drag->updateDraggers ();
130 //drag->updateLines ();
131 }
133 static void
134 vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data)
135 {
136 VPDrag *drag = (VPDrag *) data;
137 /***
138 if (drag->local_change) {
139 drag->local_change = false;
140 } else {
141 drag->updateDraggers ();
142 }
143 ***/
144 //drag->updateLines ();
145 }
147 static void
148 vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpointer data)
149 {
150 g_warning ("Please implement vp_knot_moved_handler.\n");
151 VPDragger *dragger = (VPDragger *) data;
152 //VPDrag *drag = dragger->parent;
154 NR::Point p = *ppointer;
156 dragger->point = p;
158 dragger->reshapeBoxes (p, Box3D::XYZ);
159 //dragger->parent->updateLines ();
161 //drag->local_change = false;
162 }
164 static void
165 vp_knot_grabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
166 {
167 VPDragger *dragger = (VPDragger *) data;
169 //sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
170 }
172 static void
173 vp_knot_ungrabbed_handler (SPKnot *knot, guint state, gpointer data)
174 {
175 g_warning ("Please fully implement vp_knot_ungrabbed_handler.\n");
177 VPDragger *dragger = (VPDragger *) data;
178 //VPDrag *drag = dragger->parent;
180 //sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas);
182 dragger->point_original = dragger->point = knot->pos;
184 /***
185 VanishingPoint *vp;
186 for (GSList *i = dragger->vps; i != NULL; i = i->next) {
187 vp = (VanishingPoint *) i->data;
188 vp->set_pos (knot->pos);
189 }
190 ***/
192 dragger->parent->updateDraggers ();
193 dragger->updateBoxReprs ();
195 // TODO: Update box's paths and svg representation
197 // TODO: Undo machinery!!
198 }
200 VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp)
201 {
202 if (vp == NULL) {
203 g_print ("VP used to create the VPDragger is NULL. This can happen when shift-dragging knots.\n");
204 g_print ("How to correctly handle this? Should we just ignore it, as we currently do?\n");
205 //g_assert (vp != NULL);
206 }
207 this->vps = NULL;
209 this->parent = parent;
211 this->point = p;
212 this->point_original = p;
214 // create the knot
215 this->knot = sp_knot_new (parent->desktop, NULL);
216 this->knot->setMode(SP_KNOT_MODE_XOR);
217 this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
218 this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
219 sp_knot_update_ctrl(this->knot);
221 // move knot to the given point
222 sp_knot_set_position (this->knot, &this->point, SP_KNOT_STATE_NORMAL);
223 sp_knot_show (this->knot);
225 // connect knot's signals
226 g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
227 /***
228 g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this);
229 ***/
230 g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
231 g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
232 /***
233 g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this);
234 ***/
236 // add the initial VP (which may be NULL!)
237 this->addVP (vp);
238 //updateKnotShape();
239 }
241 VPDragger::~VPDragger()
242 {
243 // unselect if it was selected
244 //this->parent->setDeselected(this);
246 // disconnect signals
247 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
248 /***
249 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_clicked_handler), this);
250 ***/
251 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
252 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
253 /***
254 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_doubleclicked_handler), this);
255 ***/
257 /* unref should call destroy */
258 g_object_unref (G_OBJECT (this->knot));
260 g_slist_free (this->vps);
261 this->vps = NULL;
262 }
264 /**
265 * Adds a vanishing point to the dragger (also updates the position)
266 */
267 void
268 VPDragger::addVP (VanishingPoint *vp)
269 {
270 if (vp == NULL) {
271 g_print ("No VP present in addVP. We return without adding a new VP to the list.\n");
272 return;
273 }
274 vp->set_pos (this->point);
275 this->vps = g_slist_prepend (this->vps, vp);
277 //this->updateTip();
278 }
280 void
281 VPDragger::removeVP (VanishingPoint *vp)
282 {
283 if (vp == NULL) {
284 g_print ("NULL vanishing point will not be removed.\n");
285 return;
286 }
287 g_assert (this->vps != NULL);
288 this->vps = g_slist_remove (this->vps, vp);
290 //this->updateTip();
291 }
293 void
294 VPDragger::reshapeBoxes (NR::Point const &p, Box3D::Axis axes)
295 {
296 Perspective3D *persp;
297 for (GSList const* i = this->vps; i != NULL; i = i->next) {
298 VanishingPoint *vp = (VanishingPoint *) i->data;
299 // TODO: We can extract the VP directly from the box's perspective. Is that vanishing point identical to 'vp'?
300 // Or is there duplicated information? If so, remove it and simplify the whole construction!
301 vp->set_pos(p);
302 persp = get_persp_of_VP (vp);
303 Box3D::Axis axis = persp->get_axis_of_VP (vp);
304 get_persp_of_VP (vp)->reshape_boxes (axis); // FIXME: we should only update the direction of the VP
305 }
306 }
308 void
309 VPDragger::updateBoxReprs ()
310 {
311 for (GSList *i = this->vps; i != NULL; i = i->next) {
312 Box3D::get_persp_of_VP ((VanishingPoint *) i->data)->update_box_reprs ();
313 }
314 }
316 VPDrag::VPDrag (SPDesktop *desktop)
317 {
318 this->desktop = desktop;
319 this->draggers = NULL;
320 this->selection = sp_desktop_selection(desktop);
322 this->sel_changed_connection = this->selection->connectChanged(
323 sigc::bind (
324 sigc::ptr_fun(&vp_drag_sel_changed),
325 (gpointer)this )
327 );
328 this->sel_modified_connection = this->selection->connectModified(
329 sigc::bind(
330 sigc::ptr_fun(&vp_drag_sel_modified),
331 (gpointer)this )
332 );
334 this->updateDraggers ();
335 //this->updateLines ();
336 }
338 VPDrag::~VPDrag()
339 {
340 this->sel_changed_connection.disconnect();
341 this->sel_modified_connection.disconnect();
343 for (GList *l = this->draggers; l != NULL; l = l->next) {
344 delete ((VPDragger *) l->data);
345 }
346 g_list_free (this->draggers);
347 this->draggers = NULL;
348 }
350 /**
351 * Select the dragger that has the given VP.
352 */
353 VPDragger *
354 VPDrag::getDraggerFor (VanishingPoint const &vp)
355 {
356 for (GList const* i = this->draggers; i != NULL; i = i->next) {
357 VPDragger *dragger = (VPDragger *) i->data;
358 for (GSList const* j = dragger->vps; j != NULL; j = j->next) {
359 VanishingPoint *vp2 = (VanishingPoint *) j->data;
360 g_assert (vp2 != NULL);
362 // TODO: Should we compare the pointers or the VPs themselves!?!?!?!
363 //if ((*vp2) == vp) {
364 if (vp2 == &vp) {
365 return (dragger);
366 }
367 }
368 }
369 return NULL;
370 }
372 /**
373 * Regenerates the draggers list from the current selection; is called when selection is changed or modified
374 */
375 void
376 VPDrag::updateDraggers ()
377 {
378 /***
379 while (selected) {
380 selected = g_list_remove(selected, selected->data);
381 }
382 ***/
383 // delete old draggers
384 for (GList const* i = this->draggers; i != NULL; i = i->next) {
385 delete ((VPDragger *) i->data);
386 }
387 g_list_free (this->draggers);
388 this->draggers = NULL;
390 g_return_if_fail (this->selection != NULL);
392 for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
393 SPItem *item = SP_ITEM(i->data);
394 //SPStyle *style = SP_OBJECT_STYLE (item);
396 if (!SP_IS_3DBOX (item)) continue;
397 SP3DBox *box = SP_3DBOX (item);
399 // FIXME: Get the VPs from the selection!!!!
400 //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::X));
401 //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::Y));
402 //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::Z));
404 //Box3D::Perspective3D *persp = box->perspective;
405 Box3D::Perspective3D *persp = Box3D::get_persp_of_box (box);
406 addDragger (persp->get_vanishing_point(Box3D::X));
407 addDragger (persp->get_vanishing_point(Box3D::Y));
408 addDragger (persp->get_vanishing_point(Box3D::Z));
409 }
410 }
412 /**
413 * If there already exists a dragger within MERGE_DIST of p, add the VP to it;
414 * otherwise create new dragger and add it to draggers list
415 */
416 void
417 VPDrag::addDragger (VanishingPoint *vp)
418 {
419 if (vp == NULL) {
420 g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)");
421 g_assert (vp != NULL);
422 }
423 NR::Point p = vp->get_pos();
425 for (GList *i = this->draggers; i != NULL; i = i->next) {
426 VPDragger *dragger = (VPDragger *) i->data;
427 if (NR::L2 (dragger->point - p) < MERGE_DIST) {
428 // distance is small, merge this draggable into dragger, no need to create new dragger
429 dragger->addVP (vp);
430 //dragger->updateKnotShape();
431 return;
432 }
433 }
435 VPDragger *new_dragger = new VPDragger(this, p, vp);
436 // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
437 this->draggers = g_list_append (this->draggers, new_dragger);
438 }
440 } // namespace Box3D
442 /*
443 Local Variables:
444 mode:c++
445 c-file-style:"stroustrup"
446 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
447 indent-tabs-mode:nil
448 fill-column:99
449 End:
450 */
451 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :