Geant4 Cross Reference |
1 // Copyright (C) 2010, Guy Barrand. All rights reserved. 2 // See the file tools.license for terms. 3 4 #ifndef tools_sg_text 5 #define tools_sg_text 6 7 #include "nodekit" 8 #include "back_area" 9 10 #include "matrix" 11 #include "text_hershey" 12 #include "base_freetype" 13 14 #include "enums" 15 #include "rgba" 16 #include "noderef" 17 #include "mf" 18 19 #include "../colorf" 20 #include "../S_STRING" 21 22 #include <utility> 23 24 namespace tools { 25 namespace sg { 26 27 class text : public back_area { 28 TOOLS_NODE(text,tools::sg::text,back_area) 29 public: 30 mf_string strings; 31 sf<bool> confine; 32 33 sf_vec<colorf,float> color; 34 sf_string font; 35 sf_enum<sg::font_modeling> font_modeling; 36 37 sf_string encoding; 38 sf<float> line_width; // for text_hershey. 39 sf_enum<winding_type> front_face; //no more used. 40 41 sf<bool> back_visible; 42 43 sf<bool> enforce_front_height; 44 sf<float> front_height; 45 sf<bool> enforce_front_width; 46 sf<float> front_width; 47 48 sf<float> wmargin_factor; 49 sf<float> hmargin_factor; 50 sf_enum<sg::hjust> hjust; 51 sf_enum<sg::vjust> vjust; 52 public: 53 virtual const desc_fields& node_desc_fields() const { 54 TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::text) 55 static const desc_fields s_v(parent::node_desc_fields(),17, //WARNING : have the right count. 56 TOOLS_ARG_FIELD_DESC(strings), 57 TOOLS_ARG_FIELD_DESC(confine), 58 59 TOOLS_ARG_FIELD_DESC(color), 60 61 TOOLS_ARG_FIELD_DESC_OPTS_BEG(font,10) 62 font_hershey().c_str(), 63 font_lato_regular_ttf().c_str(), 64 font_roboto_bold_ttf().c_str(), 65 font_arial_ttf().c_str(), 66 font_arialbd_ttf().c_str(), 67 font_timesbd_ttf().c_str(), 68 font_symbol_ttf().c_str(), 69 font_stixgeneral_otf().c_str(), 70 font_helvetica_ttf().c_str(), 71 font_times_roman_ttf().c_str() 72 TOOLS_ARG_FIELD_DESC_OPTS_END, 73 74 TOOLS_ARG_FIELD_DESC_ENUMS_BEG(font_modeling,3) 75 TOOLS_ARG_ENUM(font_outline), 76 TOOLS_ARG_ENUM(font_filled), 77 TOOLS_ARG_ENUM(font_pixmap) 78 TOOLS_ARG_FIELD_DESC_ENUMS_END, 79 80 TOOLS_ARG_FIELD_DESC(encoding), 81 TOOLS_ARG_FIELD_DESC(line_width), 82 83 TOOLS_ARG_FIELD_DESC_ENUMS_BEG(front_face,2) 84 TOOLS_ARG_ENUM(winding_ccw), 85 TOOLS_ARG_ENUM(winding_cw) 86 TOOLS_ARG_FIELD_DESC_ENUMS_END, 87 88 TOOLS_ARG_FIELD_DESC(back_visible), 89 90 TOOLS_ARG_FIELD_DESC(enforce_front_height), 91 TOOLS_ARG_FIELD_DESC(front_height), 92 TOOLS_ARG_FIELD_DESC(enforce_front_width), 93 TOOLS_ARG_FIELD_DESC(front_width), 94 TOOLS_ARG_FIELD_DESC(wmargin_factor), 95 TOOLS_ARG_FIELD_DESC(hmargin_factor), 96 97 TOOLS_ARG_FIELD_DESC_ENUMS_BEG(hjust,3) 98 TOOLS_ARG_ENUM(left), 99 TOOLS_ARG_ENUM(center), 100 TOOLS_ARG_ENUM(right) 101 TOOLS_ARG_FIELD_DESC_ENUMS_END, 102 103 TOOLS_ARG_FIELD_DESC_ENUMS_BEG(vjust,3) 104 TOOLS_ARG_ENUM(bottom), 105 TOOLS_ARG_ENUM(middle), 106 TOOLS_ARG_ENUM(top) 107 TOOLS_ARG_FIELD_DESC_ENUMS_END 108 ); 109 return s_v; 110 } 111 private: 112 void add_fields(){ 113 add_field(&strings); 114 add_field(&confine); 115 116 add_field(&color); 117 add_field(&font); 118 add_field(&font_modeling); 119 add_field(&encoding); 120 add_field(&line_width); 121 add_field(&front_face); 122 123 add_field(&back_visible); 124 125 add_field(&enforce_front_height); 126 add_field(&front_height); 127 add_field(&enforce_front_width); 128 add_field(&front_width); 129 130 add_field(&wmargin_factor); 131 add_field(&hmargin_factor); 132 add_field(&hjust); 133 add_field(&vjust); 134 } 135 public: 136 virtual void render(render_action& a_action) { 137 if(touched()) { 138 update_sg(); 139 reset_touched(); 140 } 141 if(back_visible.value()) m_back_sep.render(a_action); 142 m_sep.render(a_action); 143 } 144 virtual void pick(pick_action& a_action) { 145 if(touched()) { 146 update_sg(); 147 reset_touched(); 148 } 149 if(back_visible.value()) { 150 nodekit_pick(a_action,m_back_sep,this); 151 } 152 //m_sep.pick(a_action); 153 } 154 virtual void search(search_action& a_action) { 155 if(touched()) { 156 update_sg(); 157 reset_touched(); 158 } 159 parent::search(a_action); 160 if(a_action.done()) return; 161 if(a_action.do_path()) a_action.path_push(this); 162 if(back_visible.value()) { 163 m_back_sep.search(a_action); 164 if(a_action.done()) return; 165 } 166 m_sep.search(a_action); 167 if(a_action.done()) return; 168 if(a_action.do_path()) a_action.path_pop(); 169 } 170 virtual void bbox(bbox_action& a_action) { 171 if(touched()) { 172 update_sg(); 173 reset_touched(); 174 } 175 if(back_visible.value()) m_back_sep.bbox(a_action); 176 m_sep.bbox(a_action); 177 } 178 public: 179 text(const base_freetype& a_ttf) 180 :parent() 181 ,strings() 182 ,confine(false) 183 184 ,color(colorf_black()) 185 ,font(font_hershey()) 186 ,font_modeling(font_filled) 187 ,encoding(encoding_PAW()) 188 ,line_width(1) 189 ,front_face(winding_ccw) 190 191 ,back_visible(true) 192 193 ,enforce_front_height(false) 194 ,front_height(1) 195 ,enforce_front_width(false) 196 ,front_width(1) 197 198 ,wmargin_factor(0.9f) 199 ,hmargin_factor(0.9f) 200 ,hjust(left) // same default as base_text. 201 ,vjust(middle) // not same default as base_text (which is bottom). We take middle for backcomp of confined text. 202 // Note that text_hershey, text_freetype is (left,bottom) justified. 203 204 ,m_base_text(0) 205 ,m_TT_text(base_freetype::create(a_ttf)) 206 { 207 add_fields(); 208 } 209 virtual ~text(){ 210 delete m_TT_text; 211 } 212 public: 213 text(const text& a_from) 214 :parent(a_from) 215 ,strings(a_from.strings) 216 ,confine(a_from.confine) 217 218 ,color(a_from.color) 219 ,font(a_from.font) 220 ,font_modeling(a_from.font_modeling) 221 ,encoding(a_from.encoding) 222 ,line_width(a_from.line_width) 223 ,front_face(a_from.front_face) 224 225 ,back_visible(a_from.back_visible) 226 227 ,enforce_front_height(a_from.enforce_front_height) 228 ,front_height(a_from.front_height) 229 ,enforce_front_width(a_from.enforce_front_width) 230 ,front_width(a_from.front_width) 231 232 ,wmargin_factor(a_from.wmargin_factor) 233 ,hmargin_factor(a_from.hmargin_factor) 234 ,hjust(a_from.hjust) 235 ,vjust(a_from.vjust) 236 237 ,m_base_text(0) 238 ,m_TT_text(base_freetype::create(*a_from.m_TT_text)) 239 { 240 add_fields(); 241 } 242 text& operator=(const text& a_from){ 243 parent::operator=(a_from); 244 if(&a_from==this) return *this; 245 246 strings = a_from.strings; 247 confine = a_from.confine; 248 249 color = a_from.color; 250 font = a_from.font; 251 font_modeling = a_from.font_modeling; 252 encoding = a_from.encoding; 253 line_width = a_from.line_width; 254 front_face = a_from.front_face; 255 256 back_visible = a_from.back_visible; 257 258 enforce_front_height = a_from.enforce_front_height; 259 front_height = a_from.front_height; 260 enforce_front_width = a_from.enforce_front_width; 261 front_width = a_from.front_width; 262 263 wmargin_factor = a_from.wmargin_factor; 264 hmargin_factor = a_from.hmargin_factor; 265 hjust = a_from.hjust; 266 vjust = a_from.vjust; 267 268 m_base_text = 0; 269 270 //delete m_TT_text; 271 //m_TT_text = base_freetype::create(*a_from.m_TT_text); 272 273 return *this; 274 } 275 public: 276 float text_height() const { 277 if(!m_base_text) return 0; 278 return m_base_text->height; 279 } 280 /*bool get_bounds(float a_height, 281 float& a_mn_x,float& a_mn_y,float& a_mn_z, 282 float& a_mx_x,float& a_mx_y,float& a_mx_z) const { 283 if(!m_base_text) { 284 a_mn_x = 0; 285 a_mn_y = 0; 286 a_mn_z = 0; 287 a_mx_x = 0; 288 a_mx_y = 0; 289 a_mx_z = 0; 290 return false; 291 } 292 m_base_text->get_bounds(m_base_text->height,a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z); 293 return true; 294 }*/ 295 bool is_empty() const { 296 tools_vforcit(std::string,strings.values(),it) { 297 const std::string& line = *it; 298 if(line.size()) return false; 299 } 300 return true; 301 } 302 303 const separator& container() const {return m_back_sep;} //must be consistent with pick(). 304 public: 305 void update_sg() { 306 parent::update_sg(); 307 308 m_sep.clear(); 309 m_base_text = 0; 310 311 if(width.value()<=0) return; 312 if(height.value()<=0) return; 313 if(is_empty()) return; 314 315 rgba* mat = new rgba(); 316 mat->color = color; 317 m_sep.add(mat); 318 319 matrix* tsf = new matrix; 320 m_sep.add(tsf); 321 322 //sf<float> zfront ? 323 float zz = back_visible.value()?0.01f:0; 324 325 if(font==font_hershey()) { 326 327 draw_style* ds = new draw_style; 328 ds->style = draw_lines; 329 ds->line_width = line_width; 330 m_sep.add(ds); 331 332 text_hershey* _text = new text_hershey; 333 m_base_text = _text; 334 _text->encoding = encoding; 335 _text->strings = strings; 336 m_sep.add(_text); 337 338 } else { 339 340 m_base_text = m_TT_text; 341 //ttf/arialbd 11 342 //01234567890 343 m_TT_text->font = font; 344 m_TT_text->strings = strings; 345 m_TT_text->modeling = font_modeling; 346 347 //_text->modeling.value(font_outline); 348 349 m_sep.add(new noderef(*m_TT_text)); 350 351 } 352 353 if(enforce_front_height) { 354 355 m_base_text->height = front_height; 356 357 float mn_x,mn_y,mn_z; 358 float mx_x,mx_y,mx_z; 359 m_base_text->get_bounds(front_height,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 360 361 float bxw = mx_x-mn_x; 362 float xtrans = 0; 363 if(hjust==center) { 364 xtrans = 0; 365 } else if(hjust==left) { 366 xtrans = bxw*0.5f; 367 } else if(hjust==right) { 368 xtrans = -bxw*0.5f; 369 } 370 371 float bxh = mx_y-mn_y; 372 float ytrans = 0; 373 if(vjust==middle) { 374 ytrans = 0; 375 } else if(vjust==bottom) { 376 ytrans = bxh*0.5f; 377 } else if(vjust==top) { 378 ytrans = -bxh*0.5f; 379 } 380 381 float xx = -(mn_x+mx_x)*0.5F+xtrans; 382 float yy = -(mn_y+mx_y)*0.5F+ytrans; 383 tsf->set_translate(xx,yy,zz); 384 return; 385 } 386 387 if(enforce_front_width) { 388 389 float fh = height * hmargin_factor; 390 391 float th = fh; 392 float mn_x,mn_y,mn_z; 393 float mx_x,mx_y,mx_z; 394 m_base_text->get_bounds(th,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 395 float bxw = mx_x-mn_x; 396 397 // adjust box width : 398 // th -> bxw then to have front_width: 399 if(bxw>0) { 400 th = th*front_width/bxw; 401 m_base_text->get_bounds(th,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 402 } 403 404 bxw = mx_x-mn_x; 405 float xtrans = 0; 406 if(hjust==center) { 407 xtrans = 0; 408 } else if(hjust==left) { 409 xtrans = bxw*0.5f; 410 } else if(hjust==right) { 411 xtrans = -bxw*0.5f; 412 } 413 414 float bxh = mx_y-mn_y; 415 float ytrans = 0; 416 if(vjust==middle) { 417 ytrans = 0; 418 } else if(vjust==bottom) { 419 ytrans = bxh*0.5f; 420 } else if(vjust==top) { 421 ytrans = -bxh*0.5f; 422 } 423 424 float xx = -(mn_x+mx_x)*0.5F+xtrans; 425 float yy = -(mn_y+mx_y)*0.5F+ytrans; 426 tsf->set_translate(xx,yy,zz); 427 428 m_base_text->height = bxh; //=th? 429 return; 430 } 431 432 //various automatic text height strategies : 433 float fw = width * wmargin_factor; 434 float fh = height * hmargin_factor; 435 436 if(confine) { 437 // code common to freetype and hershey : 438 439 // try to adjust text within fw x fh (by attempting to be smart !). 440 // the below assumes that text bounds are linear according 441 // "text height". 442 {float mn_x,mn_y,mn_z; 443 float mx_x,mx_y,mx_z; 444 445 float th = fh; 446 m_base_text->get_bounds(th,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 447 float bxh = mx_y-mn_y; 448 // adjust box height : 449 // fh -> bxh then to have fh : 450 if(bxh>0) { 451 th = fh*fh/bxh; 452 m_base_text->get_bounds(th,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 453 } 454 455 //float bxw = box.mx()[0]-box.mn()[0]; 456 float bxw = mx_x-mn_x; 457 bxh = mx_y-mn_y; 458 if((fh>0)&&(bxh>0)){ 459 float fasp = fw/fh; 460 float basp = bxw/bxh; 461 if(fasp>=basp) { 462 } else { 463 // adjust box width : 464 // th -> bxw then to have fw : 465 if(bxw>0) { 466 th = th*fw/bxw; 467 m_base_text->get_bounds(th,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 468 } 469 } 470 } 471 472 m_base_text->height = th; 473 474 //bxw = box.mx()[0]-box.mn()[0]; 475 bxw = mx_x-mn_x; 476 float xtrans = 0; 477 if(hjust==center) { 478 xtrans = 0; 479 } else if(hjust==left) { 480 xtrans = -fw*0.5f+bxw*0.5f; 481 } else if(hjust==right) { 482 xtrans = fw*0.5f-bxw*0.5f; 483 } 484 485 bxh = mx_y-mn_y; 486 float ytrans = 0; 487 if(vjust==middle) { 488 ytrans = 0; 489 } else if(vjust==bottom) { 490 ytrans = -fh*0.5f+bxh*0.5f; 491 } else if(vjust==top) { 492 ytrans = fh*0.5f-bxh*0.5f; 493 } 494 495 float xx = -(mn_x+mx_x)*0.5F+xtrans; 496 float yy = -(mn_y+mx_y)*0.5F+ytrans; 497 498 tsf->set_translate(xx,yy,zz); 499 500 } 501 502 } else { 503 504 // we arrange yy so that two aside texts 505 // with same height will have their text base lines aligned. 506 // The max height is given by ascent+descent (with descent>0). 507 float th = fh; 508 509 float mxh = m_base_text->ascent(th)+ 510 m_base_text->y_advance(th)*(strings.size()-1)+ 511 m_base_text->descent(th); 512 513 //{box3f box; 514 // m_base_text->get_bounds(th,box); 515 // float bxh = box.mx()[1]-box.mn()[1]; //should be idem mxh. 516 // mxh = bxh;} 517 518 if(mxh) th = fh*fh/mxh; //end/final height. 519 520 m_base_text->height = th; //then all chars will fit into th. 521 522 mxh = m_base_text->ascent(th)+ 523 m_base_text->y_advance(th)*(strings.size()-1)+ 524 m_base_text->descent(th); 525 526 float yy = -fh*0.5f+m_base_text->descent(th)+ 527 m_base_text->y_advance(th)*(strings.size()-1); 528 529 //float xx = -fw*0.5F; //left justified. 530 //tsf->set_translate(xx,yy,zz); 531 {float mn_x,mn_y,mn_z; 532 float mx_x,mx_y,mx_z; 533 m_base_text->get_bounds(th,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 534 535 float bxw = mx_x-mn_x; 536 float xtrans = 0; 537 if(hjust==center) { 538 xtrans = 0; 539 } else if(hjust==left) { 540 xtrans = -fw*0.5f+bxw*0.5f; 541 } else if(hjust==right) { 542 xtrans = fw*0.5f-bxw*0.5f; 543 } 544 /* 545 float bxh = mx_y-mn_y; 546 float ytrans = 0; 547 if(vjust==middle) { 548 ytrans = 0; 549 } else if(vjust==bottom) { 550 ytrans = -fh*0.5f+bxh*0.5f; 551 } else if(vjust==top) { 552 ytrans = fh*0.5f-bxh*0.5f; 553 } 554 */ 555 float xx = -(mn_x+mx_x)*0.5F+xtrans; 556 tsf->set_translate(xx,yy,zz);} 557 558 // truncate text at right if out of border : 559 {std::vector<std::string> labcut; 560 tools_vforcit(std::string,strings.values(),it) { 561 std::string scut; 562 m_base_text->truncate(*it,th,fw,scut); 563 labcut.push_back(std::move(scut)); 564 } 565 m_base_text->strings = labcut;} 566 } 567 } 568 569 void map_back_area_to_text() { 570 if(!m_base_text) return; 571 572 float mn_x,mn_y,mn_z; 573 float mx_x,mx_y,mx_z; 574 m_base_text->get_bounds(front_height,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z); 575 576 float bxw = mx_x-mn_x; 577 float bxh = mx_y-mn_y; 578 parent::width = bxw/wmargin_factor; 579 parent::height = bxh/hmargin_factor; 580 } 581 582 protected: 583 separator m_sep; 584 base_text* m_base_text; 585 base_freetype* m_TT_text; //optimize : avoid too much freetype load font. 586 }; 587 588 }} 589 590 #endif