Code

d4990fd397b5b99ad9d8c5b901dfd0b15c2e943e
[inkscape.git] / src / perspective3d.cpp
1 #define __PERSPECTIVE3D_C__
3 /*
4  * Class modelling a 3D perspective
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 "box3d.h"
15 #include "box3d-context.h"
16 #include "perspective-line.h"
17 #include <iostream>
18 #include "desktop.h"
20 // can probably be removed later
21 #include "inkscape.h"
22 #include "knotholder.h"
24 namespace Box3D {
26 Perspective3D *
27 get_persp_of_box (const SP3DBox *box)
28 {
29     SPDesktop *desktop = inkscape_active_desktop(); // Should we pass the desktop as an argument?
30     for (GSList *p = desktop->perspectives; p != NULL; p = p->next) {
31         if (((Perspective3D *) p->data)->has_box (box))
32             return (Perspective3D *) p->data;
33     }
34     g_warning ("Stray 3D box!\n");
35     g_assert_not_reached();
36 }
38 /**
39  * Computes the intersection of the two perspective lines from pt1 and pt2 to the respective
40  * vanishing points in the given directions.
41  */
42 // FIXME: This has been moved to a virtual method inside PerspectiveLine; can probably be purged
43 NR::Point
44 perspective_intersection (NR::Point pt1, Box3D::Axis dir1, NR::Point pt2, Box3D::Axis dir2, Perspective3D *persp)
45 {
46     VanishingPoint const *vp1 = persp->get_vanishing_point(dir1);
47     VanishingPoint const *vp2 = persp->get_vanishing_point(dir2);
48     NR::Maybe<NR::Point> meet = Line(pt1, *vp1).intersect(Line(pt2, *vp2));
49     // FIXME: How to handle parallel lines (also depends on the type of the VPs)?
50     if (!meet) { meet = NR::Point (0.0, 0.0); }
51     return *meet;
52 }
54 /**
55  * Find the point on the perspective line from line_pt to the
56  * vanishing point in direction dir that is closest to ext_pt.
57  */
58 NR::Point
59 perspective_line_snap (NR::Point line_pt, Box3D::Axis dir, NR::Point ext_pt, Perspective3D *persp)
60 {
61     return PerspectiveLine(line_pt, dir, persp).closest_to(ext_pt);
62 }  
64 Perspective3D::Perspective3D (VanishingPoint const &pt_x, VanishingPoint const &pt_y, VanishingPoint const &pt_z)
65     : desktop (NULL),
66       boxes (NULL)
67 {
68     vp_x = new VanishingPoint (pt_x);
69     vp_y = new VanishingPoint (pt_y);
70     vp_z = new VanishingPoint (pt_z);
71 }
73 Perspective3D::Perspective3D (Perspective3D &other)
74     : desktop (other.desktop),
75       boxes (NULL) // FIXME: Should we add an option to copy the list of boxes?
76 {
77     vp_x = new VanishingPoint (*other.vp_x);
78     vp_y = new VanishingPoint (*other.vp_y);
79     vp_z = new VanishingPoint (*other.vp_z);
80 }
83 Perspective3D::~Perspective3D ()
84 {
85     // we can have desktop == NULL when building a box whose attribute "inkscape:perspective" is set
86     if (desktop != NULL) {
87         desktop->remove_perspective (this);
88     }
90     delete vp_x;
91     delete vp_y;
92     delete vp_z;
94     g_slist_free (boxes);
95 }
97 bool
98 Perspective3D::operator==(Perspective3D const &other)
99 {
100     // Two perspectives are equal iff their vanishing points coincide and have identical states
101     return (*vp_x == *other.vp_x && *vp_y == *other.vp_y && *vp_z == *other.vp_z);
104 VanishingPoint *
105 Perspective3D::get_vanishing_point (Box3D::Axis const dir)
107     switch (dir) {
108         case X:
109             return vp_x;
110             break;
111         case Y:
112             return vp_y;
113             break;
114         case Z:
115             return vp_z;
116             break;
117         case NONE:
118             g_warning ("Axis direction must be specified. As a workaround we return the VP in X direction.\n");
119             return vp_x;
120             break;
121         default:
122             g_warning ("Single axis direction needed to determine corresponding vanishing point.\n");
123             return get_vanishing_point (extract_first_axis_direction(dir));
124             break;
125     }
128 void
129 Perspective3D::set_vanishing_point (Box3D::Axis const dir, VanishingPoint const &pt)
131     switch (dir) {
132         case X:
133             (*vp_x) = pt;
134             break;
135         case Y:
136             (*vp_y) = pt;
137             break;
138         case Z:
139             (*vp_z) = pt;
140             break;
141         case NONE:
142             // no vanishing point to set
143             break;
144     }
147 void
148 Perspective3D::set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st)
150     VanishingPoint *vp;
151     switch (dir) {
152         case X:
153             vp = vp_x;
154             break;
155         case Y:
156             vp = vp_y;
157             break;
158         case Z:
159             vp = vp_z;
160             break;
161         case NONE:
162             // no vanishing point to set
163             return;
164     }
166     vp->set_pos (pt_x, pt_y);
167     vp->v_dir = NR::Point (dir_x, dir_y);
168     vp->state = st;
171 void
172 Perspective3D::add_box (SP3DBox *box)
174     if (g_slist_find (this->boxes, box) != NULL) {
175         // Don't add the same box twice
176         g_warning ("Box already uses the current perspective. We don't add it again.\n");
177         return;
178     }
179     this->boxes = g_slist_append (this->boxes, box);
182 void
183 Perspective3D::remove_box (const SP3DBox *box)
185     if (!g_slist_find (this->boxes, box)) {
186         g_warning ("Could not find box that is to be removed in the current perspective.\n");
187     }
188     this->boxes = g_slist_remove (this->boxes, box);
191 bool
192 Perspective3D::has_box (const SP3DBox *box)
194     return (g_slist_find (this->boxes, box) != NULL);
197 /**
198  * Update the shape of a box after a handle was dragged or a VP was changed, according to the stored ratios.
199  */
200 void
201 Perspective3D::reshape_boxes (Box3D::Axis axes)
203     // TODO: Leave the "correct" corner fixed according to which face is supposed to be on front.
204     NR::Point new_pt;
205     VanishingPoint *vp;
206     for (const GSList *i = this->boxes; i != NULL; i = i->next) {
207         SP3DBox *box = SP_3DBOX (i->data);
208         if (axes & Box3D::X) {
209             vp = this->get_vanishing_point (Box3D::X);
210             new_pt = vp->get_pos() + box->ratio_x * (box->corners[3] - vp->get_pos());
211             sp_3dbox_move_corner_in_XY_plane (box, 2, new_pt);
212         }
213         if (axes & Box3D::Y) {
214             vp = this->get_vanishing_point (Box3D::Y);
215             new_pt = vp->get_pos() + box->ratio_y * (box->corners[0] - vp->get_pos());
216             sp_3dbox_move_corner_in_XY_plane (box, 2, new_pt);
217         }
218         if (axes & Box3D::Z) {
219             vp = this->get_vanishing_point (Box3D::Z);
220             new_pt = vp->get_pos() + box->ratio_z * (box->corners[0] - vp->get_pos());
221             sp_3dbox_move_corner_in_Z_direction (box, 4, new_pt);
222         }                
224         sp_3dbox_set_shape (box, true);
225         // FIXME: Is there a way update the knots without accessing the
226         //        statically linked function knotholder_update_knots?
227         SPEventContext *ec = inkscape_active_event_context();
228         g_assert (ec != NULL);
229         if (ec->shape_knot_holder != NULL) {
230             knotholder_update_knots(ec->shape_knot_holder, (SPItem *) box);
231         }
232     }
235 void
236 Perspective3D::update_box_reprs ()
238     for (GSList *i = this->boxes; i != NULL; i = i->next) {
239         SP_OBJECT(SP_3DBOX (i->data))->updateRepr(SP_OBJECT_WRITE_EXT);
240     }
243 // FIXME: We get compiler errors when we try to move the code from sp_3dbox_get_perspective_string to this function
244 /***
245 gchar *
246 Perspective3D::svg_string ()
249 ***/
251 } // namespace Box3D 
252  
253 /*
254   Local Variables:
255   mode:c++
256   c-file-style:"stroustrup"
257   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
258   indent-tabs-mode:nil
259   fill-column:99
260   End:
261 */
262 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :