d4990fd397b5b99ad9d8c5b901dfd0b15c2e943e
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);
102 }
104 VanishingPoint *
105 Perspective3D::get_vanishing_point (Box3D::Axis const dir)
106 {
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 }
126 }
128 void
129 Perspective3D::set_vanishing_point (Box3D::Axis const dir, VanishingPoint const &pt)
130 {
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 }
145 }
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)
149 {
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;
169 }
171 void
172 Perspective3D::add_box (SP3DBox *box)
173 {
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);
180 }
182 void
183 Perspective3D::remove_box (const SP3DBox *box)
184 {
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);
189 }
191 bool
192 Perspective3D::has_box (const SP3DBox *box)
193 {
194 return (g_slist_find (this->boxes, box) != NULL);
195 }
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)
202 {
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 }
233 }
235 void
236 Perspective3D::update_box_reprs ()
237 {
238 for (GSList *i = this->boxes; i != NULL; i = i->next) {
239 SP_OBJECT(SP_3DBOX (i->data))->updateRepr(SP_OBJECT_WRITE_EXT);
240 }
241 }
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 ()
247 {
248 }
249 ***/
251 } // namespace Box3D
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 :