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"
23 static void sp_3dbox_class_init(SP3DBoxClass *klass);
24 static void sp_3dbox_init(SP3DBox *box3d);
26 static void sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
27 static void sp_3dbox_release (SPObject *object);
28 static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value);
29 static void sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags);
30 static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
32 static gchar *sp_3dbox_description(SPItem *item);
34 //static void sp_3dbox_set_shape(SPShape *shape);
35 //static void sp_3dbox_set_shape(SP3DBox *box3d);
37 static void sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value);
38 static void sp_3dbox_update_perspective (Box3D::Perspective3D *persp, const gchar *value);
39 static gchar * sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id);
40 static std::pair<gdouble, gdouble> sp_3dbox_get_coord_pair_from_string (const gchar *);
41 static gchar * sp_3dbox_get_perspective_string (SP3DBox *box);
43 static SPGroupClass *parent_class;
45 static gint counter = 0;
47 GType
48 sp_3dbox_get_type(void)
49 {
50 static GType type = 0;
52 if (!type) {
53 GTypeInfo info = {
54 sizeof(SP3DBoxClass),
55 NULL, /* base_init */
56 NULL, /* base_finalize */
57 (GClassInitFunc) sp_3dbox_class_init,
58 NULL, /* class_finalize */
59 NULL, /* class_data */
60 sizeof(SP3DBox),
61 16, /* n_preallocs */
62 (GInstanceInitFunc) sp_3dbox_init,
63 NULL, /* value_table */
64 };
65 type = g_type_register_static(SP_TYPE_GROUP, "SP3DBox", &info, (GTypeFlags) 0);
66 }
68 return type;
69 }
71 static void
72 sp_3dbox_class_init(SP3DBoxClass *klass)
73 {
74 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
75 SPItemClass *item_class = (SPItemClass *) klass;
77 parent_class = (SPGroupClass *) g_type_class_ref(SP_TYPE_GROUP);
79 sp_object_class->build = sp_3dbox_build;
80 sp_object_class->set = sp_3dbox_set;
81 sp_object_class->write = sp_3dbox_write;
82 sp_object_class->update = sp_3dbox_update;
83 sp_object_class->release = sp_3dbox_release;
85 item_class->description = sp_3dbox_description;
86 }
88 static void
89 sp_3dbox_init(SP3DBox *box)
90 {
91 for (int i = 0; i < 8; ++i) box->corners[i] = NR::Point(0,0);
92 for (int i = 0; i < 6; ++i) box->faces[i] = NULL;
93 }
95 static void
96 sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
97 {
98 if (((SPObjectClass *) (parent_class))->build) {
99 ((SPObjectClass *) (parent_class))->build(object, document, repr);
100 }
102 SP3DBox *box = SP_3DBOX (object);
104 box->my_counter = counter++;
106 box->front_bits = 0x0;
108 if (repr->attribute ("inkscape:perspective") == NULL) {
109 // we are creating a new box; link it to the current perspective
110 Box3D::Perspective3D::current_perspective->add_box (box);
111 } else {
112 // create a new perspective that we can compare with existing ones
113 Box3D::Perspective3D *persp = new Box3D::Perspective3D (Box3D::VanishingPoint (0,0),
114 Box3D::VanishingPoint (0,0),
115 Box3D::VanishingPoint (0,0));
116 sp_3dbox_update_perspective (persp, repr->attribute ("inkscape:perspective"));
117 Box3D::Perspective3D *comp = Box3D::Perspective3D::find_perspective (persp);
118 if (comp == NULL) {
119 // perspective doesn't exist yet
120 Box3D::Perspective3D::add_perspective (persp);
121 persp->add_box (box);
122 } else {
123 // link the box to the existing perspective and delete the temporary one
124 comp->add_box (box);
125 delete persp;
126 //g_assert (Box3D::get_persp_of_box (box) == comp);
128 // FIXME: If the paths of the box's faces do not correspond to the svg representation of the perspective
129 // the box is shown with a "wrong" initial shape that is only corrected after dragging.
130 // Should we "repair" this by updating the paths at the end of sp_3dbox_build()?
131 // Maybe it would be better to simply destroy and rebuild them in sp_3dbox_link_to_existing_paths().
132 }
133 }
135 sp_object_read_attr(object, "inkscape:box3dcornerA");
136 sp_object_read_attr(object, "inkscape:box3dcornerB");
137 sp_object_read_attr(object, "inkscape:box3dcornerC");
139 // TODO: We create all faces in the beginning, but only the non-degenerate ones
140 // should be written to the svg representation later in sp_3dbox_write.
141 Box3D::Axis cur_plane, axis, dir1, dir2;
142 Box3D::FrontOrRear cur_pos;
143 for (int i = 0; i < 3; ++i) {
144 for (int j = 0; j < 2; ++j) {
145 cur_plane = Box3D::planes[i];
146 cur_pos = Box3D::face_positions[j];
147 // FIXME: The following code could theoretically be moved to
148 // the constructor of Box3DFace (but see the comment there).
149 axis = (cur_pos == Box3D::FRONT ? Box3D::NONE : Box3D::third_axis_direction (cur_plane));
150 dir1 = extract_first_axis_direction (cur_plane);
151 dir2 = extract_second_axis_direction (cur_plane);
153 box->faces[Box3D::face_to_int(cur_plane ^ cur_pos)] =
154 new Box3DFace (box, box->corners[axis], box->corners[axis ^ dir1],
155 box->corners[axis ^ dir1 ^ dir2], box->corners[axis ^ dir2],
156 cur_plane, cur_pos);
157 }
158 }
160 // Check whether the paths of the faces of the box need to be linked to existing paths in the
161 // document (e.g., after a 'redo' operation or after opening a file) and do so if necessary.
162 sp_3dbox_link_to_existing_paths (box, repr);
164 sp_3dbox_set_ratios (box);
165 }
167 static void
168 sp_3dbox_release (SPObject *object)
169 {
170 SP3DBox *box = SP_3DBOX(object);
171 for (int i = 0; i < 6; ++i) {
172 if (box->faces[i]) {
173 delete box->faces[i]; // FIXME: Anything else to do? Do we need to clean up the face first?
174 }
175 }
177 // FIXME: We do not duplicate perspectives if they are the same for several boxes.
178 // Thus, don't delete the perspective when deleting a box but rather unlink the box from it.
179 Box3D::get_persp_of_box (box)->remove_box (box);
181 if (((SPObjectClass *) parent_class)->release) {
182 ((SPObjectClass *) parent_class)->release (object);
183 }
184 }
186 static void sp_3dbox_set(SPObject *object, unsigned int key, const gchar *value)
187 {
188 switch (key) {
189 case SP_ATTR_INKSCAPE_3DBOX_CORNER_A:
190 sp_3dbox_update_corner_with_value_from_svg (object, 2, value);
191 break;
192 case SP_ATTR_INKSCAPE_3DBOX_CORNER_B:
193 sp_3dbox_update_corner_with_value_from_svg (object, 1, value);
194 break;
195 case SP_ATTR_INKSCAPE_3DBOX_CORNER_C:
196 sp_3dbox_update_corner_with_value_from_svg (object, 5, value);
197 break;
198 case SP_ATTR_INKSCAPE_3DBOX_PERSPECTIVE:
199 sp_3dbox_update_perspective (Box3D::get_persp_of_box (SP_3DBOX (object)), value);
200 break;
201 default:
202 if (((SPObjectClass *) (parent_class))->set) {
203 ((SPObjectClass *) (parent_class))->set(object, key, value);
204 }
205 break;
206 }
207 }
209 static void
210 sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags)
211 {
212 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
213 SP3DBox *box = SP_3DBOX(object);
214 sp_3dbox_link_to_existing_paths (box, SP_OBJECT_REPR(object));
215 }
217 /* Invoke parent method */
218 if (((SPObjectClass *) (parent_class))->update)
219 ((SPObjectClass *) (parent_class))->update(object, ctx, flags);
220 }
224 static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
225 {
226 SP3DBox *box = SP_3DBOX(object);
227 // FIXME: How to handle other contexts???
228 // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
229 if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
230 return repr;
232 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
233 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
234 repr = xml_doc->createElement("svg:g");
235 repr->setAttribute("sodipodi:type", "inkscape:3dbox");
236 /* Hook paths to the faces of the box */
237 for (int i = 0; i < 6; ++i) {
238 box->faces[i]->hook_path_to_3dbox();
239 }
240 }
242 for (int i = 0; i < 6; ++i) {
243 box->faces[i]->set_path_repr();
244 }
246 if (flags & SP_OBJECT_WRITE_EXT) {
247 gchar *str;
248 str = sp_3dbox_get_corner_coords_string (box, 2);
249 repr->setAttribute("inkscape:box3dcornerA", str);
251 str = sp_3dbox_get_corner_coords_string (box, 1);
252 repr->setAttribute("inkscape:box3dcornerB", str);
254 str = sp_3dbox_get_corner_coords_string (box, 5);
255 repr->setAttribute("inkscape:box3dcornerC", str);
257 str = sp_3dbox_get_perspective_string (box);
258 repr->setAttribute("inkscape:perspective", str);
259 sp_3dbox_set_ratios (box);
261 g_free ((void *) str);
262 }
264 if (((SPObjectClass *) (parent_class))->write) {
265 ((SPObjectClass *) (parent_class))->write(object, repr, flags);
266 }
268 return repr;
269 }
271 static gchar *
272 sp_3dbox_description(SPItem *item)
273 {
274 g_return_val_if_fail(SP_IS_3DBOX(item), NULL);
276 return g_strdup(_("<b>3D Box</b>"));
277 }
279 void sp_3dbox_set_ratios (SP3DBox *box, Box3D::Axis axes)
280 {
281 Box3D::Perspective3D *persp = Box3D::get_persp_of_box (box);
282 NR::Point pt;
284 if (axes & Box3D::X) {
285 pt = persp->get_vanishing_point (Box3D::X)->get_pos();
286 box->ratio_x = NR::L2 (pt - box->corners[2]) / NR::L2 (pt - box->corners[3]);
287 }
289 if (axes & Box3D::Y) {
290 pt = persp->get_vanishing_point (Box3D::Y)->get_pos();
291 box->ratio_y = NR::L2 (pt - box->corners[2]) / NR::L2 (pt - box->corners[0]);
292 }
294 if (axes & Box3D::Z) {
295 pt = persp->get_vanishing_point (Box3D::Z)->get_pos();
296 box->ratio_z = NR::L2 (pt - box->corners[4]) / NR::L2 (pt - box->corners[0]);
297 }
298 }
300 void
301 sp_3dbox_switch_front_face (SP3DBox *box, Box3D::Axis axis)
302 {
303 if (Box3D::get_persp_of_box (box)->get_vanishing_point (axis)->is_finite()) {
304 box->front_bits = box->front_bits ^ axis;
305 }
306 }
309 void
310 sp_3dbox_position_set (SP3DBoxContext &bc)
311 {
312 SP3DBox *box3d = SP_3DBOX(bc.item);
314 sp_3dbox_set_shape(box3d);
316 // FIXME: Why does the following call not automatically update the children
317 // of box3d (which is an SPGroup, which should do this)?
318 //SP_OBJECT(box3d)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
320 /**
321 SP_OBJECT(box3d->path_face1)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
322 SP_OBJECT(box3d->path_face2)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
323 SP_OBJECT(box3d->path_face3)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
324 SP_OBJECT(box3d->path_face4)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
325 SP_OBJECT(box3d->path_face5)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
326 SP_OBJECT(box3d->path_face6)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
327 ***/
328 }
330 //static
331 void
332 // FIXME: Note that this is _not_ the virtual set_shape() method inherited from SPShape,
333 // since SP3DBox is inherited from SPGroup. The following method is "artificially"
334 // called from sp_3dbox_update().
335 //sp_3dbox_set_shape(SPShape *shape)
336 sp_3dbox_set_shape(SP3DBox *box3d, bool use_previous_corners)
337 {
338 // FIXME: How to handle other contexts???
339 // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
340 if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
341 return;
342 SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
344 /* Only update the curves during dragging; setting the svg representations
345 is expensive and only done once at the end */
346 if (!use_previous_corners) {
347 sp_3dbox_recompute_corners (box3d, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
348 } else {
349 sp_3dbox_recompute_corners (box3d, box3d->corners[2], box3d->corners[1], box3d->corners[5]);
350 }
351 if (bc->extruded) {
352 box3d->faces[0]->set_corners (box3d->corners[0], box3d->corners[4], box3d->corners[6], box3d->corners[2]);
353 box3d->faces[1]->set_corners (box3d->corners[1], box3d->corners[5], box3d->corners[7], box3d->corners[3]);
354 box3d->faces[2]->set_corners (box3d->corners[0], box3d->corners[1], box3d->corners[5], box3d->corners[4]);
355 box3d->faces[3]->set_corners (box3d->corners[2], box3d->corners[3], box3d->corners[7], box3d->corners[6]);
356 box3d->faces[5]->set_corners (box3d->corners[4], box3d->corners[5], box3d->corners[7], box3d->corners[6]);
357 }
358 box3d->faces[4]->set_corners (box3d->corners[0], box3d->corners[1], box3d->corners[3], box3d->corners[2]);
360 sp_3dbox_update_curves (box3d);
361 }
364 void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const A, NR::Point const B, NR::Point const C)
365 {
366 sp_3dbox_move_corner_in_XY_plane (box, 2, A);
367 sp_3dbox_move_corner_in_XY_plane (box, 1, B);
368 sp_3dbox_move_corner_in_Z_direction (box, 5, C);
369 }
371 void
372 sp_3dbox_update_curves (SP3DBox *box) {
373 for (int i = 0; i < 6; ++i) {
374 if (box->faces[i]) box->faces[i]->set_curve();
375 }
376 }
378 /**
379 * In some situations (e.g., after cloning boxes, undo & redo, or reading boxes from a file) there are
380 * paths already present in the document which correspond to the faces of newly created boxes, but their
381 * 'path' members don't link to them yet. The following function corrects this if necessary.
382 */
383 void
384 sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr) {
385 // TODO: We should probably destroy the existing paths and recreate them because we don't know
386 // precisely which path corresponds to which face. Does this make a difference?
387 // In sp_3dbox_write we write the correct paths anyway, don't we? But we could get into
388 // trouble at a later stage when we only write single faces for degenerate boxes.
390 SPDocument *document = SP_OBJECT_DOCUMENT(box);
391 guint face_id = 0;
393 for (Inkscape::XML::Node *i = sp_repr_children(repr); i != NULL; i = sp_repr_next(i)) {
394 if (face_id > 5) {
395 g_warning ("SVG representation of 3D boxes must contain 6 paths or less.\n");
396 break;
397 }
399 SPObject *face_object = document->getObjectByRepr((Inkscape::XML::Node *) i);
400 if (!SP_IS_PATH(face_object)) {
401 g_warning ("SVG representation of 3D boxes should only contain paths.\n");
402 continue;
403 }
404 box->faces[face_id]->hook_path_to_3dbox(SP_PATH(face_object));
405 ++face_id;
406 }
407 if (face_id < 6) {
408 //g_warning ("SVG representation of 3D boxes should contain exactly 6 paths (degenerate boxes are not yet supported).\n");
409 // TODO: Check whether it is safe to add the remaining paths to the box and do so in case it is.
410 // (But we also land here for newly created boxes where we shouldn't add any paths because
411 // This is done in sp_3dbox_write later on.
412 }
413 }
415 void
416 sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes)
417 {
418 Box3D::Perspective3D * persp = Box3D::get_persp_of_box (box);
420 NR::Point A (box->corners[id ^ Box3D::XY]);
421 if (Box3D::is_single_axis_direction (axes)) {
422 pt = Box3D::PerspectiveLine (box->corners[id], axes, persp).closest_to(pt);
423 }
425 /* set the 'front' corners */
426 box->corners[id] = pt;
428 Box3D::PerspectiveLine pl_one (A, Box3D::Y, persp);
429 Box3D::PerspectiveLine pl_two (pt, Box3D::X, persp);
430 box->corners[id ^ Box3D::X] = pl_one.meet(pl_two);
432 pl_one = Box3D::PerspectiveLine (A, Box3D::X, persp);
433 pl_two = Box3D::PerspectiveLine (pt, Box3D::Y, persp);
434 box->corners[id ^ Box3D::Y] = pl_one.meet(pl_two);
436 /* set the 'rear' corners */
437 NR::Point B (box->corners[id ^ Box3D::XYZ]);
439 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::X], Box3D::Z, persp);
440 pl_two = Box3D::PerspectiveLine (B, Box3D::Y, persp);
441 box->corners[id ^ Box3D::XZ] = pl_one.meet(pl_two);
443 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::XZ], Box3D::X, persp);
444 pl_two = Box3D::PerspectiveLine (pt, Box3D::Z, persp);
445 box->corners[id ^ Box3D::Z] = pl_one.meet(pl_two);
447 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::Z], Box3D::Y, persp);
448 pl_two = Box3D::PerspectiveLine (B, Box3D::X, persp);
449 box->corners[id ^ Box3D::YZ] = pl_one.meet(pl_two);
451 }
453 void
454 sp_3dbox_move_corner_in_Z_direction (SP3DBox *box, guint id, NR::Point pt, bool constrained)
455 {
456 if (!constrained) sp_3dbox_move_corner_in_XY_plane (box, id, pt, Box3D::XY);
458 Box3D::Perspective3D * persp = Box3D::get_persp_of_box (box);
460 /* set the four corners of the face containing corners[id] */
461 box->corners[id] = Box3D::PerspectiveLine (box->corners[id], Box3D::Z, persp).closest_to(pt);
463 Box3D::PerspectiveLine pl_one (box->corners[id], Box3D::X, persp);
464 Box3D::PerspectiveLine pl_two (box->corners[id ^ Box3D::XZ], Box3D::Z, persp);
465 box->corners[id ^ Box3D::X] = pl_one.meet(pl_two);
467 pl_one = Box3D::PerspectiveLine (box->corners[id ^ Box3D::X], Box3D::Y, persp);
468 pl_two = Box3D::PerspectiveLine (box->corners[id ^ Box3D::XYZ], Box3D::Z, persp);
469 box->corners[id ^ Box3D::XY] = pl_one.meet(pl_two);
471 pl_one = Box3D::PerspectiveLine (box->corners[id], Box3D::Y, persp);
472 pl_two = Box3D::PerspectiveLine (box->corners[id ^ Box3D::YZ], Box3D::Z, persp);
473 box->corners[id ^ Box3D::Y] = pl_one.meet(pl_two);
474 }
476 NR::Maybe<NR::Point>
477 sp_3dbox_get_center (SP3DBox *box)
478 {
479 return sp_3dbox_get_midpoint_between_corners (box, 0, 7);
480 }
482 NR::Maybe<NR::Point>
483 sp_3dbox_get_midpoint_between_corners (SP3DBox *box, guint id_corner1, guint id_corner2)
484 {
485 Box3D::Axis corner_axes = (Box3D::Axis) (id_corner1 ^ id_corner2);
487 // Is all this sufficiently precise also for degenerate cases?
488 if (sp_3dbox_corners_are_adjacent (id_corner1, id_corner2)) {
489 Box3D::Axis orth_dir = get_perpendicular_axis_direction (corner_axes);
491 Box3D::Line diag1 (box->corners[id_corner1], box->corners[id_corner2 ^ orth_dir]);
492 Box3D::Line diag2 (box->corners[id_corner1 ^ orth_dir], box->corners[id_corner2]);
493 NR::Maybe<NR::Point> adjacent_face_center = diag1.intersect(diag2);
495 if (!adjacent_face_center) return NR::Nothing();
497 Box3D::Perspective3D * persp = Box3D::get_persp_of_box (box);
499 Box3D::PerspectiveLine pl (*adjacent_face_center, orth_dir, persp);
500 return pl.intersect(Box3D::PerspectiveLine(box->corners[id_corner1], corner_axes, persp));
501 } else {
502 Box3D::Axis dir = Box3D::extract_first_axis_direction (corner_axes);
503 Box3D::Line diag1 (box->corners[id_corner1], box->corners[id_corner2]);
504 Box3D::Line diag2 (box->corners[id_corner1 ^ dir], box->corners[id_corner2 ^ dir]);
505 return diag1.intersect(diag2);
506 }
507 }
509 static gchar *
510 sp_3dbox_get_corner_coords_string (SP3DBox *box, guint id)
511 {
512 id = id % 8;
513 Inkscape::SVGOStringStream os;
514 os << box->corners[id][NR::X] << "," << box->corners[id][NR::Y];
515 return g_strdup(os.str().c_str());
516 }
518 static std::pair<gdouble, gdouble>
519 sp_3dbox_get_coord_pair_from_string (const gchar *coords)
520 {
521 gchar **coordpair = g_strsplit( coords, ",", 0);
522 // We might as well rely on g_ascii_strtod to convert the NULL pointer to 0.0,
523 // but we include the following test anyway
524 if (coordpair[0] == NULL || coordpair[1] == NULL) {
525 g_strfreev (coordpair);
526 g_warning ("Coordinate conversion failed.\n");
527 return std::make_pair(0.0, 0.0);
528 }
530 gdouble coord1 = g_ascii_strtod(coordpair[0], NULL);
531 gdouble coord2 = g_ascii_strtod(coordpair[1], NULL);
532 g_strfreev (coordpair);
534 return std::make_pair(coord1, coord2);
535 }
537 static gchar *
538 sp_3dbox_get_perspective_string (SP3DBox *box)
539 {
541 return sp_3dbox_get_svg_descr_of_persp (Box3D::get_persp_of_box (box));
542 }
544 gchar *
545 sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp)
546 {
547 // FIXME: We should move this code to perspective3d.cpp, but this yields compiler errors. Why?
548 Inkscape::SVGOStringStream os;
550 Box3D::VanishingPoint vp = *(persp->get_vanishing_point (Box3D::X));
551 os << vp[NR::X] << "," << vp[NR::Y] << ",";
552 os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
553 if (vp.is_finite()) {
554 os << "finite,";
555 } else {
556 os << "infinite,";
557 }
559 vp = *(persp->get_vanishing_point (Box3D::Y));
560 os << vp[NR::X] << "," << vp[NR::Y] << ",";
561 os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
562 if (vp.is_finite()) {
563 os << "finite,";
564 } else {
565 os << "infinite,";
566 }
568 vp = *(persp->get_vanishing_point (Box3D::Z));
569 os << vp[NR::X] << "," << vp[NR::Y] << ",";
570 os << vp.v_dir[NR::X] << "," << vp.v_dir[NR::Y] << ",";
571 if (vp.is_finite()) {
572 os << "finite";
573 } else {
574 os << "infinite";
575 }
577 return g_strdup(os.str().c_str());
578 }
580 void sp_3dbox_update_perspective_lines()
581 {
582 SPEventContext *ec = inkscape_active_event_context();
583 if (!SP_IS_3DBOX_CONTEXT (ec))
584 return;
586 SP_3DBOX_CONTEXT (ec)->_vpdrag->updateLines();
587 }
589 /*
590 * Manipulates corner1 through corner4 to contain the indices of the corners
591 * from which the perspective lines in the direction of 'axis' emerge
592 */
593 void sp_3dbox_corners_for_perspective_lines (const SP3DBox * box, Box3D::Axis axis,
594 NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4)
595 {
596 // along which axis to switch when takint
597 Box3D::Axis switch_axis;
598 if (axis == Box3D::X || axis == Box3D::Y) {
599 switch_axis = (box->front_bits & axis) ? Box3D::Z : Box3D::NONE;
600 } else {
601 switch_axis = (box->front_bits & axis) ? Box3D::X : Box3D::NONE;
602 }
604 switch (axis) {
605 case Box3D::X:
606 corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
607 corner2 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
608 corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
609 corner4 = sp_3dbox_get_corner_along_edge (box, 6 ^ switch_axis, axis, Box3D::REAR);
610 break;
611 case Box3D::Y:
612 corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
613 corner2 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
614 corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
615 corner4 = sp_3dbox_get_corner_along_edge (box, 5 ^ switch_axis, axis, Box3D::REAR);
616 break;
617 case Box3D::Z:
618 corner1 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
619 corner2 = sp_3dbox_get_corner_along_edge (box, 3 ^ switch_axis, axis, Box3D::REAR);
620 corner3 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
621 corner4 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
622 break;
623 }
624 }
626 /**
627 * Returns the id of the corner on the edge along 'axis' and passing through 'corner' that
628 * lies on the front/rear face in this direction.
629 */
630 guint
631 sp_3dbox_get_corner_id_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
632 {
633 guint result;
634 guint other_corner = corner ^ axis;
635 Box3D::VanishingPoint *vp = Box3D::get_persp_of_box (box)->get_vanishing_point(axis);
636 if (vp->is_finite()) {
637 result = ( NR::L2 (vp->get_pos() - box->corners[corner])
638 < NR::L2 (vp->get_pos() - box->corners[other_corner]) ? other_corner : corner);
639 } else {
640 // clear the axis bit and switch to the appropriate corner along axis, depending on the value of front_bits
641 result = ((corner & (0xF ^ axis)) ^ (box->front_bits & axis));
642 }
644 if (rel_pos == Box3D::FRONT) {
645 return result;
646 } else {
647 return result ^ axis;
648 }
649 }
651 NR::Point
652 sp_3dbox_get_corner_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
653 {
654 return box->corners[sp_3dbox_get_corner_id_along_edge (box, corner, axis, rel_pos)];
655 }
657 // auxiliary functions
658 static void
659 sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value)
660 {
661 if (value == NULL) return;
662 SP3DBox *box = SP_3DBOX(object);
664 std::pair<gdouble, gdouble> coord_pair = sp_3dbox_get_coord_pair_from_string (value);
665 box->corners[corner_id] = NR::Point (coord_pair.first, coord_pair.second);
666 sp_3dbox_recompute_corners (box, box->corners[2], box->corners[1], box->corners[5]);
667 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
668 }
670 static void
671 sp_3dbox_update_perspective (Box3D::Perspective3D *persp, const gchar *value)
672 {
673 // WARNING! This function changes the perspective associated to 'box'. Since there may be
674 // many other boxes linked to the same perspective, their perspective is also changed.
675 // If this behaviour is not desired in all cases, we need a different function.
676 if (value == NULL) return;
678 gchar **vps = g_strsplit( value, ",", 0);
679 for (int i = 0; i < 15; ++i) {
680 if (vps[i] == NULL) {
681 g_warning ("Malformed svg attribute 'perspective'\n");
682 return;
683 }
684 }
686 persp->set_vanishing_point (Box3D::X, g_ascii_strtod (vps[0], NULL), g_ascii_strtod (vps[1], NULL),
687 g_ascii_strtod (vps[2], NULL), g_ascii_strtod (vps[3], NULL),
688 Box3D::VP_FINITE);
689 persp->set_vanishing_point (Box3D::Y, g_ascii_strtod (vps[5], NULL), g_ascii_strtod (vps[6], NULL),
690 g_ascii_strtod (vps[7], NULL), g_ascii_strtod (vps[8], NULL),
691 Box3D::VP_FINITE);
692 persp->set_vanishing_point (Box3D::Z, g_ascii_strtod (vps[10], NULL), g_ascii_strtod (vps[11], NULL),
693 g_ascii_strtod (vps[12], NULL), g_ascii_strtod (vps[13], NULL),
694 Box3D::VP_FINITE);
696 // update the other boxes linked to the same perspective
697 persp->reshape_boxes (Box3D::XYZ);
698 }
700 /*
701 Local Variables:
702 mode:c++
703 c-file-style:"stroustrup"
704 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
705 indent-tabs-mode:nil
706 fill-column:99
707 End:
708 */
709 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :