]> git.mxchange.org Git - simgear.git/blobdiff - simgear/canvas/elements/CanvasPath.cxx
Canvas: Respect clipping while event handling.
[simgear.git] / simgear / canvas / elements / CanvasPath.cxx
index ac9d6a9c6b546fe5639f524baec4fbd895361bd6..27c847a7ef9b328cfaebe07f548099ebcee90a11 100644 (file)
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #include "CanvasPath.hxx"
-#include <simgear/misc/parse_color.hxx>
+#include <simgear/scene/util/parse_color.hxx>
 
 #include <osg/Drawable>
-#include <osg/BlendFunc>
 
 #include <vg/openvg.h>
 #include <cassert>
@@ -32,6 +31,25 @@ namespace canvas
   typedef std::vector<VGubyte>  CmdList;
   typedef std::vector<VGfloat>  CoordList;
 
+
+  static const VGubyte shCoordsPerCommand[] = {
+    0, /* VG_CLOSE_PATH */
+    2, /* VG_MOVE_TO */
+    2, /* VG_LINE_TO */
+    1, /* VG_HLINE_TO */
+    1, /* VG_VLINE_TO */
+    4, /* VG_QUAD_TO */
+    6, /* VG_CUBIC_TO */
+    2, /* VG_SQUAD_TO */
+    4, /* VG_SCUBIC_TO */
+    5, /* VG_SCCWARC_TO */
+    5, /* VG_SCWARC_TO */
+    5, /* VG_LCCWARC_TO */
+    5  /* VG_LCWARC_TO */
+  };
+  static const VGubyte shNumCommands = sizeof(shCoordsPerCommand)
+                                     / sizeof(shCoordsPerCommand[0]);
+
   /**
    * Helper to split and convert comma/whitespace separated floating point
    * values
@@ -92,7 +110,7 @@ namespace canvas
        */
       void setFill(const std::string& fill)
       {
-        if( fill == "none" )
+        if( fill.empty() || fill == "none" )
         {
           _mode &= ~VG_FILL_PATH;
         }
@@ -132,7 +150,7 @@ namespace canvas
        */
       void setStroke(const std::string& stroke)
       {
-        if( stroke == "none" )
+        if( stroke.empty() || stroke == "none" )
         {
           _mode &= ~VG_STROKE_PATH;
         }
@@ -255,6 +273,98 @@ namespace canvas
         glPopClientAttrib();
       }
 
+      osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
+      {
+        osg::BoundingBox bb;
+
+        osg::Vec2f cur(0, 0), // VG "Current point" (in local coordinates)
+                   sub(0, 0); // beginning of current sub path
+        VGubyte cmd_index = 0;
+        for( size_t i = 0,              ci = 0;
+                    i < _cmds.size() && ci < _coords.size();
+                  ++i,                  ci += shCoordsPerCommand[cmd_index] )
+        {
+          VGubyte rel = _cmds[i] & 1,
+                  cmd = _cmds[i] & ~1;
+
+          cmd_index = cmd / 2;
+          if( cmd_index >= shNumCommands )
+            return osg::BoundingBox();
+
+          const VGubyte max_coords = 3;
+          osg::Vec2f points[max_coords];
+          VGubyte num_coords = 0;
+
+          switch( cmd )
+          {
+            case VG_CLOSE_PATH:
+              cur = sub;
+              break;
+            case VG_MOVE_TO:
+            case VG_LINE_TO:
+            case VG_SQUAD_TO:
+              // x0, y0
+              points[ num_coords++ ].set(_coords[ci], _coords[ci + 1]);
+              break;
+            case VG_HLINE_TO:
+              // x0
+              points[ num_coords++ ].set( _coords[ci] + (rel ? cur.x() : 0),
+                                          cur.y() );
+              // We have handled rel/abs already, so no need to do it again...
+              rel = 0;
+              break;
+            case VG_VLINE_TO:
+              // y0
+              points[ num_coords++ ].set( cur.x(),
+                                          _coords[ci] + (rel ? cur.y() : 0) );
+              // We have handled rel/abs already, so no need to do it again...
+              rel = 0;
+              break;
+            case VG_QUAD_TO:
+            case VG_SCUBIC_TO:
+              // x0,y0,x1,y1
+              points[ num_coords++ ].set(_coords[ci    ], _coords[ci + 1]);
+              points[ num_coords++ ].set(_coords[ci + 2], _coords[ci + 3]);
+              break;
+            case VG_CUBIC_TO:
+              // x0,y0,x1,y1,x2,y2
+              points[ num_coords++ ].set(_coords[ci    ], _coords[ci + 1]);
+              points[ num_coords++ ].set(_coords[ci + 2], _coords[ci + 3]);
+              points[ num_coords++ ].set(_coords[ci + 4], _coords[ci + 5]);
+              break;
+            case VG_SCCWARC_TO:
+            case VG_SCWARC_TO:
+            case VG_LCCWARC_TO:
+            case VG_LCWARC_TO:
+              // rh,rv,rot,x0,y0
+              points[ num_coords++ ].set(_coords[ci + 3], _coords[ci + 4]);
+              break;
+            default:
+              SG_LOG(SG_GL, SG_WARN, "Unknown VG command: " << (int)cmd);
+              return osg::BoundingBox();
+          }
+
+          assert(num_coords <= max_coords);
+          for(VGubyte i = 0; i < num_coords; ++i)
+          {
+            if( rel )
+              points[i] += cur;
+
+            bb.expandBy( transformPoint(mat, points[i]) );
+          }
+
+          if( num_coords > 0 )
+          {
+            cur = points[ num_coords - 1 ];
+
+            if( cmd == VG_MOVE_TO )
+              sub = cur;
+          }
+        }
+
+        return bb;
+      }
+
       /**
        * Compute the bounding box
        */
@@ -309,11 +419,25 @@ namespace canvas
       std::vector<VGfloat>  _stroke_dash;
       VGCapStyle            _stroke_linecap;
 
+      osg::Vec3f transformPoint( const osg::Matrix& m,
+                                 osg::Vec2f pos ) const
+      {
+        return osg::Vec3
+        (
+          m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
+          m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1),
+          0
+        );
+      }
+
       /**
        * Initialize/Update the OpenVG path
        */
       void update()
       {
+        if( !vgHasContextSH() )
+          return;
+
         if( _attributes_dirty & PATH )
         {
           const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
@@ -352,23 +476,36 @@ namespace canvas
       };
   };
 
+  //----------------------------------------------------------------------------
+  const std::string Path::TYPE_NAME = "path";
+
+  //----------------------------------------------------------------------------
+  void Path::staticInit()
+  {
+    if( isInit<Path>() )
+      return;
+
+    PathDrawableRef Path::*path = &Path::_path;
+
+    addStyle("fill", "color", &PathDrawable::setFill, path);
+    addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
+    addStyle("stroke", "color", &PathDrawable::setStroke, path);
+    addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
+    addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
+    addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
+  }
+
   //----------------------------------------------------------------------------
   Path::Path( const CanvasWeakPtr& canvas,
-              SGPropertyNode_ptr node,
-              const Style& parent_style ):
-    Element(canvas, node, parent_style),
+              const SGPropertyNode_ptr& node,
+              const Style& parent_style,
+              Element* parent ):
+    Element(canvas, node, parent_style, parent),
     _path( new PathDrawable(this) )
   {
-    setDrawable(_path);
-    PathDrawable *path = _path.get();
-
-    addStyle("fill", &PathDrawable::setFill, path);
-    addStyle("fill-rule", &PathDrawable::setFillRule, path);
-    addStyle("stroke", &PathDrawable::setStroke, path);
-    addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
-    addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
-    addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
+    staticInit();
 
+    setDrawable(_path);
     setupStyle();
   }
 
@@ -395,6 +532,66 @@ namespace canvas
     Element::update(dt);
   }
 
+  //----------------------------------------------------------------------------
+  osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const
+  {
+    return _path->getTransformedBounds(m);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::moveTo(float x_abs, float y_abs)
+  {
+    return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::move(float x_rel, float y_rel)
+  {
+    return addSegment(VG_MOVE_TO_REL, x_rel, y_rel);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::lineTo(float x_abs, float y_abs)
+  {
+    return addSegment(VG_LINE_TO_ABS, x_abs, y_abs);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::line(float x_rel, float y_rel)
+  {
+    return addSegment(VG_LINE_TO_REL, x_rel, y_rel);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::horizTo(float x_abs)
+  {
+    return addSegment(VG_HLINE_TO_ABS, x_abs);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::horiz(float x_rel)
+  {
+    return addSegment(VG_HLINE_TO_REL, x_rel);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::vertTo(float y_abs)
+  {
+    return addSegment(VG_VLINE_TO_ABS, y_abs);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::vert(float y_rel)
+  {
+    return addSegment(VG_VLINE_TO_REL, y_rel);
+  }
+
+  //----------------------------------------------------------------------------
+  Path& Path::close()
+  {
+    return addSegment(VG_CLOSE_PATH);
+  }
+
   //----------------------------------------------------------------------------
   void Path::childRemoved(SGPropertyNode* child)
   {