1 #define __BOX3D_SIDE_C__
3 /*
4 * 3D box face implementation
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-side.h"
15 #include "document.h"
16 #include "xml/document.h"
17 #include "xml/repr.h"
18 #include "display/curve.h"
19 #include "svg/svg.h"
20 #include "attributes.h"
21 #include "inkscape.h"
22 #include "persp3d.h"
23 #include "box3d-context.h"
24 #include "prefs-utils.h"
25 #include "desktop-style.h"
26 #include "box3d.h"
28 static void box3d_side_class_init (Box3DSideClass *klass);
29 static void box3d_side_init (Box3DSide *side);
31 static void box3d_side_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
32 static Inkscape::XML::Node *box3d_side_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
33 static void box3d_side_set (SPObject *object, unsigned int key, const gchar *value);
34 static void box3d_side_update (SPObject *object, SPCtx *ctx, guint flags);
36 //static gchar * box3d_side_description (SPItem * item);
37 //static void box3d_side_snappoints(SPItem const *item, SnapPointsIter p);
39 //static void box3d_side_set_shape (SPShape *shape);
40 //static void box3d_side_update_patheffect (SPShape *shape, bool write);
42 static void box3d_side_apply_style (Box3DSide *side);
43 static Proj::Pt3 box3d_side_corner (Box3DSide *side, guint index);
44 static std::vector<Proj::Pt3> box3d_side_corners (Box3DSide *side);
45 static gint box3d_side_descr_to_id (gchar const *descr);
47 static SPShapeClass *parent_class;
49 GType
50 box3d_side_get_type (void)
51 {
52 static GType type = 0;
54 if (!type) {
55 GTypeInfo info = {
56 sizeof (Box3DSideClass),
57 NULL, NULL,
58 (GClassInitFunc) box3d_side_class_init,
59 NULL, NULL,
60 sizeof (Box3DSide),
61 16,
62 (GInstanceInitFunc) box3d_side_init,
63 NULL, /* value_table */
64 };
65 type = g_type_register_static (SP_TYPE_SHAPE, "Box3DSide", &info, (GTypeFlags)0);
66 }
67 return type;
68 }
70 static void
71 box3d_side_class_init (Box3DSideClass *klass)
72 {
73 GObjectClass * gobject_class;
74 SPObjectClass * sp_object_class;
75 SPItemClass * item_class;
76 SPPathClass * path_class;
77 SPShapeClass * shape_class;
79 gobject_class = (GObjectClass *) klass;
80 sp_object_class = (SPObjectClass *) klass;
81 item_class = (SPItemClass *) klass;
82 path_class = (SPPathClass *) klass;
83 shape_class = (SPShapeClass *) klass;
85 parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE);
87 sp_object_class->build = box3d_side_build;
88 sp_object_class->write = box3d_side_write;
89 sp_object_class->set = box3d_side_set;
90 sp_object_class->update = box3d_side_update;
92 //item_class->description = box3d_side_description;
93 //item_class->snappoints = box3d_side_snappoints;
95 shape_class->set_shape = box3d_side_set_shape;
96 //shape_class->update_patheffect = box3d_side_update_patheffect;
97 }
99 static void
100 box3d_side_init (Box3DSide * side)
101 {
102 side->dir1 = Box3D::NONE;
103 side->dir2 = Box3D::NONE;
104 side->front_or_rear = Box3D::FRONT;
105 }
107 static void
108 box3d_side_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr)
109 {
110 if (((SPObjectClass *) parent_class)->build)
111 ((SPObjectClass *) parent_class)->build (object, document, repr);
113 sp_object_read_attr (object, "inkscape:box3dsidetype");
114 }
116 static Inkscape::XML::Node *
117 box3d_side_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
118 {
119 Box3DSide *side = SP_BOX3D_SIDE (object);
121 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
122 g_print ("Do we ever end up here?\n");
123 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
124 repr = xml_doc->createElement("svg:path");
125 repr->setAttribute("sodipodi:type", "inkscape:box3dside"); // FIXME: Does this double the
126 }
128 if (flags & SP_OBJECT_WRITE_EXT) {
129 sp_repr_set_int(repr, "inkscape:box3dsidetype", side->dir1 ^ side->dir2 ^ side->front_or_rear);
130 }
132 sp_shape_set_shape ((SPShape *) object); // FIXME: necessary? YES!
134 /* Duplicate the path */
135 SPCurve *curve = ((SPShape *) object)->curve;
136 //Nulls might be possible if this called iteratively
137 if ( !curve ) {
138 return NULL;
139 }
140 NArtBpath *bpath = SP_CURVE_BPATH(curve);
141 if ( !bpath ) {
142 return NULL;
143 }
144 char *d = sp_svg_write_path ( bpath );
145 repr->setAttribute("d", d);
146 g_free (d);
148 box3d_side_apply_style (side);
150 if (((SPObjectClass *) (parent_class))->write)
151 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
153 return repr;
154 }
156 static void
157 box3d_side_set (SPObject *object, unsigned int key, const gchar *value)
158 {
159 Box3DSide *side = SP_BOX3D_SIDE (object);
161 // TODO: In case the box was recreated (by undo, e.g.) we need to recreate the path
162 // (along with other info?) from the parent box.
164 /* fixme: we should really collect updates */
165 switch (key) {
166 case SP_ATTR_INKSCAPE_BOX3D_SIDE_TYPE:
167 if (value) {
168 guint desc = atoi (value);
170 if (!Box3D::is_face_id(desc)) {
171 g_print ("desc is not a face id: =%s=\n", value);
172 }
173 g_return_if_fail (Box3D::is_face_id (desc));
174 Box3D::Axis plane = (Box3D::Axis) (desc & 0x7);
175 plane = (Box3D::is_plane(plane) ? plane : Box3D::orth_plane_or_axis(plane));
176 side->dir1 = Box3D::extract_first_axis_direction(plane);
177 side->dir2 = Box3D::extract_second_axis_direction(plane);
178 side->front_or_rear = (Box3D::FrontOrRear) (desc & 0x8);
180 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
181 }
182 break;
183 default:
184 if (((SPObjectClass *) parent_class)->set)
185 ((SPObjectClass *) parent_class)->set (object, key, value);
186 break;
187 }
188 }
190 static void
191 box3d_side_update (SPObject *object, SPCtx *ctx, guint flags)
192 {
193 //g_print ("box3d_side_update\n");
194 if (flags & (SP_OBJECT_MODIFIED_FLAG |
195 //SP_OBJECT_CHILD_MODIFIED_FLAG |
196 SP_OBJECT_STYLE_MODIFIED_FLAG |
197 SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
198 /***
199 g_print ("\n\nIn box3d_side_update: ");
200 if (flags & SP_OBJECT_MODIFIED_FLAG) g_print ("SP_OBJECT_MODIFIED_FLAG ");
201 if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) g_print ("SP_OBJECT_CHILD_MODIFIED_FLAG ");
202 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) g_print ("SP_OBJECT_STYLE_MODIFIED_FLAG ");
203 if (flags & SP_OBJECT_VIEWPORT_MODIFIED_FLAG) g_print ("SP_OBJECT_VIEWPORT_MODIFIED_FLAG ");
204 g_print ("\n");
205 ***/
206 sp_shape_set_shape ((SPShape *) object);
207 }
209 if (((SPObjectClass *) parent_class)->update)
210 ((SPObjectClass *) parent_class)->update (object, ctx, flags);
211 }
213 /***
214 static void
215 box3d_side_update_patheffect(SPShape *shape, bool write)
216 {
217 box3d_side_set_shape(shape);
219 if (write) {
220 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
221 if ( shape->curve != NULL ) {
222 NArtBpath *abp = sp_curve_first_bpath(shape->curve);
223 if (abp) {
224 gchar *str = sp_svg_write_path(abp);
225 repr->setAttribute("d", str);
226 g_free(str);
227 } else {
228 repr->setAttribute("d", "");
229 }
230 } else {
231 repr->setAttribute("d", NULL);
232 }
233 }
235 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
236 }
237 ***/
239 /***
240 static gchar *
241 box3d_side_description (SPItem *item)
242 {
243 Box3DSide *side = SP_BOX3D_SIDE (item);
245 // while there will never be less than 3 vertices, we still need to
246 // make calls to ngettext because the pluralization may be different
247 // for various numbers >=3. The singular form is used as the index.
248 if (side->flatsided == false )
249 return g_strdup_printf (ngettext("<b>Star</b> with %d vertex",
250 "<b>Star</b> with %d vertices",
251 star->sides), star->sides);
252 else
253 return g_strdup_printf (ngettext("<b>Polygon</b> with %d vertex",
254 "<b>Polygon</b> with %d vertices",
255 star->sides), star->sides);
256 }
257 ***/
259 void
260 box3d_side_position_set (Box3DSide *side) {
261 box3d_side_set_shape (SP_SHAPE (side));
263 /* This call is responsible for live update of the sides during the initial drag */
264 SP_OBJECT(side)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
265 }
267 void
268 box3d_side_set_shape (SPShape *shape)
269 {
270 //g_print ("box3d_side_set_shape\n");
271 Box3DSide *side = SP_BOX3D_SIDE (shape);
272 if (!SP_OBJECT_DOCUMENT(side)->root) {
273 // avoid a warning caused by sp_document_height() (which is called from sp_item_i2d_affine() below)
274 // when reading a file containing 3D boxes
275 return;
276 }
278 if (!SP_IS_BOX3D(SP_OBJECT(side)->parent)) {
279 g_warning ("Parent of 3D box side is not a 3D box.\n");
280 /**
281 g_print ("Removing the inkscape:box3dside attribute and returning from box3d_side_set_shape().\n");
282 SP_OBJECT_REPR (shape)->setAttribute("sodipodi:type", NULL);
283 SP_OBJECT_REPR (shape)->setAttribute("inkscape:box3dside", NULL);
284 **/
285 return;
286 }
288 Inkscape::XML::Node *repr = SP_OBJECT_REPR (shape);
289 Persp3D *persp = box3d_side_perspective(side);
290 //g_return_if_fail (persp != NULL);
291 if (!persp) {
292 //g_warning ("persp != NULL in box3d_side_set_shape failed!\n");
293 //persp = SP_OBJECT_DOCUMENT(side)->current_persp3d;
294 return;
295 }
297 SPCurve *c = sp_curve_new ();
298 // TODO: Draw the correct quadrangle here
299 // To do this, determine the perspective of the box, the orientation of the side (e.g., XY-FRONT)
300 // compute the coordinates of the corners in P^3, project them onto the canvas, and draw the
301 // resulting path.
303 std::vector<Proj::Pt3> corners = box3d_side_corners (side);
305 NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM(shape)));
307 // FIXME: This can better be implemented by using box3d_get_corner
308 sp_curve_moveto (c, persp->tmat.image(corners[0]).affine() * i2d);
309 sp_curve_lineto (c, persp->tmat.image(corners[1]).affine() * i2d);
310 sp_curve_lineto (c, persp->tmat.image(corners[2]).affine() * i2d);
311 sp_curve_lineto (c, persp->tmat.image(corners[3]).affine() * i2d);
313 sp_curve_closepath (c);
314 //sp_shape_perform_path_effect(c, SP_SHAPE (side));
315 sp_shape_set_curve_insync (SP_SHAPE (side), c, TRUE);
316 sp_curve_unref (c);
317 }
319 static void
320 //box3d_side_apply_style (SPBox3D *box, bool extruded) {
321 box3d_side_apply_style (Box3DSide *side) {
322 Inkscape::XML::Node *repr_face = SP_OBJECT_REPR(SP_OBJECT(side));
324 /**
325 if (!extruded && !strcmp (box3d_side_axes_string (), "XYrear")) {
326 // to avoid "flashing" during the initial dragging process, we make the rear face invisible in this case
327 repr_face->setAttribute("style", "fill:none");
328 return;
329 }
330 **/
332 gchar *descr = g_strconcat ("desktop.", box3d_side_axes_string (side), NULL);
333 const gchar * cur_style = prefs_get_string_attribute(descr, "style");
334 g_free (descr);
336 SPDesktop *desktop = inkscape_active_desktop();
337 bool use_current = prefs_get_int_attribute("tools.shapes.3dbox", "usecurrent", 0);
338 if (use_current && cur_style !=NULL) {
339 /* use last used style */
340 repr_face->setAttribute("style", cur_style);
341 } else {
342 /* use default style */
343 GString *pstring = g_string_new("");
344 g_string_printf (pstring, "tools.shapes.3dbox.%s", box3d_side_axes_string(side));
345 sp_desktop_apply_style_tool (desktop, repr_face, pstring->str, false);
346 }
347 }
349 gchar *
350 box3d_side_axes_string(Box3DSide *side)
351 {
352 GString *pstring = g_string_new("");
353 g_string_printf (pstring, "%s", Box3D::string_from_axes ((Box3D::Axis) (side->dir1 ^ side->dir2)));
354 switch ((Box3D::Axis) (side->dir1 ^ side->dir2)) {
355 case Box3D::XY:
356 g_string_append_printf (pstring, (side->front_or_rear == Box3D::FRONT) ? "front" : "rear");
357 break;
358 case Box3D::XZ:
359 g_string_append_printf (pstring, (side->front_or_rear == Box3D::FRONT) ? "top" : "bottom");
360 break;
361 case Box3D::YZ:
362 g_string_append_printf (pstring, (side->front_or_rear == Box3D::FRONT) ? "right" : "left");
363 break;
364 default:
365 break;
366 }
367 return pstring->str;
368 }
370 static Proj::Pt3
371 box3d_side_corner (Box3DSide *side, guint index) {
372 SPBox3D *box = SP_BOX3D(SP_OBJECT_PARENT(side));
373 return Proj::Pt3 ((index & 0x1) ? box->orig_corner7[Proj::X] : box->orig_corner0[Proj::X],
374 (index & 0x2) ? box->orig_corner7[Proj::Y] : box->orig_corner0[Proj::Y],
375 (index & 0x4) ? box->orig_corner7[Proj::Z] : box->orig_corner0[Proj::Z],
376 1.0);
377 }
379 static std::vector<Proj::Pt3>
380 box3d_side_corners (Box3DSide *side) {
381 std::vector<Proj::Pt3> corners;
382 Box3D::Axis orth = Box3D::third_axis_direction (side->dir1, side->dir2);
383 unsigned int i0 = (side->front_or_rear ? orth : 0);
384 unsigned int i1 = i0 ^ side->dir1;
385 unsigned int i2 = i0 ^ side->dir1 ^ side->dir2;
386 unsigned int i3 = i0 ^ side->dir2;
388 corners.push_back (box3d_side_corner (side, i0));
389 corners.push_back (box3d_side_corner (side, i1));
390 corners.push_back (box3d_side_corner (side, i2));
391 corners.push_back (box3d_side_corner (side, i3));
392 return corners;
393 }
395 static gint
396 box3d_side_descr_to_id (gchar const *descr)
397 {
398 if (!strcmp (descr, "XYrear")) { return 5; }
399 if (!strcmp (descr, "XYfront")) { return 2; }
400 if (!strcmp (descr, "XZbottom")) { return 1; }
401 if (!strcmp (descr, "XZtop")) { return 4; }
402 if (!strcmp (descr, "YZleft")) { return 3; }
403 if (!strcmp (descr, "YZright")) { return 0; }
405 g_warning ("Invalid description of 3D box face.\n");
406 g_print (" (description is: %s)\n", descr);
407 return -1;
408 }
410 Persp3D *
411 box3d_side_perspective(Box3DSide *side) {
412 return SP_BOX3D(SP_OBJECT(side)->parent)->persp_ref->getObject();
413 }
415 /*
416 Local Variables:
417 mode:c++
418 c-file-style:"stroustrup"
419 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
420 indent-tabs-mode:nil
421 fill-column:99
422 End:
423 */
424 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :