Code

Refactoring SPColor to C++ and removing legacy CMYK implementation
[inkscape.git] / src / box3d-face.cpp
index d038de84f1f6442a49a37b6489e18932c8407a4e..a1b7ae863c45f6629005f01defada8948d11d493 100644 (file)
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
+#include "svg/svg.h"
 #include "box3d-face.h"
-#include <iostream>
+#include "prefs-utils.h"
 
-Box3DFace::Box3DFace(SP3DBox *box3d) : dir1 (Box3D::NONE), dir2 (Box3D::NONE), path (NULL), parent_box3d (box3d)
+// FIXME: It's quite redundant to pass the box plus the corners plus the axes. At least the corners can
+//        theoretically be reconstructed from the box and the axes, but in order to do this we need
+//        access to box->corners, which is not possible if we only have a forward declaration of SP3DBox
+//        in box3d-face.h. (But we can't include box3d.h itself because the latter already includes
+//        box3d-face.h).
+Box3DFace::Box3DFace(SP3DBox *box, NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D,
+                     Box3D::Axis plane, Box3D::FrontOrRear rel_pos)
+    : front_or_rear (rel_pos), path (NULL), parent_box3d (box)
+ {
+    dir1 = extract_first_axis_direction (plane);
+    dir2 = extract_second_axis_direction (plane);
+    /*
+    Box3D::Axis axis = (rel_pos == Box3D::FRONT ? Box3D::NONE : Box3D::third_axis_direction (plane));
+    set_corners (box->corners[axis],
+                 box->corners[axis ^ dir1],
+                 box->corners[axis ^ dir1 ^ dir2],
+                 box->corners[axis ^ dir2]);
+    */
+    set_corners (A, B, C, D);
+}
+
+Box3DFace::~Box3DFace()
 {
     for (int i = 0; i < 4; ++i) {
-        this->corners[i] = NR::Point(0, 0);
+        if (this->corners[i]) {
+            //delete this->corners[i];
+            this->corners[i] = NULL;
+        }
     }
 } 
 
+void Box3DFace::set_corners(NR::Point &A, NR::Point &B, NR::Point &C, NR::Point &D)
+{
+    corners[0] = &A;
+    corners[1] = &B;
+    corners[2] = &C;
+    corners[3] = &D;
+}
+
+/***
 void Box3DFace::set_shape(NR::Point const ul, NR::Point const lr,
                      Box3D::Axis const dir1, Box3D::Axis const dir2,
                      unsigned int shift_count, NR::Maybe<NR::Point> pt_align, bool align_along_PL)
@@ -31,11 +65,11 @@ void Box3DFace::set_shape(NR::Point const ul, NR::Point const lr,
     } else {
         if (align_along_PL) {
             Box3D::Axis dir3 = Box3D::third_axis_direction (dir1, dir2);
-            Box3D::Line line1(*SP3DBoxContext::current_perspective->get_vanishing_point(dir1), lr);
-            Box3D::Line line2(*pt_align, *SP3DBoxContext::current_perspective->get_vanishing_point(dir3));
+            Box3D::Line line1(*Box3D::Perspective3D::current_perspective->get_vanishing_point(dir1), lr);
+            Box3D::Line line2(*pt_align, *Box3D::Perspective3D::current_perspective->get_vanishing_point(dir3));
             corners[2] = *line1.intersect(line2);
         } else {
-            corners[2] = Box3D::Line(*pt_align, *SP3DBoxContext::current_perspective->get_vanishing_point(dir1)).closest_to(lr);
+            corners[2] = Box3D::Line(*pt_align, *Box3D::Perspective3D::current_perspective->get_vanishing_point(dir1)).closest_to(lr);
         }
     }
 
@@ -68,6 +102,7 @@ void Box3DFace::set_shape(NR::Point const ul, NR::Point const lr,
        corners[0] = tmp_pt;
     }
 }
+***/
 
 Box3DFace::Box3DFace(Box3DFace const &box3dface)
 {
@@ -78,23 +113,109 @@ Box3DFace::Box3DFace(Box3DFace const &box3dface)
     this->dir2 = box3dface.dir2;
 }
 
+/**
+ * Construct a 3D box face with opposite corners A and C whose sides are directed
+ * along axis1 and axis2. The corners have the following order:
+ *
+ * A = corners[0]  --> along axis1 --> B = corners[1] --> along axis2 --> C = corners[2]
+ *                 --> along axis1 --> D = corners[3] --> along axis2 --> D = corners[0].
+ * 
+ * Note that several other functions rely on this precise order.
+ */
+/***
+void
+Box3DFace::set_face (NR::Point const A, NR::Point const C, Box3D::Axis const axis1, Box3D::Axis const axis2)
+{
+    *corners[0] = A;
+    *corners[2] = C;
+    if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
+        return;
+    SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
+    
+    Box3D::PerspectiveLine line1 (A, axis1, Box3D::Perspective3D::current_perspective);
+    Box3D::PerspectiveLine line2 (C, axis2, Box3D::Perspective3D::current_perspective);
+    NR::Maybe<NR::Point> B = line1.intersect(line2);
+
+    Box3D::PerspectiveLine line3 (*corners[0], axis2, Box3D::Perspective3D::current_perspective);
+    Box3D::PerspectiveLine line4 (*corners[2], axis1, Box3D::Perspective3D::current_perspective);
+    NR::Maybe<NR::Point> D = line3.intersect(line4);
+
+    // FIXME: How to handle the case if one of the intersections doesn't exist?
+    //        Maybe set them equal to the corresponding VPs? 
+    if (!D) D = NR::Point(0.0, 0.0);    
+    if (!B) B = NR::Point(0.0, 0.0);
+
+    *corners[1] = *B;
+    *corners[3] = *D;
+
+    this->dir1 = axis1;
+    this->dir2 = axis2;
+}
+***/
+
 NR::Point Box3DFace::operator[](unsigned int i)
 {
-    return corners[i % 4];
+    return *corners[i % 4];
 }
 
+
+
 /**
  * Append the curve's path as a child to the given 3D box (since SP3DBox
  * is derived from SPGroup, so we can append children to its svg representation)
  */
-void Box3DFace::hook_path_to_3dbox()
+void Box3DFace::hook_path_to_3dbox(SPPath * existing_path)
 {
-    SPDesktop *desktop = inkscape_active_desktop();
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(inkscape_active_event_context()));
+    if (this->path) {
+        //g_print ("Path already exists. Returning ...\n");
+        return;
+    }
+
+    if (existing_path != NULL) {
+        // no need to create a new path
+        this->path = existing_path;
+        return;
+    }
+
+    /* create new path for face */
+    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(SP_OBJECT(parent_box3d)));
+
     Inkscape::XML::Node *repr_face = xml_doc->createElement("svg:path");
-    sp_desktop_apply_style_tool (desktop, repr_face, "tools.shapes.3dbox", false);
+    repr_face->setAttribute("inkscape:box3dface", this->axes_string());
     this->path = SP_PATH(SP_OBJECT(parent_box3d)->appendChildRepr(repr_face));
     Inkscape::GC::release(repr_face);
+
+    /* set the correct style */
+    this->set_style (repr_face);
+}
+
+void Box3DFace::set_style(Inkscape::XML::Node *repr_face, bool extruded)
+{
+    if (repr_face == NULL) {
+        repr_face = SP_OBJECT_REPR (this->path);
+    }
+
+    if (!extruded && !strcmp (axes_string (), "XYrear")) {
+        // to avoid "flashing" during the initial dragging process, we make the rear face invisible in this case
+        repr_face->setAttribute("style", "fill:none");
+        return;
+    }
+
+    gchar *descr = g_strconcat ("desktop.", axes_string (), NULL);
+    const gchar * cur_style = prefs_get_string_attribute(descr, "style");
+    g_free (descr);    
+    
+    SPDesktop *desktop = inkscape_active_desktop();
+    bool use_current = prefs_get_int_attribute("tools.shapes.3dbox", "usecurrent", 0);
+    if (use_current && cur_style !=NULL) {
+        /* use last used style */
+        repr_face->setAttribute("style", cur_style);
+    } else {
+        /* use default style */
+        GString *pstring = g_string_new("");
+        g_string_printf (pstring, "tools.shapes.3dbox.%s", axes_string());
+        sp_desktop_apply_style_tool (desktop, repr_face, pstring->str, false);
+    }
 }
 
 /**
@@ -102,38 +223,65 @@ void Box3DFace::hook_path_to_3dbox()
  */
 void Box3DFace::set_path_repr()
 {
-    SP_OBJECT(this->path)->repr->setAttribute("d", svg_repr_string());
+    NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (this->parent_box3d)));
+    SPCurve * curve = sp_curve_new();
+    sp_curve_moveto (curve, ((*corners[0]) * i2d)[NR::X], ((*corners[0]) * i2d)[NR::Y]);
+    sp_curve_lineto (curve, ((*corners[1]) * i2d)[NR::X], ((*corners[1]) * i2d)[NR::Y]);
+    sp_curve_lineto (curve, ((*corners[2]) * i2d)[NR::X], ((*corners[2]) * i2d)[NR::Y]);
+    sp_curve_lineto (curve, ((*corners[3]) * i2d)[NR::X], ((*corners[3]) * i2d)[NR::Y]);
+    sp_curve_closepath (curve);
+    SP_OBJECT(this->path)->repr->setAttribute("d", sp_svg_write_path (SP_CURVE_BPATH(curve)));
 }
 
 void Box3DFace::set_curve()
 {
-    SPDocument *doc = SP_OBJECT_DOCUMENT(this->parent_box3d);
-    gdouble height = sp_document_height(doc);
-
+    if (this->path == NULL) {
+        return;
+    }
+    NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (this->parent_box3d)));
     SPCurve *curve = sp_curve_new();
-    sp_curve_moveto(curve, corners[0][NR::X], height - corners[0][NR::Y]);
-    sp_curve_lineto(curve, corners[1][NR::X], height - corners[1][NR::Y]);
-    sp_curve_lineto(curve, corners[2][NR::X], height - corners[2][NR::Y]);
-    sp_curve_lineto(curve, corners[3][NR::X], height - corners[3][NR::Y]);
+    sp_curve_moveto(curve, (*corners[0]) * i2d);
+    sp_curve_lineto(curve, (*corners[1]) * i2d);
+    sp_curve_lineto(curve, (*corners[2]) * i2d);
+    sp_curve_lineto(curve, (*corners[3]) * i2d);
     sp_curve_closepath(curve);
     sp_shape_set_curve(SP_SHAPE(this->path), curve, true);
     sp_curve_unref(curve);
 }
 
-gchar * Box3DFace::svg_repr_string()
+gchar * Box3DFace::axes_string()
 {
-    SPDocument *doc = SP_OBJECT_DOCUMENT(this->parent_box3d);
-    gdouble height = sp_document_height(doc);
-
     GString *pstring = g_string_new("");
-    g_string_sprintf (pstring, "M %f,%f L %f,%f L %f,%f L %f,%f z",
-                               corners[0][NR::X], height - corners[0][NR::Y],
-                               corners[1][NR::X], height - corners[1][NR::Y],
-                               corners[2][NR::X], height - corners[2][NR::Y],
-                               corners[3][NR::X], height - corners[3][NR::Y]);
+    g_string_printf (pstring, "%s", Box3D::string_from_axes ((Box3D::Axis) (dir1 ^ dir2)));
+    switch ((Box3D::Axis) (dir1 ^ dir2)) {
+        case Box3D::XY:
+            g_string_append_printf (pstring, (front_or_rear == Box3D::FRONT) ? "front" : "rear");
+            break;
+        case Box3D::XZ:
+            g_string_append_printf (pstring, (front_or_rear == Box3D::FRONT) ? "top" : "bottom");
+            break;
+        case Box3D::YZ:
+            g_string_append_printf (pstring, (front_or_rear == Box3D::FRONT) ? "right" : "left");
+            break;
+        default:
+            break;
+    }
     return pstring->str;
 }
 
+gint Box3DFace::descr_to_id (gchar const *descr)
+{
+    if (!strcmp (descr, "XYrear")) { return 5; }
+    if (!strcmp (descr, "XYfront")) { return 4; }
+    if (!strcmp (descr, "XZbottom")) { return 3; }
+    if (!strcmp (descr, "XZtop")) { return 2; }
+    if (!strcmp (descr, "YZleft")) { return 1; }
+    if (!strcmp (descr, "YZright")) { return 0; }
+
+    g_warning ("Invalid description of 3D box face.\n");
+    return -1;
+}
+
 /*
   Local Variables:
   mode:c++