canvas::Text: add heightForWidth method.
This commit is contained in:
parent
d3b211e787
commit
36cb7a752b
@ -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
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user