1 // A text on the canvas
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <Canvas/property_helper.hxx>
22 #include <Main/globals.hxx>
23 #include <Main/fg_props.hxx>
25 #include <osgText/Text>
33 osg::Vec2 handleHit(float x, float y);
35 virtual osg::BoundingBox computeBound() const;
38 //----------------------------------------------------------------------------
39 osg::Vec2 Text::TextOSG::handleHit(float x, float y)
41 float line_height = _characterHeight + _lineSpacing;
43 // TODO check with align other than TOP
44 float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
45 size_t line = std::max<int>(0, (y - first_line_y) / line_height);
47 if( _textureGlyphQuadMap.empty() )
48 return osg::Vec2(-1, -1);
50 // TODO check when it can be larger
51 assert( _textureGlyphQuadMap.size() == 1 );
53 const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
54 const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
55 const GlyphQuads::Coords2& coords = glyphquad._coords;
56 const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
58 const float HIT_FRACTION = 0.6;
59 const float character_width = getCharacterHeight()
60 * getCharacterAspectRatio();
62 y = (line + 0.5) * line_height;
64 bool line_found = false;
65 for(size_t i = 0; i < line_numbers.size(); ++i)
67 if( line_numbers[i] != line )
71 if( line_numbers[i] < line )
72 // Wait for the correct line...
75 // We have already passed the correct line -> It's empty...
76 return osg::Vec2(0, y);
79 // Next line and not returned -> not before any character
80 // -> return position after last character of line
81 return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
86 // Get threshold for mouse x position for setting cursor before or after
88 float threshold = coords[i * 4].x()
89 + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
94 if( i == 0 || line_numbers[i - 1] != line )
95 // first character of line
96 x = coords[i * 4].x();
97 else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
98 // If previous character width is zero set to begin of next character
99 // (Happens eg. with spaces)
100 x = coords[i * 4].x();
102 // position at center between characters
103 x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
105 return osg::Vec2(x, y);
109 // Nothing found -> return position after last character
113 (_lineCount - 0.5) * line_height
117 //----------------------------------------------------------------------------
118 osg::BoundingBox Text::TextOSG::computeBound() const
120 osg::BoundingBox bb = osgText::Text::computeBound();
124 // TODO bounding box still doesn't seem always right (eg. with center
125 // horizontal alignment not completely accurate)
126 bb._min.y() += _offset.y();
127 bb._max.y() += _offset.y();
132 //----------------------------------------------------------------------------
133 Text::Text(SGPropertyNode_ptr node):
134 Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
135 _text( new Text::TextOSG() ),
140 _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
141 _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
142 _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
144 _font_size = getChildDefault<float>(_node, "character-size", 32);
145 _font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
148 //----------------------------------------------------------------------------
154 //----------------------------------------------------------------------------
155 void Text::update(double dt)
159 if( _attributes_dirty & FONT_SIZE )
161 _text->setCharacterSize
163 _font_size->getFloatValue(),
164 _font_aspect->getFloatValue()
167 _attributes_dirty &= ~FONT_SIZE;
171 //----------------------------------------------------------------------------
172 void Text::setFont(const char* name)
174 _text->setFont( getFont(name) );
177 //----------------------------------------------------------------------------
178 void Text::setAlignment(const char* align)
180 const std::string align_string(align);
182 #define ENUM_MAPPING(enum_val, string_val) \
183 else if( align_string == string_val )\
184 _text->setAlignment( osgText::Text::enum_val );
185 #include "text-alignment.hxx"
189 if( !align_string.empty() )
194 "canvas::Text: unknown alignment '" << align_string << "'"
196 _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
200 //----------------------------------------------------------------------------
202 const char* Text::getAlignment() const
204 switch( _text->getAlignment() )
206 #define ENUM_MAPPING(enum_val, string_val) \
207 case osgText::Text::enum_val:\
209 #include "text-alignment.hxx"
216 //----------------------------------------------------------------------------
217 void Text::childChanged(SGPropertyNode* child)
219 const std::string& name = child->getNameString();
221 if( name == "hit-y" )
224 _node->getFloatValue("hit-x"),
225 _node->getFloatValue("hit-y")
227 else if( _font_size == child || _font_aspect == child )
228 _attributes_dirty |= FONT_SIZE;
229 else if( name == "text" )
232 osgText::String( child->getStringValue(),
233 osgText::String::ENCODING_UTF8 )
235 else if( name == "padding" )
236 _text->setBoundingBoxMargin( child->getFloatValue() );
237 else if( name == "draw-mode" )
240 // FILLEDBOUNDINGBOX = 4
242 _text->setDrawMode( child->getIntValue() );
243 else if( name == "max-width" )
244 _text->setMaximumWidth( child->getFloatValue() );
245 else if( name == "font" )
246 setFont( child->getStringValue() );
247 else if( name == "alignment" )
248 setAlignment( child->getStringValue() );
251 //----------------------------------------------------------------------------
252 void Text::colorChanged(const osg::Vec4& color)
254 _text->setColor(color);
257 //----------------------------------------------------------------------------
258 void Text::colorFillChanged(const osg::Vec4& color)
260 _text->setBoundingBoxColor(color);
263 //----------------------------------------------------------------------------
264 void Text::handleHit(float x, float y)
266 const osg::Vec2& pos = _text->handleHit(x, y);
267 _node->setFloatValue("cursor-x", pos.x());
268 _node->setFloatValue("cursor-y", pos.y());
271 //----------------------------------------------------------------------------
272 Text::font_ptr Text::getFont(const std::string& name)
274 SGPath path = globals->resolve_ressource_path("Fonts/" + name);
281 "canvas::Text: No such font: " << name
290 "canvas::Text: using font file " << path.str()
293 font_ptr font = osgText::readFontFile(path.c_str());
299 "canvas::Text: Failed to open font file " << path.c_str()
305 } // namespace canvas