15 #include "textAssembler.h"
16 #include "textGlyph.h"
17 #include "cullFaceAttrib.h"
18 #include "colorAttrib.h"
19 #include "cullBinAttrib.h"
20 #include "textureAttrib.h"
21 #include "transparencyAttrib.h"
22 #include "textPropertiesManager.h"
23 #include "textEncoder.h"
24 #include "config_text.h"
25 #include "geomTriangles.h"
26 #include "geomLines.h"
27 #include "geomPoints.h"
28 #include "geomVertexReader.h"
29 #include "geomVertexWriter.h"
30 #include "geomLines.h"
31 #include "geomVertexFormat.h"
32 #include "geomVertexData.h"
34 #include "modelNode.h"
40 static const PN_stdfloat small_accent_scale = 0.6f;
43 static const PN_stdfloat tiny_accent_scale = 0.4;
46 static const PN_stdfloat squash_accent_scale_x = 0.8f;
47 static const PN_stdfloat squash_accent_scale_y = 0.5f;
50 static const PN_stdfloat small_squash_accent_scale_x = 0.6f;
51 static const PN_stdfloat small_squash_accent_scale_y = 0.3;
55 static const PN_stdfloat ligature_advance_scale = 0.6f;
64 isspacew(
unsigned int ch) {
65 return isascii(ch) && isspace(ch);
75 isbreakpoint(
unsigned int ch) {
76 return (ch ==
' ' || ch ==
'\t' ||
77 ch == (
unsigned int)text_soft_hyphen_key ||
78 ch == (
unsigned int)text_soft_break_key);
90 _usage_hint(
Geom::UH_static),
92 _dynamic_merge(text_dynamic_merge),
106 _initial_cprops(copy._initial_cprops),
107 _text_string(copy._text_string),
108 _text_block(copy._text_block),
111 _next_row_ypos(copy._next_row_ypos),
112 _encoder(copy._encoder),
113 _usage_hint(copy._usage_hint),
114 _max_rows(copy._max_rows),
115 _dynamic_merge(copy._dynamic_merge),
116 _multiline_mode(copy._multiline_mode)
127 _initial_cprops = copy._initial_cprops;
128 _text_string = copy._text_string;
129 _text_block = copy._text_block;
132 _next_row_ypos = copy._next_row_ypos;
133 _encoder = copy._encoder;
134 _usage_hint = copy._usage_hint;
135 _max_rows = copy._max_rows;
136 _dynamic_merge = copy._dynamic_merge;
137 _multiline_mode = copy._multiline_mode;
158 _next_row_ypos = 0.0f;
160 _text_string.clear();
182 wstring::const_iterator si = wtext.begin();
183 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
185 while (si != wtext.end()) {
191 <<
"pop_properties encountered without preceding push_properties.\n";
192 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
196 return wordwrap_text();
218 nassertr(start >= 0 && start <= (
int)_text_string.size(),
false);
219 nassertr(count >= 0 && start + count <= (
int)_text_string.size(),
false);
224 wstring::const_iterator si = wtext.begin();
225 scan_wtext(substr, si, wtext.end(), _initial_cprops);
226 while (si != wtext.end()) {
228 <<
"pop_properties encountered without preceding push_properties.\n";
229 scan_wtext(substr, si, wtext.end(), _initial_cprops);
232 _text_string.erase(_text_string.begin() + start, _text_string.begin() + start + count);
233 _text_string.insert(_text_string.begin() + start, substr.begin(), substr.end());
235 return wordwrap_text();
255 TextString::const_iterator si;
256 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
257 const TextCharacter &tch = (*si);
259 wtext += tch._character;
287 TextBlock::const_iterator bi;
288 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
289 const TextRow &row = (*bi);
290 if (bi != _text_block.begin()) {
294 TextString::const_iterator si;
295 for (si = row._string.begin(); si != row._string.end(); ++si) {
296 const TextCharacter &tch = (*si);
298 wtext += tch._character;
322 PT(ComputedProperties) current_cprops = _initial_cprops;
324 TextString::const_iterator si;
325 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
326 const TextCharacter &tch = (*si);
327 current_cprops->append_delta(wtext, tch._cprops);
329 wtext += tch._character;
331 wtext.push_back(text_embed_graphic_key);
332 wtext += tch._graphic_wname;
333 wtext.push_back(text_embed_graphic_key);
335 current_cprops = tch._cprops;
337 current_cprops->append_delta(wtext, _initial_cprops);
365 PT(ComputedProperties) current_cprops = _initial_cprops;
367 TextBlock::const_iterator bi;
368 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
369 const TextRow &row = (*bi);
370 if (bi != _text_block.begin()) {
371 current_cprops->append_delta(wtext, _initial_cprops);
372 current_cprops = _initial_cprops;
376 TextString::const_iterator si;
377 for (si = row._string.begin(); si != row._string.end(); ++si) {
378 const TextCharacter &tch = (*si);
379 current_cprops->append_delta(wtext, tch._cprops);
381 wtext += tch._character;
383 wtext.push_back(text_embed_graphic_key);
384 wtext += tch._graphic_wname;
385 wtext.push_back(text_embed_graphic_key);
387 current_cprops = tch._cprops;
390 current_cprops->append_delta(wtext, _initial_cprops);
410 nassertr(n >= 0 && n <= (
int)_text_string.size(),
false);
412 if (n == (
int)_text_string.size()) {
414 if (_text_string.empty()) {
418 r = _text_block.size() - 1;
419 c = _text_block[r]._string.size();
431 while (r + 1 < (
int)_text_block.size() &&
432 _text_block[r + 1]._row_start < n) {
436 const TextRow &row = _text_block[r];
437 bool is_real_char =
true;
439 nassertr(n > 0,
false);
440 if (row._got_soft_hyphens) {
444 int i = row._row_start;
446 if (_text_string[i]._character != text_soft_hyphen_key &&
447 _text_string[i]._character != text_soft_break_key) {
452 if (_text_string[n - 1]._character != text_soft_hyphen_key &&
453 _text_string[n - 1]._character != text_soft_break_key) {
455 if (_text_string[n - 1]._character ==
'\n') {
456 is_real_char =
false;
459 is_real_char =
false;
465 c = min(n - row._row_start, (
int)row._string.size());
466 if (_text_string[n - 1]._character ==
'\n') {
467 is_real_char =
false;
487 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0);
488 if (r == (
int)_text_block.size()) {
490 return _text_string.size();
493 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0);
494 const TextRow &row = _text_block[r];
496 if (row._got_soft_hyphens) {
499 int n = row._row_start;
501 if (_text_string[n]._character != text_soft_hyphen_key &&
502 _text_string[n]._character != text_soft_break_key) {
512 return row._row_start + c;
530 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0.0f);
531 if (r == (
int)_text_block.size()) {
532 nassertr(c == 0, 0.0f);
536 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0.0f);
537 const TextRow &row = _text_block[r];
538 PN_stdfloat xpos = row._xpos;
539 for (
int i = 0; i < c; ++i) {
558 PlacedGlyphs placed_glyphs;
559 assemble_paragraph(placed_glyphs);
567 shadow_node->add_child(shadow_geom_node);
571 text_node->add_child(text_geom_node);
578 bool any_shadow = false;
580 GeomCollectorMap geom_collector_map;
581 GeomCollectorMap geom_shadow_collector_map;
583 PlacedGlyphs::const_iterator pgi;
584 for (pgi = placed_glyphs.begin(); pgi != placed_glyphs.end(); ++pgi) {
585 const GlyphPlacement *placement = (*pgi);
587 if (placement->_properties != properties) {
589 properties = placement->_properties;
590 text_state = RenderState::make_empty();
591 shadow_state = RenderState::make_empty();
594 if (properties->has_text_color()) {
595 text_state = text_state->add_attrib(ColorAttrib::make_flat(properties->get_text_color()));
596 if (properties->get_text_color()[3] != 1.0) {
597 text_state = text_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
601 if (properties->has_bin()) {
602 text_state = text_state->add_attrib(CullBinAttrib::make(properties->get_bin(), properties->get_draw_order() + 2));
605 if (properties->has_shadow()) {
606 shadow_state = shadow_state->add_attrib(ColorAttrib::make_flat(properties->get_shadow_color()));
607 if (properties->get_shadow_color()[3] != 1.0) {
608 shadow_state = shadow_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
611 if (properties->has_bin()) {
612 shadow_state = shadow_state->add_attrib(CullBinAttrib::make(properties->get_bin(), properties->get_draw_order() + 1));
615 LVector2 offset = properties->get_shadow();
623 if (properties->has_shadow()) {
624 if (_dynamic_merge) {
625 placement->assign_append_to(geom_shadow_collector_map, shadow_state, shadow_xform);
627 placement->assign_copy_to(shadow_geom_node, shadow_state, shadow_xform);
637 if (_dynamic_merge) {
640 placement->assign_to(text_geom_node, text_state);
645 placed_glyphs.clear();
650 parent_node->add_child(shadow_node);
653 GeomCollectorMap::iterator gc;
654 for (gc = geom_collector_map.begin(); gc != geom_collector_map.end(); ++gc) {
655 (*gc).second.append_geom(text_geom_node, (*gc).first._state);
659 for (gc = geom_shadow_collector_map.begin();
660 gc != geom_shadow_collector_map.end();
662 (*gc).second.append_geom(shadow_geom_node, (*gc).first._state);
666 parent_node->add_child(text_node);
681 if (character ==
' ') {
684 nassertr(font != (
TextFont *)NULL, 0.0f);
691 UnicodeLatinMap::AccentType accent_type;
692 int additional_flags;
693 PN_stdfloat glyph_scale;
694 PN_stdfloat advance_scale;
695 get_character_glyphs(character, &properties,
696 got_glyph, first_glyph, second_glyph, accent_type,
697 additional_flags, glyph_scale, advance_scale);
699 PN_stdfloat advance = 0.0f;
702 advance = first_glyph->
get_advance() * advance_scale;
710 return advance * glyph_scale;
744 if (character ==
' ' || character ==
'\n') {
751 nassertr(font != (
TextFont *)NULL,
false);
754 return font->
get_glyph(character, glyph);
772 if (character ==
' ' || character ==
'\n') {
781 UnicodeLatinMap::AccentType accent_type;
782 int additional_flags;
783 PN_stdfloat glyph_scale;
784 PN_stdfloat advance_scale;
785 get_character_glyphs(character, &properties,
786 got_glyph, first_glyph, second_glyph, accent_type,
787 additional_flags, glyph_scale, advance_scale);
814 if (character ==
' ' || character ==
'\n') {
821 nassertr(font != (
TextFont *)NULL,
false);
824 if (!font->
get_glyph(character, glyph)) {
831 #ifndef CPPPARSER // interrogate has a bit of trouble with wstring.
841 wstring::const_iterator &si,
842 const wstring::const_iterator &send,
843 TextAssembler::ComputedProperties *current_cprops) {
845 if ((*si) == text_push_properties_key) {
851 while (si != send && (*si) != text_push_properties_key) {
860 <<
"Unclosed push_properties in text.\n";
867 PT(ComputedProperties) new_cprops =
868 new ComputedProperties(current_cprops, wname, _encoder);
871 scan_wtext(output_string, si, send, new_cprops);
873 if (text_cat.is_debug()) {
879 <<
"push_properties not matched by pop_properties.\n";
883 }
else if ((*si) == text_pop_properties_key) {
889 }
else if ((*si) == text_embed_graphic_key) {
894 wstring graphic_wname;
896 while (si != send && (*si) != text_embed_graphic_key) {
897 graphic_wname += (*si);
905 <<
"Unclosed embed_graphic in text.\n";
913 string graphic_name = _encoder->encode_wtext(graphic_wname);
921 output_string.push_back(TextCharacter(named_graphic, graphic_wname, current_cprops));
925 <<
"Unknown TextGraphic: " << graphic_name <<
"\n";
930 output_string.push_back(TextCharacter(*si, current_cprops));
957 if (_text_string.empty()) {
964 _text_block.push_back(TextRow(p));
967 PN_stdfloat initial_width = 0.0f;
968 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
969 if (_text_string[p]._character ==
'\n') {
970 initial_width = 0.0f;
971 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
975 _text_block.back()._eol_cprops = _text_string[p]._cprops;
976 _text_block.push_back(TextRow(p + 1));
979 _text_block.back()._string.push_back(_text_string[p]);
983 bool needs_newline =
false;
985 while (p < _text_string.size()) {
986 nassertr(!isspacew(_text_string[p]._character),
false);
992 bool any_spaces =
false;
993 size_t last_space = 0;
994 PN_stdfloat last_space_width = 0.0f;
996 bool any_hyphens =
false;
997 size_t last_hyphen = 0;
998 bool output_hyphen =
false;
1000 bool overflow =
false;
1001 PN_stdfloat wordwrap_width = -1.0f;
1003 bool last_was_space =
false;
1004 PN_stdfloat width = initial_width;
1005 while (q < _text_string.size() && _text_string[q]._character !=
'\n') {
1006 if (_text_string[q]._cprops->_properties.has_wordwrap()) {
1007 wordwrap_width = _text_string[q]._cprops->_properties.get_wordwrap();
1009 wordwrap_width = -1.0f;
1012 if (isspacew(_text_string[q]._character) ||
1013 _text_string[q]._character == text_soft_break_key) {
1014 if (!last_was_space) {
1019 any_hyphens =
false;
1021 last_space_width = width;
1022 last_was_space =
true;
1025 last_was_space =
false;
1030 if (_text_string[q]._character == text_soft_hyphen_key) {
1031 if (wordwrap_width > 0.0f) {
1036 if (q != p && width + calc_hyphen_width(_text_string[q]) <= wordwrap_width) {
1048 if (wordwrap_width > 0.0f && width > wordwrap_width) {
1060 nassertr(wordwrap_width > 0.0f,
false);
1062 if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) {
1065 any_hyphens =
false;
1071 output_hyphen =
true;
1073 }
else if (any_spaces) {
1082 while ((
int)i < text_max_never_break && q - i > p &&
1083 get_text_never_break_before().find(_text_string[q - i]._character) != wstring::npos) {
1086 if ((
int)i < text_max_never_break) {
1093 size_t next_start = q;
1094 while (next_start < _text_string.size() &&
1095 isbreakpoint(_text_string[next_start]._character)) {
1100 while (q > p && isspacew(_text_string[q - 1]._character)) {
1104 if (next_start == p) {
1109 if (initial_width == 0.0f) {
1115 while (next_start < _text_string.size() &&
1116 isbreakpoint(_text_string[next_start]._character)) {
1122 if (needs_newline) {
1123 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1127 _text_block.push_back(TextRow(p));
1130 needs_newline =
true;
1133 if (_text_string[next_start - 1]._cprops->_properties.get_preserve_trailing_whitespace()) {
1137 for (
size_t pi = p; pi < q; pi++) {
1138 if (_text_string[pi]._character != text_soft_hyphen_key &&
1139 _text_string[pi]._character != text_soft_break_key) {
1140 _text_block.back()._string.push_back(_text_string[pi]);
1142 _text_block.back()._got_soft_hyphens =
true;
1145 if (output_hyphen) {
1146 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1147 wstring::const_iterator wi;
1148 for (wi = text_soft_hyphen_output.begin();
1149 wi != text_soft_hyphen_output.end();
1151 _text_block.back()._string.push_back(TextCharacter(*wi, _text_string[last_hyphen]._cprops));
1157 if (next_start < _text_string.size() && _text_string[next_start]._character ==
'\n') {
1159 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1163 _text_block.back()._eol_cprops = _text_string[next_start]._cprops;
1165 _text_block.push_back(TextRow(next_start));
1166 needs_newline =
false;
1171 initial_width = 0.0f;
1172 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
1173 if (_text_string[p]._character ==
'\n') {
1174 initial_width = 0.0f;
1175 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1179 _text_block.back()._eol_cprops = _text_string[p]._cprops;
1180 _text_block.push_back(TextRow(p + 1));
1182 initial_width +=
calc_width(_text_string[p]);
1183 _text_block.back()._string.push_back(_text_string[p]);
1199 PN_stdfloat TextAssembler::
1200 calc_hyphen_width(
const TextCharacter &tch) {
1201 TextFont *font = tch._cprops->_properties.get_font();
1202 nassertr(font != (
TextFont *)NULL, 0.0f);
1204 PN_stdfloat hyphen_width = 0.0f;
1205 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1206 wstring::const_iterator wi;
1207 for (wi = text_soft_hyphen_output.begin();
1208 wi != text_soft_hyphen_output.end();
1210 hyphen_width +=
calc_width(*wi, tch._cprops->_properties);
1213 return hyphen_width;
1223 void TextAssembler::
1225 _ul.set(0.0f, 0.0f);
1226 _lr.set(0.0f, 0.0f);
1229 PN_stdfloat ypos = 0.0f;
1230 _next_row_ypos = 0.0f;
1231 TextBlock::iterator bi;
1232 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
1233 TextRow &row = (*bi);
1236 PlacedGlyphs row_placed_glyphs;
1237 PN_stdfloat row_width, line_height, wordwrap;
1238 TextProperties::Alignment align;
1239 assemble_row(row, row_placed_glyphs,
1240 row_width, line_height, align, wordwrap);
1245 if (num_rows == 0) {
1247 _ul[1] = 0.8f * line_height;
1252 ypos -= line_height;
1254 _lr[1] = ypos - 0.2 * line_height;
1261 case TextProperties::A_left:
1263 _lr[0] = max(_lr[0], row_width);
1266 case TextProperties::A_right:
1268 _ul[0] = min(_ul[0], xpos);
1271 case TextProperties::A_center:
1272 xpos = -0.5f * row_width;
1273 _ul[0] = min(_ul[0], xpos);
1274 _lr[0] = max(_lr[0], -xpos);
1277 case TextProperties::A_boxed_left:
1279 _lr[0] = max(_lr[0], max(row_width, wordwrap));
1282 case TextProperties::A_boxed_right:
1283 xpos = wordwrap - row_width;
1284 _ul[0] = min(_ul[0], xpos);
1287 case TextProperties::A_boxed_center:
1288 xpos = -0.5f * row_width;
1289 if (wordwrap > row_width) xpos += (wordwrap * 0.5f);
1290 _ul[0] = min(_ul[0], max(xpos,(wordwrap * 0.5f)));
1291 _lr[0] = max(_lr[0], min(-xpos,-(wordwrap * 0.5f)));
1300 PlacedGlyphs::iterator pi;
1301 for (pi = row_placed_glyphs.begin(); pi != row_placed_glyphs.end(); ++pi) {
1302 (*pi)->_xform *= mat;
1303 placed_glyphs.push_back(*pi);
1308 _next_row_ypos = ypos - line_height;
1325 void TextAssembler::
1326 assemble_row(TextAssembler::TextRow &row,
1328 PN_stdfloat &row_width, PN_stdfloat &line_height,
1329 TextProperties::Alignment &align, PN_stdfloat &wordwrap) {
1333 PN_stdfloat xpos = 0.0f;
1334 align = TextProperties::A_left;
1336 bool underscore =
false;
1337 PN_stdfloat underscore_start = 0.0f;
1340 TextString::const_iterator si;
1341 for (si = row._string.begin(); si != row._string.end(); ++si) {
1342 const TextCharacter &tch = (*si);
1343 wchar_t character = tch._character;
1348 (underscore && (properties->get_text_color() != underscore_properties->get_text_color() ||
1351 if (underscore && underscore_start != xpos) {
1352 draw_underscore(row_placed_glyphs, underscore_start, xpos,
1353 underscore_properties);
1356 underscore_start = xpos;
1357 underscore_properties = properties;
1361 nassertv(font != (
TextFont *)NULL);
1364 if ((align == TextProperties::A_left) &&
1365 (properties->get_align() != TextProperties::A_left)) {
1366 align = properties->get_align();
1373 line_height = max(line_height, frame[3] - frame[2]);
1379 if (character ==
' ') {
1383 }
else if (character ==
'\t') {
1386 xpos = (floor(xpos / tab_width) + 1.0f) * tab_width;
1388 }
else if (character == text_soft_hyphen_key) {
1393 GlyphPlacement *placement =
new GlyphPlacement;
1394 row_placed_glyphs.push_back(placement);
1396 PT(
PandaNode) model = graphic->get_model().node();
1397 if (graphic->get_instance_flag()) {
1401 model_node->set_preserve_transform(ModelNode::PT_no_touch);
1402 model_node->add_child(model);
1403 placement->_graphic_model = model_node.p();
1408 placement->_graphic_model = model->copy_subgraph();
1414 PN_stdfloat advance = (frame[1] - frame[0]);
1420 glyph_xform(3, 0) += (xpos - frame[0]);
1423 if (properties->has_slant()) {
1424 LMatrix4 shear(1.0f, 0.0f, 0.0f, 0.0f,
1425 0.0f, 1.0f, 0.0f, 0.0f,
1426 properties->
get_slant(), 0.0f, 1.0f, 0.0f,
1427 0.0f, 0.0f, 0.0f, 1.0f);
1428 glyph_xform = shear * glyph_xform;
1431 placement->_xform = glyph_xform;
1432 placement->_properties = properties;
1434 xpos += advance * glyph_scale;
1441 UnicodeLatinMap::AccentType accent_type;
1442 int additional_flags;
1443 PN_stdfloat glyph_scale;
1444 PN_stdfloat advance_scale;
1445 get_character_glyphs(character, properties,
1446 got_glyph, first_glyph, second_glyph, accent_type,
1447 additional_flags, glyph_scale, advance_scale);
1451 sprintf(buffer,
"U+%04x", character);
1453 <<
"No definition in " << font->get_name()
1454 <<
" for character " << buffer;
1455 if (character < 128 && isprint((
unsigned int)character)) {
1456 text_cat.warning(
false)
1457 <<
" ('" << (char)character <<
"')";
1459 text_cat.warning(
false)
1467 GlyphPlacement *placement =
new GlyphPlacement;
1468 row_placed_glyphs.push_back(placement);
1470 PN_stdfloat advance = 0.0f;
1473 PT(
Geom) first_char_geom = first_glyph->get_geom(_usage_hint);
1474 if (first_char_geom != (
Geom *)NULL) {
1475 placement->add_piece(first_char_geom, first_glyph->get_state());
1477 advance = first_glyph->
get_advance() * advance_scale;
1479 if (second_glyph != (
TextGlyph *)NULL) {
1480 PT(
Geom) second_char_geom = second_glyph->get_geom(_usage_hint);
1481 if (second_char_geom != (
Geom *)NULL) {
1483 placement->add_piece(second_char_geom, second_glyph->get_state());
1490 if (properties->get_wordwrap() > 0.0f) {
1491 wordwrap = properties->get_wordwrap();
1497 if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
1502 bool found_any =
false;
1503 placement->calc_tight_bounds(min_vert, max_vert, found_any,
1507 LPoint3 centroid = (min_vert + max_vert) / 2.0f;
1508 tack_on_accent(accent_type, min_vert, max_vert, centroid,
1509 properties, placement);
1511 if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
1527 glyph_xform *= rotate;
1532 glyph_xform(3, 0) += xpos;
1535 if (properties->has_slant()) {
1536 LMatrix4 shear(1.0f, 0.0f, 0.0f, 0.0f,
1537 0.0f, 1.0f, 0.0f, 0.0f,
1538 properties->
get_slant(), 0.0f, 1.0f, 0.0f,
1539 0.0f, 0.0f, 0.0f, 1.0f);
1540 glyph_xform = shear * glyph_xform;
1543 placement->_xform = glyph_xform;
1544 placement->_properties = properties;
1546 xpos += advance * glyph_scale;
1547 line_height = max(line_height, font->
get_line_height() * glyph_scale);
1551 if (underscore && underscore_start != xpos) {
1552 draw_underscore(row_placed_glyphs, underscore_start, xpos,
1553 underscore_properties);
1558 if (row._eol_cprops != (ComputedProperties *)NULL) {
1563 const TextProperties *properties = &(row._eol_cprops->_properties);
1565 nassertv(font != (
TextFont *)NULL);
1567 if (line_height == 0.0f) {
1569 line_height = max(line_height, font->
get_line_height() * glyph_scale);
1580 void TextAssembler::
1582 PN_stdfloat underscore_start, PN_stdfloat underscore_end,
1584 CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
1587 vdata->reserve_num_rows(2);
1591 PN_stdfloat y = underscore_properties->get_underscore_height();
1592 vertex.add_data3(underscore_start, 0.0f, y);
1593 color.add_data4(underscore_properties->get_text_color());
1594 vertex.add_data3(underscore_end, 0.0f, y);
1595 color.add_data4(underscore_properties->get_text_color());
1598 lines->add_vertices(0, 1);
1599 lines->close_primitive();
1602 geom->add_primitive(lines);
1604 GlyphPlacement *placement = new GlyphPlacement;
1605 placement->add_piece(geom,
RenderState::make_empty());
1606 placement->_xform =
LMatrix4::ident_mat();
1607 placement->_properties = underscore_properties;
1609 row_placed_glyphs.push_back(placement);
1632 get_character_glyphs(
int character, const
TextProperties *properties,
1633 bool &got_glyph, const
TextGlyph *&glyph,
1636 int &additional_flags,
1637 PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale) {
1638 TextFont *font = properties->get_font();
1639 nassertv_always(font != (
TextFont *)NULL);
1643 second_glyph = NULL;
1644 accent_type = UnicodeLatinMap::AT_none;
1645 additional_flags = 0;
1647 advance_scale = 1.0f;
1653 if (map_entry != NULL) {
1654 if (properties->get_small_caps() &&
1655 map_entry->_toupper_character != character) {
1656 character = map_entry->_toupper_character;
1658 glyph_scale = properties->get_small_caps_scale();
1662 got_glyph = font->
get_glyph(character, glyph);
1663 if (!got_glyph && map_entry != NULL && map_entry->_ascii_equiv != 0) {
1666 got_glyph = font->
get_glyph(map_entry->_ascii_equiv, glyph);
1668 if (!got_glyph && map_entry->_toupper_character != character) {
1671 character = map_entry->_toupper_character;
1673 if (map_entry != NULL) {
1674 got_glyph = font->
get_glyph(map_entry->_ascii_equiv, glyph);
1679 accent_type = map_entry->_accent_type;
1680 additional_flags = map_entry->_additional_flags;
1682 bool got_second_glyph =
false;
1683 if (map_entry->_ascii_additional != 0) {
1686 font->
get_glyph(map_entry->_ascii_additional, second_glyph);
1689 if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
1694 advance_scale = ligature_advance_scale;
1697 if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
1699 glyph_scale = properties->get_small_caps_scale();
1713 void TextAssembler::
1714 tack_on_accent(UnicodeLatinMap::AccentType accent_type,
1718 TextAssembler::GlyphPlacement *placement)
const {
1719 switch (accent_type) {
1720 case UnicodeLatinMap::AT_grave:
1725 tack_on_accent(
'/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
1726 properties, placement);
1729 case UnicodeLatinMap::AT_acute:
1730 tack_on_accent(
'/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
1731 properties, placement);
1734 case UnicodeLatinMap::AT_breve:
1735 tack_on_accent(
')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1736 properties, placement);
1739 case UnicodeLatinMap::AT_inverted_breve:
1740 tack_on_accent(
'(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1741 properties, placement);
1744 case UnicodeLatinMap::AT_circumflex:
1745 tack_on_accent(
'^', CP_above, CT_none, min_vert, max_vert, centroid,
1746 properties, placement) ||
1747 tack_on_accent(
'v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
1748 properties, placement);
1751 case UnicodeLatinMap::AT_circumflex_below:
1752 tack_on_accent(
'^', CP_below, CT_none, min_vert, max_vert, centroid,
1753 properties, placement) ||
1754 tack_on_accent(
'v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
1755 properties, placement);
1758 case UnicodeLatinMap::AT_caron:
1759 tack_on_accent(
'^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
1760 properties, placement) ||
1761 tack_on_accent(
'v', CP_above, CT_squash, min_vert, max_vert, centroid,
1762 properties, placement);
1766 case UnicodeLatinMap::AT_tilde:
1767 tack_on_accent(
'~', CP_above, CT_none, min_vert, max_vert, centroid,
1768 properties, placement) ||
1769 tack_on_accent(
's', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1770 properties, placement);
1774 case UnicodeLatinMap::AT_tilde_below:
1775 tack_on_accent(
'~', CP_below, CT_none, min_vert, max_vert, centroid,
1776 properties, placement) ||
1777 tack_on_accent(
's', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1778 properties, placement);
1781 case UnicodeLatinMap::AT_diaeresis:
1782 tack_on_accent(
':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
1783 properties, placement);
1786 case UnicodeLatinMap::AT_diaeresis_below:
1787 tack_on_accent(
':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
1788 properties, placement);
1791 case UnicodeLatinMap::AT_dot_above:
1792 tack_on_accent(
'.', CP_above, CT_none, min_vert, max_vert, centroid,
1793 properties, placement);
1796 case UnicodeLatinMap::AT_dot_below:
1797 tack_on_accent(
'.', CP_below, CT_none, min_vert, max_vert, centroid,
1798 properties, placement);
1801 case UnicodeLatinMap::AT_macron:
1802 tack_on_accent(
'-', CP_above, CT_none, min_vert, max_vert, centroid,
1803 properties, placement);
1806 case UnicodeLatinMap::AT_line_below:
1807 tack_on_accent(
'-', CP_below, CT_none, min_vert, max_vert, centroid,
1808 properties, placement);
1811 case UnicodeLatinMap::AT_ring_above:
1812 tack_on_accent(
'o', CP_top, CT_tiny, min_vert, max_vert, centroid,
1813 properties, placement);
1816 case UnicodeLatinMap::AT_ring_below:
1817 tack_on_accent(
'o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
1818 properties, placement);
1821 case UnicodeLatinMap::AT_cedilla:
1822 tack_on_accent(
'c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
1823 properties, placement);
1828 case UnicodeLatinMap::AT_comma_below:
1829 tack_on_accent(
',', CP_below, CT_none, min_vert, max_vert, centroid,
1830 properties, placement);
1833 case UnicodeLatinMap::AT_ogonek:
1834 tack_on_accent(
',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
1835 properties, placement);
1838 case UnicodeLatinMap::AT_stroke:
1839 tack_on_accent(
'/', CP_within, CT_none, min_vert, max_vert, centroid,
1840 properties, placement);
1857 bool TextAssembler::
1858 tack_on_accent(
char accent_mark, TextAssembler::CheesyPosition position,
1859 TextAssembler::CheesyTransform transform,
1863 TextAssembler::GlyphPlacement *placement)
const {
1865 nassertr(font != (
TextFont *)NULL,
false);
1870 if (font->
get_glyph(accent_mark, accent_glyph) ||
1871 font->
get_glyph(toupper(accent_mark), accent_glyph)) {
1872 PT(
Geom) accent_geom = accent_glyph->get_geom(_usage_hint);
1873 if (accent_geom != (
Geom *)NULL) {
1874 LPoint3 min_accent, max_accent;
1875 bool found_any =
false;
1876 accent_geom->calc_tight_bounds(min_accent, max_accent, found_any,
1884 bool mirrored =
false;
1886 switch (transform) {
1894 min_accent[0] = -max_accent[0];
1902 min_accent[2] = -max_accent[2];
1912 max_accent[0] = -min_accent[2];
1913 min_accent[0] = -max_accent[2];
1922 min_accent[0] = -max_accent[0];
1925 min_accent[2] = -max_accent[2];
1934 min_accent[0] = min_accent[2];
1935 max_accent[0] = max_accent[2];
1942 min_accent[0] *= squash_accent_scale_x;
1943 max_accent[0] *= squash_accent_scale_x;
1944 min_accent[2] *= squash_accent_scale_y;
1945 max_accent[2] *= squash_accent_scale_y;
1948 case CT_squash_mirror_y:
1950 min_accent[0] *= squash_accent_scale_x;
1951 max_accent[0] *= squash_accent_scale_x;
1953 min_accent[2] = -max_accent[2] * squash_accent_scale_y;
1954 max_accent[2] = -t * squash_accent_scale_y;
1958 case CT_squash_mirror_diag:
1966 min_accent[0] = min_accent[2] * -squash_accent_scale_x;
1967 max_accent[0] = max_accent[2] * -squash_accent_scale_x;
1968 min_accent[2] = -u * squash_accent_scale_y;
1969 max_accent[2] = -t * squash_accent_scale_y;
1973 case CT_small_squash:
1974 accent_mat =
LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
1975 min_accent[0] *= small_squash_accent_scale_x;
1976 max_accent[0] *= small_squash_accent_scale_x;
1977 min_accent[2] *= small_squash_accent_scale_y;
1978 max_accent[2] *= small_squash_accent_scale_y;
1981 case CT_small_squash_mirror_y:
1982 accent_mat =
LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, -small_squash_accent_scale_y);
1983 min_accent[0] *= small_squash_accent_scale_x;
1984 max_accent[0] *= small_squash_accent_scale_x;
1986 min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
1987 max_accent[2] = -t * small_squash_accent_scale_y;
1991 case CT_small_squash_mirror_diag:
1999 min_accent[0] = min_accent[2] * -small_squash_accent_scale_x;
2000 max_accent[0] = max_accent[2] * -small_squash_accent_scale_x;
2001 min_accent[2] = -u * small_squash_accent_scale_y;
2002 max_accent[2] = -t * small_squash_accent_scale_y;
2008 min_accent *= small_accent_scale;
2009 max_accent *= small_accent_scale;
2012 case CT_small_rotate_270:
2020 min_accent[0] = min_accent[2] * small_accent_scale;
2021 max_accent[0] = max_accent[2] * small_accent_scale;
2022 min_accent[2] = -u * small_accent_scale;
2023 max_accent[2] = -t * small_accent_scale;
2028 min_accent *= tiny_accent_scale;
2029 max_accent *= tiny_accent_scale;
2032 case CT_tiny_mirror_x:
2036 min_accent[0] = -max_accent[0] * tiny_accent_scale;
2037 max_accent[0] = -t * tiny_accent_scale;
2038 min_accent[2] *= tiny_accent_scale;
2039 max_accent[2] *= tiny_accent_scale;
2043 case CT_tiny_rotate_270:
2051 min_accent[0] = min_accent[2] * tiny_accent_scale;
2052 max_accent[0] = max_accent[2] * tiny_accent_scale;
2053 min_accent[2] = -u * tiny_accent_scale;
2054 max_accent[2] = -t * tiny_accent_scale;
2058 LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
2059 PN_stdfloat accent_height = max_accent[2] - min_accent[2];
2064 trans.set(centroid[0] - accent_centroid[0], 0.0f,
2065 max_vert[2] - accent_centroid[2] + accent_height * 0.5);
2070 trans.set(centroid[0] - accent_centroid[0], 0.0f,
2071 min_vert[2] - accent_centroid[2] - accent_height * 0.5);
2076 trans.set(centroid[0] - accent_centroid[0], 0.0f,
2077 max_vert[2] - accent_centroid[2]);
2082 trans.set(centroid[0] - accent_centroid[0], 0.0f,
2083 min_vert[2] - accent_centroid[2]);
2088 trans.set(centroid[0] - accent_centroid[0], 0.0f,
2089 centroid[2] - accent_centroid[2]);
2094 accent_geom->transform_vertices(accent_mat);
2100 if (disable_backface == (const
RenderState *)NULL) {
2101 disable_backface = RenderState::make
2102 (CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
2106 accent_glyph->get_state()->compose(disable_backface);
2107 placement->add_piece(accent_geom, state);
2109 placement->add_piece(accent_geom, accent_glyph->get_state());
2126 void TextAssembler::ComputedProperties::
2127 append_delta(wstring &wtext, TextAssembler::ComputedProperties *other) {
2128 if (
this != other) {
2129 if (_depth > other->_depth) {
2131 nassertv(_based_on != NULL);
2133 wtext.push_back(text_pop_properties_key);
2134 _based_on->append_delta(wtext, other);
2136 }
else if (other->_depth > _depth) {
2138 nassertv(other->_based_on != NULL);
2140 append_delta(wtext, other->_based_on);
2141 wtext.push_back(text_push_properties_key);
2142 wtext += other->_wname;
2143 wtext.push_back(text_push_properties_key);
2145 }
else if (_depth != 0) {
2147 nassertv(_based_on != NULL && other->_based_on != NULL);
2149 wtext.push_back(text_pop_properties_key);
2150 _based_on->append_delta(wtext, other->_based_on);
2151 wtext.push_back(text_push_properties_key);
2152 wtext += other->_wname;
2153 wtext.push_back(text_push_properties_key);
2167 void TextAssembler::GlyphPlacement::
2169 bool &found_any,
Thread *current_thread)
const {
2170 Pieces::const_iterator pi;
2171 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
2172 (*pi)._geom->calc_tight_bounds(min_point, max_point, found_any,
2184 void TextAssembler::GlyphPlacement::
2186 Pieces::const_iterator pi;
2187 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
2188 (*pi)._geom->transform_vertices(_xform);
2189 geom_node->
add_geom((*pi)._geom, state->compose((*pi)._state));
2201 void TextAssembler::GlyphPlacement::
2203 const LMatrix4 &extra_xform)
const {
2204 LMatrix4 new_xform = _xform * extra_xform;
2205 Pieces::const_iterator pi;
2206 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
2207 const Geom *geom = (*pi)._geom;
2208 PT(
Geom) new_geom = geom->make_copy();
2209 new_geom->transform_vertices(new_xform);
2210 geom_node->add_geom(new_geom, state->compose((*pi)._state));
2223 assign_append_to(GeomCollectorMap &geom_collector_map,
2225 const
LMatrix4 &extra_xform)
const {
2226 LMatrix4 new_xform = _xform * extra_xform;
2227 Pieces::const_iterator pi;
2230 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
2231 const Geom *geom = (*pi)._geom;
2233 CPT(
RenderState) rs = (*pi)._state->compose(state);
2234 GeomCollectorKey key(rs, vdata->get_format());
2236 GeomCollectorMap::iterator mi = geom_collector_map.find(key);
2237 if (mi == geom_collector_map.end()) {
2238 mi = geom_collector_map.insert(GeomCollectorMap::value_type(key, GeomCollector(vdata->
get_format()))).first;
2240 GeomCollector &geom_collector = (*mi).second;
2241 geom_collector.count_geom(geom);
2246 VertexIndexMap vimap;
2249 CPT(
GeomPrimitive) primitive = geom->get_primitive(p)->decompose();
2252 GeomPrimitive *new_prim = geom_collector.get_primitive(primitive->get_type());
2256 for (sp = 0; sp < primitive->get_num_primitives(); sp++) {
2258 e = primitive->get_primitive_end(sp);
2261 for (i = s; i < e; i++) {
2262 int vi = primitive->get_vertex(i);
2265 pair<VertexIndexMap::iterator, bool> added = vimap.insert(VertexIndexMap::value_type(vi, 0));
2270 new_vertex = geom_collector.append_vertex(vdata, vi, new_xform);
2272 (*(added.first)).second = new_vertex;
2279 new_vertex = (*(added.first)).second;
2281 new_prim->add_vertex(new_vertex);
2283 new_prim->close_primitive();
2295 void TextAssembler::GlyphPlacement::
2297 const LMatrix4 &extra_xform)
const {
2298 if (_graphic_model != (
PandaNode *)NULL) {
2299 LMatrix4 new_xform = _xform * extra_xform;
2303 node->add_child(intermediate_node);
2305 intermediate_node->set_transform(TransformState::make_mat(new_xform));
2306 intermediate_node->set_state(state);
2307 intermediate_node->add_child(_graphic_model);
2318 GeomCollector(const GeomVertexFormat *format) :
2329 TextAssembler::GeomCollector::
2330 GeomCollector(
const TextAssembler::GeomCollector ©) :
2331 _vdata(copy._vdata),
2346 if (prim_type == GeomTriangles::get_class_type()) {
2349 _geom->add_primitive(_triangles);
2353 }
else if (prim_type == GeomLines::get_class_type()) {
2355 _lines =
new GeomLines(Geom::UH_static);
2356 _geom->add_primitive(_lines);
2360 }
else if (prim_type == GeomPoints::get_class_type()) {
2363 _geom->add_primitive(_points);
2368 nassertr(
false, NULL);
2378 int TextAssembler::GeomCollector::
2381 int new_row = _vdata->get_num_rows();
2385 vertex_rewriter.set_row_unsafe(new_row);
2386 LPoint3 point = vertex_rewriter.get_data3();
2387 vertex_rewriter.set_data3(point * xform);
2399 void TextAssembler::GeomCollector::
2401 if (_geom->get_num_primitives() > 0) {
const TextGraphic * get_graphic_ptr(const string &name)
Returns a pointer to the TextGraphic with the indicated name, or NULL if there is no graphic with tha...
int get_primitive_start(int n) const
Returns the element within the _vertices list at which the nth primitive starts.
static const LMatrix4f & ident_mat()
Returns an identity matrix.
A basic node of the scene graph or data graph.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
This is the base class for all three-component vectors and points.
PN_stdfloat get_advance() const
Returns the distance by which the character pointer should be advanced after placing this character; ...
static bool has_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font or can be synthesized by Panda, false otherwise.
static LMatrix4f scale_mat(const LVecBase3f &scale)
Returns a matrix that applies the indicated scale in each of the three axes.
static LMatrix4f translate_mat(const LVecBase3f &trans)
Returns a matrix that applies the indicated translation.
PN_stdfloat get_glyph_scale() const
Returns the scale factor of each letter as specified by set_glyph_scale().
wstring get_wordwrapped_plain_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
Defines a series of disconnected points.
PN_stdfloat get_glyph_shift() const
Returns the vertical shift of each letter as specified by set_glyph_shift().
This class mainly serves as a container for a largish table of the subset of the Unicode character se...
This class can be used to convert text between multiple representations, e.g.
bool get_multiline_mode() const
Returns the multline_mode flag.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
PN_stdfloat get_slant() const
Returns the factor by which the text is specified to slant to the right.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
static bool is_whitespace(wchar_t character, const TextProperties &properties)
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
PN_stdfloat get_text_scale() const
Returns the scale factor of the text as specified by set_text_scale().
static TextPropertiesManager * get_global_ptr()
Returns the pointer to the global TextPropertiesManager object.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
wstring get_wordwrapped_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
An encapsulation of a font; i.e.
wstring get_plain_wtext() const
Returns a wstring that represents the contents of the text, without any embedded properties character...
LVecBase4 get_frame() const
Returns the frame specified for the graphic.
PN_stdfloat get_space_advance() const
Returns the number of units wide a space is.
static bool has_exact_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font exactly as named, false otherwise.
PN_stdfloat get_xpos(int r, int c) const
Returns the x position of the origin of the character or graphic object at the indicated position in ...
This node is placed at key points within the scene graph to indicate the roots of "models": subtrees ...
bool set_wtext(const wstring &wtext)
Accepts a new text string and associated properties structure, and precomputes the wordwrapping layou...
static PN_stdfloat calc_width(wchar_t character, const TextProperties &properties)
Returns the width of a single character, according to its associated font.
This is a 4-by-4 transform matrix.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
This class is not normally used directly by user code, but is used by the TextNode to lay out a block...
const GeomVertexFormat * get_format() const
Returns a pointer to the GeomVertexFormat structure that defines this data.
A representation of a single glyph (character) from a font.
wstring get_wtext() const
Returns a wstring that represents the contents of the text.
bool set_wsubstr(const wstring &wtext, int start, int count)
Replaces the 'count' characters from 'start' of the current text with the indicated replacement text...
bool get_underscore() const
Returns the underscore flag.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Defines a series of disconnected line segments.
This is the base class for all three-component vectors and points.
TextFont * get_font() const
Returns the font currently in use, if any.
virtual bool is_whitespace() const
Returns true if this glyph represents invisible whitespace, or false if it corresponds to some visibl...
This is a two-component vector offset.
void clear()
Reinitializes the contents of the TextAssembler.
PN_stdfloat get_tab_width() const
Returns the width set via set_tab_width().
static LMatrix4f rotate_mat_normaxis(float angle, const LVecBase3f &axis, CoordinateSystem cs=CS_default)
Returns a matrix that rotates by the given angle in degrees counterclockwise about the indicated vect...
A thread; that is, a lightweight process.
This defines the set of visual properties that may be assigned to the individual characters of the te...
Defines a series of disconnected triangles.
int calc_index(int r, int c) const
Computes the character index of the character at the rth row and cth column position.
void set_row(int row, const LVecBase4f &v)
Replaces the indicated row of the matrix.
static const Entry * look_up(wchar_t character)
Returns the Entry associated with the indicated character, if there is one.
TypeHandle is the identifier used to differentiate C++ class types.
This defines all of the TextProperties structures that might be referenced by name from an embedded t...
PN_stdfloat get_underscore_height() const
Returns the vertical height of the underscore; see set_underscore_height().
PN_stdfloat get_line_height() const
Returns the number of units high each line of text is.
const TextGlyph * get_glyph(int character)
Gets the glyph associated with the given character code, as well as an optional scaling parameter tha...
This defines a special model that has been constructed for the purposes of embedding an arbitrary gra...
A node that holds Geom objects, renderable pieces of geometry.
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter, combined together into one convenient package.
void set_rotate_mat_normaxis(float angle, const LVecBase3f &axis, CoordinateSystem cs=CS_default)
Fills mat with a matrix that rotates by the given angle in degrees counterclockwise about the indicat...
int get_num_primitives() const
Returns the number of GeomPrimitive objects stored within the Geom, each of which represents a number...
bool calc_r_c(int &r, int &c, int n) const
Computes the row and column index of the nth character or graphic object in the text.