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 setFill(const std::string& fill);
|
||||||
void setBackgroundColor(const std::string& fill);
|
void setBackgroundColor(const std::string& fill);
|
||||||
|
|
||||||
|
int heightForWidth(int w) const;
|
||||||
osg::Vec2 handleHit(const osg::Vec2f& pos);
|
osg::Vec2 handleHit(const osg::Vec2f& pos);
|
||||||
|
|
||||||
virtual osg::BoundingBox computeBound() const;
|
virtual osg::BoundingBox computeBound() const;
|
||||||
@ -95,6 +96,281 @@ namespace canvas
|
|||||||
setBoundingBoxColor( color );
|
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)
|
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
|
||||||
{
|
{
|
||||||
@ -369,6 +645,12 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int Text::heightForWidth(int w) const
|
||||||
|
{
|
||||||
|
return _text->heightForWidth(w);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,7 @@ namespace canvas
|
|||||||
void setFont(const char* name);
|
void setFont(const char* name);
|
||||||
void setAlignment(const char* align);
|
void setAlignment(const char* align);
|
||||||
|
|
||||||
|
int heightForWidth(int w) const;
|
||||||
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
|
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -113,6 +113,18 @@ class SGRect
|
|||||||
void setTop(T t) { _min.y() = t; }
|
void setTop(T t) { _min.y() = t; }
|
||||||
void setBottom(T b) { _max.y() = b; }
|
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
|
* Move rect by vector
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user