]> git.mxchange.org Git - simgear.git/commitdiff
canvas::Text: add heightForWidth method.
authorThomas Geymayer <tomgey@gmail.com>
Thu, 12 Jun 2014 22:04:57 +0000 (00:04 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Thu, 12 Jun 2014 22:08:57 +0000 (00:08 +0200)
simgear/canvas/elements/CanvasText.cxx
simgear/canvas/elements/CanvasText.hxx
simgear/math/SGRect.hxx

index eb399e169c2fa594882a4c9a383cca93a6d6891c..8beed49c1ba5227406185fb5fba290cfa2a8c581 100644 (file)
@@ -40,6 +40,7 @@ namespace canvas
       void setFill(const std::string& fill);
       void setBackgroundColor(const std::string& fill);
 
+      int heightForWidth(int w) const;
       osg::Vec2 handleHit(const osg::Vec2f& pos);
 
       virtual osg::BoundingBox computeBound() const;
@@ -95,6 +96,281 @@ namespace canvas
       setBoundingBoxColor( color );
   }
 
+  //----------------------------------------------------------------------------
+  // simplified version of osgText::Text::computeGlyphRepresentation() to
+  // just calculate the height for a given weight. Glpyh calculations/creating
+  // is not necessary for this...
+  int Text::TextOSG::heightForWidth(int w) const
+  {
+    if( _text.empty() )
+      return 0;
+
+    osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
+    if( !activefont )
+      return -1;
+
+    float max_width_safe = _maximumWidth;
+    const_cast<TextOSG*>(this)->_maximumWidth = w;
+
+    SGRecti bb;
+
+    osg::Vec2 startOfLine_coords(0.0f,0.0f);
+    osg::Vec2 cursor(startOfLine_coords);
+    osg::Vec2 local(0.0f,0.0f);
+
+    unsigned int previous_charcode = 0;
+    unsigned int line_length = 0;
+    bool horizontal = _layout != VERTICAL;
+    bool kerning = true;
+
+    float hr = _characterHeight;
+    float wr = hr / getCharacterAspectRatio();
+
+    // osg should really care more about const :-/
+    osgText::String& text = const_cast<osgText::String&>(_text);
+    typedef osgText::String::iterator TextIterator;
+
+    for( TextIterator itr = text.begin(); itr != text.end(); )
+    {
+      // record the start of the current line
+      TextIterator startOfLine_itr = itr;
+
+      // find the end of the current line.
+      osg::Vec2 endOfLine_coords(cursor);
+      TextIterator endOfLine_itr =
+        const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
+          endOfLine_coords, itr, text.end()
+        );
+
+      line_length = endOfLine_itr - startOfLine_itr;
+
+      // Set line position to correct alignment.
+      switch( _layout )
+      {
+        case LEFT_TO_RIGHT:
+        {
+          switch( _alignment )
+          {
+            // nothing to be done for these
+            //case LEFT_TOP:
+            //case LEFT_CENTER:
+            //case LEFT_BOTTOM:
+            //case LEFT_BASE_LINE:
+            //case LEFT_BOTTOM_BASE_LINE:
+            //  break;
+            case CENTER_TOP:
+            case CENTER_CENTER:
+            case CENTER_BOTTOM:
+            case CENTER_BASE_LINE:
+            case CENTER_BOTTOM_BASE_LINE:
+              cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
+              break;
+            case RIGHT_TOP:
+            case RIGHT_CENTER:
+            case RIGHT_BOTTOM:
+            case RIGHT_BASE_LINE:
+            case RIGHT_BOTTOM_BASE_LINE:
+              cursor.x() = cursor.x() - endOfLine_coords.x();
+              break;
+            default:
+              break;
+          }
+          break;
+        }
+        case RIGHT_TO_LEFT:
+        {
+          switch( _alignment )
+          {
+            case LEFT_TOP:
+            case LEFT_CENTER:
+            case LEFT_BOTTOM:
+            case LEFT_BASE_LINE:
+            case LEFT_BOTTOM_BASE_LINE:
+              cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
+              break;
+            case CENTER_TOP:
+            case CENTER_CENTER:
+            case CENTER_BOTTOM:
+            case CENTER_BASE_LINE:
+            case CENTER_BOTTOM_BASE_LINE:
+              cursor.x() = cursor.x()
+                  + (cursor.x() - endOfLine_coords.x()) * 0.5f;
+              break;
+              // nothing to be done for these
+              //case RIGHT_TOP:
+              //case RIGHT_CENTER:
+              //case RIGHT_BOTTOM:
+              //case RIGHT_BASE_LINE:
+              //case RIGHT_BOTTOM_BASE_LINE:
+              //  break;
+            default:
+              break;
+          }
+          break;
+        }
+        case VERTICAL:
+        {
+          switch( _alignment )
+          {
+            // TODO: current behaviour top baselines lined up in both cases - need to implement
+            //       top of characters alignment - Question is this necessary?
+            // ... otherwise, nothing to be done for these 6 cases
+            //case LEFT_TOP:
+            //case CENTER_TOP:
+            //case RIGHT_TOP:
+            //  break;
+            //case LEFT_BASE_LINE:
+            //case CENTER_BASE_LINE:
+            //case RIGHT_BASE_LINE:
+            //  break;
+            case LEFT_CENTER:
+            case CENTER_CENTER:
+            case RIGHT_CENTER:
+              cursor.y() = cursor.y()
+                  + (cursor.y() - endOfLine_coords.y()) * 0.5f;
+              break;
+            case LEFT_BOTTOM_BASE_LINE:
+            case CENTER_BOTTOM_BASE_LINE:
+            case RIGHT_BOTTOM_BASE_LINE:
+              cursor.y() = cursor.y() - (line_length * _characterHeight);
+              break;
+            case LEFT_BOTTOM:
+            case CENTER_BOTTOM:
+            case RIGHT_BOTTOM:
+              cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
+              break;
+            default:
+              break;
+          }
+          break;
+        }
+      }
+
+      if( itr != endOfLine_itr )
+      {
+
+        for(;itr != endOfLine_itr;++itr)
+        {
+          unsigned int charcode = *itr;
+
+          osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
+          if( glyph )
+          {
+            float width = (float) (glyph->getWidth()) * wr;
+            float height = (float) (glyph->getHeight()) * hr;
+
+            if( _layout == RIGHT_TO_LEFT )
+            {
+              cursor.x() -= glyph->getHorizontalAdvance() * wr;
+            }
+
+            // adjust cursor position w.r.t any kerning.
+            if( kerning && previous_charcode )
+            {
+              switch( _layout )
+              {
+                case LEFT_TO_RIGHT:
+                {
+                  osg::Vec2 delta( activefont->getKerning( previous_charcode,
+                                                           charcode,
+                                                           _kerningType ) );
+                  cursor.x() += delta.x() * wr;
+                  cursor.y() += delta.y() * hr;
+                  break;
+                }
+                case RIGHT_TO_LEFT:
+                {
+                  osg::Vec2 delta( activefont->getKerning( charcode,
+                                                           previous_charcode,
+                                                           _kerningType ) );
+                  cursor.x() -= delta.x() * wr;
+                  cursor.y() -= delta.y() * hr;
+                  break;
+                }
+                case VERTICAL:
+                  break; // no kerning when vertical.
+              }
+            }
+
+            local = cursor;
+            osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
+                                          : glyph->getVerticalBearing() );
+            local.x() += bearing.x() * wr;
+            local.y() += bearing.y() * hr;
+
+            // set up the coords of the quad
+            osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
+            osg::Vec2 lowLeft = local;
+            osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
+            osg::Vec2 upRight = local + osg::Vec2(width, height);
+
+            // move the cursor onto the next character.
+            // also expand bounding box
+            switch( _layout )
+            {
+              case LEFT_TO_RIGHT:
+                cursor.x() += glyph->getHorizontalAdvance() * wr;
+                bb.expandBy(lowLeft.x(), lowLeft.y());
+                bb.expandBy(upRight.x(), upRight.y());
+                break;
+              case VERTICAL:
+                cursor.y() -= glyph->getVerticalAdvance() * hr;
+                bb.expandBy(upLeft.x(), upLeft.y());
+                bb.expandBy(lowRight.x(), lowRight.y());
+                break;
+              case RIGHT_TO_LEFT:
+                bb.expandBy(lowRight.x(), lowRight.y());
+                bb.expandBy(upLeft.x(), upLeft.y());
+                break;
+            }
+            previous_charcode = charcode;
+          }
+        }
+
+        // skip over spaces and return.
+        while( itr != text.end() && *itr == ' ' )
+          ++itr;
+        if( itr != text.end() && *itr == '\n' )
+          ++itr;
+      }
+      else
+      {
+        ++itr;
+      }
+
+      // move to new line.
+      switch( _layout )
+      {
+        case LEFT_TO_RIGHT:
+        {
+          startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
+          cursor = startOfLine_coords;
+          previous_charcode = 0;
+          break;
+        }
+        case RIGHT_TO_LEFT:
+        {
+          startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
+          cursor = startOfLine_coords;
+          previous_charcode = 0;
+          break;
+        }
+        case VERTICAL:
+        {
+          startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
+                                  / getCharacterAspectRatio();
+          cursor = startOfLine_coords;
+          previous_charcode = 0;
+          break;
+        }
+      }
+    }
+
+    const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
+
+    return bb.height();
+  }
+
   //----------------------------------------------------------------------------
   osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
   {
@@ -369,6 +645,12 @@ namespace canvas
   }
 #endif
 
+  //----------------------------------------------------------------------------
+  int Text::heightForWidth(int w) const
+  {
+    return _text->heightForWidth(w);
+  }
+
   //----------------------------------------------------------------------------
   osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
   {
index aba96319173d6220be31c8d42b79db502ef3ea7d..57ccd290a7bf805c61153f15f80a6f70338be16b 100644 (file)
@@ -46,6 +46,7 @@ namespace canvas
       void setFont(const char* name);
       void setAlignment(const char* align);
 
+      int heightForWidth(int w) const;
       osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
 
     protected:
index da5cdcfe65dcbace4949ea6a7174cd4764d8a32f..86dd482c9c8875c54bdd848abff6fe2df6656e32 100644 (file)
@@ -113,6 +113,18 @@ class SGRect
     void setTop(T t) { _min.y() = t; }
     void setBottom(T b) { _max.y() = b; }
 
+    /**
+     * Expand rectangle to include the given position
+     */
+    void expandBy(T x, T y)
+    {
+      if( x < _min.x() ) _min.x() = x;
+      if( x > _max.x() ) _max.x() = x;
+
+      if( y < _min.y() ) _min.y() = y;
+      if( y > _max.y() ) _max.y() = y;
+    }
+
     /**
      * Move rect by vector
      */