1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2008-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 #if defined (HAVE_CONFIG_H) 27 # include "config.h" 28 #endif 29 30 #include <sstream> 31 32 #if defined (HAVE_WINDOWS_H) 33 # define WIN32_LEAN_AND_MEAN 34 # include <windows.h> 35 #endif 36 37 #include "lo-mappers.h" 38 #include "oct-locbuf.h" 39 #include "oct-refcount.h" 40 41 #include "errwarn.h" 42 #include "gl-render.h" 43 #include "interpreter-private.h" 44 #include "oct-opengl.h" 45 #include "text-renderer.h" 46 47 namespace octave 48 { 49 #if defined (HAVE_OPENGL) 50 51 static int next_power_of_2(int n)52 next_power_of_2 (int n) 53 { 54 int m = 1; 55 56 while (m < n && m < std::numeric_limits<int>::max ()) 57 m <<= 1; 58 59 return m; 60 } 61 62 #define LIGHT_MODE GL_FRONT_AND_BACK 63 64 // Use symbolic names for axes 65 enum 66 { 67 X_AXIS, 68 Y_AXIS, 69 Z_AXIS 70 }; 71 72 // Use symbolic names for color mode 73 enum 74 { 75 UNIFORM, 76 FLAT, 77 INTERP, 78 TEXTURE 79 }; 80 81 // Use symbolic names for lighting 82 enum 83 { 84 NONE, 85 //FLAT, // Already declared in anonymous enum for color mode 86 GOURAUD = 2 87 }; 88 89 // Win32 API requires the CALLBACK attributes for 90 // GLU callback functions. Define it to empty on 91 // other platforms. 92 #if ! defined (CALLBACK) 93 # define CALLBACK 94 #endif 95 96 class 97 opengl_texture 98 { 99 protected: 100 class texture_rep 101 { 102 public: texture_rep(opengl_functions & glfcns)103 texture_rep (opengl_functions& glfcns) 104 : m_glfcns (glfcns), id (), w (), h (), tw (), th (), tx (), ty (), 105 valid (false), count (1) 106 { } 107 texture_rep(opengl_functions & glfcns,GLuint id_arg,int w_arg,int h_arg,int tw_arg,int th_arg)108 texture_rep (opengl_functions& glfcns, GLuint id_arg, 109 int w_arg, int h_arg, int tw_arg, int th_arg) 110 : m_glfcns (glfcns), id (id_arg), w (w_arg), h (h_arg), 111 tw (tw_arg), th (th_arg), tx (double(w)/tw), ty (double(h)/th), 112 valid (true), count (1) 113 { } 114 ~texture_rep(void)115 ~texture_rep (void) 116 { 117 if (valid) 118 m_glfcns.glDeleteTextures (1, &id); 119 } 120 bind(int mode) const121 void bind (int mode) const 122 { if (valid) m_glfcns.glBindTexture (mode, id); } 123 tex_coord(double q,double r) const124 void tex_coord (double q, double r) const 125 { if (valid) m_glfcns.glTexCoord2d (q*tx, r*ty); } 126 127 opengl_functions& m_glfcns; 128 GLuint id; 129 int w, h; 130 int tw, th; 131 double tx, ty; 132 bool valid; 133 refcount<octave_idx_type> count; 134 }; 135 136 texture_rep *rep; 137 138 private: opengl_texture(texture_rep * _rep)139 opengl_texture (texture_rep *_rep) : rep (_rep) { } 140 141 public: opengl_texture(opengl_functions & glfcns)142 opengl_texture (opengl_functions& glfcns) 143 : rep (new texture_rep (glfcns)) { } 144 opengl_texture(const opengl_texture & tx)145 opengl_texture (const opengl_texture& tx) 146 : rep (tx.rep) 147 { 148 rep->count++; 149 } 150 ~opengl_texture(void)151 ~opengl_texture (void) 152 { 153 if (--rep->count == 0) 154 delete rep; 155 } 156 operator =(const opengl_texture & tx)157 opengl_texture& operator = (const opengl_texture& tx) 158 { 159 if (&tx != this) 160 { 161 if (--rep->count == 0) 162 delete rep; 163 164 rep = tx.rep; 165 rep->count++; 166 } 167 168 return *this; 169 } 170 171 static opengl_texture create (opengl_functions& glfcns, 172 const octave_value& data); 173 bind(int mode=GL_TEXTURE_2D) const174 void bind (int mode = GL_TEXTURE_2D) const 175 { rep->bind (mode); } 176 tex_coord(double q,double r) const177 void tex_coord (double q, double r) const 178 { rep->tex_coord (q, r); } 179 is_valid(void) const180 bool is_valid (void) const 181 { return rep->valid; } 182 }; 183 184 opengl_texture create(opengl_functions & glfcns,const octave_value & data)185 opengl_texture::create (opengl_functions& glfcns, const octave_value& data) 186 { 187 opengl_texture retval (glfcns); 188 189 dim_vector dv (data.dims ()); 190 191 // Expect RGB data 192 if (dv.ndims () == 3 && dv(2) == 3) 193 { 194 // FIXME: dim_vectors hold octave_idx_type values. 195 // Should we check for dimensions larger than intmax? 196 int h, w, tw, th; 197 h = dv(0), w = dv(1); 198 199 // Return early if the image data are larger than the texture 200 // can hold 201 int max_size; 202 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size); 203 static bool warned = false; 204 if (h > max_size || w > max_size) 205 { 206 if (! warned) 207 { 208 warning ("opengl_texture::create: the opengl library in use " 209 "doesn't support images with either dimension larger " 210 "than %d. Not rendering.", max_size); 211 warned = true; 212 } 213 214 return opengl_texture (glfcns); 215 } 216 217 GLuint id; 218 bool ok = true; 219 220 tw = next_power_of_2 (w); 221 th = next_power_of_2 (h); 222 223 glfcns.glGenTextures (1, &id); 224 glfcns.glBindTexture (GL_TEXTURE_2D, id); 225 226 if (data.is_double_type ()) 227 { 228 const NDArray xdata = data.array_value (); 229 230 OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); 231 232 for (int i = 0; i < h; i++) 233 { 234 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) 235 { 236 a[idx] = xdata(i,j,0); 237 a[idx+1] = xdata(i,j,1); 238 a[idx+2] = xdata(i,j,2); 239 } 240 } 241 242 glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, 243 GL_FLOAT, a); 244 } 245 246 else if (data.is_single_type ()) 247 { 248 const FloatNDArray xdata = data.float_array_value (); 249 250 OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); 251 252 for (int i = 0; i < h; i++) 253 { 254 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) 255 { 256 a[idx] = xdata(i,j,0); 257 a[idx+1] = xdata(i,j,1); 258 a[idx+2] = xdata(i,j,2); 259 } 260 } 261 262 glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, 263 GL_FLOAT, a); 264 } 265 else if (data.is_uint16_type ()) 266 { 267 const uint16NDArray xdata = data.uint16_array_value (); 268 269 OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th)); 270 271 for (int i = 0; i < h; i++) 272 { 273 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) 274 { 275 a[idx] = xdata(i,j,0); 276 a[idx+1] = xdata(i,j,1); 277 a[idx+2] = xdata(i,j,2); 278 } 279 } 280 281 glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, 282 GL_RGB, GL_UNSIGNED_SHORT, a); 283 } 284 else if (data.is_uint8_type ()) 285 { 286 const uint8NDArray xdata = data.uint8_array_value (); 287 288 OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th)); 289 290 for (int i = 0; i < h; i++) 291 { 292 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) 293 { 294 a[idx] = xdata(i,j,0); 295 a[idx+1] = xdata(i,j,1); 296 a[idx+2] = xdata(i,j,2); 297 } 298 } 299 300 glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, 301 GL_RGB, GL_UNSIGNED_BYTE, a); 302 } 303 else 304 { 305 ok = false; 306 warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16"); 307 } 308 309 if (ok) 310 { 311 glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 312 glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 313 314 if (glfcns.glGetError () != GL_NO_ERROR) 315 warning ("opengl_texture::create: OpenGL error while generating texture data"); 316 else 317 retval = opengl_texture (new texture_rep (glfcns, id, w, h, tw, th)); 318 } 319 } 320 else 321 warning ("opengl_texture::create: invalid texture data size"); 322 323 return retval; 324 } 325 326 class 327 opengl_tessellator 328 { 329 public: 330 #if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS) 331 typedef GLvoid (CALLBACK *fcn) (...); 332 #else 333 typedef void (CALLBACK *fcn) (void); 334 #endif 335 336 public: 337 opengl_tessellator(void)338 opengl_tessellator (void) : glu_tess (nullptr), fill () { init (); } 339 340 // No copying! 341 342 opengl_tessellator (const opengl_tessellator&) = delete; 343 344 opengl_tessellator operator = (const opengl_tessellator&) = delete; 345 ~opengl_tessellator(void)346 virtual ~opengl_tessellator (void) 347 { if (glu_tess) gluDeleteTess (glu_tess); } 348 begin_polygon(bool filled=true)349 void begin_polygon (bool filled = true) 350 { 351 gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY, 352 (filled ? GL_FALSE : GL_TRUE)); 353 fill = filled; 354 gluTessBeginPolygon (glu_tess, this); 355 } 356 end_polygon(void) const357 void end_polygon (void) const 358 { gluTessEndPolygon (glu_tess); } 359 begin_contour(void) const360 void begin_contour (void) const 361 { gluTessBeginContour (glu_tess); } 362 end_contour(void) const363 void end_contour (void) const 364 { gluTessEndContour (glu_tess); } 365 add_vertex(double * loc,void * data) const366 void add_vertex (double *loc, void *data) const 367 { gluTessVertex (glu_tess, loc, data); } 368 369 protected: begin(GLenum)370 virtual void begin (GLenum /*type*/) { } 371 end(void)372 virtual void end (void) { } 373 vertex(void *)374 virtual void vertex (void * /*data*/) { } 375 combine(GLdouble[3],void * [4],GLfloat[4],void **)376 virtual void combine (GLdouble [3] /*c*/, void * [4] /*data*/, 377 GLfloat [4] /*w*/, void ** /*out_data*/) { } 378 edge_flag(GLboolean)379 virtual void edge_flag (GLboolean /*flag*/) { } 380 error(GLenum err)381 virtual void error (GLenum err) 382 { ::error ("OpenGL tessellation error (%d)", err); } 383 init(void)384 virtual void init (void) 385 { 386 glu_tess = gluNewTess (); 387 388 gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA, 389 reinterpret_cast<fcn> (tess_begin)); 390 gluTessCallback (glu_tess, GLU_TESS_END_DATA, 391 reinterpret_cast<fcn> (tess_end)); 392 gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA, 393 reinterpret_cast<fcn> (tess_vertex)); 394 gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA, 395 reinterpret_cast<fcn> (tess_combine)); 396 gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA, 397 reinterpret_cast<fcn> (tess_edge_flag)); 398 gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA, 399 reinterpret_cast<fcn> (tess_error)); 400 } 401 is_filled(void) const402 bool is_filled (void) const { return fill; } 403 404 private: tess_begin(GLenum type,void * t)405 static void CALLBACK tess_begin (GLenum type, void *t) 406 { reinterpret_cast<opengl_tessellator *> (t)->begin (type); } 407 tess_end(void * t)408 static void CALLBACK tess_end (void *t) 409 { reinterpret_cast<opengl_tessellator *> (t)->end (); } 410 tess_vertex(void * v,void * t)411 static void CALLBACK tess_vertex (void *v, void *t) 412 { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); } 413 tess_combine(GLdouble c[3],void * v[4],GLfloat w[4],void ** out,void * t)414 static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4], 415 void **out, void *t) 416 { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); } 417 tess_edge_flag(GLboolean flag,void * t)418 static void CALLBACK tess_edge_flag (GLboolean flag, void *t) 419 { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); } 420 tess_error(GLenum err,void * t)421 static void CALLBACK tess_error (GLenum err, void *t) 422 { reinterpret_cast<opengl_tessellator *> (t)->error (err); } 423 424 private: 425 426 GLUtesselator *glu_tess; 427 bool fill; 428 }; 429 430 class 431 vertex_data 432 { 433 public: 434 class vertex_data_rep 435 { 436 public: 437 Matrix coords; 438 Matrix color; 439 Matrix vertex_normal; 440 Matrix face_normal; 441 double alpha; 442 float ambient; 443 float diffuse; 444 float specular; 445 float specular_exp; 446 float specular_color_refl; 447 448 // reference counter 449 refcount<octave_idx_type> count; 450 vertex_data_rep(void)451 vertex_data_rep (void) 452 : coords (), color (), vertex_normal (), face_normal (), alpha (), 453 ambient (), diffuse (), specular (), specular_exp (), 454 specular_color_refl (), count (1) { } 455 vertex_data_rep(const Matrix & c,const Matrix & col,const Matrix & vn,const Matrix & fn,double a,float as,float ds,float ss,float se,float scr)456 vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& vn, 457 const Matrix& fn, double a, float as, float ds, float ss, 458 float se, float scr) 459 : coords (c), color (col), vertex_normal (vn), face_normal (fn), 460 alpha (a), ambient (as), diffuse (ds), specular (ss), 461 specular_exp (se), specular_color_refl (scr), count (1) { } 462 }; 463 464 private: 465 vertex_data_rep *rep; 466 nil_rep(void) const467 vertex_data_rep * nil_rep (void) const 468 { 469 static vertex_data_rep *nr = new vertex_data_rep (); 470 471 return nr; 472 } 473 474 public: vertex_data(void)475 vertex_data (void) : rep (nil_rep ()) 476 { rep->count++; } 477 vertex_data(const vertex_data & v)478 vertex_data (const vertex_data& v) : rep (v.rep) 479 { rep->count++; } 480 vertex_data(const Matrix & c,const Matrix & col,const Matrix & vn,const Matrix & fn,double a,float as,float ds,float ss,float se,float scr)481 vertex_data (const Matrix& c, const Matrix& col, const Matrix& vn, 482 const Matrix& fn, double a, float as, float ds, float ss, 483 float se, float scr) 484 : rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr)) 485 { } 486 vertex_data(vertex_data_rep * new_rep)487 vertex_data (vertex_data_rep *new_rep) 488 : rep (new_rep) { } 489 ~vertex_data(void)490 ~vertex_data (void) 491 { 492 if (--rep->count == 0) 493 delete rep; 494 } 495 operator =(const vertex_data & v)496 vertex_data& operator = (const vertex_data& v) 497 { 498 if (&v != this) 499 { 500 if (--rep->count == 0) 501 delete rep; 502 503 rep = v.rep; 504 rep->count++; 505 } 506 507 return *this; 508 } 509 get_rep(void) const510 vertex_data_rep * get_rep (void) const { return rep; } 511 }; 512 513 class 514 opengl_renderer::patch_tessellator : public opengl_tessellator 515 { 516 public: patch_tessellator(opengl_renderer * r,int cmode,int lmode,bool fl,float idx=0.0)517 patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl, 518 float idx = 0.0) 519 : opengl_tessellator (), renderer (r), 520 color_mode (cmode), light_mode (lmode), face_lighting (fl), index (idx), 521 first (true), tmp_vdata () 522 { } 523 524 protected: begin(GLenum type)525 void begin (GLenum type) 526 { 527 opengl_functions& glfcns = renderer->get_opengl_functions (); 528 529 //printf ("patch_tessellator::begin (%d)\n", type); 530 first = true; 531 532 if (color_mode == INTERP || light_mode == GOURAUD) 533 glfcns.glShadeModel (GL_SMOOTH); 534 else 535 glfcns.glShadeModel (GL_FLAT); 536 537 if (is_filled ()) 538 renderer->set_polygon_offset (true, index); 539 540 glfcns.glBegin (type); 541 } 542 end(void)543 void end (void) 544 { 545 opengl_functions& glfcns = renderer->get_opengl_functions (); 546 547 //printf ("patch_tessellator::end\n"); 548 glfcns.glEnd (); 549 renderer->set_polygon_offset (false); 550 } 551 vertex(void * data)552 void vertex (void *data) 553 { 554 opengl_functions& glfcns = renderer->get_opengl_functions (); 555 556 vertex_data::vertex_data_rep *v 557 = reinterpret_cast<vertex_data::vertex_data_rep *> (data); 558 //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->coords(2)); 559 560 // NOTE: OpenGL can re-order vertices. For "flat" coloring of FaceColor 561 // the first vertex must be identified in the draw_patch routine. 562 563 if (color_mode == INTERP || (color_mode == FLAT && ! is_filled ())) 564 { 565 Matrix col = v->color; 566 567 if (col.numel () == 3) 568 { 569 glfcns.glColor4d (col(0), col(1), col(2), v->alpha); 570 if (light_mode > 0) 571 { 572 // edge lighting only uses ambient light 573 float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; 574 575 if (face_lighting) 576 for (int k = 0; k < 3; k++) 577 buf[k] = v->specular * (v->specular_color_refl + 578 (1 - v->specular_color_refl) * col(k)); 579 glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf); 580 581 if (face_lighting) 582 for (int k = 0; k < 3; k++) 583 buf[k] = (v->diffuse * col(k)); 584 glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf); 585 586 for (int k = 0; k < 3; k++) 587 buf[k] = (v->ambient * col(k)); 588 glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf); 589 } 590 } 591 } 592 593 if (light_mode == FLAT && first) 594 glfcns.glNormal3dv (v->face_normal.data ()); 595 else if (light_mode == GOURAUD) 596 glfcns.glNormal3dv (v->vertex_normal.data ()); 597 598 glfcns.glVertex3dv (v->coords.data ()); 599 600 first = false; 601 } 602 combine(GLdouble xyz[3],void * data[4],GLfloat w[4],void ** out_data)603 void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data) 604 { 605 //printf ("patch_tessellator::combine\n"); 606 607 vertex_data::vertex_data_rep *v[4]; 608 int vmax = 4; 609 610 for (int i = 0; i < 4; i++) 611 { 612 v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]); 613 614 if (vmax == 4 && ! v[i]) 615 vmax = i; 616 } 617 618 Matrix vv (1, 3, 0.0); 619 Matrix cc; 620 Matrix vnn (1, 3, 0.0); 621 Matrix fnn (1, 3, 0.0); 622 double aa = 0.0; 623 624 vv(0) = xyz[0]; 625 vv(1) = xyz[1]; 626 vv(2) = xyz[2]; 627 628 if (v[0]->color.numel ()) 629 { 630 cc.resize (1, 3, 0.0); 631 for (int ic = 0; ic < 3; ic++) 632 for (int iv = 0; iv < vmax; iv++) 633 cc(ic) += (w[iv] * v[iv]->color (ic)); 634 } 635 636 if (v[0]->vertex_normal.numel () > 0) 637 { 638 for (int in = 0; in < 3; in++) 639 for (int iv = 0; iv < vmax; iv++) 640 vnn(in) += (w[iv] * v[iv]->vertex_normal (in)); 641 } 642 643 if (v[0]->face_normal.numel () > 0) 644 { 645 for (int in = 0; in < 3; in++) 646 for (int iv = 0; iv < vmax; iv++) 647 fnn(in) += (w[iv] * v[iv]->face_normal (in)); 648 } 649 650 for (int iv = 0; iv < vmax; iv++) 651 aa += (w[iv] * v[iv]->alpha); 652 653 vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->ambient, v[0]->diffuse, 654 v[0]->specular, v[0]->specular_exp, 655 v[0]->specular_color_refl); 656 tmp_vdata.push_back (new_v); 657 658 *out_data = new_v.get_rep (); 659 } 660 661 private: 662 663 // No copying! 664 665 patch_tessellator (const patch_tessellator&) = delete; 666 667 patch_tessellator& operator = (const patch_tessellator&) = delete; 668 669 opengl_renderer *renderer; 670 int color_mode; 671 int light_mode; 672 bool face_lighting; 673 int index; 674 bool first; 675 std::list<vertex_data> tmp_vdata; 676 }; 677 678 #else 679 680 class 681 opengl_renderer::patch_tessellator 682 { 683 // Dummy class. 684 }; 685 686 #endif 687 opengl_renderer(opengl_functions & glfcns)688 opengl_renderer::opengl_renderer (opengl_functions& glfcns) 689 : m_glfcns (glfcns), xmin (), xmax (), ymin (), ymax (), zmin (), zmax (), 690 m_devpixratio (1.0), xform (), toolkit (), xZ1 (), xZ2 (), marker_id (), 691 filled_marker_id (), camera_pos (), camera_dir (), view_vector (), 692 interpreter ("none"), txt_renderer (), m_current_light (0), 693 m_max_lights (0), selecting (false), m_printing (false) 694 { 695 // This constructor will fail if we don't have OpenGL or if the data 696 // types we assumed in our public interface aren't compatible with the 697 // OpenGL types. 698 699 #if defined (HAVE_OPENGL) 700 701 // Ensure that we can't request an image larger than OpenGL can handle. 702 // FIXME: should we check signed vs. unsigned? 703 704 static bool ok = (sizeof (int) <= sizeof (GLsizei)); 705 706 if (! ok) 707 error ("the size of GLsizei is smaller than the size of int"); 708 709 #else 710 711 err_disabled_feature ("opengl_renderer", "OpenGL"); 712 713 #endif 714 } 715 716 void draw(const graphics_object & go,bool toplevel)717 opengl_renderer::draw (const graphics_object& go, bool toplevel) 718 { 719 if (! go.valid_object ()) 720 return; 721 722 const base_properties& props = go.get_properties (); 723 724 if (! toolkit) 725 toolkit = props.get_toolkit (); 726 727 if (go.isa ("figure")) 728 draw_figure (dynamic_cast<const figure::properties&> (props)); 729 else if (go.isa ("axes")) 730 draw_axes (dynamic_cast<const axes::properties&> (props)); 731 else if (go.isa ("line")) 732 draw_line (dynamic_cast<const line::properties&> (props)); 733 else if (go.isa ("surface")) 734 draw_surface (dynamic_cast<const surface::properties&> (props)); 735 else if (go.isa ("patch")) 736 draw_patch (dynamic_cast<const patch::properties&> (props)); 737 else if (go.isa ("light")) 738 draw_light (dynamic_cast<const light::properties&> (props)); 739 else if (go.isa ("hggroup")) 740 draw_hggroup (dynamic_cast<const hggroup::properties&> (props)); 741 else if (go.isa ("text")) 742 draw_text (dynamic_cast<const text::properties&> (props)); 743 else if (go.isa ("image")) 744 draw_image (dynamic_cast<const image::properties&> (props)); 745 else if (go.isa ("uimenu") || go.isa ("uicontrol") 746 || go.isa ("uicontextmenu") || go.isa ("uitoolbar") 747 || go.isa ("uipushtool") || go.isa ("uitoggletool") 748 || go.isa ("uitable")) 749 ; // SKIP 750 else if (go.isa ("uipanel")) 751 { 752 if (toplevel) 753 draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go); 754 } 755 else if (go.isa ("uibuttongroup")) 756 { 757 if (toplevel) 758 draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go); 759 } 760 else 761 { 762 warning ("opengl_renderer: cannot render object of type '%s'", 763 props.graphics_object_name ().c_str ()); 764 } 765 766 #if defined (HAVE_OPENGL) 767 768 GLenum gl_error = m_glfcns.glGetError (); 769 if (gl_error) 770 warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object", 771 gluErrorString (gl_error), gl_error, props.graphics_object_name ().c_str ()); 772 773 #endif 774 } 775 776 void draw_figure(const figure::properties & props)777 opengl_renderer::draw_figure (const figure::properties& props) 778 { 779 m_printing = props.is___printing__ (); 780 781 // Initialize OpenGL context 782 init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); 783 784 #if defined (HAVE_OPENGL) 785 786 props.set___gl_extensions__ (get_string (GL_EXTENSIONS)); 787 props.set___gl_renderer__ (get_string (GL_RENDERER)); 788 props.set___gl_vendor__ (get_string (GL_VENDOR)); 789 props.set___gl_version__ (get_string (GL_VERSION)); 790 791 #endif 792 793 // Draw children 794 795 draw (props.get_all_children (), false); 796 } 797 798 void draw_uipanel(const uipanel::properties & props,const graphics_object & go)799 opengl_renderer::draw_uipanel (const uipanel::properties& props, 800 const graphics_object& go) 801 { 802 graphics_object fig = go.get_ancestor ("figure"); 803 const figure::properties& figProps 804 = dynamic_cast<const figure::properties&> (fig.get_properties ()); 805 806 // Initialize OpenGL context 807 808 init_gl_context (figProps.is_graphicssmoothing (), 809 props.get_backgroundcolor_rgb ()); 810 811 // Draw children 812 813 draw (props.get_all_children (), false); 814 } 815 816 void draw_uibuttongroup(const uibuttongroup::properties & props,const graphics_object & go)817 opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props, 818 const graphics_object& go) 819 { 820 graphics_object fig = go.get_ancestor ("figure"); 821 const figure::properties& figProps 822 = dynamic_cast<const figure::properties&> (fig.get_properties ()); 823 824 // Initialize OpenGL context 825 826 init_gl_context (figProps.is_graphicssmoothing (), 827 props.get_backgroundcolor_rgb ()); 828 829 // Draw children 830 831 draw (props.get_all_children (), false); 832 } 833 834 void init_gl_context(bool enhanced,const Matrix & c)835 opengl_renderer::init_gl_context (bool enhanced, const Matrix& c) 836 { 837 #if defined (HAVE_OPENGL) 838 839 // Initialize OpenGL context 840 841 m_glfcns.glEnable (GL_DEPTH_TEST); 842 m_glfcns.glDepthFunc (GL_LEQUAL); 843 m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 844 m_glfcns.glAlphaFunc (GL_GREATER, 0.0f); 845 m_glfcns.glEnable (GL_NORMALIZE); 846 m_glfcns.glEnable (GL_BLEND); 847 848 if (enhanced) 849 { 850 m_glfcns.glEnable (GL_MULTISAMPLE); 851 bool has_multisample = false; 852 if (! m_glfcns.glGetError ()) 853 { 854 GLint iMultiSample, iNumSamples; 855 m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample); 856 m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples); 857 if (iMultiSample == GL_TRUE && iNumSamples > 0) 858 has_multisample = true; 859 } 860 861 if (! has_multisample) 862 { 863 // MultiSample not implemented. Use old-style anti-aliasing 864 m_glfcns.glDisable (GL_MULTISAMPLE); 865 // Disabling GL_MULTISAMPLE will raise a gl error if it is not 866 // implemented. Thus, call glGetError to reset the error state. 867 m_glfcns.glGetError (); 868 869 m_glfcns.glEnable (GL_LINE_SMOOTH); 870 m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); 871 } 872 } 873 else 874 { 875 m_glfcns.glDisable (GL_LINE_SMOOTH); 876 } 877 878 // Clear background 879 880 if (c.numel () >= 3) 881 { 882 m_glfcns.glClearColor (c(0), c(1), c(2), 1); 883 m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 884 } 885 886 GLenum gl_error = m_glfcns.glGetError (); 887 if (gl_error) 888 warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context", 889 gluErrorString (gl_error), gl_error); 890 891 #else 892 893 octave_unused_parameter (enhanced); 894 octave_unused_parameter (c); 895 896 // This shouldn't happen because construction of opengl_renderer 897 // objects is supposed to be impossible if OpenGL is not available. 898 899 panic_impossible (); 900 901 #endif 902 } 903 904 void render_grid(const double linewidth,const std::string & gridstyle,const Matrix & gridcolor,const double gridalpha,const Matrix & ticks,double lim1,double lim2,double p1,double p1N,double p2,double p2N,int xyz,bool is_3D)905 opengl_renderer::render_grid (const double linewidth, 906 const std::string& gridstyle, 907 const Matrix& gridcolor, const double gridalpha, 908 const Matrix& ticks, double lim1, double lim2, 909 double p1, double p1N, double p2, double p2N, 910 int xyz, bool is_3D) 911 { 912 #if defined (HAVE_OPENGL) 913 914 m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha); 915 set_linestyle (gridstyle, true, linewidth); 916 m_glfcns.glBegin (GL_LINES); 917 for (int i = 0; i < ticks.numel (); i++) 918 { 919 double val = ticks(i); 920 if (lim1 <= val && val <= lim2) 921 { 922 if (xyz == X_AXIS) 923 { 924 m_glfcns.glVertex3d (val, p1N, p2); 925 m_glfcns.glVertex3d (val, p1, p2); 926 if (is_3D) 927 { 928 m_glfcns.glVertex3d (val, p1, p2N); 929 m_glfcns.glVertex3d (val, p1, p2); 930 } 931 } 932 else if (xyz == Y_AXIS) 933 { 934 m_glfcns.glVertex3d (p1N, val, p2); 935 m_glfcns.glVertex3d (p1, val, p2); 936 if (is_3D) 937 { 938 m_glfcns.glVertex3d (p1, val, p2N); 939 m_glfcns.glVertex3d (p1, val, p2); 940 } 941 } 942 else if (xyz == Z_AXIS) 943 { 944 m_glfcns.glVertex3d (p1N, p2, val); 945 m_glfcns.glVertex3d (p1, p2, val); 946 m_glfcns.glVertex3d (p1, p2N, val); 947 m_glfcns.glVertex3d (p1, p2, val); 948 } 949 } 950 } 951 m_glfcns.glEnd (); 952 set_linestyle ("-"); // Disable LineStipple 953 double black[3] = {0, 0, 0}; 954 m_glfcns.glColor3dv (black); 955 956 #else 957 958 octave_unused_parameter (linewidth); 959 octave_unused_parameter (gridstyle); 960 octave_unused_parameter (gridcolor); 961 octave_unused_parameter (gridalpha); 962 octave_unused_parameter (ticks); 963 octave_unused_parameter (lim1); 964 octave_unused_parameter (lim2); 965 octave_unused_parameter (p1); 966 octave_unused_parameter (p1N); 967 octave_unused_parameter (p2); 968 octave_unused_parameter (p2N); 969 octave_unused_parameter (xyz); 970 octave_unused_parameter (is_3D); 971 972 // This shouldn't happen because construction of opengl_renderer 973 // objects is supposed to be impossible if OpenGL is not available. 974 975 panic_impossible (); 976 977 #endif 978 } 979 980 void render_tickmarks(const Matrix & ticks,double lim1,double lim2,double p1,double p1N,double p2,double p2N,double dx,double dy,double dz,int xyz,bool mirror)981 opengl_renderer::render_tickmarks (const Matrix& ticks, 982 double lim1, double lim2, 983 double p1, double p1N, 984 double p2, double p2N, 985 double dx, double dy, double dz, 986 int xyz, bool mirror) 987 { 988 #if defined (HAVE_OPENGL) 989 990 m_glfcns.glBegin (GL_LINES); 991 992 for (int i = 0; i < ticks.numel (); i++) 993 { 994 double val = ticks(i); 995 996 if (lim1 <= val && val <= lim2) 997 { 998 if (xyz == X_AXIS) 999 { 1000 m_glfcns.glVertex3d (val, p1, p2); 1001 m_glfcns.glVertex3d (val, p1+dy, p2+dz); 1002 if (mirror) 1003 { 1004 m_glfcns.glVertex3d (val, p1N, p2N); 1005 m_glfcns.glVertex3d (val, p1N-dy, p2N-dz); 1006 } 1007 } 1008 else if (xyz == Y_AXIS) 1009 { 1010 m_glfcns.glVertex3d (p1, val, p2); 1011 m_glfcns.glVertex3d (p1+dx, val, p2+dz); 1012 if (mirror) 1013 { 1014 m_glfcns.glVertex3d (p1N, val, p2N); 1015 m_glfcns.glVertex3d (p1N-dx, val, p2N-dz); 1016 } 1017 } 1018 else if (xyz == Z_AXIS) 1019 { 1020 m_glfcns.glVertex3d (p1, p2, val); 1021 m_glfcns.glVertex3d (p1+dx, p2+dy, val); 1022 if (mirror) 1023 { 1024 m_glfcns.glVertex3d (p1N, p2N, val); 1025 m_glfcns.glVertex3d (p1N-dx, p2N-dy, val); 1026 } 1027 } 1028 } 1029 } 1030 1031 m_glfcns.glEnd (); 1032 1033 #else 1034 1035 octave_unused_parameter (ticks); 1036 octave_unused_parameter (lim1); 1037 octave_unused_parameter (lim2); 1038 octave_unused_parameter (p1); 1039 octave_unused_parameter (p1N); 1040 octave_unused_parameter (p2); 1041 octave_unused_parameter (p2N); 1042 octave_unused_parameter (dx); 1043 octave_unused_parameter (dy); 1044 octave_unused_parameter (dz); 1045 octave_unused_parameter (xyz); 1046 octave_unused_parameter (mirror); 1047 1048 // This shouldn't happen because construction of opengl_renderer 1049 // objects is supposed to be impossible if OpenGL is not available. 1050 1051 panic_impossible (); 1052 1053 #endif 1054 } 1055 1056 void render_ticktexts(const Matrix & ticks,const string_vector & ticklabels,double lim1,double lim2,double p1,double p2,int xyz,int ha,int va,int & wmax,int & hmax)1057 opengl_renderer::render_ticktexts (const Matrix& ticks, 1058 const string_vector& ticklabels, 1059 double lim1, double lim2, 1060 double p1, double p2, 1061 int xyz, int ha, int va, 1062 int& wmax, int& hmax) 1063 { 1064 #if defined (HAVE_OPENGL) 1065 1066 int nticks = ticks.numel (); 1067 int nlabels = ticklabels.numel (); 1068 1069 if (nlabels == 0) 1070 return; 1071 1072 for (int i = 0; i < nticks; i++) 1073 { 1074 double val = ticks(i); 1075 1076 if (lim1 <= val && val <= lim2) 1077 { 1078 Matrix b; 1079 1080 std::string label (ticklabels(i % nlabels)); 1081 label.erase (0, label.find_first_not_of (' ')); 1082 label = label.substr (0, label.find_last_not_of (' ')+1); 1083 1084 // FIXME: As tick text is transparent, shouldn't it be 1085 // drawn after axes object, for correct rendering? 1086 if (xyz == X_AXIS) 1087 { 1088 b = render_text (label, val, p1, p2, ha, va); 1089 } 1090 else if (xyz == Y_AXIS) 1091 { 1092 b = render_text (label, p1, val, p2, ha, va); 1093 } 1094 else if (xyz == Z_AXIS) 1095 { 1096 b = render_text (label, p1, p2, val, ha, va); 1097 } 1098 1099 wmax = std::max (wmax, static_cast<int> (b(2))); 1100 hmax = std::max (hmax, static_cast<int> (b(3))); 1101 } 1102 } 1103 1104 #else 1105 1106 octave_unused_parameter (ticks); 1107 octave_unused_parameter (ticklabels); 1108 octave_unused_parameter (lim1); 1109 octave_unused_parameter (lim2); 1110 octave_unused_parameter (p1); 1111 octave_unused_parameter (p2); 1112 octave_unused_parameter (xyz); 1113 octave_unused_parameter (ha); 1114 octave_unused_parameter (va); 1115 octave_unused_parameter (wmax); 1116 octave_unused_parameter (hmax); 1117 1118 // This shouldn't happen because construction of opengl_renderer 1119 // objects is supposed to be impossible if OpenGL is not available. 1120 1121 panic_impossible (); 1122 1123 #endif 1124 } 1125 1126 void draw_zoom_rect(int x1,int y1,int x2,int y2)1127 opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2) 1128 { 1129 #if defined (HAVE_OPENGL) 1130 1131 m_glfcns.glVertex2d (x1, y1); 1132 m_glfcns.glVertex2d (x2, y1); 1133 m_glfcns.glVertex2d (x2, y2); 1134 m_glfcns.glVertex2d (x1, y2); 1135 m_glfcns.glVertex2d (x1, y1); 1136 1137 #else 1138 1139 octave_unused_parameter (x1); 1140 octave_unused_parameter (x2); 1141 octave_unused_parameter (y1); 1142 octave_unused_parameter (y2); 1143 1144 // This shouldn't happen because construction of opengl_renderer 1145 // objects is supposed to be impossible if OpenGL is not available. 1146 1147 panic_impossible (); 1148 1149 #endif 1150 } 1151 1152 void draw_zoom_box(int width,int height,int x1,int y1,int x2,int y2,const Matrix & overlaycolor,double overlayalpha,const Matrix & bordercolor,double borderalpha,double borderwidth)1153 opengl_renderer::draw_zoom_box (int width, int height, 1154 int x1, int y1, int x2, int y2, 1155 const Matrix& overlaycolor, 1156 double overlayalpha, 1157 const Matrix& bordercolor, 1158 double borderalpha, double borderwidth) 1159 { 1160 #if defined (HAVE_OPENGL) 1161 1162 m_glfcns.glMatrixMode (GL_MODELVIEW); 1163 m_glfcns.glPushMatrix (); 1164 m_glfcns.glLoadIdentity (); 1165 1166 m_glfcns.glMatrixMode (GL_PROJECTION); 1167 m_glfcns.glPushMatrix (); 1168 m_glfcns.glLoadIdentity (); 1169 m_glfcns.glOrtho (0, width, height, 0, 1, -1); 1170 1171 m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT); 1172 m_glfcns.glDisable (GL_DEPTH_TEST); 1173 1174 m_glfcns.glBegin (GL_POLYGON); 1175 m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2), 1176 overlayalpha); 1177 draw_zoom_rect (x1, y1, x2, y2); 1178 m_glfcns.glEnd (); 1179 1180 m_glfcns.glLineWidth (borderwidth); 1181 m_glfcns.glBegin (GL_LINE_STRIP); 1182 m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2), 1183 borderalpha); 1184 draw_zoom_rect (x1, y1, x2, y2); 1185 m_glfcns.glEnd (); 1186 1187 m_glfcns.glPopAttrib (); 1188 1189 m_glfcns.glMatrixMode (GL_MODELVIEW); 1190 m_glfcns.glPopMatrix (); 1191 1192 m_glfcns.glMatrixMode (GL_PROJECTION); 1193 m_glfcns.glPopMatrix (); 1194 1195 #else 1196 1197 octave_unused_parameter (width); 1198 octave_unused_parameter (height); 1199 octave_unused_parameter (x1); 1200 octave_unused_parameter (x2); 1201 octave_unused_parameter (y1); 1202 octave_unused_parameter (y2); 1203 octave_unused_parameter (overlaycolor); 1204 octave_unused_parameter (overlayalpha); 1205 octave_unused_parameter (bordercolor); 1206 octave_unused_parameter (borderalpha); 1207 octave_unused_parameter (borderwidth); 1208 1209 // This shouldn't happen because construction of opengl_renderer 1210 // objects is supposed to be impossible if OpenGL is not available. 1211 1212 panic_impossible (); 1213 1214 #endif 1215 } 1216 1217 uint8NDArray get_pixels(int width,int height)1218 opengl_renderer::get_pixels (int width, int height) 1219 { 1220 #if defined (HAVE_OPENGL) 1221 1222 m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1); 1223 uint8NDArray pix(dim_vector (3, width, height), 0); 1224 1225 m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, 1226 pix.fortran_vec ()); 1227 1228 // Permute and flip data 1229 Array<octave_idx_type> perm (dim_vector (3, 1)); 1230 perm(0) = 2; 1231 perm(1) = 1; 1232 perm(2) = 0; 1233 1234 Array<idx_vector> idx (dim_vector (3, 1)); 1235 idx(0) = idx_vector::make_range (height - 1, -1, height); 1236 idx(1) = idx_vector::colon; 1237 idx(2) = idx_vector::colon; 1238 1239 return pix.permute (perm).index (idx); 1240 1241 #else 1242 1243 // This shouldn't happen because construction of opengl_renderer 1244 // objects is supposed to be impossible if OpenGL is not available. 1245 1246 octave_unused_parameter (width); 1247 octave_unused_parameter (height); 1248 1249 panic_impossible (); 1250 1251 #endif 1252 } 1253 1254 void finish(void)1255 opengl_renderer::finish (void) 1256 { 1257 #if defined (HAVE_OPENGL) 1258 1259 m_glfcns.glFinish (); 1260 1261 #else 1262 1263 // This shouldn't happen because construction of opengl_renderer 1264 // objects is supposed to be impossible if OpenGL is not available. 1265 1266 panic_impossible (); 1267 1268 #endif 1269 } 1270 1271 void setup_opengl_transformation(const axes::properties & props)1272 opengl_renderer::setup_opengl_transformation (const axes::properties& props) 1273 { 1274 #if defined (HAVE_OPENGL) 1275 1276 // setup OpenGL transformation 1277 1278 Matrix x_zlim = props.get_transform_zlim (); 1279 1280 xZ1 = std::max (-1e6, x_zlim(0)-(x_zlim(1)-x_zlim(0))*100.0); 1281 xZ2 = std::min (1e6, x_zlim(1)+(x_zlim(1)-x_zlim(0))*100.0); 1282 1283 Matrix x_mat1 = props.get_opengl_matrix_1 (); 1284 Matrix x_mat2 = props.get_opengl_matrix_2 (); 1285 1286 m_glfcns.glMatrixMode (GL_MODELVIEW); 1287 m_glfcns.glLoadIdentity (); 1288 m_glfcns.glScaled (1, 1, -1); 1289 m_glfcns.glMultMatrixd (x_mat1.data ()); 1290 m_glfcns.glMatrixMode (GL_PROJECTION); 1291 m_glfcns.glLoadIdentity (); 1292 1293 Matrix vp = get_viewport_scaled (); 1294 m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2); 1295 m_glfcns.glMultMatrixd (x_mat2.data ()); 1296 m_glfcns.glMatrixMode (GL_MODELVIEW); 1297 1298 m_glfcns.glClear (GL_DEPTH_BUFFER_BIT); 1299 1300 // store axes transformation data 1301 1302 xform = props.get_transform (); 1303 1304 #else 1305 1306 octave_unused_parameter (props); 1307 1308 // This shouldn't happen because construction of opengl_renderer 1309 // objects is supposed to be impossible if OpenGL is not available. 1310 1311 panic_impossible (); 1312 1313 #endif 1314 } 1315 1316 void draw_axes_planes(const axes::properties & props)1317 opengl_renderer::draw_axes_planes (const axes::properties& props) 1318 { 1319 #if defined (HAVE_OPENGL) 1320 1321 Matrix axe_color = props.get_color_rgb (); 1322 if (axe_color.isempty () || ! props.is_visible ()) 1323 return; 1324 1325 double xPlane = props.get_xPlane (); 1326 double yPlane = props.get_yPlane (); 1327 double zPlane = props.get_zPlane (); 1328 double xPlaneN = props.get_xPlaneN (); 1329 double yPlaneN = props.get_yPlaneN (); 1330 double zPlaneN = props.get_zPlaneN (); 1331 bool is2D = props.get_is2D (); 1332 1333 // Axes planes 1334 set_color (axe_color); 1335 set_polygon_offset (true, 9.0); 1336 1337 m_glfcns.glBegin (GL_QUADS); 1338 1339 if (! is2D) 1340 { 1341 // X plane 1342 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); 1343 m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); 1344 m_glfcns.glVertex3d (xPlane, yPlane, zPlane); 1345 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); 1346 1347 // Y plane 1348 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); 1349 m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); 1350 m_glfcns.glVertex3d (xPlane, yPlane, zPlane); 1351 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); 1352 } 1353 1354 // Z plane 1355 m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); 1356 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); 1357 m_glfcns.glVertex3d (xPlane, yPlane, zPlane); 1358 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); 1359 1360 m_glfcns.glEnd (); 1361 1362 set_polygon_offset (false); 1363 1364 #else 1365 1366 octave_unused_parameter (props); 1367 1368 // This shouldn't happen because construction of opengl_renderer 1369 // objects is supposed to be impossible if OpenGL is not available. 1370 1371 panic_impossible (); 1372 1373 #endif 1374 } 1375 1376 void draw_axes_boxes(const axes::properties & props)1377 opengl_renderer::draw_axes_boxes (const axes::properties& props) 1378 { 1379 #if defined (HAVE_OPENGL) 1380 1381 if (! props.is_visible ()) 1382 return; 1383 1384 bool xySym = props.get_xySym (); 1385 bool layer2Dtop = props.get_layer2Dtop (); 1386 bool is2D = props.get_is2D (); 1387 bool isXOrigin = props.xaxislocation_is ("origin") 1388 && ! props.yscale_is ("log"); 1389 bool isYOrigin = props.yaxislocation_is ("origin") 1390 && ! props.xscale_is ("log"); 1391 bool boxFull = (props.get_boxstyle () == "full"); 1392 double linewidth = props.get_linewidth (); 1393 double xPlane = props.get_xPlane (); 1394 double yPlane = props.get_yPlane (); 1395 double zPlane = props.get_zPlane (); 1396 double xPlaneN = props.get_xPlaneN (); 1397 double yPlaneN = props.get_yPlaneN (); 1398 double zPlaneN = props.get_zPlaneN (); 1399 double xpTick = props.get_xpTick (); 1400 double ypTick = props.get_ypTick (); 1401 double zpTick = props.get_zpTick (); 1402 double xpTickN = props.get_xpTickN (); 1403 double ypTickN = props.get_ypTickN (); 1404 double zpTickN = props.get_zpTickN (); 1405 1406 bool plotyy = (props.has_property ("__plotyy_axes__")); 1407 1408 // Axes box 1409 1410 set_linecap ("square"); 1411 set_linestyle ("-", true, linewidth); 1412 1413 m_glfcns.glBegin (GL_LINES); 1414 1415 if (layer2Dtop) 1416 std::swap (zpTick, zpTickN); 1417 1418 // X box 1419 Matrix color = props.get_xcolor_rgb (); 1420 1421 if (! color.isempty ()) 1422 { 1423 set_color (color); 1424 1425 if (! isXOrigin || props.is_box() || ! is2D) 1426 { 1427 m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick); 1428 m_glfcns.glVertex3d (xPlane, ypTick, zpTick); 1429 } 1430 1431 if (props.is_box ()) 1432 { 1433 m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick); 1434 m_glfcns.glVertex3d (xPlane, ypTickN, zpTick); 1435 if (! is2D) 1436 { 1437 m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN); 1438 m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN); 1439 if (boxFull) 1440 { 1441 m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN); 1442 m_glfcns.glVertex3d (xPlane, ypTick, zpTickN); 1443 } 1444 } 1445 } 1446 } 1447 1448 // Y box 1449 color = props.get_ycolor_rgb (); 1450 1451 if (! color.isempty ()) 1452 { 1453 set_color (color); 1454 if (! isYOrigin || props.is_box() || ! is2D) 1455 { 1456 m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick); 1457 m_glfcns.glVertex3d (xpTick, yPlane, zpTick); 1458 } 1459 1460 if (props.is_box () && ! plotyy) 1461 { 1462 m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick); 1463 m_glfcns.glVertex3d (xpTickN, yPlane, zpTick); 1464 1465 if (! is2D) 1466 { 1467 m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN); 1468 m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN); 1469 if (boxFull) 1470 { 1471 m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN); 1472 m_glfcns.glVertex3d (xpTick, yPlane, zpTickN); 1473 } 1474 } 1475 } 1476 } 1477 1478 // Z box 1479 color = props.get_zcolor_rgb (); 1480 1481 if (! color.isempty () && ! is2D) 1482 { 1483 set_color (color); 1484 1485 if (xySym) 1486 { 1487 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); 1488 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); 1489 } 1490 else 1491 { 1492 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); 1493 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); 1494 } 1495 1496 if (props.is_box ()) 1497 { 1498 m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); 1499 m_glfcns.glVertex3d (xPlane, yPlane, zPlane); 1500 1501 if (xySym) 1502 { 1503 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); 1504 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); 1505 } 1506 else 1507 { 1508 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); 1509 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); 1510 } 1511 1512 if (boxFull) 1513 { 1514 m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN); 1515 m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); 1516 } 1517 } 1518 } 1519 1520 m_glfcns.glEnd (); 1521 1522 set_linestyle ("-"); // Disable LineStipple 1523 1524 #else 1525 1526 octave_unused_parameter (props); 1527 1528 // This shouldn't happen because construction of opengl_renderer 1529 // objects is supposed to be impossible if OpenGL is not available. 1530 1531 panic_impossible (); 1532 1533 #endif 1534 } 1535 1536 void draw_axes_x_grid(const axes::properties & props)1537 opengl_renderer::draw_axes_x_grid (const axes::properties& props) 1538 { 1539 #if defined (HAVE_OPENGL) 1540 1541 gh_manager& gh_mgr 1542 = __get_gh_manager__ ("opengl_renderer::draw_axes_x_grid"); 1543 1544 int xstate = props.get_xstate (); 1545 1546 if (xstate != AXE_DEPTH_DIR 1547 && (props.is_visible () 1548 || (selecting && props.pickableparts_is ("all")))) 1549 { 1550 int zstate = props.get_zstate (); 1551 bool x2Dtop = props.get_x2Dtop (); 1552 bool layer2Dtop = props.get_layer2Dtop (); 1553 bool xyzSym = props.get_xyzSym (); 1554 bool nearhoriz = props.get_nearhoriz (); 1555 double xticklen = props.get_xticklen (); 1556 double xtickoffset = props.get_xtickoffset (); 1557 double fy = props.get_fy (); 1558 double fz = props.get_fz (); 1559 double x_min = props.get_x_min (); 1560 double x_max = props.get_x_max (); 1561 double y_min = props.get_y_min (); 1562 double y_max = props.get_y_max (); 1563 double yPlane = props.get_yPlane (); 1564 double yPlaneN = props.get_yPlaneN (); 1565 double ypTick = props.get_ypTick (); 1566 double ypTickN = props.get_ypTickN (); 1567 double zPlane = props.get_zPlane (); 1568 double zPlaneN = props.get_zPlaneN (); 1569 double zpTick = props.get_zpTick (); 1570 double zpTickN = props.get_zpTickN (); 1571 1572 // X ticks and grid properties 1573 Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); 1574 Matrix xmticks = xform.xscale (props.get_xminortickvalues ().matrix_value ()); 1575 bool do_xminortick = props.is_xminortick () && ! xticks.isempty (); 1576 string_vector xticklabels = props.get_xticklabel ().string_vector_value (); 1577 int wmax = 0; 1578 int hmax = 0; 1579 bool tick_along_z = nearhoriz || math::isinf (fy); 1580 double linewidth = props.get_linewidth (); 1581 std::string gridstyle = props.get_gridlinestyle (); 1582 std::string minorgridstyle = props.get_minorgridlinestyle (); 1583 Matrix gridcolor = props.get_gridcolor_rgb (); 1584 Matrix minorgridcolor = props.get_minorgridcolor_rgb (); 1585 double gridalpha = props.get_gridalpha (); 1586 double minorgridalpha = props.get_minorgridalpha (); 1587 bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); 1588 bool do_xminorgrid = (props.is_xminorgrid () 1589 && (minorgridstyle != "none") 1590 && ! xticks.isempty ()); 1591 bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D () 1592 && ! props.yscale_is ("log"); 1593 bool is_origin_low = is_origin && (y_min + y_max) < 0; 1594 bool mirror = props.is_box () && xstate != AXE_ANY_DIR; 1595 1596 // X grid 1597 1598 // possibly use axis color for gridcolor & minorgridcolor 1599 if (props.gridcolormode_is ("auto")) 1600 if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) 1601 gridcolor = props.get_xcolor_rgb (); 1602 1603 if (props.minorgridcolormode_is ("auto")) 1604 if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) 1605 minorgridcolor = props.get_xcolor_rgb (); 1606 1607 if (gridcolor.isempty ()) 1608 do_xgrid = false; 1609 1610 if (minorgridcolor.isempty ()) 1611 do_xminorgrid = false; 1612 1613 // set styles when drawing only minor grid 1614 if (do_xminorgrid && ! do_xgrid) 1615 { 1616 gridstyle = minorgridstyle; 1617 gridcolor = minorgridcolor; 1618 gridalpha = minorgridalpha; 1619 do_xgrid = true; 1620 } 1621 1622 // minor grid lines 1623 if (do_xminorgrid) 1624 render_grid (linewidth, 1625 minorgridstyle, minorgridcolor, minorgridalpha, 1626 xmticks, x_min, x_max, 1627 yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1628 0, (zstate != AXE_DEPTH_DIR)); 1629 1630 // grid lines 1631 if (do_xgrid) 1632 render_grid (linewidth, 1633 gridstyle, gridcolor, gridalpha, 1634 xticks, x_min, x_max, 1635 yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1636 0, (zstate != AXE_DEPTH_DIR)); 1637 1638 // Skip drawing axis, ticks, and ticklabels when color is "none" 1639 if (props.xcolor_is ("none")) 1640 return; 1641 1642 set_color (props.get_xcolor_rgb ()); 1643 1644 // axis line 1645 double y_axis_pos = 0.; 1646 if (is_origin) 1647 { 1648 y_axis_pos = math::max (math::min (0., y_max), y_min); 1649 m_glfcns.glBegin (GL_LINES); 1650 set_color (props.get_xcolor_rgb ()); 1651 m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick); 1652 m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick); 1653 m_glfcns.glEnd (); 1654 } 1655 1656 // minor tick marks 1657 if (do_xminortick) 1658 { 1659 if (tick_along_z) 1660 render_tickmarks (xmticks, x_min, x_max, 1661 is_origin ? y_axis_pos : ypTick, ypTick, 1662 zpTick, zpTickN, 0., 0., 1663 (is_origin_low ? -1. : 1.) * 1664 math::signum (zpTick-zpTickN)*fz*xticklen/2, 1665 0, ! is_origin && mirror); 1666 else 1667 render_tickmarks (xmticks, x_min, x_max, 1668 is_origin ? y_axis_pos : ypTick, ypTickN, 1669 zpTick, zpTick, 0., 1670 (is_origin_low ? -1. : 1.) * 1671 math::signum (ypTick-ypTickN)*fy*xticklen/2, 1672 0., 0, ! is_origin && mirror); 1673 } 1674 1675 // tick marks 1676 if (tick_along_z) 1677 render_tickmarks (xticks, x_min, x_max, 1678 is_origin ? y_axis_pos : ypTick, ypTick, 1679 zpTick, zpTickN, 0., 0., 1680 (is_origin_low ? -1. : 1.) * 1681 math::signum (zpTick-zpTickN)*fz*xticklen, 1682 0, ! is_origin && mirror); 1683 else 1684 render_tickmarks (xticks, x_min, x_max, 1685 is_origin ? y_axis_pos : ypTick, ypTickN, 1686 zpTick, zpTick, 0., 1687 (is_origin_low ? -1. : 1.) * 1688 math::signum (ypTick-ypTickN)*fy*xticklen, 1689 0., 0, ! is_origin && mirror); 1690 1691 // tick texts 1692 if (xticklabels.numel () > 0) 1693 { 1694 int halign = (xstate == AXE_HORZ_DIR 1695 ? 1 1696 : (xyzSym || is_origin_low ? 0 : 2)); 1697 int valign = (xstate == AXE_VERT_DIR 1698 ? 1 1699 : (x2Dtop || is_origin_low ? 0 : 2)); 1700 1701 if (tick_along_z) 1702 render_ticktexts (xticks, xticklabels, x_min, x_max, 1703 is_origin ? y_axis_pos : ypTick, 1704 zpTick + 1705 (is_origin_low ? -1. : 1.) * 1706 math::signum (zpTick-zpTickN)*fz*xtickoffset, 1707 0, halign, valign, wmax, hmax); 1708 else 1709 render_ticktexts (xticks, xticklabels, x_min, x_max, 1710 (is_origin ? y_axis_pos : ypTick) + 1711 (is_origin_low ? -1. : 1.) * 1712 math::signum (ypTick-ypTickN)*fy*xtickoffset, 1713 zpTick, 0, halign, valign, wmax, hmax); 1714 } 1715 1716 gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on"); 1717 } 1718 else 1719 gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off"); 1720 1721 #else 1722 1723 octave_unused_parameter (props); 1724 1725 // This shouldn't happen because construction of opengl_renderer 1726 // objects is supposed to be impossible if OpenGL is not available. 1727 1728 panic_impossible (); 1729 1730 #endif 1731 } 1732 1733 void draw_axes_y_grid(const axes::properties & props)1734 opengl_renderer::draw_axes_y_grid (const axes::properties& props) 1735 { 1736 #if defined (HAVE_OPENGL) 1737 1738 gh_manager& gh_mgr 1739 = __get_gh_manager__ ("opengl_renderer::draw_axes_y_grid"); 1740 1741 int ystate = props.get_ystate (); 1742 1743 if (ystate != AXE_DEPTH_DIR && props.is_visible () 1744 && (props.is_visible () 1745 || (selecting && props.pickableparts_is ("all")))) 1746 { 1747 int zstate = props.get_zstate (); 1748 bool y2Dright = props.get_y2Dright (); 1749 bool layer2Dtop = props.get_layer2Dtop (); 1750 bool xyzSym = props.get_xyzSym (); 1751 bool nearhoriz = props.get_nearhoriz (); 1752 double yticklen = props.get_yticklen (); 1753 double ytickoffset = props.get_ytickoffset (); 1754 double fx = props.get_fx (); 1755 double fz = props.get_fz (); 1756 double xPlane = props.get_xPlane (); 1757 double xPlaneN = props.get_xPlaneN (); 1758 double xpTick = props.get_xpTick (); 1759 double xpTickN = props.get_xpTickN (); 1760 double y_min = props.get_y_min (); 1761 double y_max = props.get_y_max (); 1762 double x_min = props.get_x_min (); 1763 double x_max = props.get_x_max (); 1764 double zPlane = props.get_zPlane (); 1765 double zPlaneN = props.get_zPlaneN (); 1766 double zpTick = props.get_zpTick (); 1767 double zpTickN = props.get_zpTickN (); 1768 1769 // Y ticks and grid properties 1770 Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); 1771 Matrix ymticks = xform.yscale (props.get_yminortickvalues ().matrix_value ()); 1772 bool do_yminortick = props.is_yminortick () && ! yticks.isempty (); 1773 string_vector yticklabels = props.get_yticklabel ().string_vector_value (); 1774 int wmax = 0; 1775 int hmax = 0; 1776 bool tick_along_z = nearhoriz || math::isinf (fx); 1777 double linewidth = props.get_linewidth (); 1778 std::string gridstyle = props.get_gridlinestyle (); 1779 std::string minorgridstyle = props.get_minorgridlinestyle (); 1780 Matrix gridcolor = props.get_gridcolor_rgb (); 1781 Matrix minorgridcolor = props.get_minorgridcolor_rgb (); 1782 double gridalpha = props.get_gridalpha (); 1783 double minorgridalpha = props.get_minorgridalpha (); 1784 bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); 1785 bool do_yminorgrid = (props.is_yminorgrid () 1786 && (minorgridstyle != "none") 1787 && ! yticks.isempty ()); 1788 bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D () 1789 && ! props.xscale_is ("log"); 1790 bool is_origin_low = is_origin && (x_min + x_max) < 0; 1791 bool mirror = props.is_box () && ystate != AXE_ANY_DIR 1792 && (! props.has_property ("__plotyy_axes__")); 1793 1794 // Y grid 1795 1796 // possibly use axis color for gridcolor & minorgridcolor 1797 if (props.gridcolormode_is ("auto")) 1798 if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) 1799 gridcolor = props.get_ycolor_rgb (); 1800 1801 if (props.minorgridcolormode_is ("auto")) 1802 if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) 1803 minorgridcolor = props.get_ycolor_rgb (); 1804 1805 if (gridcolor.isempty ()) 1806 do_ygrid = false; 1807 1808 if (minorgridcolor.isempty ()) 1809 do_yminorgrid = false; 1810 1811 // set styles when drawing only minor grid 1812 if (do_yminorgrid && ! do_ygrid) 1813 { 1814 gridstyle = minorgridstyle; 1815 gridcolor = minorgridcolor; 1816 gridalpha = minorgridalpha; 1817 do_ygrid = true; 1818 } 1819 1820 // minor grid lines 1821 if (do_yminorgrid) 1822 render_grid (linewidth, 1823 minorgridstyle, minorgridcolor, minorgridalpha, 1824 ymticks, y_min, y_max, 1825 xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1826 1, (zstate != AXE_DEPTH_DIR)); 1827 1828 // grid lines 1829 if (do_ygrid) 1830 render_grid (linewidth, 1831 gridstyle, gridcolor, gridalpha, 1832 yticks, y_min, y_max, 1833 xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1834 1, (zstate != AXE_DEPTH_DIR)); 1835 1836 // Skip drawing axis, ticks, and ticklabels when color is "none" 1837 if (props.ycolor_is ("none")) 1838 return; 1839 1840 set_color (props.get_ycolor_rgb ()); 1841 1842 // axis line 1843 double x_axis_pos = 0.; 1844 if (is_origin) 1845 { 1846 x_axis_pos = math::max (math::min (0., x_max), x_min); 1847 m_glfcns.glBegin (GL_LINES); 1848 set_color (props.get_ycolor_rgb ()); 1849 m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick); 1850 m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick); 1851 m_glfcns.glEnd (); 1852 } 1853 1854 // minor tick marks 1855 if (do_yminortick) 1856 { 1857 if (tick_along_z) 1858 render_tickmarks (ymticks, y_min, y_max, 1859 is_origin ? x_axis_pos : xpTick, xpTick, 1860 zpTick, zpTickN, 0., 0., 1861 (is_origin_low ? -1. : 1.) * 1862 math::signum (zpTick-zpTickN)*fz*yticklen/2, 1863 1, ! is_origin && mirror); 1864 else 1865 render_tickmarks (ymticks, y_min, y_max, 1866 is_origin ? x_axis_pos : xpTick, xpTickN, 1867 zpTick, zpTick, 1868 (is_origin_low ? -1. : 1.) * 1869 math::signum (xpTick-xpTickN)*fx*yticklen/2, 1870 0., 0., 1, ! is_origin && mirror); 1871 } 1872 1873 // tick marks 1874 if (tick_along_z) 1875 render_tickmarks (yticks, y_min, y_max, 1876 is_origin ? x_axis_pos : xpTick, xpTick, 1877 zpTick, zpTickN, 0., 0., 1878 (is_origin_low ? -1. : 1.) * 1879 math::signum (zpTick-zpTickN)*fz*yticklen, 1880 1, ! is_origin && mirror); 1881 else 1882 render_tickmarks (yticks, y_min, y_max, 1883 is_origin ? x_axis_pos : xpTick, xpTickN, 1884 zpTick, zpTick, 1885 (is_origin_low ? -1. : 1.) * 1886 math::signum (xPlaneN-xPlane)*fx*yticklen, 1887 0., 0., 1, ! is_origin && mirror); 1888 1889 // tick texts 1890 if (yticklabels.numel () > 0) 1891 { 1892 int halign = (ystate == AXE_HORZ_DIR 1893 ? 1 1894 : (! xyzSym || y2Dright || is_origin_low ? 0 : 2)); 1895 int valign = (ystate == AXE_VERT_DIR 1896 ? 1 1897 : (is_origin_low ? 0 : 2)); 1898 1899 if (tick_along_z) 1900 render_ticktexts (yticks, yticklabels, y_min, y_max, 1901 is_origin ? x_axis_pos : xpTick, 1902 zpTick + 1903 (is_origin_low ? -1. : 1.) * 1904 math::signum (zpTick-zpTickN)*fz*ytickoffset, 1905 1, halign, valign, wmax, hmax); 1906 else 1907 render_ticktexts (yticks, yticklabels, y_min, y_max, 1908 (is_origin ? x_axis_pos : xpTick) + 1909 (is_origin_low ? -1. : 1.) * 1910 math::signum (xpTick-xpTickN)*fx*ytickoffset, 1911 zpTick, 1, halign, valign, wmax, hmax); 1912 } 1913 1914 gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on"); 1915 } 1916 else 1917 gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off"); 1918 1919 #else 1920 1921 octave_unused_parameter (props); 1922 1923 // This shouldn't happen because construction of opengl_renderer 1924 // objects is supposed to be impossible if OpenGL is not available. 1925 1926 panic_impossible (); 1927 1928 #endif 1929 } 1930 1931 void draw_axes_z_grid(const axes::properties & props)1932 opengl_renderer::draw_axes_z_grid (const axes::properties& props) 1933 { 1934 gh_manager& gh_mgr 1935 = __get_gh_manager__ ("opengl_renderer::draw_axes_z_grid"); 1936 1937 int zstate = props.get_zstate (); 1938 1939 if (zstate != AXE_DEPTH_DIR && props.is_visible () 1940 && (props.is_visible () 1941 || (selecting && props.pickableparts_is ("all")))) 1942 { 1943 bool xySym = props.get_xySym (); 1944 bool zSign = props.get_zSign (); 1945 double zticklen = props.get_zticklen (); 1946 double ztickoffset = props.get_ztickoffset (); 1947 double fx = props.get_fx (); 1948 double fy = props.get_fy (); 1949 double xPlane = props.get_xPlane (); 1950 double xPlaneN = props.get_xPlaneN (); 1951 double yPlane = props.get_yPlane (); 1952 double yPlaneN = props.get_yPlaneN (); 1953 double z_min = props.get_z_min (); 1954 double z_max = props.get_z_max (); 1955 1956 // Z ticks and grid properties 1957 Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); 1958 Matrix zmticks = xform.zscale (props.get_zminortickvalues ().matrix_value ()); 1959 bool do_zminortick = props.is_zminortick () && ! zticks.isempty (); 1960 string_vector zticklabels = props.get_zticklabel ().string_vector_value (); 1961 int wmax = 0; 1962 int hmax = 0; 1963 double linewidth = props.get_linewidth (); 1964 std::string gridstyle = props.get_gridlinestyle (); 1965 std::string minorgridstyle = props.get_minorgridlinestyle (); 1966 Matrix gridcolor = props.get_gridcolor_rgb (); 1967 Matrix minorgridcolor = props.get_minorgridcolor_rgb (); 1968 double gridalpha = props.get_gridalpha (); 1969 double minorgridalpha = props.get_minorgridalpha (); 1970 bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); 1971 bool do_zminorgrid = (props.is_zminorgrid () 1972 && (minorgridstyle != "none") 1973 && ! zticks.isempty ()); 1974 bool mirror = props.is_box () && zstate != AXE_ANY_DIR; 1975 1976 // Z grid 1977 1978 // possibly use axis color for gridcolor & minorgridcolor 1979 if (props.gridcolormode_is ("auto")) 1980 if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) 1981 gridcolor = props.get_zcolor_rgb (); 1982 1983 if (props.minorgridcolormode_is ("auto")) 1984 if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) 1985 minorgridcolor = props.get_zcolor_rgb (); 1986 1987 if (gridcolor.isempty ()) 1988 do_zgrid = false; 1989 1990 if (minorgridcolor.isempty ()) 1991 do_zminorgrid = false; 1992 1993 // set styles when drawing only minor grid 1994 if (do_zminorgrid && ! do_zgrid) 1995 { 1996 gridstyle = minorgridstyle; 1997 gridcolor = minorgridcolor; 1998 gridalpha = minorgridalpha; 1999 do_zgrid = true; 2000 } 2001 2002 // minor grid lines 2003 if (do_zminorgrid) 2004 render_grid (linewidth, 2005 minorgridstyle, minorgridcolor, minorgridalpha, 2006 zmticks, z_min, z_max, 2007 xPlane, xPlaneN, yPlane, yPlaneN, 2, true); 2008 2009 // grid lines 2010 if (do_zgrid) 2011 render_grid (linewidth, 2012 gridstyle, gridcolor, gridalpha, 2013 zticks, z_min, z_max, 2014 xPlane, xPlaneN, yPlane, yPlaneN, 2, true); 2015 2016 // Skip drawing axis, ticks, and ticklabels when color is "none" 2017 if (props.zcolor_is ("none")) 2018 return; 2019 2020 set_color (props.get_zcolor_rgb ()); 2021 2022 // minor tick marks 2023 if (do_zminortick) 2024 { 2025 if (xySym) 2026 { 2027 if (math::isinf (fy)) 2028 render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane, 2029 yPlane, yPlane, 2030 math::signum (xPlaneN-xPlane)*fx*zticklen/2, 2031 0., 0., 2, mirror); 2032 else 2033 render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, 2034 yPlane, yPlane, 0., 2035 math::signum (yPlane-yPlaneN)*fy*zticklen/2, 2036 0., 2, false); 2037 } 2038 else 2039 { 2040 if (math::isinf (fx)) 2041 render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, 2042 yPlaneN, yPlane, 0., 2043 math::signum (yPlaneN-yPlane)*fy*zticklen/2, 2044 0., 2, mirror); 2045 else 2046 render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, 2047 yPlaneN, yPlaneN, 2048 math::signum (xPlane-xPlaneN)*fx*zticklen/2, 2049 0., 0., 2, false); 2050 } 2051 } 2052 2053 // tick marks 2054 if (xySym) 2055 { 2056 if (math::isinf (fy)) 2057 render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, 2058 yPlane, yPlane, 2059 math::signum (xPlaneN-xPlane)*fx*zticklen, 2060 0., 0., 2, mirror); 2061 else 2062 render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, 2063 yPlane, yPlane, 0., 2064 math::signum (yPlane-yPlaneN)*fy*zticklen, 2065 0., 2, false); 2066 } 2067 else 2068 { 2069 if (math::isinf (fx)) 2070 render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, 2071 yPlaneN, yPlane, 0., 2072 math::signum (yPlaneN-yPlane)*fy*zticklen, 2073 0., 2, mirror); 2074 else 2075 render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, 2076 yPlaneN, yPlane, 2077 math::signum (xPlane-xPlaneN)*fx*zticklen, 2078 0., 0., 2, false); 2079 } 2080 2081 // tick texts 2082 if (zticklabels.numel () > 0) 2083 { 2084 int halign = 2; 2085 int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2)); 2086 2087 if (xySym) 2088 { 2089 if (math::isinf (fy)) 2090 render_ticktexts (zticks, zticklabels, z_min, z_max, 2091 xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset, 2092 yPlane, 2, halign, valign, wmax, hmax); 2093 else 2094 render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN, 2095 yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset, 2096 2, halign, valign, wmax, hmax); 2097 } 2098 else 2099 { 2100 if (math::isinf (fx)) 2101 render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane, 2102 yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset, 2103 2, halign, valign, wmax, hmax); 2104 else 2105 render_ticktexts (zticks, zticklabels, z_min, z_max, 2106 xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset, 2107 yPlaneN, 2, halign, valign, wmax, hmax); 2108 } 2109 } 2110 2111 gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on"); 2112 } 2113 else 2114 gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off"); 2115 } 2116 2117 void draw_axes_grids(const axes::properties & props)2118 opengl_renderer::draw_axes_grids (const axes::properties& props) 2119 { 2120 #if defined (HAVE_OPENGL) 2121 // Disable line smoothing for axes 2122 GLboolean antialias; 2123 2124 m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias); 2125 2126 if (antialias == GL_TRUE) 2127 m_glfcns.glDisable (GL_LINE_SMOOTH); 2128 2129 set_linecap ("butt"); 2130 set_linewidth (props.get_linewidth ()); 2131 set_font (props); 2132 set_interpreter (props.get_ticklabelinterpreter ()); 2133 2134 draw_axes_x_grid (props); 2135 draw_axes_y_grid (props); 2136 draw_axes_z_grid (props); 2137 2138 if (antialias == GL_TRUE) 2139 m_glfcns.glEnable (GL_LINE_SMOOTH); 2140 #else 2141 2142 octave_unused_parameter (props); 2143 2144 // This shouldn't happen because construction of opengl_renderer 2145 // objects is supposed to be impossible if OpenGL is not available. 2146 2147 panic_impossible (); 2148 2149 #endif 2150 } 2151 2152 void draw_all_lights(const base_properties & props,std::list<graphics_object> & obj_list)2153 opengl_renderer::draw_all_lights (const base_properties& props, 2154 std::list<graphics_object>& obj_list) 2155 { 2156 #if defined (HAVE_OPENGL) 2157 gh_manager& gh_mgr 2158 = __get_gh_manager__ ("opengl_renderer::draw_axes_all_lights"); 2159 2160 Matrix children = props.get_all_children (); 2161 2162 for (octave_idx_type i = children.numel () - 1; i >= 0; i--) 2163 { 2164 graphics_object go = gh_mgr.get_object (children(i)); 2165 2166 base_properties p = go.get_properties (); 2167 2168 if (p.is_visible () 2169 || (selecting && p.pickableparts_is ("all"))) 2170 { 2171 if (go.isa ("light") && ! selecting) 2172 { 2173 if (m_current_light-GL_LIGHT0 < m_max_lights) 2174 { 2175 set_clipping (p.is_clipping ()); 2176 draw (go); 2177 m_current_light++; 2178 } 2179 } 2180 else if (go.isa ("hggroup") 2181 && ! (selecting && p.pickableparts_is ("none"))) 2182 draw_all_lights (go.get_properties (), obj_list); 2183 else if (! (selecting && p.pickableparts_is ("none"))) 2184 obj_list.push_back (go); 2185 } 2186 } 2187 #else 2188 2189 octave_unused_parameter (props); 2190 octave_unused_parameter (obj_list); 2191 2192 // This shouldn't happen because construction of opengl_renderer 2193 // objects is supposed to be impossible if OpenGL is not available. 2194 2195 panic_impossible (); 2196 2197 #endif 2198 } 2199 2200 void draw_axes_children(const axes::properties & props)2201 opengl_renderer::draw_axes_children (const axes::properties& props) 2202 { 2203 #if defined (HAVE_OPENGL) 2204 // list for non-light child objects 2205 std::list<graphics_object> obj_list; 2206 std::list<graphics_object>::iterator it; 2207 2208 // 1st pass: draw light objects 2209 2210 // FIXME: max_lights only needs to be set once. 2211 // It would be better if this could be in the constructor for gl_renderer 2212 // but this seems to lead to calls of OpenGL functions before the context 2213 // is actually initialized. See bug #48669. 2214 // Check actual maximum number of lights possible 2215 init_maxlights (); 2216 2217 // Start with the last element of the array of child objects to 2218 // display them in the order they were added to the array. 2219 2220 if (props.get_num_lights () > m_max_lights) 2221 warning_with_id ("Octave:max-lights-exceeded", 2222 "light: Maximum number of lights (%d) in these axes is " 2223 "exceeded.", m_max_lights); 2224 2225 m_current_light = GL_LIGHT0; 2226 draw_all_lights (props, obj_list); 2227 2228 // disable other OpenGL lights 2229 for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++) 2230 m_glfcns.glDisable (GL_LIGHT0 + i); 2231 2232 // save camera position and set ambient light color before drawing 2233 // other objects 2234 view_vector = props.get_cameraposition ().matrix_value (); 2235 2236 float cb[4] = { 1.0, 1.0, 1.0, 1.0 }; 2237 ColumnVector ambient_color = props.get_ambientlightcolor_rgb (); 2238 for (int i = 0; i < 3; i++) 2239 cb[i] = ambient_color(i); 2240 m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb); 2241 2242 // 2nd pass: draw other objects (with units set to "data") 2243 2244 it = obj_list.begin (); 2245 while (it != obj_list.end ()) 2246 { 2247 graphics_object go = (*it); 2248 2249 // FIXME: check whether object has "units" property and it is set 2250 // to "data" 2251 if (! go.isa ("text") || go.get ("units").string_value () == "data") 2252 { 2253 set_clipping (go.get_properties ().is_clipping ()); 2254 draw (go); 2255 2256 it = obj_list.erase (it); 2257 } 2258 else 2259 it++; 2260 } 2261 2262 // 3rd pass: draw remaining objects 2263 2264 m_glfcns.glDisable (GL_DEPTH_TEST); 2265 2266 for (it = obj_list.begin (); it != obj_list.end (); it++) 2267 { 2268 graphics_object go = (*it); 2269 2270 set_clipping (go.get_properties ().is_clipping ()); 2271 draw (go); 2272 } 2273 2274 set_clipping (false); 2275 2276 // FIXME: finalize rendering (transparency processing) 2277 // FIXME: draw zoom box, if needed 2278 2279 #else 2280 2281 octave_unused_parameter (props); 2282 2283 // This shouldn't happen because construction of opengl_renderer 2284 // objects is supposed to be impossible if OpenGL is not available. 2285 2286 panic_impossible (); 2287 2288 #endif 2289 } 2290 2291 void draw_axes(const axes::properties & props)2292 opengl_renderer::draw_axes (const axes::properties& props) 2293 { 2294 #if defined (HAVE_OPENGL) 2295 2296 // Legends are not drawn when "visible" is "off". 2297 if (! props.is_visible () && props.get_tag () == "legend") 2298 return; 2299 2300 // Don't draw the axes and its children if we are in selection and 2301 // pickable parts is "none". 2302 if (selecting && props.pickableparts_is ("none")) 2303 return; 2304 2305 static double floatmax = std::numeric_limits<float>::max (); 2306 2307 double x_min = props.get_x_min (); 2308 double x_max = props.get_x_max (); 2309 double y_min = props.get_y_min (); 2310 double y_max = props.get_y_max (); 2311 double z_min = props.get_z_min (); 2312 double z_max = props.get_z_max (); 2313 2314 if (x_max > floatmax || y_max > floatmax || z_max > floatmax 2315 || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax) 2316 { 2317 warning ("opengl_renderer: data values greater than float capacity. (1) Scale data, or (2) Use gnuplot"); 2318 return; 2319 } 2320 2321 setup_opengl_transformation (props); 2322 2323 // For 2D axes with only 2D primitives, draw from back to front without 2324 // depth sorting 2325 bool is2D = props.get_is2D (true); 2326 if (is2D) 2327 m_glfcns.glDisable (GL_DEPTH_TEST); 2328 else 2329 m_glfcns.glEnable (GL_DEPTH_TEST); 2330 2331 draw_axes_planes (props); 2332 2333 if (! is2D || props.layer_is ("bottom")) 2334 { 2335 draw_axes_grids (props); 2336 if (props.get_tag () != "legend" || props.get_box () != "off") 2337 draw_axes_boxes (props); 2338 } 2339 2340 set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max); 2341 2342 draw_axes_children (props); 2343 2344 if (is2D && props.layer_is ("top")) 2345 { 2346 draw_axes_grids (props); 2347 if (props.get_tag () != "legend" || props.get_box () != "off") 2348 draw_axes_boxes (props); 2349 } 2350 2351 #else 2352 2353 octave_unused_parameter (props); 2354 2355 // This shouldn't happen because construction of opengl_renderer 2356 // objects is supposed to be impossible if OpenGL is not available. 2357 2358 panic_impossible (); 2359 2360 #endif 2361 } 2362 2363 void draw_line(const line::properties & props)2364 opengl_renderer::draw_line (const line::properties& props) 2365 { 2366 #if defined (HAVE_OPENGL) 2367 2368 bool draw_all = selecting && props.pickableparts_is ("all"); 2369 2370 Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); 2371 Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); 2372 Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); 2373 2374 bool has_z = (z.numel () > 0); 2375 int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()), 2376 (has_z ? z.numel () 2377 : std::numeric_limits<int>::max ()))); 2378 uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); 2379 uint8_t clip_ok = 0x40; 2380 2381 std::vector<uint8_t> clip (n); 2382 2383 if (has_z) 2384 for (int i = 0; i < n; i++) 2385 clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask); 2386 else 2387 { 2388 double z_mid = (zmin+zmax)/2; 2389 2390 for (int i = 0; i < n; i++) 2391 clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask); 2392 } 2393 2394 if (! props.linestyle_is ("none") && ! props.color_is ("none")) 2395 { 2396 set_color (props.get_color_rgb ()); 2397 set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); 2398 set_linewidth (props.get_linewidth ()); 2399 set_linecap ("butt"); 2400 set_linejoin (props.get_linejoin ()); 2401 2402 if (has_z) 2403 { 2404 bool flag = false; 2405 2406 for (int i = 1; i < n; i++) 2407 { 2408 if ((clip[i-1] & clip[i]) == clip_ok) 2409 { 2410 if (! flag) 2411 { 2412 flag = true; 2413 m_glfcns.glBegin (GL_LINE_STRIP); 2414 m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1)); 2415 } 2416 m_glfcns.glVertex3d (x(i), y(i), z(i)); 2417 } 2418 else if (flag) 2419 { 2420 flag = false; 2421 m_glfcns.glEnd (); 2422 } 2423 } 2424 2425 if (flag) 2426 m_glfcns.glEnd (); 2427 } 2428 else 2429 { 2430 bool flag = false; 2431 2432 for (int i = 1; i < n; i++) 2433 { 2434 if ((clip[i-1] & clip[i]) == clip_ok) 2435 { 2436 if (! flag) 2437 { 2438 flag = true; 2439 m_glfcns.glBegin (GL_LINE_STRIP); 2440 m_glfcns.glVertex2d (x(i-1), y(i-1)); 2441 } 2442 m_glfcns.glVertex2d (x(i), y(i)); 2443 } 2444 else if (flag) 2445 { 2446 flag = false; 2447 m_glfcns.glEnd (); 2448 } 2449 } 2450 2451 if (flag) 2452 m_glfcns.glEnd (); 2453 } 2454 2455 set_linewidth (0.5f); 2456 set_linestyle ("-"); 2457 } 2458 2459 set_clipping (false); 2460 2461 if (! props.marker_is ("none") 2462 && ! (props.markeredgecolor_is ("none") 2463 && props.markerfacecolor_is ("none"))) 2464 { 2465 Matrix lc, fc; 2466 2467 if (draw_all) 2468 lc = Matrix (1, 3, 0.0); 2469 else if (props.markeredgecolor_is ("auto")) 2470 lc = props.get_color_rgb (); 2471 else if (! props.markeredgecolor_is ("none")) 2472 lc = props.get_markeredgecolor_rgb (); 2473 2474 if (draw_all) 2475 fc = Matrix (1, 3, 0.0); 2476 if (props.markerfacecolor_is ("auto")) 2477 fc = props.get_color_rgb (); 2478 else if (! props.markerfacecolor_is ("none")) 2479 fc = props.get_markerfacecolor_rgb (); 2480 2481 init_marker (props.get_marker (), props.get_markersize (), 2482 props.get_linewidth ()); 2483 2484 for (int i = 0; i < n; i++) 2485 { 2486 if (clip[i] == clip_ok) 2487 draw_marker (x(i), y(i), 2488 has_z ? z(i) : 0.0, 2489 lc, fc); 2490 } 2491 2492 end_marker (); 2493 } 2494 2495 set_clipping (props.is_clipping ()); 2496 2497 #else 2498 2499 octave_unused_parameter (props); 2500 2501 // This shouldn't happen because construction of opengl_renderer 2502 // objects is supposed to be impossible if OpenGL is not available. 2503 2504 panic_impossible (); 2505 2506 #endif 2507 } 2508 2509 void draw_surface(const surface::properties & props)2510 opengl_renderer::draw_surface (const surface::properties& props) 2511 { 2512 #if defined (HAVE_OPENGL) 2513 2514 bool draw_all = selecting && props.pickableparts_is ("all"); 2515 2516 const Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); 2517 const Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); 2518 const Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); 2519 2520 int zr = z.rows (); 2521 int zc = z.columns (); 2522 2523 NDArray c; 2524 const NDArray vn = props.get_vertexnormals ().array_value (); 2525 dim_vector vn_dims = vn.dims (); 2526 bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc 2527 && vn_dims(2) == 3); 2528 const NDArray fn = props.get_facenormals ().array_value (); 2529 dim_vector fn_dims = fn.dims (); 2530 bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1 2531 && fn_dims(2) == 3); 2532 2533 // FIXME: handle transparency 2534 Matrix a; 2535 2536 int fc_mode = (props.facecolor_is_rgb () ? 0 : 2537 (props.facecolor_is ("flat") ? 1 : 2538 (props.facecolor_is ("interp") ? 2 : 2539 (props.facecolor_is ("texturemap") ? 3 : -1)))); 2540 int fl_mode = (props.facelighting_is ("none") ? 0 : 2541 (props.facelighting_is ("flat") ? 2542 (has_face_normals ? 1 : 0) : 2543 (has_vertex_normals ? 2 : 0))); 2544 int fa_mode = (props.facealpha_is_double () ? 0 : 2545 (props.facealpha_is ("flat") ? 1 : 2)); 2546 int ec_mode = (props.edgecolor_is_rgb () ? 0 : 2547 (props.edgecolor_is ("flat") ? 1 : 2548 (props.edgecolor_is ("interp") ? 2 : -1))); 2549 int el_mode = (props.edgelighting_is ("none") ? 0 : 2550 (props.edgelighting_is ("flat") ? 2551 (has_face_normals ? 1 : 0) : 2552 (has_vertex_normals ? 2 : 0))); 2553 int ea_mode = (props.edgealpha_is_double () ? 0 : 2554 (props.edgealpha_is ("flat") ? 1 : 2)); 2555 int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : 2556 (props.backfacelighting_is ("reverselit") ? 1 : 2)); 2557 bool do_lighting = props.get_do_lighting (); 2558 2559 Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0) 2560 : props.get_facecolor_rgb ()); 2561 Matrix ecolor = props.get_edgecolor_rgb (); 2562 double fa = 1.0; 2563 2564 float as = props.get_ambientstrength (); 2565 float ds = props.get_diffusestrength (); 2566 float ss = props.get_specularstrength (); 2567 float se = props.get_specularexponent () * 5; // to fit Matlab 2568 float scr = props.get_specularcolorreflectance (); 2569 float cb[4] = { 0.0, 0.0, 0.0, 1.0 }; 2570 2571 opengl_texture tex (m_glfcns); 2572 2573 int i1, i2, j1, j2; 2574 bool x_mat = (x.rows () == z.rows ()); 2575 bool y_mat = (y.columns () == z.columns ()); 2576 2577 i1 = i2 = j1 = j2 = 0; 2578 2579 if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0) 2580 c = props.get_color_data ().array_value (); 2581 2582 boolMatrix clip (z.dims (), false); 2583 2584 for (int i = 0; i < zr; i++) 2585 { 2586 if (x_mat) 2587 i1 = i; 2588 2589 for (int j = 0; j < zc; j++) 2590 { 2591 if (y_mat) 2592 j1 = j; 2593 2594 clip(i,j) = is_nan_or_inf (x(i1,j), y(i,j1), z(i,j)); 2595 } 2596 } 2597 2598 if (fa_mode > 0 || ea_mode > 0) 2599 { 2600 // FIXME: implement alphadata conversion 2601 //a = props.get_alpha_data (); 2602 } 2603 2604 if (fl_mode > 0 || el_mode > 0) 2605 m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); 2606 2607 // FIXME: good candidate for caching, 2608 // transferring pixel data to OpenGL is time consuming. 2609 if (fc_mode == TEXTURE) 2610 tex = opengl_texture::create (m_glfcns, props.get_color_data ()); 2611 2612 if (draw_all || ! props.facecolor_is ("none")) 2613 { 2614 if (fa_mode == 0) 2615 { 2616 fa = props.get_facealpha_double (); 2617 cb[3] = fa; 2618 if (fc_mode == UNIFORM || fc_mode == TEXTURE) 2619 { 2620 m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); 2621 if (fl_mode > 0) 2622 { 2623 for (int i = 0; i < 3; i++) 2624 cb[i] = as * fcolor(i); 2625 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2626 2627 for (int i = 0; i < 3; i++) 2628 cb[i] = ds * fcolor(i); 2629 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2630 2631 for (int i = 0; i < 3; i++) 2632 cb[i] = ss * (scr + (1-scr) * fcolor(i)); 2633 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2634 } 2635 } 2636 2637 if ((fl_mode > 0) && do_lighting) 2638 m_glfcns.glEnable (GL_LIGHTING); 2639 m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD) 2640 ? GL_SMOOTH : GL_FLAT); 2641 set_polygon_offset (true, 1.0); 2642 if (fc_mode == TEXTURE) 2643 m_glfcns.glEnable (GL_TEXTURE_2D); 2644 2645 for (int i = 1; i < zc; i++) 2646 { 2647 if (y_mat) 2648 { 2649 i1 = i-1; 2650 i2 = i; 2651 } 2652 2653 for (int j = 1; j < zr; j++) 2654 { 2655 2656 if (clip(j-1, i-1) || clip(j, i-1) 2657 || clip(j-1, i) || clip(j, i)) 2658 continue; 2659 2660 if (fc_mode == FLAT) 2661 { 2662 // "flat" only needs color at lower-left vertex 2663 if (! math::isfinite (c(j-1,i-1))) 2664 continue; 2665 } 2666 else if (fc_mode == INTERP) 2667 { 2668 // "interp" needs valid color at all 4 vertices 2669 if (! (math::isfinite (c(j-1, i-1)) && math::isfinite (c(j, i-1)) 2670 && math::isfinite (c(j-1, i)) && math::isfinite (c(j, i)))) 2671 continue; 2672 } 2673 2674 if (x_mat) 2675 { 2676 j1 = j-1; 2677 j2 = j; 2678 } 2679 2680 m_glfcns.glBegin (GL_QUADS); 2681 2682 // Vertex 1 2683 if (fc_mode == TEXTURE) 2684 tex.tex_coord (double (i-1) / (zc-1), 2685 double (j-1) / (zr-1)); 2686 else if (fc_mode > 0) 2687 { 2688 // FIXME: is there a smarter way to do this? 2689 for (int k = 0; k < 3; k++) 2690 cb[k] = c(j-1, i-1, k); 2691 m_glfcns.glColor4fv (cb); 2692 2693 if (fl_mode > 0) 2694 { 2695 for (int k = 0; k < 3; k++) 2696 cb[k] *= as; 2697 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2698 2699 for (int k = 0; k < 3; k++) 2700 cb[k] = ds * c(j-1, i-1, k); 2701 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2702 2703 for (int k = 0; k < 3; k++) 2704 cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k)); 2705 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2706 } 2707 } 2708 if (fl_mode > 0) 2709 set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn), 2710 j-1, i-1); 2711 2712 m_glfcns.glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1)); 2713 2714 // Vertex 2 2715 if (fc_mode == TEXTURE) 2716 tex.tex_coord (double (i) / (zc-1), double (j-1) / (zr-1)); 2717 else if (fc_mode == INTERP) 2718 { 2719 for (int k = 0; k < 3; k++) 2720 cb[k] = c(j-1, i, k); 2721 m_glfcns.glColor4fv (cb); 2722 2723 if (fl_mode > 0) 2724 { 2725 for (int k = 0; k < 3; k++) 2726 cb[k] *= as; 2727 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2728 2729 for (int k = 0; k < 3; k++) 2730 cb[k] = ds * c(j-1, i, k); 2731 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2732 2733 for (int k = 0; k < 3; k++) 2734 cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); 2735 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2736 } 2737 } 2738 2739 if (fl_mode == GOURAUD) 2740 set_normal (bfl_mode, vn, j-1, i); 2741 2742 m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); 2743 2744 // Vertex 3 2745 if (fc_mode == TEXTURE) 2746 tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1)); 2747 else if (fc_mode == INTERP) 2748 { 2749 for (int k = 0; k < 3; k++) 2750 cb[k] = c(j, i, k); 2751 m_glfcns.glColor4fv (cb); 2752 2753 if (fl_mode > 0) 2754 { 2755 for (int k = 0; k < 3; k++) 2756 cb[k] *= as; 2757 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2758 2759 for (int k = 0; k < 3; k++) 2760 cb[k] = ds * c(j, i, k); 2761 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2762 2763 for (int k = 0; k < 3; k++) 2764 cb[k] = ss * (scr + (1-scr) * c(j, i, k)); 2765 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2766 } 2767 } 2768 if (fl_mode == GOURAUD) 2769 set_normal (bfl_mode, vn, j, i); 2770 2771 m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); 2772 2773 // Vertex 4 2774 if (fc_mode == TEXTURE) 2775 tex.tex_coord (double (i-1) / (zc-1), double (j) / (zr-1)); 2776 else if (fc_mode == INTERP) 2777 { 2778 for (int k = 0; k < 3; k++) 2779 cb[k] = c(j, i-1, k); 2780 m_glfcns.glColor4fv (cb); 2781 2782 if (fl_mode > 0) 2783 { 2784 for (int k = 0; k < 3; k++) 2785 cb[k] *= as; 2786 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2787 2788 for (int k = 0; k < 3; k++) 2789 cb[k] = ds * c(j, i-1, k); 2790 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2791 2792 for (int k = 0; k < 3; k++) 2793 cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); 2794 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2795 } 2796 } 2797 if (fl_mode == GOURAUD) 2798 set_normal (bfl_mode, vn, j, i-1); 2799 2800 m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); 2801 2802 m_glfcns.glEnd (); 2803 } 2804 } 2805 2806 set_polygon_offset (false); 2807 if (fc_mode == TEXTURE) 2808 m_glfcns.glDisable (GL_TEXTURE_2D); 2809 2810 if ((fl_mode > 0) && do_lighting) 2811 m_glfcns.glDisable (GL_LIGHTING); 2812 } 2813 else 2814 { 2815 // FIXME: implement flat, interp and texturemap transparency 2816 } 2817 } 2818 2819 if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")) 2820 { 2821 if (props.get_edgealpha_double () == 1) 2822 { 2823 cb[3] = 1.0; // edgealpha isn't implemented yet 2824 if (ec_mode == UNIFORM) 2825 { 2826 m_glfcns.glColor3dv (ecolor.data ()); 2827 if (el_mode > 0) 2828 { 2829 for (int i = 0; i < 3; i++) 2830 cb[i] = as * ecolor(i); 2831 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2832 2833 for (int i = 0; i < 3; i++) 2834 cb[i] = ds * ecolor(i); 2835 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2836 2837 for (int i = 0; i < 3; i++) 2838 cb[i] = ss * (scr + (1-scr) * ecolor(i)); 2839 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2840 } 2841 } 2842 2843 if ((el_mode > 0) && do_lighting) 2844 m_glfcns.glEnable (GL_LIGHTING); 2845 m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) 2846 ? GL_SMOOTH : GL_FLAT); 2847 2848 set_linestyle (props.get_linestyle (), false, 2849 props.get_linewidth ()); 2850 set_linewidth (props.get_linewidth ()); 2851 set_linecap ("butt"); 2852 set_linejoin ("miter"); 2853 2854 // Mesh along Y-axis 2855 2856 if (props.meshstyle_is ("both") || props.meshstyle_is ("column")) 2857 { 2858 for (int i = 0; i < zc; i++) 2859 { 2860 if (y_mat) 2861 { 2862 i1 = i-1; 2863 i2 = i; 2864 } 2865 2866 for (int j = 1; j < zr; j++) 2867 { 2868 if (clip(j-1,i) || clip(j,i)) 2869 continue; 2870 2871 if (ec_mode == FLAT) 2872 { 2873 // "flat" only needs color at lower-left vertex 2874 if (! math::isfinite (c(j-1,i))) 2875 continue; 2876 } 2877 else if (ec_mode == INTERP) 2878 { 2879 // "interp" needs valid color at both vertices 2880 if (! (math::isfinite (c(j-1, i)) && math::isfinite (c(j, i)))) 2881 continue; 2882 } 2883 2884 if (x_mat) 2885 { 2886 j1 = j-1; 2887 j2 = j; 2888 } 2889 2890 m_glfcns.glBegin (GL_LINES); 2891 2892 // Vertex 1 2893 if (ec_mode > 0) 2894 { 2895 for (int k = 0; k < 3; k++) 2896 cb[k] = c(j-1, i, k); 2897 m_glfcns.glColor3fv (cb); 2898 2899 if (el_mode > 0) 2900 { 2901 for (int k = 0; k < 3; k++) 2902 cb[k] *= as; 2903 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2904 2905 for (int k = 0; k < 3; k++) 2906 cb[k] = ds * c(j-1, i, k); 2907 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2908 2909 for (int k = 0; k < 3; k++) 2910 cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); 2911 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2912 } 2913 } 2914 if (el_mode > 0) 2915 { 2916 if (el_mode == GOURAUD) 2917 set_normal (bfl_mode, vn, j-1, i); 2918 else 2919 set_normal (bfl_mode, fn, j-1, std::min (i, zc-2)); 2920 } 2921 2922 m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); 2923 2924 // Vertex 2 2925 if (ec_mode == INTERP) 2926 { 2927 for (int k = 0; k < 3; k++) 2928 cb[k] = c(j, i, k); 2929 m_glfcns.glColor3fv (cb); 2930 2931 if (el_mode > 0) 2932 { 2933 for (int k = 0; k < 3; k++) 2934 cb[k] *= as; 2935 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 2936 2937 for (int k = 0; k < 3; k++) 2938 cb[k] = ds * c(j, i, k); 2939 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 2940 2941 for (int k = 0; k < 3; k++) 2942 cb[k] = ss * (scr + (1-scr) * c(j, i, k)); 2943 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 2944 } 2945 } 2946 if (el_mode == GOURAUD) 2947 set_normal (bfl_mode, vn, j, i); 2948 2949 m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); 2950 2951 m_glfcns.glEnd (); 2952 } 2953 } 2954 } 2955 2956 // Mesh along X-axis 2957 2958 if (props.meshstyle_is ("both") || props.meshstyle_is ("row")) 2959 { 2960 for (int j = 0; j < zr; j++) 2961 { 2962 if (x_mat) 2963 { 2964 j1 = j-1; 2965 j2 = j; 2966 } 2967 2968 for (int i = 1; i < zc; i++) 2969 { 2970 if (clip(j,i-1) || clip(j,i)) 2971 continue; 2972 2973 if (ec_mode == FLAT) 2974 { 2975 // "flat" only needs color at lower-left vertex 2976 if (! math::isfinite (c(j,i-1))) 2977 continue; 2978 } 2979 else if (ec_mode == INTERP) 2980 { 2981 // "interp" needs valid color at both vertices 2982 if (! (math::isfinite (c(j, i-1)) && math::isfinite (c(j, i)))) 2983 continue; 2984 } 2985 2986 if (y_mat) 2987 { 2988 i1 = i-1; 2989 i2 = i; 2990 } 2991 2992 m_glfcns.glBegin (GL_LINES); 2993 2994 // Vertex 1 2995 if (ec_mode > 0) 2996 { 2997 for (int k = 0; k < 3; k++) 2998 cb[k] = c(j, i-1, k); 2999 m_glfcns.glColor3fv (cb); 3000 3001 if (el_mode > 0) 3002 { 3003 for (int k = 0; k < 3; k++) 3004 cb[k] *= as; 3005 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 3006 3007 for (int k = 0; k < 3; k++) 3008 cb[k] = ds * c(j, i-1, k); 3009 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 3010 3011 for (int k = 0; k < 3; k++) 3012 cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); 3013 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 3014 } 3015 } 3016 if (el_mode > 0) 3017 { 3018 if (el_mode == GOURAUD) 3019 set_normal (bfl_mode, vn, j, i-1); 3020 else 3021 set_normal (bfl_mode, fn, std::min (j, zr-2), i-1); 3022 } 3023 3024 m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); 3025 3026 // Vertex 2 3027 if (ec_mode == INTERP) 3028 { 3029 for (int k = 0; k < 3; k++) 3030 cb[k] = c(j, i, k); 3031 m_glfcns.glColor3fv (cb); 3032 3033 if (el_mode > 0) 3034 { 3035 for (int k = 0; k < 3; k++) 3036 cb[k] *= as; 3037 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 3038 3039 for (int k = 0; k < 3; k++) 3040 cb[k] = ds * c(j, i, k); 3041 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 3042 3043 for (int k = 0; k < 3; k++) 3044 cb[k] = ss * (scr + (1-scr) * c(j, i, k)); 3045 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 3046 } 3047 } 3048 if (el_mode == GOURAUD) 3049 set_normal (bfl_mode, vn, j, i); 3050 3051 m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); 3052 3053 m_glfcns.glEnd (); 3054 } 3055 } 3056 } 3057 3058 set_linestyle ("-"); // Disable LineStipple 3059 set_linewidth (0.5f); 3060 3061 if ((el_mode > 0) && do_lighting) 3062 m_glfcns.glDisable (GL_LIGHTING); 3063 } 3064 else 3065 { 3066 // FIXME: implement transparency 3067 } 3068 } 3069 3070 if (! props.marker_is ("none") 3071 && ! (props.markeredgecolor_is ("none") 3072 && props.markerfacecolor_is ("none"))) 3073 { 3074 // FIXME: check how transparency should be handled in markers 3075 // FIXME: check what to do with marker facecolor set to auto 3076 // and facecolor set to none. 3077 3078 bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); 3079 bool do_face = draw_all || ! props.markerfacecolor_is ("none"); 3080 3081 Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : 3082 props.get_markeredgecolor_rgb ()); 3083 Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : 3084 props.get_markerfacecolor_rgb ()); 3085 Matrix cc (1, 3, 0.0); 3086 3087 if (mecolor.isempty () && props.markeredgecolor_is ("auto")) 3088 { 3089 mecolor = props.get_edgecolor_rgb (); 3090 do_edge = ! props.edgecolor_is ("none"); 3091 } 3092 3093 if (mfcolor.isempty () && props.markerfacecolor_is ("auto")) 3094 { 3095 mfcolor = props.get_facecolor_rgb (); 3096 do_face = ! props.facecolor_is ("none"); 3097 } 3098 3099 if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ()) 3100 c = props.get_color_data ().array_value (); 3101 3102 init_marker (props.get_marker (), props.get_markersize (), 3103 props.get_linewidth ()); 3104 3105 uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); 3106 uint8_t clip_ok = 0x40; 3107 3108 for (int i = 0; i < zc; i++) 3109 { 3110 if (y_mat) 3111 i1 = i; 3112 3113 for (int j = 0; j < zr; j++) 3114 { 3115 if (x_mat) 3116 j1 = j; 3117 3118 if ((clip_code (x(j1,i), y(j,i1), z(j,i)) & clip_mask) 3119 != clip_ok) 3120 continue; 3121 3122 if ((do_edge && mecolor.isempty ()) 3123 || (do_face && mfcolor.isempty ())) 3124 { 3125 if (! math::isfinite (c(j,i))) 3126 continue; // Skip NaNs in color data 3127 3128 for (int k = 0; k < 3; k++) 3129 cc(k) = c(j,i,k); 3130 } 3131 3132 Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) 3133 : Matrix ()); 3134 Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) 3135 : Matrix ()); 3136 3137 draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc); 3138 } 3139 } 3140 3141 end_marker (); 3142 } 3143 3144 #else 3145 3146 octave_unused_parameter (props); 3147 3148 // This shouldn't happen because construction of opengl_renderer 3149 // objects is supposed to be impossible if OpenGL is not available. 3150 3151 panic_impossible (); 3152 3153 #endif 3154 } 3155 3156 // FIXME: global optimization (rendering, data structures...), 3157 // there is probably a smarter/faster/less-memory-consuming way to do this. 3158 void draw_patch(const patch::properties & props)3159 opengl_renderer::draw_patch (const patch::properties& props) 3160 { 3161 #if defined (HAVE_OPENGL) 3162 3163 // Do not render if the patch has incoherent data 3164 std::string msg; 3165 if (props.has_bad_data (msg)) 3166 { 3167 warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); 3168 return; 3169 } 3170 3171 bool draw_all = selecting && props.pickableparts_is ("all"); 3172 const Matrix f = props.get_faces ().matrix_value (); 3173 const Matrix v = xform.scale (props.get_vertices ().matrix_value ()); 3174 Matrix c; 3175 Matrix a; 3176 double fa = 1.0; 3177 3178 int nv = v.rows (); 3179 int nf = f.rows (); 3180 int fcmax = f.columns (); 3181 3182 bool has_z = (v.columns () > 2); 3183 bool has_facecolor = false; 3184 bool has_facealpha = false; 3185 3186 int fc_mode = ((props.facecolor_is ("none") 3187 || props.facecolor_is_rgb () || draw_all) ? 0 : 3188 (props.facecolor_is ("flat") ? 1 : 2)); 3189 int fl_mode = (props.facelighting_is ("none") ? 0 : 3190 (props.facelighting_is ("flat") ? 1 : 2)); 3191 int fa_mode = (props.facealpha_is_double () ? 0 : 3192 (props.facealpha_is ("flat") ? 1 : 2)); 3193 int ec_mode = ((props.edgecolor_is ("none") 3194 || props.edgecolor_is_rgb ()) ? 0 : 3195 (props.edgecolor_is ("flat") ? 1 : 2)); 3196 int el_mode = (props.edgelighting_is ("none") ? 0 : 3197 (props.edgelighting_is ("flat") ? 1 : 2)); 3198 int ea_mode = (props.edgealpha_is_double () ? 0 : 3199 (props.edgealpha_is ("flat") ? 1 : 2)); 3200 int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : 3201 (props.backfacelighting_is ("reverselit") ? 1 : 2)); 3202 bool do_lighting = props.get_do_lighting (); 3203 3204 Matrix fcolor = props.get_facecolor_rgb (); 3205 Matrix ecolor = props.get_edgecolor_rgb (); 3206 3207 float as = props.get_ambientstrength (); 3208 float ds = props.get_diffusestrength (); 3209 float ss = props.get_specularstrength (); 3210 float se = props.get_specularexponent () * 5; // to fit Matlab 3211 float scr = props.get_specularcolorreflectance (); 3212 3213 const Matrix vn = props.get_vertexnormals ().matrix_value (); 3214 bool has_vertex_normals = (vn.rows () == nv); 3215 const Matrix fn = props.get_facenormals ().matrix_value (); 3216 bool has_face_normals = (fn.rows () == nf); 3217 3218 boolMatrix clip (1, nv, false); 3219 3220 if (has_z) 3221 for (int i = 0; i < nv; i++) 3222 clip(i) = is_nan_or_inf (v(i,0), v(i,1), v(i,2)); 3223 else 3224 for (int i = 0; i < nv; i++) 3225 clip(i) = is_nan_or_inf (v(i,0), v(i,1), 0); 3226 3227 boolMatrix clip_f (1, nf, false); 3228 Array<int> count_f (dim_vector (nf, 1), 0); 3229 3230 for (int i = 0; i < nf; i++) 3231 { 3232 bool fclip = false; 3233 int count = 0; 3234 3235 for (int j = 0; j < fcmax && ! math::isnan (f(i,j)); j++, count++) 3236 fclip = (fclip || clip(int (f(i,j) - 1))); 3237 3238 clip_f(i) = fclip; 3239 count_f(i) = count; 3240 } 3241 3242 if (draw_all || fc_mode > 0 || ec_mode > 0) 3243 { 3244 if (draw_all) 3245 c = Matrix (1, 3, 0.0); 3246 else 3247 c = props.get_color_data ().matrix_value (); 3248 3249 if (c.rows () == 1) 3250 { 3251 // Single color specifications, we can simplify a little bit 3252 3253 if (draw_all || fc_mode > 0) 3254 { 3255 fcolor = c; 3256 fc_mode = UNIFORM; 3257 } 3258 3259 if (draw_all || ec_mode > 0) 3260 { 3261 ecolor = c; 3262 ec_mode = UNIFORM; 3263 } 3264 3265 c = Matrix (); 3266 } 3267 else 3268 has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ())); 3269 } 3270 3271 if (fa_mode > 0 || ea_mode > 0) 3272 { 3273 // FIXME: retrieve alpha data from patch object 3274 //a = props.get_alpha_data (); 3275 has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ())); 3276 } 3277 3278 if (fa_mode == 0) 3279 fa = props.get_facealpha_double (); 3280 3281 octave_idx_type fr = f.rows (); 3282 std::vector<vertex_data> vdata (f.numel ()); 3283 3284 for (int i = 0; i < nf; i++) 3285 for (int j = 0; j < count_f(i); j++) 3286 { 3287 int idx = int (f(i,j) - 1); 3288 3289 Matrix vv (1, 3, 0.0); 3290 Matrix cc; 3291 Matrix vnn (1, 3, 0.0); 3292 Matrix fnn (1, 3, 0.0); 3293 double aa = 1.0; 3294 3295 vv(0) = v(idx,0); vv(1) = v(idx,1); 3296 if (has_z) 3297 vv(2) = v(idx,2); 3298 if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals) 3299 { 3300 double dir = 1.0; 3301 if (bfl_mode > 0) 3302 dir = ((fn(i,0) * view_vector(0) 3303 + fn(i,1) * view_vector(1) 3304 + fn(i,2) * view_vector(2) < 0) 3305 ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); 3306 fnn(0) = dir * fn(i,0); 3307 fnn(1) = dir * fn(i,1); 3308 fnn(2) = dir * fn(i,2); 3309 } 3310 if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals) 3311 { 3312 double dir = 1.0; 3313 if (bfl_mode > 0) 3314 dir = ((vn(idx,0) * view_vector(0) 3315 + vn(idx,1) * view_vector(1) 3316 + vn(idx,2) * view_vector(2) < 0) 3317 ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); 3318 vnn(0) = dir * vn(idx,0); 3319 vnn(1) = dir * vn(idx,1); 3320 vnn(2) = dir * vn(idx,2); 3321 } 3322 if (c.numel () > 0) 3323 { 3324 cc.resize (1, 3); 3325 if (has_facecolor) 3326 cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2); 3327 else 3328 cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2); 3329 } 3330 if (fa_mode == 0) 3331 aa = fa; 3332 else if (a.numel () > 0) 3333 { 3334 if (has_facealpha) 3335 aa = a(i); 3336 else 3337 aa = a(idx); 3338 } 3339 3340 vdata[i+j*fr] = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr); 3341 } 3342 3343 if (fl_mode > 0 || el_mode > 0) 3344 m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); 3345 3346 if (draw_all || ! props.facecolor_is ("none")) 3347 { 3348 // FIXME: adapt to double-radio property 3349 if (fa_mode == 0) 3350 { 3351 if (fc_mode == UNIFORM) 3352 { 3353 m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); 3354 if (fl_mode > 0) 3355 { 3356 float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; 3357 3358 for (int i = 0; i < 3; i++) 3359 cb[i] = as * fcolor(i); 3360 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 3361 3362 for (int i = 0; i < 3; i++) 3363 cb[i] = ds * fcolor(i); 3364 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 3365 3366 for (int i = 0; i < 3; i++) 3367 cb[i] = ss * (scr + (1-scr) * fcolor(i)); 3368 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 3369 } 3370 } 3371 3372 if ((fl_mode > 0) && do_lighting) 3373 m_glfcns.glEnable (GL_LIGHTING); 3374 3375 // NOTE: Push filled part of patch backwards to avoid Z-fighting 3376 // with tessellator outline. A value of 1.0 seems to work fine. 3377 // Value can't be too large or the patch will be pushed below the 3378 // axes planes at +2.5. 3379 patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0); 3380 3381 std::vector<octave_idx_type>::const_iterator it; 3382 octave_idx_type i_start, i_end; 3383 3384 for (int i = 0; i < nf; i++) 3385 { 3386 if (clip_f(i)) 3387 continue; 3388 3389 bool is_non_planar = false; 3390 if (props.coplanar_last_idx.size () > 0 3391 && props.coplanar_last_idx[i].size () > 1) 3392 { 3393 is_non_planar = true; 3394 it = props.coplanar_last_idx[i].end (); 3395 it--; 3396 } 3397 3398 // loop over planar subsets of face 3399 do 3400 { 3401 if (is_non_planar) 3402 { 3403 i_end = *it; 3404 if (it == props.coplanar_last_idx[i].begin ()) 3405 i_start = 0; 3406 else 3407 { 3408 it--; 3409 i_start = *it - 1; 3410 } 3411 } 3412 else 3413 { 3414 i_end = count_f(i) - 1; 3415 i_start = 0; 3416 } 3417 3418 tess.begin_polygon (true); 3419 tess.begin_contour (); 3420 3421 // Add vertices in reverse order for Matlab compatibility 3422 for (int j = i_end; j > i_start; j--) 3423 { 3424 vertex_data::vertex_data_rep *vv 3425 = vdata[i+j*fr].get_rep (); 3426 3427 tess.add_vertex (vv->coords.fortran_vec (), vv); 3428 } 3429 3430 if (count_f(i) > 0) 3431 { 3432 vertex_data::vertex_data_rep *vv = vdata[i].get_rep (); 3433 3434 if (fc_mode == FLAT) 3435 { 3436 // For "flat" shading, use color of 1st vertex. 3437 Matrix col = vv->color; 3438 3439 if (col.numel () == 3) 3440 { 3441 m_glfcns.glColor4d (col(0), col(1), col(2), fa); 3442 if (fl_mode > 0) 3443 { 3444 float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; 3445 3446 for (int k = 0; k < 3; k++) 3447 cb[k] = (vv->ambient * col(k)); 3448 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 3449 3450 for (int k = 0; k < 3; k++) 3451 cb[k] = (vv->diffuse * col(k)); 3452 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 3453 3454 for (int k = 0; k < 3; k++) 3455 cb[k] = vv->specular * 3456 (vv->specular_color_refl 3457 + (1-vv->specular_color_refl) * 3458 col(k)); 3459 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 3460 } 3461 } 3462 } 3463 3464 tess.add_vertex (vv->coords.fortran_vec (), vv); 3465 } 3466 3467 tess.end_contour (); 3468 tess.end_polygon (); 3469 } while (i_start > 0); 3470 } 3471 3472 if ((fl_mode > 0) && do_lighting) 3473 m_glfcns.glDisable (GL_LIGHTING); 3474 } 3475 else 3476 { 3477 // FIXME: implement flat and interp transparency 3478 } 3479 } 3480 3481 if (draw_all 3482 || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))) 3483 { 3484 // FIXME: adapt to double-radio property 3485 if (props.get_edgealpha_double () == 1) 3486 { 3487 if (ec_mode == UNIFORM) 3488 { 3489 m_glfcns.glColor3dv (ecolor.data ()); 3490 if (el_mode > 0) 3491 { 3492 // edge lighting only uses ambient light 3493 float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; 3494 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); 3495 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); 3496 3497 for (int i = 0; i < 3; i++) 3498 cb[i] = (as * ecolor(i)); 3499 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); 3500 } 3501 } 3502 3503 if ((el_mode > 0) && do_lighting) 3504 m_glfcns.glEnable (GL_LIGHTING); 3505 3506 double linewidth = props.get_linewidth (); 3507 set_linestyle (props.get_linestyle (), false, linewidth); 3508 set_linewidth (linewidth); 3509 set_linecap ("butt"); 3510 set_linejoin ("miter"); 3511 3512 // NOTE: patch contour cannot be offset. Offset must occur with the 3513 // filled portion of the patch above. The tessellator uses 3514 // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL 3515 // automatically sets the glType to GL_LINE_LOOP. This primitive is 3516 // not supported by glPolygonOffset which is used to do Z offsets. 3517 patch_tessellator tess (this, ec_mode, el_mode, false); 3518 3519 for (int i = 0; i < nf; i++) 3520 { 3521 bool is_non_planar = false; 3522 if (props.coplanar_last_idx.size () > 0 3523 && props.coplanar_last_idx[i].size () > 1) 3524 is_non_planar = true; 3525 if (clip_f(i) || is_non_planar) 3526 { 3527 // This is an unclosed contour or a non-planar face. 3528 // Draw it as a line. 3529 bool flag = false; 3530 3531 m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) 3532 ? GL_SMOOTH : GL_FLAT); 3533 3534 // Add vertices in reverse order for Matlab compatibility 3535 for (int j = count_f(i)-1; j >= 0; j--) 3536 { 3537 if (! clip(int (f(i,j) - 1))) 3538 { 3539 vertex_data::vertex_data_rep *vv 3540 = vdata[i+j*fr].get_rep (); 3541 const Matrix m = vv->coords; 3542 if (! flag) 3543 { 3544 flag = true; 3545 m_glfcns.glBegin (GL_LINE_STRIP); 3546 } 3547 if (ec_mode != UNIFORM) 3548 { 3549 Matrix col = vv->color; 3550 3551 if (col.numel () == 3) 3552 m_glfcns.glColor3dv (col.data ()); 3553 } 3554 m_glfcns.glVertex3d (m(0), m(1), m(2)); 3555 } 3556 else if (flag) 3557 { 3558 flag = false; 3559 m_glfcns.glEnd (); 3560 } 3561 } 3562 // Do loop body with vertex N to "close" GL_LINE_STRIP 3563 // from vertex 0 to vertex N. 3564 int j = count_f(i)-1; 3565 if (flag && ! clip(int (f(i,j) - 1))) 3566 { 3567 vertex_data::vertex_data_rep *vv 3568 = vdata[i+j*fr].get_rep (); 3569 const Matrix m = vv->coords; 3570 if (ec_mode != UNIFORM) 3571 { 3572 Matrix col = vv->color; 3573 3574 if (col.numel () == 3) 3575 m_glfcns.glColor3dv (col.data ()); 3576 } 3577 m_glfcns.glVertex3d (m(0), m(1), m(2)); 3578 } 3579 3580 if (flag) 3581 m_glfcns.glEnd (); 3582 } 3583 else // Normal edge contour drawn with tessellator 3584 { 3585 tess.begin_polygon (false); 3586 tess.begin_contour (); 3587 3588 for (int j = count_f(i)-1; j >= 0; j--) 3589 { 3590 vertex_data::vertex_data_rep *vv 3591 = vdata[i+j*fr].get_rep (); 3592 tess.add_vertex (vv->coords.fortran_vec (), vv); 3593 } 3594 3595 tess.end_contour (); 3596 tess.end_polygon (); 3597 } 3598 } 3599 3600 set_linestyle ("-"); // Disable LineStipple 3601 set_linewidth (0.5f); 3602 3603 if ((el_mode > 0) && do_lighting) 3604 m_glfcns.glDisable (GL_LIGHTING); 3605 } 3606 else 3607 { 3608 // FIXME: implement transparency 3609 } 3610 } 3611 3612 if (! props.marker_is ("none") 3613 && ! (props.markeredgecolor_is ("none") 3614 && props.markerfacecolor_is ("none"))) 3615 { 3616 bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); 3617 bool do_face = draw_all || ! props.markerfacecolor_is ("none"); 3618 3619 Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : 3620 props.get_markeredgecolor_rgb ()); 3621 Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : 3622 props.get_markerfacecolor_rgb ()); 3623 3624 bool has_markerfacecolor = draw_all || false; 3625 3626 if ((mecolor.isempty () && ! props.markeredgecolor_is ("none")) 3627 || (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))) 3628 { 3629 Matrix mc = props.get_color_data ().matrix_value (); 3630 3631 if (mc.rows () == 1) 3632 { 3633 // Single color specifications, we can simplify a little bit 3634 if (mfcolor.isempty () && ! props.markerfacecolor_is ("none")) 3635 mfcolor = mc; 3636 3637 if (mecolor.isempty () && ! props.markeredgecolor_is ("none")) 3638 mecolor = mc; 3639 } 3640 else 3641 { 3642 if (c.isempty ()) 3643 c = props.get_color_data ().matrix_value (); 3644 has_markerfacecolor = ((c.numel () > 0) 3645 && (c.rows () == f.rows ())); 3646 } 3647 } 3648 3649 init_marker (props.get_marker (), props.get_markersize (), 3650 props.get_linewidth ()); 3651 3652 uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); 3653 uint8_t clip_ok = 0x40; 3654 3655 for (int i = 0; i < nf; i++) 3656 for (int j = 0; j < count_f(i); j++) 3657 { 3658 int idx = int (f(i,j) - 1); 3659 3660 if ((clip_code (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0)) 3661 & clip_mask) != clip_ok) 3662 continue; 3663 3664 Matrix cc; 3665 if (c.numel () > 0) 3666 { 3667 cc.resize (1, 3); 3668 if (has_markerfacecolor) 3669 cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2); 3670 else 3671 cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2); 3672 } 3673 3674 Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) 3675 : Matrix ()); 3676 Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) 3677 : Matrix ()); 3678 3679 draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc); 3680 } 3681 3682 end_marker (); 3683 } 3684 3685 #else 3686 3687 octave_unused_parameter (props); 3688 3689 // This shouldn't happen because construction of opengl_renderer 3690 // objects is supposed to be impossible if OpenGL is not available. 3691 3692 panic_impossible (); 3693 3694 #endif 3695 } 3696 3697 void draw_light(const light::properties & props)3698 opengl_renderer::draw_light (const light::properties& props) 3699 { 3700 #if defined (HAVE_OPENGL) 3701 3702 // enable light source 3703 m_glfcns.glEnable (m_current_light); 3704 3705 // light position 3706 float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local 3707 Matrix lpos = props.get_position ().matrix_value (); 3708 for (int i = 0; i < 3; i++) 3709 pos[i] = lpos(i); 3710 if (props.style_is ("local")) 3711 pos[3] = 1; 3712 m_glfcns.glLightfv (m_current_light, GL_POSITION, pos); 3713 3714 // light color 3715 float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning) 3716 Matrix lcolor = props.get_color ().matrix_value (); 3717 for (int i = 0; i < 3; i++) 3718 col[i] = lcolor(i); 3719 m_glfcns.glLightfv (m_current_light, GL_DIFFUSE, col); 3720 m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col); 3721 3722 #else 3723 3724 octave_unused_parameter (props); 3725 3726 // This shouldn't happen because construction of opengl_renderer 3727 // objects is supposed to be impossible if OpenGL is not available. 3728 3729 panic_impossible (); 3730 3731 #endif 3732 } 3733 3734 void draw_hggroup(const hggroup::properties & props)3735 opengl_renderer::draw_hggroup (const hggroup::properties& props) 3736 { 3737 draw (props.get_children ()); 3738 } 3739 3740 void draw_text(const text::properties & props)3741 opengl_renderer::draw_text (const text::properties& props) 3742 { 3743 #if defined (HAVE_OPENGL) 3744 3745 if (props.get_string ().isempty () || props.color_is ("none")) 3746 return; 3747 3748 Matrix pos = xform.scale (props.get_data_position ()); 3749 3750 // Handle clipping manually when drawing text background 3751 if (! props.is_clipping () 3752 || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40)) 3753 { 3754 set_clipping (false); 3755 draw_text_background (props); 3756 set_clipping (props.is_clipping ()); 3757 } 3758 3759 set_font (props); 3760 3761 const Matrix bbox = props.get_extent_matrix (); 3762 3763 bool blend = m_glfcns.glIsEnabled (GL_BLEND); 3764 3765 m_glfcns.glEnable (GL_BLEND); 3766 m_glfcns.glEnable (GL_ALPHA_TEST); 3767 m_glfcns.glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0); 3768 m_glfcns.glBitmap (0, 0, 0, 0, bbox(0), bbox(1), nullptr); 3769 m_glfcns.glDrawPixels (bbox(2), bbox(3), GL_RGBA, GL_UNSIGNED_BYTE, 3770 props.get_pixels ().data ()); 3771 m_glfcns.glDisable (GL_ALPHA_TEST); 3772 if (! blend) 3773 m_glfcns.glDisable (GL_BLEND); 3774 3775 #else 3776 3777 octave_unused_parameter (props); 3778 3779 // This shouldn't happen because construction of opengl_renderer 3780 // objects is supposed to be impossible if OpenGL is not available. 3781 3782 panic_impossible (); 3783 3784 #endif 3785 } 3786 3787 void draw_text_background(const text::properties & props,bool do_rotate)3788 opengl_renderer::draw_text_background (const text::properties& props, 3789 bool do_rotate) 3790 { 3791 #if defined (HAVE_OPENGL) 3792 3793 Matrix bgcol = props.get_backgroundcolor_rgb (); 3794 Matrix ecol = props.get_edgecolor_rgb (); 3795 3796 if (bgcol.isempty () && ecol.isempty ()) 3797 return; 3798 3799 Matrix pos = props.get_data_position (); 3800 ColumnVector pixpos = get_transform ().transform (pos(0), pos(1), 3801 pos(2), true); 3802 3803 // Save current transform matrices and set orthogonal window coordinates 3804 m_glfcns.glMatrixMode (GL_PROJECTION); 3805 m_glfcns.glPushMatrix (); 3806 m_glfcns.glLoadIdentity (); 3807 3808 Matrix vp = get_viewport_scaled (); 3809 m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2); 3810 m_glfcns.glMatrixMode (GL_MODELVIEW); 3811 m_glfcns.glPushMatrix (); 3812 m_glfcns.glLoadIdentity (); 3813 3814 // Translate coordinates so that the text anchor is (0,0) 3815 m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); 3816 3817 // FIXME: Only multiples of 90° are handled by the text renderer. 3818 // Handle others here. 3819 double rotation = props.get_rotation (); 3820 3821 if (do_rotate && rotation != 0.0 && rotation != 90.0 3822 && rotation != 180.0 && rotation != 270.0) 3823 m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); 3824 3825 double m = points_to_pixels (props.get_margin ()); 3826 const Matrix bbox = props.get_extent_matrix (); 3827 double x0 = bbox (0) / m_devpixratio - m; 3828 double x1 = x0 + bbox(2) / m_devpixratio + 2 * m; 3829 double y0 = -(bbox (1) / m_devpixratio - m); 3830 double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m); 3831 3832 if (! bgcol.isempty ()) 3833 { 3834 m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2)); 3835 3836 bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST); 3837 if (depth_test) 3838 set_polygon_offset (true, 4.0); 3839 3840 m_glfcns.glBegin (GL_QUADS); 3841 m_glfcns.glVertex2d (x0, y0); 3842 m_glfcns.glVertex2d (x1, y0); 3843 m_glfcns.glVertex2d (x1, y1); 3844 m_glfcns.glVertex2d (x0, y1); 3845 m_glfcns.glEnd (); 3846 3847 if (depth_test) 3848 set_polygon_offset (false); 3849 } 3850 3851 if (! ecol.isempty ()) 3852 { 3853 m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2)); 3854 3855 set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); 3856 set_linewidth (props.get_linewidth ()); 3857 3858 m_glfcns.glBegin (GL_LINE_STRIP); 3859 m_glfcns.glVertex2d (x0, y0); 3860 m_glfcns.glVertex2d (x1, y0); 3861 m_glfcns.glVertex2d (x1, y1); 3862 m_glfcns.glVertex2d (x0, y1); 3863 m_glfcns.glVertex2d (x0, y0); 3864 m_glfcns.glEnd (); 3865 3866 set_linestyle ("-"); 3867 } 3868 3869 // Restore previous coordinate system 3870 m_glfcns.glPopMatrix(); 3871 m_glfcns.glMatrixMode (GL_PROJECTION); 3872 m_glfcns.glPopMatrix(); 3873 3874 #else 3875 3876 octave_unused_parameter (props); 3877 octave_unused_parameter (do_rotate); 3878 3879 // This shouldn't happen because construction of opengl_renderer 3880 // objects is supposed to be impossible if OpenGL is not available. 3881 3882 panic_impossible (); 3883 3884 #endif 3885 } 3886 3887 void draw_image(const image::properties & props)3888 opengl_renderer::draw_image (const image::properties& props) 3889 { 3890 #if defined (HAVE_OPENGL) 3891 3892 octave_value cdata = props.get_color_data (); 3893 dim_vector dv (cdata.dims ()); 3894 int h = dv(0); 3895 int w = dv(1); 3896 double x0, x1, y0, y1; 3897 3898 Matrix x = props.get_xdata ().matrix_value (); 3899 double dx = 1.0; 3900 if (w > 1) 3901 dx = (x(1) - x(0)) / (w - 1); 3902 3903 x0 = x(0)-dx/2; 3904 x1 = x(1)+dx/2; 3905 3906 Matrix y = props.get_ydata ().matrix_value (); 3907 double dy = 1.0; 3908 if (h > 1) 3909 dy = (y(1) - y(0)) / (h - 1); 3910 3911 y0 = y(0)-dy/2; 3912 y1 = y(1)+dy/2; 3913 3914 // Expect RGB data 3915 if (dv.ndims () == 3 && dv(2) == 3) 3916 { 3917 opengl_texture tex = opengl_texture::create (m_glfcns, cdata); 3918 if (tex.is_valid ()) 3919 { 3920 m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0); 3921 3922 m_glfcns.glEnable (GL_TEXTURE_2D); 3923 3924 m_glfcns.glBegin (GL_QUADS); 3925 3926 tex.tex_coord (0.0, 0.0); 3927 m_glfcns.glVertex3d (x0, y0, 0.0); 3928 3929 tex.tex_coord (1.0, 0.0); 3930 m_glfcns.glVertex3d (x1, y0, 0.0); 3931 3932 tex.tex_coord (1.0, 1.0); 3933 m_glfcns.glVertex3d (x1, y1, 0.0); 3934 3935 tex.tex_coord (0.0, 1.0); 3936 m_glfcns.glVertex3d (x0, y1, 0.0); 3937 3938 m_glfcns.glEnd (); 3939 m_glfcns.glDisable (GL_TEXTURE_2D); 3940 } 3941 } 3942 else 3943 warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); 3944 3945 #else 3946 3947 octave_unused_parameter (props); 3948 3949 // This shouldn't happen because construction of opengl_renderer 3950 // objects is supposed to be impossible if OpenGL is not available. 3951 3952 panic_impossible (); 3953 3954 #endif 3955 } 3956 draw(const Matrix & hlist,bool toplevel)3957 void opengl_renderer::draw (const Matrix& hlist, bool toplevel) 3958 { 3959 int len = hlist.numel (); 3960 3961 gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw"); 3962 3963 for (int i = len-1; i >= 0; i--) 3964 { 3965 graphics_object obj = gh_mgr.get_object (hlist(i)); 3966 3967 if (obj) 3968 draw (obj, toplevel); 3969 } 3970 } 3971 3972 void set_viewport(int w,int h)3973 opengl_renderer::set_viewport (int w, int h) 3974 { 3975 #if defined (HAVE_OPENGL) 3976 3977 m_glfcns.glViewport (0, 0, w, h); 3978 3979 #else 3980 3981 octave_unused_parameter (w); 3982 octave_unused_parameter (h); 3983 3984 // This shouldn't happen because construction of opengl_renderer 3985 // objects is supposed to be impossible if OpenGL is not available. 3986 3987 panic_impossible (); 3988 3989 #endif 3990 } 3991 3992 Matrix get_viewport_scaled(void) const3993 opengl_renderer::get_viewport_scaled (void) const 3994 { 3995 Matrix retval (1, 4, 0.0); 3996 3997 #if defined (HAVE_OPENGL) 3998 #if defined (HAVE_FRAMEWORK_OPENGL) 3999 GLint vp[4]; 4000 #else 4001 int vp[4]; 4002 #endif 4003 4004 m_glfcns.glGetIntegerv (GL_VIEWPORT, vp); 4005 4006 for (int i = 0; i < 4; i++) 4007 retval(i) = static_cast<double> (vp[i]) / m_devpixratio; 4008 4009 #else 4010 4011 // This shouldn't happen because construction of opengl_renderer 4012 // objects is supposed to be impossible if OpenGL is not available. 4013 4014 panic_impossible (); 4015 4016 #endif 4017 4018 return retval; 4019 } 4020 4021 void set_color(const Matrix & c)4022 opengl_renderer::set_color (const Matrix& c) 4023 { 4024 #if defined (HAVE_OPENGL) 4025 4026 m_glfcns.glColor3dv (c.data ()); 4027 4028 if (! c.isempty ()) 4029 txt_renderer.set_color (c); 4030 4031 #else 4032 4033 octave_unused_parameter (c); 4034 4035 // This shouldn't happen because construction of opengl_renderer 4036 // objects is supposed to be impossible if OpenGL is not available. 4037 4038 panic_impossible (); 4039 4040 #endif 4041 } 4042 4043 void set_font(const base_properties & props)4044 opengl_renderer::set_font (const base_properties& props) 4045 { 4046 bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on"; 4047 txt_renderer.set_anti_aliasing (do_anti_alias); 4048 txt_renderer.set_font (props.get ("fontname").string_value (), 4049 props.get ("fontweight").string_value (), 4050 props.get ("fontangle").string_value (), 4051 props.get ("__fontsize_points__").double_value () 4052 * m_devpixratio); 4053 } 4054 4055 void set_polygon_offset(bool on,float offset)4056 opengl_renderer::set_polygon_offset (bool on, float offset) 4057 { 4058 #if defined (HAVE_OPENGL) 4059 4060 if (on) 4061 { 4062 m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL); 4063 m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE); 4064 m_glfcns.glPolygonOffset (offset, offset); 4065 } 4066 else 4067 { 4068 m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL); 4069 m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE); 4070 } 4071 4072 #else 4073 4074 octave_unused_parameter (on); 4075 octave_unused_parameter (offset); 4076 4077 // This shouldn't happen because construction of opengl_renderer 4078 // objects is supposed to be impossible if OpenGL is not available. 4079 4080 panic_impossible (); 4081 4082 #endif 4083 } 4084 4085 void set_linewidth(float w)4086 opengl_renderer::set_linewidth (float w) 4087 { 4088 #if defined (HAVE_OPENGL) 4089 // Measure LineWidth in points. See bug #53056. 4090 m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio); 4091 4092 #else 4093 4094 octave_unused_parameter (w); 4095 4096 // This shouldn't happen because construction of opengl_renderer 4097 // objects is supposed to be impossible if OpenGL is not available. 4098 4099 panic_impossible (); 4100 4101 #endif 4102 } 4103 4104 void set_linestyle(const std::string & s,bool use_stipple,double linewidth)4105 opengl_renderer::set_linestyle (const std::string& s, bool use_stipple, 4106 double linewidth) 4107 { 4108 #if defined (HAVE_OPENGL) 4109 // Measure LineWidth in points. See bug #53056. 4110 int factor = math::round (points_to_pixels (linewidth) * m_devpixratio); 4111 if (factor < 1) 4112 factor = 1; 4113 4114 uint16_t pattern = 0xFFFF; 4115 4116 bool solid = false; 4117 4118 if (s == "-") 4119 solid = true; 4120 else if (s == ":") 4121 { 4122 if (factor > 1) 4123 pattern = 0x5555; 4124 else 4125 pattern = 0x1111; 4126 } 4127 else if (s == "--") 4128 { 4129 if (factor > 1) 4130 pattern = 0x0F0F; 4131 else 4132 pattern = 0x01FF; 4133 } 4134 else if (s == "-.") 4135 { 4136 if (factor > 1) 4137 pattern = 0x6F6F; 4138 else 4139 pattern = 0x18FF; 4140 } 4141 else 4142 pattern = 0x0000; 4143 4144 m_glfcns.glLineStipple (factor, pattern); 4145 4146 if (solid && ! use_stipple) 4147 m_glfcns.glDisable (GL_LINE_STIPPLE); 4148 else 4149 m_glfcns.glEnable (GL_LINE_STIPPLE); 4150 4151 #else 4152 4153 octave_unused_parameter (s); 4154 octave_unused_parameter (use_stipple); 4155 octave_unused_parameter (linewidth); 4156 4157 // This shouldn't happen because construction of opengl_renderer 4158 // objects is supposed to be impossible if OpenGL is not available. 4159 4160 panic_impossible (); 4161 4162 #endif 4163 } 4164 4165 void set_clipbox(double x1,double x2,double y1,double y2,double z1,double z2)4166 opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2, 4167 double z1, double z2) 4168 { 4169 #if defined (HAVE_OPENGL) 4170 4171 double dx = (x2-x1); 4172 double dy = (y2-y1); 4173 double dz = (z2-z1); 4174 4175 x1 -= 0.001*dx; x2 += 0.001*dx; 4176 y1 -= 0.001*dy; y2 += 0.001*dy; 4177 z1 -= 0.001*dz; z2 += 0.001*dz; 4178 4179 ColumnVector p (4, 0.0); 4180 4181 p(0) = -1; p(3) = x2; 4182 m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ()); 4183 p(0) = 1; p(3) = -x1; 4184 m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ()); 4185 p(0) = 0; p(1) = -1; p(3) = y2; 4186 m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ()); 4187 p(1) = 1; p(3) = -y1; 4188 m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ()); 4189 p(1) = 0; p(2) = -1; p(3) = z2; 4190 m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ()); 4191 p(2) = 1; p(3) = -z1; 4192 m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ()); 4193 4194 xmin = x1; xmax = x2; 4195 ymin = y1; ymax = y2; 4196 zmin = z1; zmax = z2; 4197 4198 #else 4199 4200 octave_unused_parameter (x1); 4201 octave_unused_parameter (x2); 4202 octave_unused_parameter (y1); 4203 octave_unused_parameter (y2); 4204 octave_unused_parameter (z1); 4205 octave_unused_parameter (z2); 4206 4207 // This shouldn't happen because construction of opengl_renderer 4208 // objects is supposed to be impossible if OpenGL is not available. 4209 4210 panic_impossible (); 4211 4212 #endif 4213 } 4214 4215 void set_clipping(bool enable)4216 opengl_renderer::set_clipping (bool enable) 4217 { 4218 #if defined (HAVE_OPENGL) 4219 4220 bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE); 4221 4222 if (enable != has_clipping) 4223 { 4224 if (enable) 4225 for (int i = 0; i < 6; i++) 4226 m_glfcns.glEnable (GL_CLIP_PLANE0+i); 4227 else 4228 for (int i = 0; i < 6; i++) 4229 m_glfcns.glDisable (GL_CLIP_PLANE0+i); 4230 } 4231 4232 #else 4233 4234 octave_unused_parameter (enable); 4235 4236 // This shouldn't happen because construction of opengl_renderer 4237 // objects is supposed to be impossible if OpenGL is not available. 4238 4239 panic_impossible (); 4240 4241 #endif 4242 } 4243 4244 void init_marker(const std::string & m,double size,float width)4245 opengl_renderer::init_marker (const std::string& m, double size, float width) 4246 { 4247 #if defined (HAVE_OPENGL) 4248 m_glfcns.glMatrixMode (GL_PROJECTION); 4249 m_glfcns.glPushMatrix (); 4250 m_glfcns.glLoadIdentity (); 4251 4252 Matrix vp = get_viewport_scaled (); 4253 m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2); 4254 m_glfcns.glMatrixMode (GL_MODELVIEW); 4255 m_glfcns.glPushMatrix (); 4256 4257 set_clipping (false); 4258 set_linewidth (width); 4259 4260 marker_id = make_marker_list (m, size, false); 4261 filled_marker_id = make_marker_list (m, size, true); 4262 4263 #else 4264 4265 octave_unused_parameter (m); 4266 octave_unused_parameter (size); 4267 octave_unused_parameter (width); 4268 4269 // This shouldn't happen because construction of opengl_renderer 4270 // objects is supposed to be impossible if OpenGL is not available. 4271 4272 panic_impossible (); 4273 4274 #endif 4275 } 4276 4277 void end_marker(void)4278 opengl_renderer::end_marker (void) 4279 { 4280 #if defined (HAVE_OPENGL) 4281 4282 m_glfcns.glDeleteLists (marker_id, 1); 4283 m_glfcns.glDeleteLists (filled_marker_id, 1); 4284 4285 m_glfcns.glMatrixMode (GL_MODELVIEW); 4286 m_glfcns.glPopMatrix (); 4287 m_glfcns.glMatrixMode (GL_PROJECTION); 4288 m_glfcns.glPopMatrix (); 4289 set_linewidth (0.5f); 4290 4291 #else 4292 4293 // This shouldn't happen because construction of opengl_renderer 4294 // objects is supposed to be impossible if OpenGL is not available. 4295 4296 panic_impossible (); 4297 4298 #endif 4299 } 4300 4301 void draw_marker(double x,double y,double z,const Matrix & lc,const Matrix & fc)4302 opengl_renderer::draw_marker (double x, double y, double z, 4303 const Matrix& lc, const Matrix& fc) 4304 { 4305 #if defined (HAVE_OPENGL) 4306 4307 ColumnVector tmp = xform.transform (x, y, z, false); 4308 4309 m_glfcns.glLoadIdentity (); 4310 m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2)); 4311 4312 if (filled_marker_id > 0 && fc.numel () > 0) 4313 { 4314 m_glfcns.glColor3dv (fc.data ()); 4315 set_polygon_offset (true, -1.0); 4316 m_glfcns.glCallList (filled_marker_id); 4317 if (lc.numel () > 0) 4318 { 4319 m_glfcns.glColor3dv (lc.data ()); 4320 m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); 4321 m_glfcns.glEdgeFlag (GL_TRUE); 4322 set_polygon_offset (true, -2.0); 4323 m_glfcns.glCallList (filled_marker_id); 4324 m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); 4325 } 4326 set_polygon_offset (false); 4327 } 4328 else if (marker_id > 0 && lc.numel () > 0) 4329 { 4330 m_glfcns.glColor3dv (lc.data ()); 4331 m_glfcns.glCallList (marker_id); 4332 } 4333 4334 #else 4335 4336 octave_unused_parameter (x); 4337 octave_unused_parameter (y); 4338 octave_unused_parameter (z); 4339 octave_unused_parameter (lc); 4340 octave_unused_parameter (fc); 4341 4342 // This shouldn't happen because construction of opengl_renderer 4343 // objects is supposed to be impossible if OpenGL is not available. 4344 4345 panic_impossible (); 4346 4347 #endif 4348 } 4349 4350 void init_maxlights(void)4351 opengl_renderer::init_maxlights (void) 4352 { 4353 #if defined (HAVE_OPENGL) 4354 4355 // Check actual maximum number of lights possible 4356 if (m_max_lights == 0) 4357 { 4358 GLint max_lights; 4359 m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights); 4360 m_max_lights = max_lights; 4361 } 4362 4363 #else 4364 4365 // This shouldn't happen because construction of opengl_renderer 4366 // objects is supposed to be impossible if OpenGL is not available. 4367 4368 panic_impossible (); 4369 4370 #endif 4371 } 4372 4373 std::string get_string(unsigned int id) const4374 opengl_renderer::get_string (unsigned int id) const 4375 { 4376 #if defined (HAVE_OPENGL) 4377 4378 // This is kind of ugly, but glGetString returns a pointer to GLubyte 4379 // and there is no std::string constructor that matches. Is there a 4380 // better way? 4381 4382 std::ostringstream buf; 4383 4384 buf << m_glfcns.glGetString (static_cast<GLenum> (id)); 4385 4386 return std::string (buf.str ()); 4387 4388 #else 4389 4390 octave_unused_parameter (id); 4391 4392 // This shouldn't happen because construction of opengl_renderer 4393 // objects is supposed to be impossible if OpenGL is not available. 4394 4395 panic_impossible (); 4396 return std::string (); 4397 4398 #endif 4399 } 4400 4401 void set_normal(int bfl_mode,const NDArray & n,int j,int i)4402 opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i) 4403 { 4404 #if defined (HAVE_OPENGL) 4405 4406 double x = n(j,i,0); 4407 double y = n(j,i,1); 4408 double z = n(j,i,2); 4409 4410 double d = sqrt (x*x + y*y + z*z); 4411 4412 double dir = 1.0; 4413 4414 if (bfl_mode > 0) 4415 dir = ((x * view_vector(0) + y * view_vector(1) + z * view_vector(2) < 0) 4416 ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); 4417 4418 m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d); 4419 4420 #else 4421 4422 octave_unused_parameter (bfl_mode); 4423 octave_unused_parameter (n); 4424 octave_unused_parameter (j); 4425 octave_unused_parameter (i); 4426 4427 // This shouldn't happen because construction of opengl_renderer 4428 // objects is supposed to be impossible if OpenGL is not available. 4429 4430 panic_impossible (); 4431 4432 #endif 4433 } 4434 4435 double points_to_pixels(const double val) const4436 opengl_renderer::points_to_pixels (const double val) const 4437 { 4438 gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::points_to_pixels"); 4439 4440 // FIXME: Does making this static cause problems if figure is moved to a 4441 // 2nd monitor with a different value for "screenpixelsperinch"? 4442 static const double pix_per_pts = 4443 gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0; 4444 4445 double retval = val; 4446 4447 if (! m_printing) 4448 retval *= pix_per_pts; 4449 4450 return retval; 4451 } 4452 4453 unsigned int make_marker_list(const std::string & marker,double size,bool filled) const4454 opengl_renderer::make_marker_list (const std::string& marker, double size, 4455 bool filled) const 4456 { 4457 #if defined (HAVE_OPENGL) 4458 4459 char c = marker[0]; 4460 4461 if (filled && (c == '+' || c == 'x' || c == '*' || c == '.')) 4462 return 0; 4463 4464 unsigned int ID = m_glfcns.glGenLists (1); 4465 4466 // FIXME: See bug #53056 (measure LineWidth in points). 4467 double sz = points_to_pixels (size); 4468 4469 // constants for the * marker 4470 const double sqrt2d4 = 0.35355339059327; 4471 double tt = sz*sqrt2d4; 4472 4473 m_glfcns.glNewList (ID, GL_COMPILE); 4474 4475 switch (marker[0]) 4476 { 4477 case '+': 4478 m_glfcns.glBegin (GL_LINES); 4479 m_glfcns.glVertex2d (-sz/2, 0); 4480 m_glfcns.glVertex2d (sz/2, 0); 4481 m_glfcns.glVertex2d (0, -sz/2); 4482 m_glfcns.glVertex2d (0, sz/2); 4483 m_glfcns.glEnd (); 4484 break; 4485 case 'x': 4486 m_glfcns.glBegin (GL_LINES); 4487 m_glfcns.glVertex2d (-sz/2, -sz/2); 4488 m_glfcns.glVertex2d (sz/2, sz/2); 4489 m_glfcns.glVertex2d (-sz/2, sz/2); 4490 m_glfcns.glVertex2d (sz/2, -sz/2); 4491 m_glfcns.glEnd (); 4492 break; 4493 case '*': 4494 m_glfcns.glBegin (GL_LINES); 4495 m_glfcns.glVertex2d (-sz/2, 0); 4496 m_glfcns.glVertex2d (sz/2, 0); 4497 m_glfcns.glVertex2d (0, -sz/2); 4498 m_glfcns.glVertex2d (0, sz/2); 4499 m_glfcns.glVertex2d (-tt, -tt); 4500 m_glfcns.glVertex2d (+tt, +tt); 4501 m_glfcns.glVertex2d (-tt, +tt); 4502 m_glfcns.glVertex2d (+tt, -tt); 4503 m_glfcns.glEnd (); 4504 break; 4505 case '.': 4506 { 4507 // The dot marker is special and is drawn at 1/3rd the specified size 4508 4509 // Ensure that something is drawn even at very small markersizes 4510 if (sz > 0 && sz < 3) 4511 sz = 3; 4512 4513 int div = static_cast<int> (M_PI * sz / 12); 4514 if (! (div % 2)) 4515 div += 1; // ensure odd number for left/right symmetry 4516 div = std::max (div, 3); // ensure at least a few vertices are drawn 4517 double ang_step = M_PI / div; 4518 4519 m_glfcns.glBegin (GL_POLYGON); 4520 for (double ang = 0; ang < 2*M_PI; ang += ang_step) 4521 m_glfcns.glVertex2d (sz/6*cos (ang), sz/6*sin (ang)); 4522 m_glfcns.glEnd (); 4523 } 4524 break; 4525 case 's': 4526 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4527 m_glfcns.glVertex2d (-sz/2, -sz/2); 4528 m_glfcns.glVertex2d (-sz/2, sz/2); 4529 m_glfcns.glVertex2d (sz/2, sz/2); 4530 m_glfcns.glVertex2d (sz/2, -sz/2); 4531 m_glfcns.glEnd (); 4532 break; 4533 case 'o': 4534 { 4535 int div = static_cast<int> (M_PI * sz / 4); 4536 if (! (div % 2)) 4537 div += 1; // ensure odd number for left/right symmetry 4538 div = std::max (div, 5); // ensure at least a few vertices are drawn 4539 double ang_step = M_PI / div; 4540 4541 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4542 for (double ang = 0; ang < 2*M_PI; ang += ang_step) 4543 m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang)); 4544 m_glfcns.glEnd (); 4545 } 4546 break; 4547 case 'd': 4548 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4549 m_glfcns.glVertex2d (0, -sz/2); 4550 m_glfcns.glVertex2d (sz/2, 0); 4551 m_glfcns.glVertex2d (0, sz/2); 4552 m_glfcns.glVertex2d (-sz/2, 0); 4553 m_glfcns.glEnd (); 4554 break; 4555 case 'v': 4556 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4557 m_glfcns.glVertex2d (0, sz/2); 4558 m_glfcns.glVertex2d (sz/2, -sz/2); 4559 m_glfcns.glVertex2d (-sz/2, -sz/2); 4560 m_glfcns.glEnd (); 4561 break; 4562 case '^': 4563 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4564 m_glfcns.glVertex2d (0, -sz/2); 4565 m_glfcns.glVertex2d (-sz/2, sz/2); 4566 m_glfcns.glVertex2d (sz/2, sz/2); 4567 m_glfcns.glEnd (); 4568 break; 4569 case '>': 4570 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4571 m_glfcns.glVertex2d (sz/2, 0); 4572 m_glfcns.glVertex2d (-sz/2, sz/2); 4573 m_glfcns.glVertex2d (-sz/2, -sz/2); 4574 m_glfcns.glEnd (); 4575 break; 4576 case '<': 4577 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4578 m_glfcns.glVertex2d (-sz/2, 0); 4579 m_glfcns.glVertex2d (sz/2, -sz/2); 4580 m_glfcns.glVertex2d (sz/2, sz/2); 4581 m_glfcns.glEnd (); 4582 break; 4583 case 'p': 4584 { 4585 double ang, r, dr; 4586 dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02; 4587 4588 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4589 for (int i = 0; i < 2*5; i++) 4590 { 4591 ang = (-0.5 + double (i+1) / 5) * M_PI; 4592 r = 1.0 - (dr * fmod (double (i+1), 2.0)); 4593 m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); 4594 } 4595 m_glfcns.glEnd (); 4596 } 4597 break; 4598 case 'h': 4599 { 4600 double ang, r, dr; 4601 dr = 1.0 - 0.5/sin (M_PI/3)*1.02; 4602 4603 m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); 4604 for (int i = 0; i < 2*6; i++) 4605 { 4606 ang = (0.5 + double (i+1) / 6.0) * M_PI; 4607 r = 1.0 - (dr * fmod (double (i+1), 2.0)); 4608 m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); 4609 } 4610 m_glfcns.glEnd (); 4611 } 4612 break; 4613 default: 4614 warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ()); 4615 break; 4616 } 4617 4618 m_glfcns.glEndList (); 4619 4620 return ID; 4621 4622 #else 4623 4624 octave_unused_parameter (marker); 4625 octave_unused_parameter (size); 4626 octave_unused_parameter (filled); 4627 4628 // This shouldn't happen because construction of opengl_renderer 4629 // objects is supposed to be impossible if OpenGL is not available. 4630 4631 panic_impossible (); 4632 4633 #endif 4634 } 4635 4636 void text_to_pixels(const std::string & txt,uint8NDArray & pixels,Matrix & bbox,int halign,int valign,double rotation)4637 opengl_renderer::text_to_pixels (const std::string& txt, 4638 uint8NDArray& pixels, 4639 Matrix& bbox, 4640 int halign, int valign, double rotation) 4641 { 4642 txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign, 4643 rotation, interpreter); 4644 } 4645 4646 void text_to_strlist(const std::string & txt,std::list<text_renderer::string> & lst,Matrix & bbox,int halign,int valign,double rotation)4647 opengl_renderer::text_to_strlist (const std::string& txt, 4648 std::list<text_renderer::string>& lst, 4649 Matrix& bbox, 4650 int halign, int valign, double rotation) 4651 { 4652 txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign, 4653 rotation, interpreter); 4654 } 4655 4656 Matrix render_text(const std::string & txt,double x,double y,double z,int halign,int valign,double rotation)4657 opengl_renderer::render_text (const std::string& txt, 4658 double x, double y, double z, 4659 int halign, int valign, double rotation) 4660 { 4661 #if defined (HAVE_OPENGL) 4662 4663 Matrix bbox (1, 4, 0.0); 4664 4665 if (txt.empty ()) 4666 return bbox; 4667 4668 if (txt_renderer.ok ()) 4669 { 4670 uint8NDArray pixels; 4671 text_to_pixels (txt, pixels, bbox, halign, valign, rotation); 4672 4673 bool blend = m_glfcns.glIsEnabled (GL_BLEND); 4674 4675 m_glfcns.glEnable (GL_BLEND); 4676 m_glfcns.glEnable (GL_ALPHA_TEST); 4677 m_glfcns.glRasterPos3d (x, y, z); 4678 m_glfcns.glBitmap(0, 0, 0, 0, bbox(0), bbox(1), nullptr); 4679 m_glfcns.glDrawPixels (bbox(2), bbox(3), 4680 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ()); 4681 m_glfcns.glDisable (GL_ALPHA_TEST); 4682 4683 if (! blend) 4684 m_glfcns.glDisable (GL_BLEND); 4685 } 4686 4687 return bbox; 4688 4689 #else 4690 4691 octave_unused_parameter (txt); 4692 octave_unused_parameter (x); 4693 octave_unused_parameter (y); 4694 octave_unused_parameter (z); 4695 octave_unused_parameter (halign); 4696 octave_unused_parameter (valign); 4697 octave_unused_parameter (rotation); 4698 4699 // This shouldn't happen because construction of opengl_renderer 4700 // objects is supposed to be impossible if OpenGL is not available. 4701 4702 panic_impossible (); 4703 4704 #endif 4705 } 4706 } 4707