1 #define __SP_3DBOX_C__
3 /*
4 * SVG <box3d> implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Maximilian Albert <Anhalter42@gmx.de>
10 *
11 * Copyright (C) 2007 Authors
12 * Copyright (C) 1999-2002 Lauris Kaplinski
13 * Copyright (C) 2000-2001 Ximian, Inc.
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 */
18 #include <glibmm/i18n.h>
19 #include "attributes.h"
20 #include "svg/stringstream.h"
21 #include "box3d.h"
22 #include "desktop-handles.h"
24 static void sp_3dbox_class_init(SP3DBoxClass *klass);
25 static void sp_3dbox_init(SP3DBox *box3d);
27 static void sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
28 static void sp_3dbox_release (SPObject *object);
29 static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value);
30 static void sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags);
31 static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
33 static gchar *sp_3dbox_description(SPItem *item);
35 //static void sp_3dbox_set_shape(SPShape *shape);
36 //static void sp_3dbox_set_shape(SP3DBox *box3d);
38 static void sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value);
39 static void sp_3dbox_update_perspective (Box3D::Perspective3D *persp, const gchar *value);
40 static gchar * sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id);
41 static std::pair<gdouble, gdouble> sp_3dbox_get_coord_pair_from_string (const gchar *);
42 static gchar * sp_3dbox_get_perspective_string (SP3DBox *box);
44 static SPGroupClass *parent_class;
46 static gint counter = 0;
48 GType
49 sp_3dbox_get_type(void)
50 {
51 static GType type = 0;
53 if (!type) {
54 GTypeInfo info = {
55 sizeof(SP3DBoxClass),
56 NULL, /* base_init */
57 NULL, /* base_finalize */
58 (GClassInitFunc) sp_3dbox_class_init,
59 NULL, /* class_finalize */
60 NULL, /* class_data */
61 sizeof(SP3DBox),
62 16, /* n_preallocs */
63 (GInstanceInitFunc) sp_3dbox_init,
64 NULL, /* value_table */
65 };
66 type = g_type_register_static(SP_TYPE_GROUP, "SP3DBox", &info, (GTypeFlags) 0);
67 }
69 return type;
70 }
72 static void
73 sp_3dbox_class_init(SP3DBoxClass *klass)
74 {
75 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
76 SPItemClass *item_class = (SPItemClass *) klass;
78 parent_class = (SPGroupClass *) g_type_class_ref(SP_TYPE_GROUP);
80 sp_object_class->build = sp_3dbox_build;
81 sp_object_class->set = sp_3dbox_set;
82 sp_object_class->write = sp_3dbox_write;
83 sp_object_class->update = sp_3dbox_update;
84 sp_object_class->release = sp_3dbox_release;
86 item_class->description = sp_3dbox_description;
87 }
89 static void
90 sp_3dbox_init(SP3DBox *box)
91 {
92 for (int i = 0; i < 8; ++i) box->corners[i] = NR::Point(0,0);
93 for (int i = 0; i < 6; ++i) box->faces[i] = NULL;
94 }
96 static void
97 sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
98 {
99 if (((SPObjectClass *) (parent_class))->build) {
100 ((SPObjectClass *) (parent_class))->build(object, document, repr);
101 }
103 SP3DBox *box = SP_3DBOX (object);
105 box->my_counter = counter++;
107 /* we initialize the z-orders to zero so that they are updated during dragging */
108 for (int i = 0; i < 6; ++i) {
109 box->z_orders[i] = 0;
110 }
112 box->front_bits = 0x0;
115 if (repr->attribute ("inkscape:perspective") == NULL) {
116 // we are creating a new box; link it to the current perspective
117 document->current_perspective->add_box (box);
118 } else {
119 // create a new perspective that we can compare with existing ones
120 Box3D::Perspective3D *persp = new Box3D::Perspective3D (Box3D::VanishingPoint (0,0),
121 Box3D::VanishingPoint (0,0),
122 Box3D::VanishingPoint (0,0),
123 document);
124 sp_3dbox_update_perspective (persp, repr->attribute ("inkscape:perspective"));
125 Box3D::Perspective3D *comp = document->find_perspective (persp);
126 if (comp == NULL) {
127 // perspective doesn't exist yet
128 document->add_perspective (persp);
129 persp->add_box (box);
130 } else {
131 // link the box to the existing perspective and delete the temporary one
132 comp->add_box (box);
133 delete persp;
134 //g_assert (Box3D::get_persp_of_box (box) == comp);
136 // FIXME: If the paths of the box's faces do not correspond to the svg representation of the perspective
137 // the box is shown with a "wrong" initial shape that is only corrected after dragging.
138 // Should we "repair" this by updating the paths at the end of sp_3dbox_build()?
139 // Maybe it would be better to simply destroy and rebuild them in sp_3dbox_link_to_existing_paths().
140 }
141 }
143 sp_object_read_attr(object, "inkscape:box3dcornerA");
144 sp_object_read_attr(object, "inkscape:box3dcornerB");
145 sp_object_read_attr(object, "inkscape:box3dcornerC");
147 // TODO: We create all faces in the beginning, but only the non-degenerate ones
148 // should be written to the svg representation later in sp_3dbox_write.
149 Box3D::Axis cur_plane, axis, dir1, dir2;
150 Box3D::FrontOrRear cur_pos;
151 for (int i = 0; i < 3; ++i) {
152 for (int j = 0; j < 2; ++j) {
153 cur_plane = Box3D::planes[i];
154 cur_pos = Box3D::face_positions[j];
155 // FIXME: The following code could theoretically be moved to
156 // the constructor of Box3DFace (but see the comment there).
157 axis = (cur_pos == Box3D::FRONT ? Box3D::NONE : Box3D::third_axis_direction (cur_plane));
158 dir1 = extract_first_axis_direction (cur_plane);
159 dir2 = extract_second_axis_direction (cur_plane);
161 box->faces[Box3D::face_to_int(cur_plane ^ cur_pos)] =
162 new Box3DFace (box, box->corners[axis], box->corners[axis ^ dir1],
163 box->corners[axis ^ dir1 ^ dir2], box->corners[axis ^ dir2],
164 cur_plane, cur_pos);
165 }
166 }
168 // Check whether the paths of the faces of the box need to be linked to existing paths in the
169 // document (e.g., after a 'redo' operation or after opening a file) and do so if necessary.
170 sp_3dbox_link_to_existing_paths (box, repr);
172 sp_3dbox_set_ratios (box, Box3D::XYZ);
174 // Store the center (if it already exists) and certain corners for later use during center-dragging
175 NR::Maybe<NR::Point> cen = sp_3dbox_get_center (box);
176 if (cen) {
177 box->old_center = *cen;
178 }
179 box->old_corner2 = box->corners[2];
180 box->old_corner1 = box->corners[1];
181 box->old_corner0 = box->corners[0];
182 box->old_corner3 = box->corners[3];
183 box->old_corner5 = box->corners[5];
184 box->old_corner7 = box->corners[7];
185 }
187 static void
188 sp_3dbox_release (SPObject *object)
189 {
190 SP3DBox *box = SP_3DBOX(object);
191 for (int i = 0; i < 6; ++i) {
192 if (box->faces[i]) {
193 delete box->faces[i]; // FIXME: Anything else to do? Do we need to clean up the face first?
194 }
195 }
197 // FIXME: We do not duplicate perspectives if they are the same for several boxes.
198 // Thus, don't delete the perspective when deleting a box but rather unlink the box from it.
199 SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->remove_box (box);
201 if (((SPObjectClass *) parent_class)->release) {
202 ((SPObjectClass *) parent_class)->release (object);
203 }
204 }
206 static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
207 {
208 switch (key) {
209 case SP_ATTR_INKSCAPE_3DBOX_CORNER_A:
210 sp_3dbox_update_corner_with_value_from_svg (object, 2, value);
211 break;
212 case SP_ATTR_INKSCAPE_3DBOX_CORNER_B:
213 sp_3dbox_update_corner_with_value_from_svg (object, 1, value);
214 break;
215 case SP_ATTR_INKSCAPE_3DBOX_CORNER_C:
216 sp_3dbox_update_corner_with_value_from_svg (object, 5, value);
217 break;
218 case SP_ATTR_INKSCAPE_3DBOX_PERSPECTIVE:
219 {
220 SP3DBox *box = SP_3DBOX (object);
221 sp_3dbox_update_perspective (SP_OBJECT_DOCUMENT (object)->get_persp_of_box (box), value);
222 break;
223 }
224 default:
225 if (((SPObjectClass *) (parent_class))->set) {
226 ((SPObjectClass *) (parent_class))->set(object, key, value);
227 }
228 break;
229 }
230 }
232 static void
233 sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags)
234 {
235 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
236 SP3DBox *box = SP_3DBOX(object);
237 sp_3dbox_link_to_existing_paths (box, SP_OBJECT_REPR(object));
238 }
240 /* Invoke parent method */
241 if (((SPObjectClass *) (parent_class))->update)
242 ((SPObjectClass *) (parent_class))->update(object, ctx, flags);
243 }
247 static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
248 {
249 SP3DBox *box = SP_3DBOX(object);
250 // FIXME: How to handle other contexts???
251 // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
252 if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
253 return repr;
255 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
256 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
257 repr = xml_doc->createElement("svg:g");
258 repr->setAttribute("sodipodi:type", "inkscape:3dbox");
259 /* Hook paths to the faces of the box */
260 for (int i = 0; i < 6; ++i) {
261 box->faces[i]->hook_path_to_3dbox();
262 }
263 }
265 for (int i = 0; i < 6; ++i) {
266 box->faces[i]->set_path_repr();
267 }
269 if (flags & SP_OBJECT_WRITE_EXT) {
270 gchar *str;
271 str = sp_3dbox_get_corner_coords_string (box, 2);
272 repr->setAttribute("inkscape:box3dcornerA", str);
274 str = sp_3dbox_get_corner_coords_string (box, 1);
275 repr->setAttribute("inkscape:box3dcornerB", str);
277 str = sp_3dbox_get_corner_coords_string (box, 5);
278 repr->setAttribute("inkscape:box3dcornerC", str);
280 str = sp_3dbox_get_perspective_string (box);
281 repr->setAttribute("inkscape:perspective", str);
282 sp_3dbox_set_ratios (box);
284 g_free ((void *) str);
286 /* store center and construction-corners for later use during center-dragging */
287 NR::Maybe<NR::Point> cen = sp_3dbox_get_center (box);
288 if (cen) {
289 box->old_center = *cen;
290 }
291 box->old_corner2 = box->corners[2];
292 box->old_corner1 = box->corners[1];
293 box->old_corner0 = box->corners[0];
294 box->old_corner3 = box->corners[3];
295 box->old_corner5 = box->corners[5];
296 box->old_corner7 = box->corners[7];
297 }
299 if (((SPObjectClass *) (parent_class))->write) {
300 ((SPObjectClass *) (parent_class))->write(object, repr, flags);
301 }
303 return repr;
304 }
306 static gchar *
307 sp_3dbox_description(SPItem *item)
308 {
309 g_return_val_if_fail(SP_IS_3DBOX(item), NULL);
311 return g_strdup(_("<b>3D Box</b>"));
312 }
314 void sp_3dbox_set_ratios (SP3DBox *box, Box3D::Axis axes)
315 {
316 Box3D::Perspective3D *persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
317 NR::Point pt;
319 if (axes & Box3D::X) {
320 pt = persp->get_vanishing_point (Box3D::X)->get_pos();
321 box->ratio_x = NR::L2 (pt - box->corners[2]) / NR::L2 (pt - box->corners[3]);
322 }
324 if (axes & Box3D::Y) {
325 pt = persp->get_vanishing_point (Box3D::Y)->get_pos();
326 box->ratio_y = NR::L2 (pt - box->corners[2]) / NR::L2 (pt - box->corners[0]);
327 }
329 if (axes & Box3D::Z) {
330 pt = persp->get_vanishing_point (Box3D::Z)->get_pos();
331 box->ratio_z = NR::L2 (pt - box->corners[4]) / NR::L2 (pt - box->corners[0]);
332 }
333 }
335 void
336 sp_3dbox_switch_front_face (SP3DBox *box, Box3D::Axis axis)
337 {
338 if (SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->get_vanishing_point (axis)->is_finite()) {
339 box->front_bits = box->front_bits ^ axis;
340 }
341 }
344 void
345 sp_3dbox_position_set (SP3DBoxContext &bc)
346 {
347 SP3DBox *box3d = SP_3DBOX(bc.item);
349 sp_3dbox_set_shape(box3d);
351 // FIXME: Why does the following call not automatically update the children
352 // of box3d (which is an SPGroup, which should do this)?
353 //SP_OBJECT(box3d)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
355 /**
356 SP_OBJECT(box3d->path_face1)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
357 SP_OBJECT(box3d->path_face2)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
358 SP_OBJECT(box3d->path_face3)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
359 SP_OBJECT(box3d->path_face4)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
360 SP_OBJECT(box3d->path_face5)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
361 SP_OBJECT(box3d->path_face6)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
362 ***/
363 }
365 static void
366 sp_3dbox_set_shape_from_points (SP3DBox *box, NR::Point const &cornerA, NR::Point const &cornerB, NR::Point const &cornerC)
367 {
368 sp_3dbox_recompute_corners (box, cornerA, cornerB, cornerC);
370 // FIXME: How to handle other contexts???
371 // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
372 if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
373 return;
374 SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
376 if (bc->extruded) {
377 box->faces[0]->set_corners (box->corners[0], box->corners[4], box->corners[6], box->corners[2]);
378 box->faces[1]->set_corners (box->corners[1], box->corners[5], box->corners[7], box->corners[3]);
379 box->faces[2]->set_corners (box->corners[0], box->corners[1], box->corners[5], box->corners[4]);
380 box->faces[3]->set_corners (box->corners[2], box->corners[3], box->corners[7], box->corners[6]);
381 box->faces[5]->set_corners (box->corners[4], box->corners[5], box->corners[7], box->corners[6]);
382 }
383 box->faces[4]->set_corners (box->corners[0], box->corners[1], box->corners[3], box->corners[2]);
385 sp_3dbox_update_curves (box);
386 }
388 void
389 // FIXME: Note that this is _not_ the virtual set_shape() method inherited from SPShape,
390 // since SP3DBox is inherited from SPGroup. The following method is "artificially"
391 // called from sp_3dbox_update().
392 //sp_3dbox_set_shape(SPShape *shape)
393 sp_3dbox_set_shape(SP3DBox *box, bool use_previous_corners)
394 {
395 if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
396 return;
397 SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
399 if (!use_previous_corners) {
400 sp_3dbox_set_shape_from_points (box, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
401 } else {
402 sp_3dbox_set_shape_from_points (box, box->corners[2], box->corners[1], box->corners[5]);
403 }
404 }
407 void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const A, NR::Point const B, NR::Point const C)
408 {
409 sp_3dbox_move_corner_in_XY_plane (box, 2, A);
410 sp_3dbox_move_corner_in_XY_plane (box, 1, B);
411 sp_3dbox_move_corner_in_Z_direction (box, 5, C);
412 }
414 inline static double
415 normalized_angle (double angle) {
416 if (angle < -M_PI) {
417 return angle + 2*M_PI;
418 } else if (angle > M_PI) {
419 return angle - 2*M_PI;
420 }
421 return angle;
422 }
424 static gdouble
425 sp_3dbox_corner_angle_to_VP (SP3DBox *box, Box3D::Axis axis, guint extreme_corner)
426 {
427 Box3D::VanishingPoint *vp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->get_vanishing_point (axis);
428 NR::Point dir;
430 if (vp->is_finite()) {
431 dir = NR::unit_vector (vp->get_pos() - box->corners[extreme_corner]);
432 } else {
433 dir = NR::unit_vector (vp->v_dir);
434 }
436 return atan2 (dir[NR::Y], dir[NR::X]);
437 }
440 bool sp_3dbox_recompute_z_orders (SP3DBox *box)
441 {
442 guint new_z_orders[6];
444 // TODO: Determine the front corner depending on the distance from VPs and/or the user presets
445 guint front_corner = sp_3dbox_get_front_corner_id (box);
447 gdouble dir_1x = sp_3dbox_corner_angle_to_VP (box, Box3D::X, front_corner);
448 gdouble dir_3x = sp_3dbox_corner_angle_to_VP (box, Box3D::X, front_corner ^ Box3D::Y);
450 gdouble dir_1y = sp_3dbox_corner_angle_to_VP (box, Box3D::Y, front_corner);
451 //gdouble dir_0y = sp_3dbox_corner_angle_to_VP (box, Box3D::Y, front_corner ^ Box3D::X);
453 gdouble dir_1z = sp_3dbox_corner_angle_to_VP (box, Box3D::Z, front_corner);
454 gdouble dir_3z = sp_3dbox_corner_angle_to_VP (box, Box3D::Z, front_corner ^ Box3D::Y);
456 // Still not perfect, but only fails in some rather degenerate cases.
457 // I suspect that there is a more elegant model, though. :)
458 new_z_orders[0] = Box3D::face_containing_corner (Box3D::XY, front_corner);
459 if (normalized_angle (dir_1y - dir_1z) > 0) {
460 new_z_orders[1] = Box3D::face_containing_corner (Box3D::YZ, front_corner);
461 if (normalized_angle (dir_1x - dir_1z) > 0) {
462 new_z_orders[2] = Box3D::face_containing_corner (Box3D::XZ, front_corner ^ Box3D::Y);
463 } else {
464 new_z_orders[2] = Box3D::face_containing_corner (Box3D::XZ, front_corner);
465 }
466 } else {
467 if (normalized_angle (dir_3x - dir_3z) > 0) {
468 new_z_orders[1] = Box3D::face_containing_corner (Box3D::XZ, front_corner ^ Box3D::Y);
469 new_z_orders[2] = Box3D::face_containing_corner (Box3D::YZ, front_corner ^ Box3D::X);
470 } else {
471 if (normalized_angle (dir_1x - dir_1z) > 0) {
472 new_z_orders[1] = Box3D::face_containing_corner (Box3D::YZ, front_corner ^ Box3D::X);
473 new_z_orders[2] = Box3D::face_containing_corner (Box3D::XZ, front_corner);
474 } else {
475 new_z_orders[1] = Box3D::face_containing_corner (Box3D::XZ, front_corner);
476 new_z_orders[2] = Box3D::face_containing_corner (Box3D::YZ, front_corner ^ Box3D::X);
477 }
478 }
479 }
481 new_z_orders[3] = Box3D::opposite_face (new_z_orders[2]);
482 new_z_orders[4] = Box3D::opposite_face (new_z_orders[1]);
483 new_z_orders[5] = Box3D::opposite_face (new_z_orders[0]);
485 /* We only need to look for changes among the topmost three faces because the order
486 of the other ones is just inverted. */
487 if ((box->z_orders[0] != new_z_orders[0]) ||
488 (box->z_orders[1] != new_z_orders[1]) ||
489 (box->z_orders[2] != new_z_orders[2]))
490 {
491 for (int i = 0; i < 6; ++i) {
492 box->z_orders[i] = new_z_orders[i];
493 }
494 return true;
495 }
497 return false;
498 }
500 void sp_3dbox_set_z_orders (SP3DBox *box)
501 {
502 // For efficiency reasons, we only set the new z-orders if something really changed
503 if (sp_3dbox_recompute_z_orders (box)) {
504 box->faces[box->z_orders[0]]->lower_to_bottom ();
505 box->faces[box->z_orders[1]]->lower_to_bottom ();
506 box->faces[box->z_orders[2]]->lower_to_bottom ();
507 box->faces[box->z_orders[3]]->lower_to_bottom ();
508 box->faces[box->z_orders[4]]->lower_to_bottom ();
509 box->faces[box->z_orders[5]]->lower_to_bottom ();
510 }
511 }
513 void
514 sp_3dbox_update_curves (SP3DBox *box) {
515 for (int i = 0; i < 6; ++i) {
516 if (box->faces[i]) box->faces[i]->set_curve();
517 }
518 }
520 /**
521 * In some situations (e.g., after cloning boxes, undo & redo, or reading boxes from a file) there are
522 * paths already present in the document which correspond to the faces of newly created boxes, but their
523 * 'path' members don't link to them yet. The following function corrects this if necessary.
524 */
525 void
526 sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr) {
527 // TODO: We should probably destroy the existing paths and recreate them because we don't know
528 // precisely which path corresponds to which face. Does this make a difference?
529 // In sp_3dbox_write we write the correct paths anyway, don't we? But we could get into
530 // trouble at a later stage when we only write single faces for degenerate boxes.
532 SPDocument *document = SP_OBJECT_DOCUMENT(box);
533 guint face_id = 0;
535 for (Inkscape::XML::Node *i = sp_repr_children(repr); i != NULL; i = sp_repr_next(i)) {
536 if (face_id > 5) {
537 g_warning ("SVG representation of 3D boxes must contain 6 paths or less.\n");
538 break;
539 }
541 SPObject *face_object = document->getObjectByRepr((Inkscape::XML::Node *) i);
542 if (!SP_IS_PATH(face_object)) {
543 g_warning ("SVG representation of 3D boxes should only contain paths.\n");
544 continue;
545 }
546 // TODO: Currently we don't check whether all paths are being linked to different faces.
547 // This is no problem with valid SVG files. It may lead to crashes, however,
548 // in case a file is corrupt (e.g., two or more faces have identical descriptions).
549 gint id = Box3DFace::descr_to_id (i->attribute ("inkscape:box3dface"));
550 box->faces[id]->hook_path_to_3dbox(SP_PATH(face_object));
551 ++face_id;
552 }
553 if (face_id < 6) {
554 //g_warning ("SVG representation of 3D boxes should contain exactly 6 paths (degenerate boxes are not yet supported).\n");
555 // TODO: Check whether it is safe to add the remaining paths to the box and do so in case it is.
556 // (But we also land here for newly created boxes where we shouldn't add any paths because
557 // This is done in sp_3dbox_write later on.
558 }
559 }
561 void
562 sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes)
563 {
564 Box3D::Perspective3D * persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
566 NR::Point A (box->corners[id ^ Box3D::XY]);
567 if (Box3D::is_single_axis_direction (axes)) {
568 pt = Box3D::PerspectiveLine (box->corners[id], axes, persp).closest_to(pt);
569 }
571 /* set the 'front' corners */
572 box->corners[id] = pt;
574 Box3D::PerspectiveLine pl_one (A, Box3D::Y, persp);
575 Box3D::PerspectiveLine pl_two (pt, Box3D::X, persp);
576 box->corners[id ^ Box3D::X] = pl_one.meet(pl_two);
578 pl_one = Box3D::PerspectiveLine (A, Box3D::X, persp);
579 pl_two = Box3D::PerspectiveLine (pt, Box3D::Y, persp);
580 box->corners[id ^ Box3D::Y] = pl_one.meet(pl_two);
582 /* set the 'rear' corners */
583 NR::Point B (box->corners[id ^ Box3D::XYZ]);
585 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::X], Box3D::Z, persp);
586 pl_two = Box3D::PerspectiveLine (B, Box3D::Y, persp);
587 box->corners[id ^ Box3D::XZ] = pl_one.meet(pl_two);
589 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::XZ], Box3D::X, persp);
590 pl_two = Box3D::PerspectiveLine (pt, Box3D::Z, persp);
591 box->corners[id ^ Box3D::Z] = pl_one.meet(pl_two);
593 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::Z], Box3D::Y, persp);
594 pl_two = Box3D::PerspectiveLine (B, Box3D::X, persp);
595 box->corners[id ^ Box3D::YZ] = pl_one.meet(pl_two);
597 }
599 void
600 sp_3dbox_move_corner_in_Z_direction (SP3DBox *box, guint id, NR::Point pt, bool constrained)
601 {
602 if (!constrained) sp_3dbox_move_corner_in_XY_plane (box, id, pt, Box3D::XY);
604 Box3D::Perspective3D * persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
606 /* set the four corners of the face containing corners[id] */
607 box->corners[id] = Box3D::PerspectiveLine (box->corners[id], Box3D::Z, persp).closest_to(pt);
609 Box3D::PerspectiveLine pl_one (box->corners[id], Box3D::X, persp);
610 Box3D::PerspectiveLine pl_two (box->corners[id ^ Box3D::XZ], Box3D::Z, persp);
611 box->corners[id ^ Box3D::X] = pl_one.meet(pl_two);
613 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::X], Box3D::Y, persp);
614 pl_two = Box3D::PerspectiveLine (box->corners[id ^ Box3D::XYZ], Box3D::Z, persp);
615 box->corners[id ^ Box3D::XY] = pl_one.meet(pl_two);
617 pl_one = Box3D::PerspectiveLine (box->corners[id], Box3D::Y, persp);
618 pl_two = Box3D::PerspectiveLine (box->corners[id ^ Box3D::YZ], Box3D::Z, persp);
619 box->corners[id ^ Box3D::Y] = pl_one.meet(pl_two);
620 }
622 static void
623 sp_3dbox_reshape_edge_after_VP_toggling (SP3DBox *box, const guint corner, const Box3D::Axis axis, Box3D::Perspective3D *persp)
624 {
625 /* Hmm, perhaps we should simply use one of the corners as the pivot point.
626 But this way we minimize the amount of reshaping.
627 On second thought, we need to find a way to ensure that all boxes sharing the same
628 perspective are updated consistently _as a group_. That is, they should also retain
629 their relative positions towards each other. */
630 NR::Maybe<NR::Point> pt = sp_3dbox_get_midpoint_between_corners (box, corner, corner ^ axis);
631 g_return_if_fail (pt);
633 Box3D::Axis axis2 = ((axis == Box3D::Y) ? Box3D::X : Box3D::Y);
635 Box3D::PerspectiveLine line1 (box->corners[corner], axis2, persp);
636 Box3D::PerspectiveLine line2 (box->corners[corner ^ axis], axis2, persp);
638 Box3D::PerspectiveLine line3 (*pt, axis, persp);
640 NR::Point new_corner1 = line1.meet (line3);
641 NR::Point new_corner2 = line2.meet (line3);
643 box->corners[corner] = new_corner1;
644 box->corners[corner ^ axis] = new_corner2;
645 }
647 void
648 sp_3dbox_reshape_after_VP_toggling (SP3DBox *box, Box3D::Axis axis)
649 {
650 Box3D::Perspective3D *persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
651 std::pair<Box3D::Axis, Box3D::Axis> dirs = Box3D::get_remaining_axes (axis);
653 sp_3dbox_reshape_edge_after_VP_toggling (box, 0, axis, persp);
654 sp_3dbox_reshape_edge_after_VP_toggling (box, 0 ^ dirs.first, axis, persp);
655 sp_3dbox_reshape_edge_after_VP_toggling (box, 0 ^ dirs.first ^ dirs.second, axis, persp);
656 sp_3dbox_reshape_edge_after_VP_toggling (box, 0 ^ dirs.second, axis, persp);
657 }
659 NR::Maybe<NR::Point>
660 sp_3dbox_get_center (SP3DBox *box)
661 {
662 return sp_3dbox_get_midpoint_between_corners (box, 0, 7);
663 }
665 NR::Point
666 sp_3dbox_get_midpoint_in_axis_direction (NR::Point const &C, NR::Point const &D, Box3D::Axis axis, Box3D::Perspective3D *persp)
667 {
668 Box3D::PerspectiveLine pl (D, axis, persp);
669 return pl.pt_with_given_cross_ratio (C, D, -1.0);
670 }
672 // TODO: The following function can probably be rewritten in a much more elegant and robust way
673 // by using projective coordinates for all points and using the cross ratio.
674 NR::Maybe<NR::Point>
675 sp_3dbox_get_midpoint_between_corners (SP3DBox *box, guint id_corner1, guint id_corner2)
676 {
677 Box3D::Axis corner_axes = (Box3D::Axis) (id_corner1 ^ id_corner2);
679 // Is all this sufficiently precise also for degenerate cases?
680 if (sp_3dbox_corners_are_adjacent (id_corner1, id_corner2)) {
681 Box3D::Axis orth_dir = get_perpendicular_axis_direction (corner_axes);
683 Box3D::Line diag1 (box->corners[id_corner1], box->corners[id_corner2 ^ orth_dir]);
684 Box3D::Line diag2 (box->corners[id_corner1 ^ orth_dir], box->corners[id_corner2]);
685 NR::Maybe<NR::Point> adjacent_face_center = diag1.intersect(diag2);
687 if (!adjacent_face_center) return NR::Nothing();
689 Box3D::Perspective3D * persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
691 Box3D::PerspectiveLine pl (*adjacent_face_center, orth_dir, persp);
692 return pl.intersect(Box3D::PerspectiveLine(box->corners[id_corner1], corner_axes, persp));
693 } else {
694 Box3D::Axis dir = Box3D::extract_first_axis_direction (corner_axes);
695 Box3D::Line diag1 (box->corners[id_corner1], box->corners[id_corner2]);
696 Box3D::Line diag2 (box->corners[id_corner1 ^ dir], box->corners[id_corner2 ^ dir]);
697 return diag1.intersect(diag2);
698 }
699 }
701 static gchar *
702 sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id)
703 {
704 id = id % 8;
705 Inkscape::SVGOStringStream os;
706 os << box->corners[id][NR::X] << "," << box->corners[id][NR::Y];
707 return g_strdup(os.str().c_str());
708 }
710 static std::pair<gdouble, gdouble>
711 sp_3dbox_get_coord_pair_from_string (const gchar *coords)
712 {
713 gchar **coordpair = g_strsplit( coords, ",", 0);
714 // We might as well rely on g_ascii_strtod to convert the NULL pointer to 0.0,
715 // but we include the following test anyway
716 if (coordpair[0] == NULL || coordpair[1] == NULL) {
717 g_strfreev (coordpair);
718 g_warning ("Coordinate conversion failed.\n");
719 return std::make_pair(0.0, 0.0);
720 }
722 gdouble coord1 = g_ascii_strtod(coordpair[0], NULL);
723 gdouble coord2 = g_ascii_strtod(coordpair[1], NULL);
724 g_strfreev (coordpair);
726 return std::make_pair(coord1, coord2);
727 }
729 static gchar *
730 sp_3dbox_get_perspective_string (SP3DBox *box)
731 {
733 return sp_3dbox_get_svg_descr_of_persp (SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box));
734 }
736 gchar *
737 sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp)
738 {
739 // FIXME: We should move this code to perspective3d.cpp, but this yields compiler errors. Why?
740 Inkscape::SVGOStringStream os;
742 Box3D::VanishingPoint vp = *(persp->get_vanishing_point (Box3D::X));
743 os << vp[NR::X] << "," << vp[NR::Y] << ",";
744 os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
745 if (vp.is_finite()) {
746 os << "finite,";
747 } else {
748 os << "infinite,";
749 }
751 vp = *(persp->get_vanishing_point (Box3D::Y));
752 os << vp[NR::X] << "," << vp[NR::Y] << ",";
753 os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
754 if (vp.is_finite()) {
755 os << "finite,";
756 } else {
757 os << "infinite,";
758 }
760 vp = *(persp->get_vanishing_point (Box3D::Z));
761 os << vp[NR::X] << "," << vp[NR::Y] << ",";
762 os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
763 if (vp.is_finite()) {
764 os << "finite";
765 } else {
766 os << "infinite";
767 }
769 return g_strdup(os.str().c_str());
770 }
772 // auxiliary function
773 static std::pair<NR::Point, NR::Point>
774 sp_3dbox_new_midpoints (Box3D::Perspective3D *persp, Box3D::Axis axis, NR::Point const &M0, NR::Point const &M, NR::Point const &A, NR::Point const &B)
775 {
776 double cr1 = Box3D::cross_ratio (*persp->get_vanishing_point (axis), M0, M, A);
777 double cr2 = Box3D::cross_ratio (*persp->get_vanishing_point (axis), M, B, M0);
778 if (fabs (cr1 - 1) < Box3D::epsilon) {
779 // FIXME: cr == 1 is a degenerate case; how should we deal with it?
780 return std::make_pair (NR::Point (0,0), NR::Point (0,0));
781 }
782 Box3D::PerspectiveLine pl (M0, axis, persp);
783 NR::Point B_new = pl.pt_with_given_cross_ratio (M0, M, cr1 / (cr1 - 1));
784 NR::Point A_new = pl.pt_with_given_cross_ratio (M0, M, 1 - cr2);
785 return std::make_pair (A_new, B_new);
786 }
788 void sp_3dbox_recompute_Z_corners_from_new_center (SP3DBox *box, NR::Point const new_center)
789 {
790 // TODO: Clean this function up
792 Box3D::Perspective3D *persp = sp_desktop_document (inkscape_active_desktop())->get_persp_of_box (box);
793 NR::Point old_center = box->old_center;
795 Box3D::PerspectiveLine aux_line1 (old_center, Box3D::Z, persp);
796 Box3D::PerspectiveLine aux_line2 (new_center, Box3D::Y, persp);
797 NR::Point Z1 = aux_line1.meet (aux_line2);
799 NR::Point A0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner2, box->old_corner0, Box3D::Y, persp));
800 NR::Point B0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner7, box->old_corner5, Box3D::Y, persp));
801 Box3D::PerspectiveLine aux_line3 (A0, Box3D::X, persp);
802 Box3D::PerspectiveLine aux_line4 (B0, Box3D::X, persp);
804 NR::Point C0 = aux_line3.meet (aux_line1);
805 NR::Point D0 = aux_line4.meet (aux_line1);
807 std::pair<NR::Point, NR::Point> new_midpts = sp_3dbox_new_midpoints (persp, Box3D::Z, old_center, Z1, C0, D0);
808 NR::Point C1 (new_midpts.first);
809 NR::Point D1 (new_midpts.second);
810 Box3D::PerspectiveLine aux_line5 (C1, Box3D::X, persp);
811 Box3D::PerspectiveLine aux_line6 (D1, Box3D::X, persp);
813 Box3D::PerspectiveLine aux_line7 (A0, Box3D::Z, persp);
814 Box3D::PerspectiveLine aux_line8 (B0, Box3D::Z, persp);
816 NR::Point A1 = aux_line5.meet (aux_line7);
817 NR::Point B1 = aux_line6.meet (aux_line8);
819 Box3D::PerspectiveLine aux_line9 (box->old_corner2, Box3D::Z, persp);
820 Box3D::PerspectiveLine aux_line10 (box->old_corner5, Box3D::Z, persp);
822 Box3D::PerspectiveLine aux_line11 (A1, Box3D::Y, persp);
823 Box3D::PerspectiveLine aux_line12 (B1, Box3D::Y, persp);
825 NR::Point new_corner2 = aux_line9.meet (aux_line11);
826 NR::Point new_corner5 = aux_line10.meet (aux_line12);
828 Box3D::PerspectiveLine aux_line13 (A1, Box3D::X, persp);
829 NR::Point E1 = aux_line13.meet (aux_line8);
830 Box3D::PerspectiveLine aux_line14 (E1, Box3D::Y, persp);
832 NR::Point new_corner1 = aux_line10.meet (aux_line14);
834 sp_3dbox_set_shape_from_points (box, new_corner2, new_corner1, new_corner5);
835 }
837 void sp_3dbox_recompute_XY_corners_from_new_center (SP3DBox *box, NR::Point const new_center)
838 {
839 // TODO: Clean this function up
841 Box3D::Perspective3D *persp = sp_desktop_document (inkscape_active_desktop())->get_persp_of_box (box);
842 NR::Point old_center = box->old_center;
844 NR::Point A0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner2, box->old_corner0, Box3D::Y, persp));
845 NR::Point B0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner1, box->old_corner3, Box3D::Y, persp));
847 /* we first move the box along the X-axis ... */
848 Box3D::PerspectiveLine aux_line1 (old_center, Box3D::X, persp);
849 Box3D::PerspectiveLine aux_line2 (new_center, Box3D::Y, persp);
850 NR::Point Z1 = aux_line1.meet (aux_line2);
852 Box3D::PerspectiveLine ref_line (B0, Box3D::X, persp);
853 Box3D::PerspectiveLine pline2 (old_center, Box3D::Z, persp);
854 Box3D::PerspectiveLine pline3 (Z1, Box3D::Z, persp);
855 NR::Point M0 = ref_line.meet (pline2);
856 NR::Point M1 = ref_line.meet (pline3);
858 std::pair<NR::Point, NR::Point> new_midpts = sp_3dbox_new_midpoints (persp, Box3D::X, M0, M1, A0, B0);
859 NR::Point A1 (new_midpts.first);
860 NR::Point B1 (new_midpts.second);
862 /* ... and then along the Y-axis */
863 Box3D::PerspectiveLine pline4 (box->old_corner1, Box3D::X, persp);
864 Box3D::PerspectiveLine pline5 (box->old_corner3, Box3D::X, persp);
865 Box3D::PerspectiveLine aux_line3 (M1, Box3D::Y, persp);
866 NR::Point C1 = aux_line3.meet (pline4);
867 NR::Point D1 = aux_line3.meet (pline5);
869 Box3D::PerspectiveLine aux_line4 (new_center, Box3D::Z, persp);
870 NR::Point M2 = aux_line4.meet (aux_line3);
872 std::pair<NR::Point, NR::Point> other_new_midpts = sp_3dbox_new_midpoints (persp, Box3D::Y, M1, M2, C1, D1);
873 NR::Point C2 (other_new_midpts.first);
874 NR::Point D2 (other_new_midpts.second);
876 Box3D::PerspectiveLine plXC (C2, Box3D::X, persp);
877 Box3D::PerspectiveLine plXD (D2, Box3D::X, persp);
878 Box3D::PerspectiveLine plYA (A1, Box3D::Y, persp);
879 Box3D::PerspectiveLine plYB (B1, Box3D::Y, persp);
881 NR::Point new_corner2 (plXD.meet (plYA));
882 NR::Point new_corner1 (plXC.meet (plYB));
884 NR::Point tmp_corner1 (pline4.meet (plYB));
885 Box3D::PerspectiveLine pline6 (box->old_corner5, Box3D::X, persp);
886 Box3D::PerspectiveLine pline7 (tmp_corner1, Box3D::Z, persp);
887 NR::Point tmp_corner5 (pline6.meet (pline7));
889 Box3D::PerspectiveLine pline8 (tmp_corner5, Box3D::Y, persp);
890 Box3D::PerspectiveLine pline9 (new_corner1, Box3D::Z, persp);
891 NR::Point new_corner5 (pline8.meet (pline9));
893 sp_3dbox_set_shape_from_points (box, new_corner2, new_corner1, new_corner5);
894 }
896 void sp_3dbox_update_perspective_lines()
897 {
898 SPEventContext *ec = inkscape_active_event_context();
899 if (!SP_IS_3DBOX_CONTEXT (ec))
900 return;
902 SP_3DBOX_CONTEXT (ec)->_vpdrag->updateLines();
903 }
905 /*
906 * Manipulates corner1 through corner4 to contain the indices of the corners
907 * from which the perspective lines in the direction of 'axis' emerge
908 */
909 void sp_3dbox_corners_for_perspective_lines (const SP3DBox * box, Box3D::Axis axis,
910 NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4)
911 {
912 // along which axis to switch when takint
913 Box3D::Axis switch_axis;
914 if (axis == Box3D::X || axis == Box3D::Y) {
915 switch_axis = (box->front_bits & axis) ? Box3D::Z : Box3D::NONE;
916 } else {
917 switch_axis = (box->front_bits & axis) ? Box3D::X : Box3D::NONE;
918 }
920 switch (axis) {
921 case Box3D::X:
922 corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
923 corner2 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
924 corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
925 corner4 = sp_3dbox_get_corner_along_edge (box, 6 ^ switch_axis, axis, Box3D::REAR);
926 break;
927 case Box3D::Y:
928 corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
929 corner2 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
930 corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
931 corner4 = sp_3dbox_get_corner_along_edge (box, 5 ^ switch_axis, axis, Box3D::REAR);
932 break;
933 case Box3D::Z:
934 corner1 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
935 corner2 = sp_3dbox_get_corner_along_edge (box, 3 ^ switch_axis, axis, Box3D::REAR);
936 corner3 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
937 corner4 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
938 break;
939 default:
940 // do nothing
941 break;
942 }
943 }
945 /**
946 * Returns the id of the corner on the edge along 'axis' and passing through 'corner' that
947 * lies on the front/rear face in this direction.
948 */
949 guint
950 sp_3dbox_get_corner_id_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
951 {
952 guint result;
953 guint other_corner = corner ^ axis;
954 Box3D::VanishingPoint *vp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box)->get_vanishing_point(axis);
955 if (vp->is_finite()) {
956 result = ( NR::L2 (vp->get_pos() - box->corners[corner])
957 < NR::L2 (vp->get_pos() - box->corners[other_corner]) ? other_corner : corner);
958 } else {
959 // clear the axis bit and switch to the appropriate corner along axis, depending on the value of front_bits
960 result = ((corner & (0xF ^ axis)) ^ (box->front_bits & axis));
961 }
963 if (rel_pos == Box3D::FRONT) {
964 return result;
965 } else {
966 return result ^ axis;
967 }
968 }
970 NR::Point
971 sp_3dbox_get_corner_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
972 {
973 return box->corners[sp_3dbox_get_corner_id_along_edge (box, corner, axis, rel_pos)];
974 }
976 guint
977 sp_3dbox_get_front_corner_id (const SP3DBox *box)
978 {
979 guint front_corner = 1; // this could in fact be any corner, but we choose the one that is normally in front
980 front_corner = sp_3dbox_get_corner_id_along_edge (box, front_corner, Box3D::X, Box3D::FRONT);
981 front_corner = sp_3dbox_get_corner_id_along_edge (box, front_corner, Box3D::Y, Box3D::FRONT);
982 front_corner = sp_3dbox_get_corner_id_along_edge (box, front_corner, Box3D::Z, Box3D::FRONT);
983 return front_corner;
984 }
986 // auxiliary functions
987 static void
988 sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value)
989 {
990 if (value == NULL) return;
991 SP3DBox *box = SP_3DBOX(object);
993 std::pair<gdouble, gdouble> coord_pair = sp_3dbox_get_coord_pair_from_string (value);
994 box->corners[corner_id] = NR::Point (coord_pair.first, coord_pair.second);
995 sp_3dbox_recompute_corners (box, box->corners[2], box->corners[1], box->corners[5]);
996 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
997 }
999 static void
1000 sp_3dbox_update_perspective (Box3D::Perspective3D *persp, const gchar *value)
1001 {
1002 // WARNING! This function changes the perspective associated to 'box'. Since there may be
1003 // many other boxes linked to the same perspective, their perspective is also changed.
1004 // If this behaviour is not desired in all cases, we need a different function.
1005 if (value == NULL) return;
1007 gchar **vps = g_strsplit( value, ",", 0);
1008 for (int i = 0; i < 15; ++i) {
1009 if (vps[i] == NULL) {
1010 g_warning ("Malformed svg attribute 'perspective'\n");
1011 return;
1012 }
1013 }
1015 persp->set_vanishing_point (Box3D::X, g_ascii_strtod (vps[0], NULL), g_ascii_strtod (vps[1], NULL),
1016 g_ascii_strtod (vps[2], NULL), g_ascii_strtod (vps[3], NULL),
1017 strcmp (vps[4], "finite") == 0 ? Box3D::VP_FINITE : Box3D::VP_INFINITE);
1018 persp->set_vanishing_point (Box3D::Y, g_ascii_strtod (vps[5], NULL), g_ascii_strtod (vps[6], NULL),
1019 g_ascii_strtod (vps[7], NULL), g_ascii_strtod (vps[8], NULL),
1020 strcmp (vps[9], "finite") == 0 ? Box3D::VP_FINITE : Box3D::VP_INFINITE);
1021 persp->set_vanishing_point (Box3D::Z, g_ascii_strtod (vps[10], NULL), g_ascii_strtod (vps[11], NULL),
1022 g_ascii_strtod (vps[12], NULL), g_ascii_strtod (vps[13], NULL),
1023 strcmp (vps[14], "finite") == 0 ? Box3D::VP_FINITE : Box3D::VP_INFINITE);
1025 // update the other boxes linked to the same perspective
1026 persp->reshape_boxes (Box3D::XYZ);
1027 }
1029 /*
1030 Local Variables:
1031 mode:c++
1032 c-file-style:"stroustrup"
1033 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1034 indent-tabs-mode:nil
1035 fill-column:99
1036 End:
1037 */
1038 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :