Code

rewrite nr_path_matrix_point_bbox_wind_distance in 2geom terms: pathv_matrix_point_bb...
authorjohanengelen <johanengelen@users.sourceforge.net>
Thu, 26 Jun 2008 14:52:09 +0000 (14:52 +0000)
committerjohanengelen <johanengelen@users.sourceforge.net>
Thu, 26 Jun 2008 14:52:09 +0000 (14:52 +0000)
src/helper/geom.cpp
src/helper/geom.h

index a056b285121fa39ee5ae8c7886e0526762064ff5..9916a633355467c6b8f557c628c6f09f461fb200 100644 (file)
 #include "helper/geom.h"\r
 \r
 #include <2geom/pathvector.h>\r
+#include <2geom/path.h>\r
 #include <2geom/transforms.h>\r
 #include <2geom/rect.h>\r
 #include <2geom/coord.h>\r
+#include <2geom/sbasis-to-bezier.h>\r
+#include <libnr/nr-convert2geom.h>\r
 #include <glibmm.h>\r
 \r
+using Geom::X;\r
+using Geom::Y;\r
+\r
+#define NR_HUGE   1e18\r
+\r
+//#################################################################################\r
+// BOUNDING BOX CALCULATIONS\r
+\r
 /* Fast bbox calculation */\r
 /* Thanks to Nathan Hurst for suggesting it */\r
 static void\r
@@ -178,6 +189,299 @@ bounds_exact_transformed(Geom::PathVector const & pv, Geom::Matrix const & t)
     return bbox;\r
 }\r
 \r
+\r
+\r
+static void\r
+geom_line_wind_distance (Geom::Coord x0, Geom::Coord y0, Geom::Coord x1, Geom::Coord y1, Geom::Point &pt, int *wind, Geom::Coord *best)\r
+{\r
+    Geom::Coord Ax, Ay, Bx, By, Dx, Dy, s;\r
+    Geom::Coord dist2;\r
+\r
+    /* Find distance */\r
+    Ax = x0;\r
+    Ay = y0;\r
+    Bx = x1;\r
+    By = y1;\r
+    Dx = x1 - x0;\r
+    Dy = y1 - y0;\r
+    const Geom::Coord Px = pt[X];\r
+    const Geom::Coord Py = pt[Y];\r
+\r
+    if (best) {\r
+        s = ((Px - Ax) * Dx + (Py - Ay) * Dy) / (Dx * Dx + Dy * Dy);\r
+        if (s <= 0.0) {\r
+            dist2 = (Px - Ax) * (Px - Ax) + (Py - Ay) * (Py - Ay);\r
+        } else if (s >= 1.0) {\r
+            dist2 = (Px - Bx) * (Px - Bx) + (Py - By) * (Py - By);\r
+        } else {\r
+            Geom::Coord Qx, Qy;\r
+            Qx = Ax + s * Dx;\r
+            Qy = Ay + s * Dy;\r
+            dist2 = (Px - Qx) * (Px - Qx) + (Py - Qy) * (Py - Qy);\r
+        }\r
+\r
+        if (dist2 < (*best * *best)) *best = sqrt (dist2);\r
+    }\r
+\r
+    if (wind) {\r
+        /* Find wind */\r
+        if ((Ax >= Px) && (Bx >= Px)) return;\r
+        if ((Ay >= Py) && (By >= Py)) return;\r
+        if ((Ay < Py) && (By < Py)) return;\r
+        if (Ay == By) return;\r
+        /* Ctach upper y bound */\r
+        if (Ay == Py) {\r
+            if (Ax < Px) *wind -= 1;\r
+            return;\r
+        } else if (By == Py) {\r
+            if (Bx < Px) *wind += 1;\r
+            return;\r
+        } else {\r
+            Geom::Coord Qx;\r
+            /* Have to calculate intersection */\r
+            Qx = Ax + Dx * (Py - Ay) / Dy;\r
+            if (Qx < Px) {\r
+                *wind += (Dy > 0.0) ? 1 : -1;\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+static void\r
+geom_cubic_bbox_wind_distance (Geom::Coord x000, Geom::Coord y000,\r
+                 Geom::Coord x001, Geom::Coord y001,\r
+                 Geom::Coord x011, Geom::Coord y011,\r
+                 Geom::Coord x111, Geom::Coord y111,\r
+                 Geom::Point &pt,\r
+                 Geom::Rect *bbox, int *wind, Geom::Coord *best,\r
+                 Geom::Coord tolerance)\r
+{\r
+    Geom::Coord x0, y0, x1, y1, len2;\r
+    int needdist, needwind, needline;\r
+\r
+    const Geom::Coord Px = pt[X];\r
+    const Geom::Coord Py = pt[Y];\r
+\r
+    needdist = 0;\r
+    needwind = 0;\r
+    needline = 0;\r
+\r
+    if (bbox) cubic_bbox (x000, y000, x001, y001, x011, y011, x111, y111, *bbox);\r
+\r
+    x0 = MIN (x000, x001);\r
+    x0 = MIN (x0, x011);\r
+    x0 = MIN (x0, x111);\r
+    y0 = MIN (y000, y001);\r
+    y0 = MIN (y0, y011);\r
+    y0 = MIN (y0, y111);\r
+    x1 = MAX (x000, x001);\r
+    x1 = MAX (x1, x011);\r
+    x1 = MAX (x1, x111);\r
+    y1 = MAX (y000, y001);\r
+    y1 = MAX (y1, y011);\r
+    y1 = MAX (y1, y111);\r
+\r
+    if (best) {\r
+        /* Quickly adjust to endpoints */\r
+        len2 = (x000 - Px) * (x000 - Px) + (y000 - Py) * (y000 - Py);\r
+        if (len2 < (*best * *best)) *best = (Geom::Coord) sqrt (len2);\r
+        len2 = (x111 - Px) * (x111 - Px) + (y111 - Py) * (y111 - Py);\r
+        if (len2 < (*best * *best)) *best = (Geom::Coord) sqrt (len2);\r
+\r
+        if (((x0 - Px) < *best) && ((y0 - Py) < *best) && ((Px - x1) < *best) && ((Py - y1) < *best)) {\r
+            /* Point is inside sloppy bbox */\r
+            /* Now we have to decide, whether subdivide */\r
+            /* fixme: (Lauris) */\r
+            if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {\r
+                needdist = 1;\r
+            } else {\r
+                needline = 1;\r
+            }\r
+        }\r
+    }\r
+    if (!needdist && wind) {\r
+        if ((y1 >= Py) && (y0 < Py) && (x0 < Px)) {\r
+            /* Possible intersection at the left */\r
+            /* Now we have to decide, whether subdivide */\r
+            /* fixme: (Lauris) */\r
+            if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {\r
+                needwind = 1;\r
+            } else {\r
+                needline = 1;\r
+            }\r
+        }\r
+    }\r
+\r
+    if (needdist || needwind) {\r
+        Geom::Coord x00t, x0tt, xttt, x1tt, x11t, x01t;\r
+        Geom::Coord y00t, y0tt, yttt, y1tt, y11t, y01t;\r
+        Geom::Coord s, t;\r
+\r
+        t = 0.5;\r
+        s = 1 - t;\r
+\r
+        x00t = s * x000 + t * x001;\r
+        x01t = s * x001 + t * x011;\r
+        x11t = s * x011 + t * x111;\r
+        x0tt = s * x00t + t * x01t;\r
+        x1tt = s * x01t + t * x11t;\r
+        xttt = s * x0tt + t * x1tt;\r
+\r
+        y00t = s * y000 + t * y001;\r
+        y01t = s * y001 + t * y011;\r
+        y11t = s * y011 + t * y111;\r
+        y0tt = s * y00t + t * y01t;\r
+        y1tt = s * y01t + t * y11t;\r
+        yttt = s * y0tt + t * y1tt;\r
+\r
+        geom_cubic_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt, NULL, wind, best, tolerance);\r
+        geom_cubic_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt, NULL, wind, best, tolerance);\r
+    } else if (1 || needline) {\r
+        geom_line_wind_distance (x000, y000, x111, y111, pt, wind, best);\r
+    }\r
+}\r
+\r
+static void\r
+geom_curve_bbox_wind_distance(Geom::Curve const * c, Geom::Matrix const &m,\r
+                 Geom::Point &pt,\r
+                 Geom::Rect *bbox, int *wind, Geom::Coord *dist,\r
+                 Geom::Coord tolerance, Geom::Rect *viewbox,\r
+                 Geom::Point &p0) // pass p0 through as it represents the last endpoint added (the finalPoint of last curve)\r
+{\r
+    if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const  *>(c)) {   // TODO: make it work for HLineSegment too! (use finalPoint)\r
+        Geom::Point pe = (*line_segment)[1] * m;\r
+        if (bbox) {\r
+            bbox->expandTo(pe);\r
+        }\r
+        if (dist || wind) {\r
+            if (wind) { // we need to pick fill, so do what we're told\r
+                geom_line_wind_distance (p0[X], p0[Y], pe[X], pe[Y], pt, wind, dist);\r
+            } else { // only stroke is being picked; skip this segment if it's totally outside the viewbox\r
+                Geom::Rect swept(p0, pe);\r
+                if (!viewbox || swept.intersects(*viewbox))\r
+                    geom_line_wind_distance (p0[X], p0[Y], pe[X], pe[Y], pt, wind, dist);\r
+            }\r
+        }\r
+        p0 = pe;\r
+    }\r
+    else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const  *>(c)) {\r
+        Geom::Point p1 = (*cubic_bezier)[1] * m;\r
+        Geom::Point p2 = (*cubic_bezier)[2] * m;\r
+        Geom::Point p3 = (*cubic_bezier)[3] * m;\r
+\r
+        // get approximate bbox from handles (convex hull property of beziers):\r
+        Geom::Rect swept(p0, p3);\r
+        swept.expandTo(p1);\r
+        swept.expandTo(p2);\r
+\r
+        if (!viewbox || swept.intersects(*viewbox)) { // we see this segment, so do full processing\r
+            geom_cubic_bbox_wind_distance ( p0[X], p0[Y],\r
+                                            p1[X], p1[Y],\r
+                                            p2[X], p2[Y],\r
+                                            p3[X], p3[Y],\r
+                                            pt,\r
+                                            bbox, wind, dist, tolerance);\r
+        } else {\r
+            if (wind) { // if we need fill, we can just pretend it's a straight line\r
+                geom_line_wind_distance (p0[X], p0[Y], p3[X], p3[Y], pt, wind, dist);\r
+            } else { // otherwise, skip it completely\r
+            }\r
+        }\r
+        p0 = p3;\r
+    } else { \r
+        //this case handles sbasis as well as all other curve types\r
+        Geom::Path sbasis_path = Geom::path_from_sbasis(c->toSBasis(), 0.1);\r
+\r
+        //recurse to convert the new path resulting from the sbasis to svgd\r
+        for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {\r
+            geom_curve_bbox_wind_distance(&(*iter), m, pt, bbox, wind, dist, tolerance, viewbox, p0);\r
+        }\r
+    }\r
+}\r
+\r
+/* Calculates...\r
+   and returns ... in *wind and the distance to ... in *dist.\r
+   Returns bounding box in *bbox if bbox!=NULL.\r
+ */\r
+void\r
+pathv_matrix_point_bbox_wind_distance (Geom::PathVector const & pathv, Geom::Matrix const &m, Geom::Point &pt,\r
+                         Geom::Rect *bbox, int *wind, Geom::Coord *dist,\r
+                         Geom::Coord tolerance, Geom::Rect *viewbox)\r
+{\r
+    if (pathv.empty()) {\r
+        if (wind) *wind = 0;\r
+        if (dist) *dist = NR_HUGE;\r
+        return;\r
+    }\r
+\r
+    // remember last point of last curve\r
+    Geom::Point p0(0,0);\r
+\r
+    // remembering the start of subpath\r
+    Geom::Point p_start(0,0);\r
+    bool start_set = false;\r
+\r
+    for (Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
+\r
+        if (start_set) { // this is a new subpath\r
+            if (wind && (p0 != p_start)) // for correct fill picking, each subpath must be closed\r
+                geom_line_wind_distance (p0[X], p0[Y], p_start[X], p_start[Y], pt, wind, dist);\r
+        }\r
+        p0 = it->initialPoint() * m;\r
+        p_start = p0;\r
+        start_set = true;\r
+        if (bbox) {\r
+            bbox->expandTo(p0);\r
+        }\r
+\r
+        // loop including closing segment if path is closed\r
+        for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_default(); ++cit) {\r
+            geom_curve_bbox_wind_distance(&*cit, m, pt, bbox, wind, dist, tolerance, viewbox, p0);\r
+        }\r
+    }\r
+\r
+    if (start_set) { \r
+        if (wind && (p0 != p_start)) // for correct picking, each subpath must be closed\r
+            geom_line_wind_distance (p0[X], p0[Y], p_start[X], p_start[Y], pt, wind, dist);\r
+    }\r
+}\r
+\r
+// temporary wrapper\r
+void\r
+pathv_matrix_point_bbox_wind_distance (Geom::PathVector const & pathv, NR::Matrix const &m, NR::Point &pt,\r
+                         NR::Rect *bbox, int *wind, NR::Coord *dist,\r
+                         NR::Coord tolerance, NR::Rect *viewbox)\r
+{\r
+    Geom::Point _pt(to_2geom(pt));\r
+    Geom::Rect _bbox;\r
+    if (bbox)\r
+        _bbox = to_2geom(*bbox);\r
+    Geom::Coord _dist;\r
+    if (dist)\r
+        _dist = *dist;\r
+    Geom::Rect _viewbox;\r
+    if (viewbox)\r
+        _viewbox = to_2geom(*viewbox);\r
+\r
+    pathv_matrix_point_bbox_wind_distance( pathv, to_2geom(m), _pt,\r
+                                           bbox ? &_bbox : NULL,\r
+                                           wind,\r
+                                           dist ? &_dist : NULL,\r
+                                           tolerance,\r
+                                           viewbox ? &_viewbox : NULL );\r
+\r
+    if (bbox)\r
+        *bbox = from_2geom(_bbox);\r
+    if (dist)\r
+        *dist = _dist;\r
+    if (viewbox)\r
+        *viewbox = from_2geom(_viewbox);\r
+}\r
+//#################################################################################\r
+\r
+\r
+\r
+\r
 /*\r
   Local Variables:\r
   mode:c++\r
index d7548756ad3fed5990aedb2ffe25497679e91544..1096c6ba178a836ae639281cea61f7128bfdc9fe 100644 (file)
  */\r
 \r
 #include <2geom/forward.h>\r
+#include <libnr/nr-forward.h>\r
+#include <libnr/nr-coord.h>\r
 \r
 Geom::Rect bounds_fast_transformed(Geom::PathVector const & pv, Geom::Matrix const & t);\r
 Geom::Rect bounds_exact_transformed(Geom::PathVector const & pv, Geom::Matrix const & t);\r
 \r
+void pathv_matrix_point_bbox_wind_distance ( Geom::PathVector const & pathv, NR::Matrix const &m, NR::Point &pt,\r
+                                             NR::Rect *bbox, int *wind, NR::Coord *dist,\r
+                                             NR::Coord tolerance, NR::Rect *viewbox) __attribute__ ((deprecated));\r
+void pathv_matrix_point_bbox_wind_distance ( Geom::PathVector const & pathv, Geom::Matrix const &m, Geom::Point &pt,\r
+                                             Geom::Rect *bbox, int *wind, Geom::Coord *dist,\r
+                                             Geom::Coord tolerance, Geom::Rect *viewbox);\r
+\r
 #endif  // INKSCAPE_HELPER_GEOM_H\r
 \r
 /*\r