Mercurial > octave
diff libinterp/corefcn/gl-render.cc @ 31607:aac27ad79be6 stable
maint: Re-indent code after switch to using namespace macros.
* build-env.h, build-env.in.cc, Cell.h, __betainc__.cc, __eigs__.cc,
__ftp__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __magick_read__.cc,
__pchip_deriv__.cc, amd.cc, base-text-renderer.cc, base-text-renderer.h,
besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.h, call-stack.cc,
call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, dasrt.cc, data.cc,
debug.cc, defaults.cc, defaults.h, det.cc, display.cc, display.h, dlmread.cc,
dynamic-ld.cc, dynamic-ld.h, ellipj.cc, environment.cc, environment.h,
error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h,
event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc,
file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h,
gcd.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h,
graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, gsvd.cc, gtk-manager.cc,
gtk-manager.h, help.cc, help.h, hook-fcn.cc, hook-fcn.h, input.cc, input.h,
interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h,
inv.cc, jsondecode.cc, jsonencode.cc, latex-text-renderer.cc,
latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h,
lookup.cc, ls-hdf5.cc, ls-mat4.cc, ls-mat5.cc, lsode.cc, lu.cc, mappers.cc,
matrix_type.cc, max.cc, mex.cc, mexproto.h, mxarray.h, mxtypes.in.h,
oct-errno.in.cc, oct-hdf5-types.cc, oct-hist.cc, oct-hist.h, oct-map.cc,
oct-map.h, oct-opengl.h, oct-prcstrm.h, oct-process.cc, oct-process.h,
oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.h,
octave-default-image.h, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc,
pow2.cc, pr-output.cc, psi.cc, qr.cc, quadcc.cc, rand.cc, regexp.cc,
settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xpow.cc,
sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfns.cc, svd.cc,
syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h,
symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h,
text-renderer.cc, text-renderer.h, time.cc, toplev.cc, typecast.cc,
url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h,
variables.cc, variables.h, xdiv.cc, __delaunayn__.cc, __init_fltk__.cc,
__init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audioread.cc, convhulln.cc,
gzip.cc, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc,
cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h,
cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h,
cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc,
ov-base-mat.h, ov-base-scalar.cc, ov-base.cc, ov-base.h, ov-bool-mat.cc,
ov-bool-mat.h, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.h, ov-cell.cc,
ov-ch-mat.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h,
ov-complex.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc,
ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-float.cc,
ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc,
ov-flt-re-mat.cc, ov-flt-re-mat.h, ov-intx.h, ov-java.cc, ov-lazy-idx.cc,
ov-legacy-range.cc, ov-magic-int.cc, ov-mex-fcn.cc, ov-mex-fcn.h,
ov-null-mat.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc,
ov-re-mat.h, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc,
ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, ovl.h,
octave.cc, octave.h, op-b-sbm.cc, op-bm-sbm.cc, op-cs-scm.cc, op-fm-fcm.cc,
op-fs-fcm.cc, op-s-scm.cc, op-scm-cs.cc, op-scm-s.cc, op-sm-cs.cc, ops.h,
anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h,
comment-list.cc, comment-list.h, filepos.h, lex.h, oct-lvalue.cc, oct-lvalue.h,
parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h,
pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h,
pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc,
pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc,
pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h,
pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h,
pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h,
pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h,
pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h,
pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h,
pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h,
pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h,
idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h,
aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h,
gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc,
lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h,
oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc,
oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h,
randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h,
sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc,
sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h,
file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h,
lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h,
oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc,
oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h,
action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h,
cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h,
lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h,
lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc,
oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc,
oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc,
oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc,
pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc,
url-transfer.h:
Re-indent code after switch to using namespace macros.
author | Rik <rik@octave.org> |
---|---|
date | Thu, 01 Dec 2022 18:02:15 -0800 |
parents | e88a07dec498 |
children | a74935a6cc75 597f3ee61a48 |
line wrap: on
line diff
--- a/libinterp/corefcn/gl-render.cc Thu Dec 01 14:23:45 2022 -0800 +++ b/libinterp/corefcn/gl-render.cc Thu Dec 01 18:02:15 2022 -0800 @@ -49,4941 +49,4942 @@ #if defined (HAVE_OPENGL) - static int - next_power_of_2 (int n) - { - int m = 1; - - while (m < n && m < std::numeric_limits<int>::max ()) - m <<= 1; - - return m; - } +static int +next_power_of_2 (int n) +{ + int m = 1; + + while (m < n && m < std::numeric_limits<int>::max ()) + m <<= 1; + + return m; +} #define LIGHT_MODE GL_FRONT_AND_BACK - // Use symbolic names for axes - enum - { - X_AXIS, - Y_AXIS, - Z_AXIS - }; - - // Use symbolic names for color mode - enum - { - UNIFORM, - FLAT, - INTERP, - TEXTURE - }; - - // Use symbolic names for lighting - enum - { - NONE, - //FLAT, // Already declared in anonymous enum for color mode - GOURAUD = 2 - }; - - // Win32 API requires the CALLBACK attributes for - // GLU callback functions. Define it to empty on - // other platforms. +// Use symbolic names for axes +enum +{ + X_AXIS, + Y_AXIS, + Z_AXIS +}; + +// Use symbolic names for color mode +enum +{ + UNIFORM, + FLAT, + INTERP, + TEXTURE +}; + +// Use symbolic names for lighting +enum +{ + NONE, + //FLAT, // Already declared in anonymous enum for color mode + GOURAUD = 2 +}; + +// Win32 API requires the CALLBACK attributes for +// GLU callback functions. Define it to empty on +// other platforms. #if ! defined (CALLBACK) # define CALLBACK #endif - class opengl_texture - { - private: - - class texture_rep - { - public: - - texture_rep (opengl_functions& glfcns) - : m_glfcns (glfcns), m_id (), m_w (), m_h (), m_tw (), m_th (), - m_tx (), m_ty (), m_valid (false) - { } - - texture_rep (opengl_functions& glfcns, GLuint id, int w, int h, - int tw, int th) - : m_glfcns (glfcns), m_id (id), m_w (w), m_h (h), m_tw (tw), m_th (th), - m_tx (double(m_w)/m_tw), m_ty (double(m_h)/m_th), m_valid (true) - { } - - ~texture_rep (void) - { - if (m_valid) - m_glfcns.glDeleteTextures (1, &m_id); - } - - void bind (int mode) const - { - if (m_valid) - m_glfcns.glBindTexture (mode, m_id); - } - - void tex_coord (double q, double r) const - { - if (m_valid) - m_glfcns.glTexCoord2d (q*m_tx, r*m_ty); - } - - opengl_functions& m_glfcns; - GLuint m_id; - int m_w, m_h; - int m_tw, m_th; - double m_tx, m_ty; - bool m_valid; - }; - - public: - - opengl_texture (opengl_functions& glfcns) - : m_rep (new texture_rep (glfcns)) - { } - - opengl_texture (opengl_functions& glfcns, GLuint id, int w, int h, - int tw, int th) - : m_rep (new texture_rep (glfcns, id, w, h, tw, th)) - { } - - opengl_texture (const opengl_texture&) = default; - - ~opengl_texture (void) = default; - - opengl_texture& operator = (const opengl_texture&) = default; - - static opengl_texture create (opengl_functions& glfcns, - const octave_value& data); - - void bind (int mode = GL_TEXTURE_2D) const { m_rep->bind (mode); } - - void tex_coord (double q, double r) const { m_rep->tex_coord (q, r); } - - bool is_valid (void) const { return m_rep->m_valid; } - - private: - - opengl_texture (const std::shared_ptr<texture_rep>& new_rep) - : m_rep (new_rep) - { } - - std::shared_ptr<texture_rep> m_rep; - }; - - opengl_texture - opengl_texture::create (opengl_functions& glfcns, const octave_value& data) - { - opengl_texture retval (glfcns); - - dim_vector dv (data.dims ()); - - // Expect RGB data - if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4)) - { - // FIXME: dim_vectors hold octave_idx_type values. - // Should we check for dimensions larger than intmax? - int h, w, tw, th; - h = dv(0), w = dv(1); - - // Return early if the image data are larger than the texture - // can hold - int max_size; - glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size); - static bool warned = false; - if (h > max_size || w > max_size) - { - if (! warned) - { - warning ("opengl_texture::create: the opengl library in use " - "doesn't support images with either dimension larger " - "than %d. Not rendering.", max_size); - warned = true; - } - - return opengl_texture (glfcns); - } - - GLuint id; - bool ok = true; - - tw = next_power_of_2 (w); - th = next_power_of_2 (h); - - glfcns.glGenTextures (1, &id); - glfcns.glBindTexture (GL_TEXTURE_2D, id); - - if (data.is_double_type ()) - { - const NDArray xdata = data.array_value (); - - OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); - - for (int i = 0; i < h; i++) - { - for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) - { - a[idx] = xdata(i,j,0); - a[idx+1] = xdata(i,j,1); - a[idx+2] = xdata(i,j,2); - } - } - - glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, - GL_FLOAT, a); - } - - else if (data.is_single_type ()) - { - const FloatNDArray xdata = data.float_array_value (); - - OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); - - for (int i = 0; i < h; i++) - { - for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) - { - a[idx] = xdata(i,j,0); - a[idx+1] = xdata(i,j,1); - a[idx+2] = xdata(i,j,2); - } - } - - glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, - GL_FLOAT, a); - } - else if (data.is_uint16_type ()) - { - const uint16NDArray xdata = data.uint16_array_value (); - - OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th)); - - for (int i = 0; i < h; i++) - { - for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) - { - a[idx] = xdata(i,j,0); - a[idx+1] = xdata(i,j,1); - a[idx+2] = xdata(i,j,2); - } - } - - glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, - GL_RGB, GL_UNSIGNED_SHORT, a); - } - else if (data.is_uint8_type () && dv(2) == 3) - { - const uint8NDArray xdata = data.uint8_array_value (); - - OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th)); - - for (int i = 0; i < h; i++) - { - for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) - { - a[idx] = xdata(i,j,0); - a[idx+1] = xdata(i,j,1); - a[idx+2] = xdata(i,j,2); - } - } - - glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, - GL_RGB, GL_UNSIGNED_BYTE, a); - } - else if (data.is_uint8_type () && dv(2) == 4) - { - const uint8NDArray xdata = data.uint8_array_value (); - - OCTAVE_LOCAL_BUFFER (GLubyte, a, (4*tw*th)); - - for (int i = 0; i < h; i++) - { - for (int j = 0, idx = i*tw*4; j < w; j++, idx += 4) - { - a[idx] = xdata(i,j,0); - a[idx+1] = xdata(i,j,1); - a[idx+2] = xdata(i,j,2); - a[idx+3] = xdata(i,j,3); - } - } - - glfcns.glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, - GL_RGBA, GL_UNSIGNED_BYTE, a); - } - else - { - ok = false; - warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16"); - } - - if (ok) - { - glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_NEAREST); - glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - GL_NEAREST); - - if (glfcns.glGetError () != GL_NO_ERROR) - warning ("opengl_texture::create: OpenGL error while generating texture data"); - else - retval = opengl_texture (glfcns, id, w, h, tw, th); - } - } - else - warning ("opengl_texture::create: invalid texture data size"); - - return retval; - } - - class - opengl_tessellator +class opengl_texture +{ +private: + + class texture_rep { public: -#if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS) - typedef GLvoid (CALLBACK *fcn) (...); -#else - typedef void (CALLBACK *fcn) (void); -#endif - - public: - - opengl_tessellator (void) : m_glu_tess (nullptr), m_fill () { init (); } - - // No copying! - - opengl_tessellator (const opengl_tessellator&) = delete; - - opengl_tessellator operator = (const opengl_tessellator&) = delete; - - virtual ~opengl_tessellator (void) - { if (m_glu_tess) gluDeleteTess (m_glu_tess); } - - void begin_polygon (bool filled = true) + + texture_rep (opengl_functions& glfcns) + : m_glfcns (glfcns), m_id (), m_w (), m_h (), m_tw (), m_th (), + m_tx (), m_ty (), m_valid (false) + { } + + texture_rep (opengl_functions& glfcns, GLuint id, int w, int h, + int tw, int th) + : m_glfcns (glfcns), m_id (id), m_w (w), m_h (h), m_tw (tw), m_th (th), + m_tx (double(m_w)/m_tw), m_ty (double(m_h)/m_th), m_valid (true) + { } + + ~texture_rep (void) { - gluTessProperty (m_glu_tess, GLU_TESS_BOUNDARY_ONLY, - (filled ? GL_FALSE : GL_TRUE)); - m_fill = filled; - gluTessBeginPolygon (m_glu_tess, this); + if (m_valid) + m_glfcns.glDeleteTextures (1, &m_id); + } + + void bind (int mode) const + { + if (m_valid) + m_glfcns.glBindTexture (mode, m_id); + } + + void tex_coord (double q, double r) const + { + if (m_valid) + m_glfcns.glTexCoord2d (q*m_tx, r*m_ty); } - void end_polygon (void) const - { gluTessEndPolygon (m_glu_tess); } - - void begin_contour (void) const - { gluTessBeginContour (m_glu_tess); } - - void end_contour (void) const - { gluTessEndContour (m_glu_tess); } - - void add_vertex (double *loc, void *data) const - { gluTessVertex (m_glu_tess, loc, data); } - - protected: - virtual void begin (GLenum /*type*/) { } - - virtual void end (void) { } - - virtual void vertex (void * /*data*/) { } - - virtual void combine (GLdouble [3] /*c*/, void * [4] /*data*/, - GLfloat [4] /*w*/, void ** /*out_data*/) { } - - virtual void edge_flag (GLboolean /*flag*/) { } - - virtual void error (GLenum err) - { ::error ("OpenGL tessellation error (%d)", err); } - - virtual void init (void) - { - m_glu_tess = gluNewTess (); - - gluTessCallback (m_glu_tess, GLU_TESS_BEGIN_DATA, - reinterpret_cast<fcn> (tess_begin)); - gluTessCallback (m_glu_tess, GLU_TESS_END_DATA, - reinterpret_cast<fcn> (tess_end)); - gluTessCallback (m_glu_tess, GLU_TESS_VERTEX_DATA, - reinterpret_cast<fcn> (tess_vertex)); - gluTessCallback (m_glu_tess, GLU_TESS_COMBINE_DATA, - reinterpret_cast<fcn> (tess_combine)); - gluTessCallback (m_glu_tess, GLU_TESS_EDGE_FLAG_DATA, - reinterpret_cast<fcn> (tess_edge_flag)); - gluTessCallback (m_glu_tess, GLU_TESS_ERROR_DATA, - reinterpret_cast<fcn> (tess_error)); - } - - bool is_filled (void) const { return m_fill; } - - private: - static void CALLBACK tess_begin (GLenum type, void *t) - { reinterpret_cast<opengl_tessellator *> (t)->begin (type); } - - static void CALLBACK tess_end (void *t) - { reinterpret_cast<opengl_tessellator *> (t)->end (); } - - static void CALLBACK tess_vertex (void *v, void *t) - { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); } - - static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4], - void **out, void *t) - { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); } - - static void CALLBACK tess_edge_flag (GLboolean flag, void *t) - { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); } - - static void CALLBACK tess_error (GLenum err, void *t) - { reinterpret_cast<opengl_tessellator *> (t)->error (err); } - - //-------- - - GLUtesselator *m_glu_tess; - bool m_fill; + opengl_functions& m_glfcns; + GLuint m_id; + int m_w, m_h; + int m_tw, m_th; + double m_tx, m_ty; + bool m_valid; }; - class vertex_data - { - public: - - class vertex_data_rep +public: + + opengl_texture (opengl_functions& glfcns) + : m_rep (new texture_rep (glfcns)) + { } + + opengl_texture (opengl_functions& glfcns, GLuint id, int w, int h, + int tw, int th) + : m_rep (new texture_rep (glfcns, id, w, h, tw, th)) + { } + + opengl_texture (const opengl_texture&) = default; + + ~opengl_texture (void) = default; + + opengl_texture& operator = (const opengl_texture&) = default; + + static opengl_texture create (opengl_functions& glfcns, + const octave_value& data); + + void bind (int mode = GL_TEXTURE_2D) const { m_rep->bind (mode); } + + void tex_coord (double q, double r) const { m_rep->tex_coord (q, r); } + + bool is_valid (void) const { return m_rep->m_valid; } + +private: + + opengl_texture (const std::shared_ptr<texture_rep>& new_rep) + : m_rep (new_rep) + { } + + std::shared_ptr<texture_rep> m_rep; +}; + +opengl_texture +opengl_texture::create (opengl_functions& glfcns, const octave_value& data) +{ + opengl_texture retval (glfcns); + + dim_vector dv (data.dims ()); + + // Expect RGB data + if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4)) { - public: - - vertex_data_rep (void) - : m_coords (), m_color (), m_vertex_normal (), m_face_normal (), - m_alpha (), m_ambient (), m_diffuse (), m_specular (), - m_specular_exp (), m_specular_color_refl () - { } - - 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) - : m_coords (c), m_color (col), m_vertex_normal (vn), - m_face_normal (fn), m_alpha (a), m_ambient (as), m_diffuse (ds), - m_specular (ss), m_specular_exp (se), m_specular_color_refl (scr) - { } - - Matrix m_coords; - Matrix m_color; - Matrix m_vertex_normal; - Matrix m_face_normal; - double m_alpha; - float m_ambient; - float m_diffuse; - float m_specular; - float m_specular_exp; - float m_specular_color_refl; - }; - - public: - - // Required to instantiate std::list<vertex_data> objects. - vertex_data (void) : m_rep (nil_rep ()) { } - - 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) - : m_rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr)) - { } - - vertex_data (const vertex_data&) = default; - - ~vertex_data (void) = default; - - vertex_data& operator = (const vertex_data&) = default; - - vertex_data_rep * get_rep (void) const { return m_rep.get (); } - - private: - - static std::shared_ptr<vertex_data_rep> nil_rep (void) - { - static std::shared_ptr<vertex_data_rep> nr (new vertex_data_rep ()); - - return nr; - } - - std::shared_ptr<vertex_data_rep> m_rep; - }; - - class - opengl_renderer::patch_tessellator : public opengl_tessellator - { - public: - patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl, - float idx = 0.0) - : opengl_tessellator (), m_renderer (r), - m_color_mode (cmode), m_light_mode (lmode), m_face_lighting (fl), - m_index (idx), m_first (true), m_tmp_vdata () - { } - - protected: - void begin (GLenum type) - { - opengl_functions& glfcns = m_renderer->get_opengl_functions (); - - //printf ("patch_tessellator::begin (%d)\n", type); - m_first = true; - - if (m_color_mode == INTERP || m_light_mode == GOURAUD) - glfcns.glShadeModel (GL_SMOOTH); - else - glfcns.glShadeModel (GL_FLAT); - - if (is_filled ()) - m_renderer->set_polygon_offset (true, m_index); - - glfcns.glBegin (type); - } - - void end (void) - { - opengl_functions& glfcns = m_renderer->get_opengl_functions (); - - //printf ("patch_tessellator::end\n"); - glfcns.glEnd (); - m_renderer->set_polygon_offset (false); - } - - void vertex (void *data) - { - opengl_functions& glfcns = m_renderer->get_opengl_functions (); - - vertex_data::vertex_data_rep *v - = reinterpret_cast<vertex_data::vertex_data_rep *> (data); - //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->m_coords(0), v->m_coords(1), v->m_coords(2)); - - // NOTE: OpenGL can re-order vertices. For "flat" coloring of FaceColor - // the first vertex must be identified in the draw_patch routine. - - if (m_color_mode == INTERP || (m_color_mode == FLAT && ! is_filled ())) + // FIXME: dim_vectors hold octave_idx_type values. + // Should we check for dimensions larger than intmax? + int h, w, tw, th; + h = dv(0), w = dv(1); + + // Return early if the image data are larger than the texture + // can hold + int max_size; + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size); + static bool warned = false; + if (h > max_size || w > max_size) + { + if (! warned) + { + warning ("opengl_texture::create: the opengl library in use " + "doesn't support images with either dimension larger " + "than %d. Not rendering.", max_size); + warned = true; + } + + return opengl_texture (glfcns); + } + + GLuint id; + bool ok = true; + + tw = next_power_of_2 (w); + th = next_power_of_2 (h); + + glfcns.glGenTextures (1, &id); + glfcns.glBindTexture (GL_TEXTURE_2D, id); + + if (data.is_double_type ()) + { + const NDArray xdata = data.array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); + + for (int i = 0; i < h; i++) + { + for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) + { + a[idx] = xdata(i, j, 0); + a[idx+1] = xdata(i, j, 1); + a[idx+2] = xdata(i, j, 2); + } + } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, + GL_FLOAT, a); + } + + else if (data.is_single_type ()) { - Matrix col = v->m_color; - - if (col.numel () == 3) + const FloatNDArray xdata = data.float_array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); + + for (int i = 0; i < h; i++) { - glfcns.glColor4d (col(0), col(1), col(2), v->m_alpha); - if (m_light_mode > 0) + for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) + { + a[idx] = xdata(i, j, 0); + a[idx+1] = xdata(i, j, 1); + a[idx+2] = xdata(i, j, 2); + } + } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, + GL_FLOAT, a); + } + else if (data.is_uint16_type ()) + { + const uint16NDArray xdata = data.uint16_array_value (); + + OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th)); + + for (int i = 0; i < h; i++) + { + for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) { - // edge lighting only uses ambient light - float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; - - if (m_face_lighting) - for (int k = 0; k < 3; k++) - buf[k] = (v->m_specular - * (v->m_specular_color_refl + - (1 - v->m_specular_color_refl) * col(k))); - glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf); - - if (m_face_lighting) - for (int k = 0; k < 3; k++) - buf[k] = (v->m_diffuse * col(k)); - glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf); - - for (int k = 0; k < 3; k++) - buf[k] = (v->m_ambient * col(k)); - glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf); + a[idx] = xdata(i, j, 0); + a[idx+1] = xdata(i, j, 1); + a[idx+2] = xdata(i, j, 2); + } + } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, + GL_RGB, GL_UNSIGNED_SHORT, a); + } + else if (data.is_uint8_type () && dv(2) == 3) + { + const uint8NDArray xdata = data.uint8_array_value (); + + OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th)); + + for (int i = 0; i < h; i++) + { + for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) + { + a[idx] = xdata(i, j, 0); + a[idx+1] = xdata(i, j, 1); + a[idx+2] = xdata(i, j, 2); + } + } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, + GL_RGB, GL_UNSIGNED_BYTE, a); + } + else if (data.is_uint8_type () && dv(2) == 4) + { + const uint8NDArray xdata = data.uint8_array_value (); + + OCTAVE_LOCAL_BUFFER (GLubyte, a, (4*tw*th)); + + for (int i = 0; i < h; i++) + { + for (int j = 0, idx = i*tw*4; j < w; j++, idx += 4) + { + a[idx] = xdata(i, j, 0); + a[idx+1] = xdata(i, j, 1); + a[idx+2] = xdata(i, j, 2); + a[idx+3] = xdata(i, j, 3); } } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, + GL_RGBA, GL_UNSIGNED_BYTE, a); } - - if (m_light_mode == FLAT && m_first) - glfcns.glNormal3dv (v->m_face_normal.data ()); - else if (m_light_mode == GOURAUD) - glfcns.glNormal3dv (v->m_vertex_normal.data ()); - - glfcns.glVertex3dv (v->m_coords.data ()); - - m_first = false; - } - - void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data) - { - vertex_data::vertex_data_rep *v[4]; - int vmax = 4; - - for (int i = 0; i < 4; i++) + else { - v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]); - - if (vmax == 4 && ! v[i]) - vmax = i; + ok = false; + warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16"); } - Matrix vv (1, 3, 0.0); - Matrix cc; - Matrix vnn (1, 3, 0.0); - Matrix fnn (1, 3, 0.0); - double aa = 0.0; - - vv(0) = xyz[0]; - vv(1) = xyz[1]; - vv(2) = xyz[2]; - - if (v[0]->m_color.numel ()) - { - cc.resize (1, 3, 0.0); - for (int ic = 0; ic < 3; ic++) - for (int iv = 0; iv < vmax; iv++) - cc(ic) += (w[iv] * v[iv]->m_color (ic)); - } - - if (v[0]->m_vertex_normal.numel () > 0) + if (ok) { - for (int in = 0; in < 3; in++) - for (int iv = 0; iv < vmax; iv++) - vnn(in) += (w[iv] * v[iv]->m_vertex_normal (in)); - } - - if (v[0]->m_face_normal.numel () > 0) - { - for (int in = 0; in < 3; in++) - for (int iv = 0; iv < vmax; iv++) - fnn(in) += (w[iv] * v[iv]->m_face_normal (in)); + glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + + if (glfcns.glGetError () != GL_NO_ERROR) + warning ("opengl_texture::create: OpenGL error while generating texture data"); + else + retval = opengl_texture (glfcns, id, w, h, tw, th); } - - for (int iv = 0; iv < vmax; iv++) - aa += (w[iv] * v[iv]->m_alpha); - - vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->m_ambient, v[0]->m_diffuse, - v[0]->m_specular, v[0]->m_specular_exp, - v[0]->m_specular_color_refl); - m_tmp_vdata.push_back (new_v); - - *out_data = new_v.get_rep (); } - - private: - - // No copying! - - patch_tessellator (const patch_tessellator&) = delete; - - patch_tessellator& operator = (const patch_tessellator&) = delete; - - opengl_renderer *m_renderer; - int m_color_mode; - int m_light_mode; - bool m_face_lighting; - int m_index; - bool m_first; - std::list<vertex_data> m_tmp_vdata; - }; - + else + warning ("opengl_texture::create: invalid texture data size"); + + return retval; +} + +class +opengl_tessellator +{ +public: +#if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS) + typedef GLvoid (CALLBACK *fcn) (...); #else - - class - opengl_renderer::patch_tessellator - { - // Dummy class. - }; - + typedef void (CALLBACK *fcn) (void); #endif - opengl_renderer::opengl_renderer (opengl_functions& glfcns) - : m_glfcns (glfcns), m_xmin (), m_xmax (), m_ymin (), m_ymax (), - m_zmin (), m_zmax (), m_devpixratio (1.0), m_xform (), m_toolkit (), - m_xZ1 (), m_xZ2 (), m_marker_id (), m_filled_marker_id (), - m_camera_pos (), m_camera_dir (), m_view_vector (), - m_interpreter ("none"), m_txt_renderer (), m_current_light (0), - m_max_lights (0), m_selecting (false), m_printing (false) - { - // This constructor will fail if we don't have OpenGL or if the data - // types we assumed in our public interface aren't compatible with the - // OpenGL types. - -#if defined (HAVE_OPENGL) - - // Ensure that we can't request an image larger than OpenGL can handle. - // FIXME: should we check signed vs. unsigned? - - static bool ok = (sizeof (int) <= sizeof (GLsizei)); - - if (! ok) - error ("the size of GLsizei is smaller than the size of int"); - -#else - - err_disabled_feature ("opengl_renderer", "OpenGL"); - -#endif - } - - void - opengl_renderer::draw (const graphics_object& go, bool toplevel) - { - if (! go.valid_object ()) - return; - - const base_properties& props = go.get_properties (); - - if (! m_toolkit) - m_toolkit = props.get_toolkit (); - - if (go.isa ("figure")) - draw_figure (dynamic_cast<const figure::properties&> (props)); - else if (go.isa ("axes")) - draw_axes (dynamic_cast<const axes::properties&> (props)); - else if (go.isa ("line")) - draw_line (dynamic_cast<const line::properties&> (props)); - else if (go.isa ("surface")) - draw_surface (dynamic_cast<const surface::properties&> (props)); - else if (go.isa ("patch")) - draw_patch (dynamic_cast<const patch::properties&> (props)); - else if (go.isa ("scatter")) - draw_scatter (dynamic_cast<const scatter::properties&> (props)); - else if (go.isa ("light")) - draw_light (dynamic_cast<const light::properties&> (props)); - else if (go.isa ("hggroup")) - draw_hggroup (dynamic_cast<const hggroup::properties&> (props)); - else if (go.isa ("text")) - draw_text (dynamic_cast<const text::properties&> (props)); - else if (go.isa ("image")) - draw_image (dynamic_cast<const image::properties&> (props)); - else if (go.isa ("uimenu") || go.isa ("uicontrol") - || go.isa ("uicontextmenu") || go.isa ("uitoolbar") - || go.isa ("uipushtool") || go.isa ("uitoggletool") - || go.isa ("uitable")) - ; // SKIP - else if (go.isa ("uipanel")) - { - if (toplevel) - draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go); - } - else if (go.isa ("uibuttongroup")) - { - if (toplevel) - draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go); - } - else - { - warning ("opengl_renderer: cannot render object of type '%s'", - props.graphics_object_name ().c_str ()); - } - -#if defined (HAVE_OPENGL) - - GLenum gl_error = m_glfcns.glGetError (); - if (gl_error) - warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object", - gluErrorString (gl_error), gl_error, - props.graphics_object_name ().c_str ()); - -#endif - } - - void - opengl_renderer::draw_figure (const figure::properties& props) - { - m_printing = props.is___printing__ (); - - // Initialize OpenGL context - init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); - -#if defined (HAVE_OPENGL) - - props.set___gl_extensions__ (get_string (GL_EXTENSIONS)); - props.set___gl_renderer__ (get_string (GL_RENDERER)); - props.set___gl_vendor__ (get_string (GL_VENDOR)); - props.set___gl_version__ (get_string (GL_VERSION)); - -#endif - - // Draw children - - draw (props.get_all_children (), false); - } - - void - opengl_renderer::draw_uipanel (const uipanel::properties& props, - const graphics_object& go) - { - graphics_object fig = go.get_ancestor ("figure"); - const figure::properties& figProps - = dynamic_cast<const figure::properties&> (fig.get_properties ()); - - // Initialize OpenGL context - - init_gl_context (figProps.is_graphicssmoothing (), - props.get_backgroundcolor_rgb ()); - - // Draw children - - draw (props.get_all_children (), false); - } - - void - opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props, - const graphics_object& go) +public: + + opengl_tessellator (void) : m_glu_tess (nullptr), m_fill () { init (); } + + // No copying! + + opengl_tessellator (const opengl_tessellator&) = delete; + + opengl_tessellator operator = (const opengl_tessellator&) = delete; + + virtual ~opengl_tessellator (void) + { if (m_glu_tess) gluDeleteTess (m_glu_tess); } + + void begin_polygon (bool filled = true) { - graphics_object fig = go.get_ancestor ("figure"); - const figure::properties& figProps - = dynamic_cast<const figure::properties&> (fig.get_properties ()); - - // Initialize OpenGL context - - init_gl_context (figProps.is_graphicssmoothing (), - props.get_backgroundcolor_rgb ()); - - // Draw children - - draw (props.get_all_children (), false); + gluTessProperty (m_glu_tess, GLU_TESS_BOUNDARY_ONLY, + (filled ? GL_FALSE : GL_TRUE)); + m_fill = filled; + gluTessBeginPolygon (m_glu_tess, this); } - void - opengl_renderer::init_gl_context (bool enhanced, const Matrix& c) - { -#if defined (HAVE_OPENGL) - - // Initialize OpenGL context - - m_glfcns.glEnable (GL_DEPTH_TEST); - m_glfcns.glDepthFunc (GL_LEQUAL); - m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_glfcns.glAlphaFunc (GL_GREATER, 0.0f); - m_glfcns.glEnable (GL_NORMALIZE); - m_glfcns.glEnable (GL_BLEND); - - if (enhanced) - { - m_glfcns.glEnable (GL_MULTISAMPLE); - bool has_multisample = false; - if (! m_glfcns.glGetError ()) - { - GLint iMultiSample, iNumSamples; - m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample); - m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples); - if (iMultiSample == GL_TRUE && iNumSamples > 0) - has_multisample = true; - } - - if (! has_multisample) - { - // MultiSample not implemented. Use old-style anti-aliasing - m_glfcns.glDisable (GL_MULTISAMPLE); - // Disabling GL_MULTISAMPLE will raise a gl error if it is not - // implemented. Thus, call glGetError to reset the error state. - m_glfcns.glGetError (); - - m_glfcns.glEnable (GL_LINE_SMOOTH); - m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); - } - } - else - { - m_glfcns.glDisable (GL_LINE_SMOOTH); - } - - // Clear background - - if (c.numel () >= 3) - { - m_glfcns.glClearColor (c(0), c(1), c(2), 1); - m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - - GLenum gl_error = m_glfcns.glGetError (); - if (gl_error) - warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context", - gluErrorString (gl_error), gl_error); - -#else - - octave_unused_parameter (enhanced); - octave_unused_parameter (c); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::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) + void end_polygon (void) const + { gluTessEndPolygon (m_glu_tess); } + + void begin_contour (void) const + { gluTessBeginContour (m_glu_tess); } + + void end_contour (void) const + { gluTessEndContour (m_glu_tess); } + + void add_vertex (double *loc, void *data) const + { gluTessVertex (m_glu_tess, loc, data); } + +protected: + virtual void begin (GLenum /*type*/) { } + + virtual void end (void) { } + + virtual void vertex (void * /*data*/) { } + + virtual void combine (GLdouble [3] /*c*/, void *[4] /*data*/, + GLfloat [4] /*w*/, void ** /*out_data*/) { } + + virtual void edge_flag (GLboolean /*flag*/) { } + + virtual void error (GLenum err) + { ::error ("OpenGL tessellation error (%d)", err); } + + virtual void init (void) { -#if defined (HAVE_OPENGL) - - m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha); - set_linestyle (gridstyle, true, linewidth); - m_glfcns.glBegin (GL_LINES); - for (int i = 0; i < ticks.numel (); i++) - { - double val = ticks(i); - if (lim1 <= val && val <= lim2) - { - if (xyz == X_AXIS) - { - m_glfcns.glVertex3d (val, p1N, p2); - m_glfcns.glVertex3d (val, p1, p2); - if (is_3D) - { - m_glfcns.glVertex3d (val, p1, p2N); - m_glfcns.glVertex3d (val, p1, p2); - } - } - else if (xyz == Y_AXIS) - { - m_glfcns.glVertex3d (p1N, val, p2); - m_glfcns.glVertex3d (p1, val, p2); - if (is_3D) - { - m_glfcns.glVertex3d (p1, val, p2N); - m_glfcns.glVertex3d (p1, val, p2); - } - } - else if (xyz == Z_AXIS) - { - m_glfcns.glVertex3d (p1N, p2, val); - m_glfcns.glVertex3d (p1, p2, val); - m_glfcns.glVertex3d (p1, p2N, val); - m_glfcns.glVertex3d (p1, p2, val); - } - } - } - m_glfcns.glEnd (); - set_linestyle ("-"); // Disable LineStipple - double black[3] = {0, 0, 0}; - m_glfcns.glColor3dv (black); - -#else - - octave_unused_parameter (linewidth); - octave_unused_parameter (gridstyle); - octave_unused_parameter (gridcolor); - octave_unused_parameter (gridalpha); - octave_unused_parameter (ticks); - octave_unused_parameter (lim1); - octave_unused_parameter (lim2); - octave_unused_parameter (p1); - octave_unused_parameter (p1N); - octave_unused_parameter (p2); - octave_unused_parameter (p2N); - octave_unused_parameter (xyz); - octave_unused_parameter (is_3D); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::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) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glBegin (GL_LINES); - - for (int i = 0; i < ticks.numel (); i++) - { - double val = ticks(i); - - if (lim1 <= val && val <= lim2) - { - if (xyz == X_AXIS) - { - m_glfcns.glVertex3d (val, p1, p2); - m_glfcns.glVertex3d (val, p1+dy, p2+dz); - if (mirror) - { - m_glfcns.glVertex3d (val, p1N, p2N); - m_glfcns.glVertex3d (val, p1N-dy, p2N-dz); - } - } - else if (xyz == Y_AXIS) - { - m_glfcns.glVertex3d (p1, val, p2); - m_glfcns.glVertex3d (p1+dx, val, p2+dz); - if (mirror) - { - m_glfcns.glVertex3d (p1N, val, p2N); - m_glfcns.glVertex3d (p1N-dx, val, p2N-dz); - } - } - else if (xyz == Z_AXIS) - { - m_glfcns.glVertex3d (p1, p2, val); - m_glfcns.glVertex3d (p1+dx, p2+dy, val); - if (mirror) - { - m_glfcns.glVertex3d (p1N, p2N, val); - m_glfcns.glVertex3d (p1N-dx, p2N-dy, val); - } - } - } - } - - m_glfcns.glEnd (); - -#else - - octave_unused_parameter (ticks); - octave_unused_parameter (lim1); - octave_unused_parameter (lim2); - octave_unused_parameter (p1); - octave_unused_parameter (p1N); - octave_unused_parameter (p2); - octave_unused_parameter (p2N); - octave_unused_parameter (dx); - octave_unused_parameter (dy); - octave_unused_parameter (dz); - octave_unused_parameter (xyz); - octave_unused_parameter (mirror); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif + m_glu_tess = gluNewTess (); + + gluTessCallback (m_glu_tess, GLU_TESS_BEGIN_DATA, + reinterpret_cast<fcn> (tess_begin)); + gluTessCallback (m_glu_tess, GLU_TESS_END_DATA, + reinterpret_cast<fcn> (tess_end)); + gluTessCallback (m_glu_tess, GLU_TESS_VERTEX_DATA, + reinterpret_cast<fcn> (tess_vertex)); + gluTessCallback (m_glu_tess, GLU_TESS_COMBINE_DATA, + reinterpret_cast<fcn> (tess_combine)); + gluTessCallback (m_glu_tess, GLU_TESS_EDGE_FLAG_DATA, + reinterpret_cast<fcn> (tess_edge_flag)); + gluTessCallback (m_glu_tess, GLU_TESS_ERROR_DATA, + reinterpret_cast<fcn> (tess_error)); } - void - opengl_renderer::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) - { -#if defined (HAVE_OPENGL) - - int nticks = ticks.numel (); - int nlabels = ticklabels.numel (); - - if (nlabels == 0) - return; - - for (int i = 0; i < nticks; i++) - { - double val = ticks(i); - - if (lim1 <= val && val <= lim2) - { - Matrix b; - - std::string label (ticklabels(i % nlabels)); - label.erase (0, label.find_first_not_of (' ')); - label = label.substr (0, label.find_last_not_of (' ')+1); - - // FIXME: As tick text is transparent, shouldn't it be - // drawn after axes object, for correct rendering? - if (xyz == X_AXIS) - { - b = render_text (label, val, p1, p2, ha, va); - } - else if (xyz == Y_AXIS) - { - b = render_text (label, p1, val, p2, ha, va); - } - else if (xyz == Z_AXIS) - { - b = render_text (label, p1, p2, val, ha, va); - } - - wmax = std::max (wmax, static_cast<int> (b(2))); - hmax = std::max (hmax, static_cast<int> (b(3))); - } - } - -#else - - octave_unused_parameter (ticks); - octave_unused_parameter (ticklabels); - octave_unused_parameter (lim1); - octave_unused_parameter (lim2); - octave_unused_parameter (p1); - octave_unused_parameter (p2); - octave_unused_parameter (xyz); - octave_unused_parameter (ha); - octave_unused_parameter (va); - octave_unused_parameter (wmax); - octave_unused_parameter (hmax); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glVertex2d (x1, y1); - m_glfcns.glVertex2d (x2, y1); - m_glfcns.glVertex2d (x2, y2); - m_glfcns.glVertex2d (x1, y2); - m_glfcns.glVertex2d (x1, y1); - -#else - - octave_unused_parameter (x1); - octave_unused_parameter (x2); - octave_unused_parameter (y1); - octave_unused_parameter (y2); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::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) + bool is_filled (void) const { return m_fill; } + +private: + static void CALLBACK tess_begin (GLenum type, void *t) + { reinterpret_cast<opengl_tessellator *> (t)->begin (type); } + + static void CALLBACK tess_end (void *t) + { reinterpret_cast<opengl_tessellator *> (t)->end (); } + + static void CALLBACK tess_vertex (void *v, void *t) + { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); } + + static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4], + void **out, void *t) + { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); } + + static void CALLBACK tess_edge_flag (GLboolean flag, void *t) + { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); } + + static void CALLBACK tess_error (GLenum err, void *t) + { reinterpret_cast<opengl_tessellator *> (t)->error (err); } + + //-------- + + GLUtesselator *m_glu_tess; + bool m_fill; +}; + +class vertex_data +{ +public: + + class vertex_data_rep { -#if defined (HAVE_OPENGL) - - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glPushMatrix (); - m_glfcns.glLoadIdentity (); - - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glPushMatrix (); - m_glfcns.glLoadIdentity (); - m_glfcns.glOrtho (0, width, height, 0, 1, -1); - - m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT); - m_glfcns.glDisable (GL_DEPTH_TEST); - - m_glfcns.glBegin (GL_POLYGON); - m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2), - overlayalpha); - draw_zoom_rect (x1, y1, x2, y2); - m_glfcns.glEnd (); - - m_glfcns.glLineWidth (borderwidth); - m_glfcns.glBegin (GL_LINE_STRIP); - m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2), - borderalpha); - draw_zoom_rect (x1, y1, x2, y2); - m_glfcns.glEnd (); - - m_glfcns.glPopAttrib (); - - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glPopMatrix (); - - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glPopMatrix (); - -#else - - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (x1); - octave_unused_parameter (x2); - octave_unused_parameter (y1); - octave_unused_parameter (y2); - octave_unused_parameter (overlaycolor); - octave_unused_parameter (overlayalpha); - octave_unused_parameter (bordercolor); - octave_unused_parameter (borderalpha); - octave_unused_parameter (borderwidth); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - uint8NDArray - opengl_renderer::get_pixels (int width, int height) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1); - uint8NDArray pix(dim_vector (3, width, height), 0); - - m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, - pix.fortran_vec ()); - - // Permute and flip data - Array<octave_idx_type> perm (dim_vector (3, 1)); - perm(0) = 2; - perm(1) = 1; - perm(2) = 0; - - Array<idx_vector> idx (dim_vector (3, 1)); - idx(0) = idx_vector::make_range (height - 1, -1, height); - idx(1) = idx_vector::colon; - idx(2) = idx_vector::colon; - - return pix.permute (perm).index (idx); - -#else - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - octave_unused_parameter (width); - octave_unused_parameter (height); - - panic_impossible (); - -#endif - } - - void - opengl_renderer::finish (void) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glFinish (); - -#else - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::setup_opengl_transformation (const axes::properties& props) + public: + + vertex_data_rep (void) + : m_coords (), m_color (), m_vertex_normal (), m_face_normal (), + m_alpha (), m_ambient (), m_diffuse (), m_specular (), + m_specular_exp (), m_specular_color_refl () + { } + + 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) + : m_coords (c), m_color (col), m_vertex_normal (vn), + m_face_normal (fn), m_alpha (a), m_ambient (as), m_diffuse (ds), + m_specular (ss), m_specular_exp (se), m_specular_color_refl (scr) + { } + + Matrix m_coords; + Matrix m_color; + Matrix m_vertex_normal; + Matrix m_face_normal; + double m_alpha; + float m_ambient; + float m_diffuse; + float m_specular; + float m_specular_exp; + float m_specular_color_refl; + }; + +public: + + // Required to instantiate std::list<vertex_data> objects. + vertex_data (void) : m_rep (nil_rep ()) { } + + 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) + : m_rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr)) + { } + + vertex_data (const vertex_data&) = default; + + ~vertex_data (void) = default; + + vertex_data& operator = (const vertex_data&) = default; + + vertex_data_rep * get_rep (void) const { return m_rep.get (); } + +private: + + static std::shared_ptr<vertex_data_rep> nil_rep (void) { -#if defined (HAVE_OPENGL) - - // setup OpenGL transformation - - Matrix x_zlim = props.get_transform_zlim (); - - // Expand the distance between the clipping planes symmetrically by - // an arbitrary factor (see bug #54551). - const double expansion_fac = 100.0; - // Also make sure that the distance between the clipping planes - // differs in single precision (see bug #58956). This factor is also - // arbitrary. Different values (>2) might also work. - const double single_prec_fac = 10.0; - - double avgZ = x_zlim(0) / 2.0 + x_zlim(1) / 2.0; - double span - = std::max (expansion_fac * (x_zlim(1)-x_zlim(0)), - single_prec_fac * std::abs (avgZ) - * std::numeric_limits<float>::epsilon ()); - m_xZ1 = avgZ - span; - m_xZ2 = avgZ + span; - - Matrix x_mat1 = props.get_opengl_matrix_1 (); - Matrix x_mat2 = props.get_opengl_matrix_2 (); - - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glLoadIdentity (); - m_glfcns.glScaled (1, 1, -1); - m_glfcns.glMultMatrixd (x_mat1.data ()); - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glLoadIdentity (); - - Matrix vp = get_viewport_scaled (); - m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2); - m_glfcns.glMultMatrixd (x_mat2.data ()); - m_glfcns.glMatrixMode (GL_MODELVIEW); - - m_glfcns.glClear (GL_DEPTH_BUFFER_BIT); - - // store axes transformation data - - m_xform = props.get_transform (); - -#else - - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif + static std::shared_ptr<vertex_data_rep> nr (new vertex_data_rep ()); + + return nr; } - void - opengl_renderer::draw_axes_planes (const axes::properties& props) + std::shared_ptr<vertex_data_rep> m_rep; +}; + +class +opengl_renderer::patch_tessellator : public opengl_tessellator +{ +public: + patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl, + float idx = 0.0) + : opengl_tessellator (), m_renderer (r), + m_color_mode (cmode), m_light_mode (lmode), m_face_lighting (fl), + m_index (idx), m_first (true), m_tmp_vdata () + { } + +protected: + void begin (GLenum type) { -#if defined (HAVE_OPENGL) - - Matrix axe_color = props.get_color_rgb (); - if (axe_color.isempty () || ! props.is_visible ()) - return; - - double xPlane = props.get_xPlane (); - double yPlane = props.get_yPlane (); - double zPlane = props.get_zPlane (); - double xPlaneN = props.get_xPlaneN (); - double yPlaneN = props.get_yPlaneN (); - double zPlaneN = props.get_zPlaneN (); - bool is2D = props.get_is2D (); - - // Axes planes - set_color (axe_color); - set_polygon_offset (true, 9.0); - - m_glfcns.glBegin (GL_QUADS); - - if (! is2D) - { - // X plane - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlane, zPlane); - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); - - // Y plane - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlane, zPlane); - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); - } - - // Z plane - m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); - m_glfcns.glVertex3d (xPlane, yPlane, zPlane); - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); - - m_glfcns.glEnd (); - - set_polygon_offset (false); - -#else - - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif + opengl_functions& glfcns = m_renderer->get_opengl_functions (); + + //printf ("patch_tessellator::begin (%d)\n", type); + m_first = true; + + if (m_color_mode == INTERP || m_light_mode == GOURAUD) + glfcns.glShadeModel (GL_SMOOTH); + else + glfcns.glShadeModel (GL_FLAT); + + if (is_filled ()) + m_renderer->set_polygon_offset (true, m_index); + + glfcns.glBegin (type); } - void - opengl_renderer::draw_axes_boxes (const axes::properties& props) + void end (void) { -#if defined (HAVE_OPENGL) - - if (! props.is_visible ()) - return; - - bool xySym = props.get_xySym (); - bool layer2Dtop = props.get_layer2Dtop (); - bool is2D = props.get_is2D (); - bool isXOrigin = props.xaxislocation_is ("origin") - && ! props.yscale_is ("log"); - bool isYOrigin = props.yaxislocation_is ("origin") - && ! props.xscale_is ("log"); - bool boxFull = (props.get_boxstyle () == "full"); - double linewidth = props.get_linewidth (); - double xPlane = props.get_xPlane (); - double yPlane = props.get_yPlane (); - double zPlane = props.get_zPlane (); - double xPlaneN = props.get_xPlaneN (); - double yPlaneN = props.get_yPlaneN (); - double zPlaneN = props.get_zPlaneN (); - double xpTick = props.get_xpTick (); - double ypTick = props.get_ypTick (); - double zpTick = props.get_zpTick (); - double xpTickN = props.get_xpTickN (); - double ypTickN = props.get_ypTickN (); - double zpTickN = props.get_zpTickN (); - - bool plotyy = (props.has_property ("__plotyy_axes__")); - - // Axes box - - set_linecap ("square"); - set_linestyle ("-", true, linewidth); - - m_glfcns.glBegin (GL_LINES); - - if (layer2Dtop) - std::swap (zpTick, zpTickN); - - // X box - Matrix color = props.get_xcolor_rgb (); - - if (! color.isempty ()) + opengl_functions& glfcns = m_renderer->get_opengl_functions (); + + //printf ("patch_tessellator::end\n"); + glfcns.glEnd (); + m_renderer->set_polygon_offset (false); + } + + void vertex (void *data) + { + opengl_functions& glfcns = m_renderer->get_opengl_functions (); + + vertex_data::vertex_data_rep *v + = reinterpret_cast<vertex_data::vertex_data_rep *> (data); + //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->m_coords(0), v->m_coords(1), v->m_coords(2)); + + // NOTE: OpenGL can re-order vertices. For "flat" coloring of FaceColor + // the first vertex must be identified in the draw_patch routine. + + if (m_color_mode == INTERP || (m_color_mode == FLAT && ! is_filled ())) { - set_color (color); - - if (! isXOrigin || props.is_box() || ! is2D) + Matrix col = v->m_color; + + if (col.numel () == 3) { - m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick); - m_glfcns.glVertex3d (xPlane, ypTick, zpTick); - } - - if (props.is_box ()) - { - m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick); - m_glfcns.glVertex3d (xPlane, ypTickN, zpTick); - if (! is2D) + glfcns.glColor4d (col(0), col(1), col(2), v->m_alpha); + if (m_light_mode > 0) { - m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN); - m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN); - if (boxFull) - { - m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN); - m_glfcns.glVertex3d (xPlane, ypTick, zpTickN); - } - } - } - } - - // Y box - color = props.get_ycolor_rgb (); - - if (! color.isempty ()) - { - set_color (color); - if (! isYOrigin || props.is_box() || ! is2D) - { - m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick); - m_glfcns.glVertex3d (xpTick, yPlane, zpTick); - } - - if (props.is_box () && ! plotyy) - { - m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick); - m_glfcns.glVertex3d (xpTickN, yPlane, zpTick); - - if (! is2D) - { - m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN); - m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN); - if (boxFull) - { - m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN); - m_glfcns.glVertex3d (xpTick, yPlane, zpTickN); - } - } - } - } - - // Z box - color = props.get_zcolor_rgb (); - - if (! color.isempty () && ! is2D) - { - set_color (color); - - if (xySym) - { - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); - } - else - { - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); - } - - if (props.is_box ()) - { - m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlane, zPlane); - - if (xySym) - { - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); - m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); - } - else - { - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); - m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); - } - - if (boxFull) - { - m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN); - m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); + // edge lighting only uses ambient light + float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; + + if (m_face_lighting) + for (int k = 0; k < 3; k++) + buf[k] = (v->m_specular + * (v->m_specular_color_refl + + (1 - v->m_specular_color_refl) * col(k))); + glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf); + + if (m_face_lighting) + for (int k = 0; k < 3; k++) + buf[k] = (v->m_diffuse * col(k)); + glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf); + + for (int k = 0; k < 3; k++) + buf[k] = (v->m_ambient * col(k)); + glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf); } } } - m_glfcns.glEnd (); - - set_linestyle ("-"); // Disable LineStipple + if (m_light_mode == FLAT && m_first) + glfcns.glNormal3dv (v->m_face_normal.data ()); + else if (m_light_mode == GOURAUD) + glfcns.glNormal3dv (v->m_vertex_normal.data ()); + + glfcns.glVertex3dv (v->m_coords.data ()); + + m_first = false; + } + + void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data) + { + vertex_data::vertex_data_rep *v[4]; + int vmax = 4; + + for (int i = 0; i < 4; i++) + { + v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]); + + if (vmax == 4 && ! v[i]) + vmax = i; + } + + Matrix vv (1, 3, 0.0); + Matrix cc; + Matrix vnn (1, 3, 0.0); + Matrix fnn (1, 3, 0.0); + double aa = 0.0; + + vv(0) = xyz[0]; + vv(1) = xyz[1]; + vv(2) = xyz[2]; + + if (v[0]->m_color.numel ()) + { + cc.resize (1, 3, 0.0); + for (int ic = 0; ic < 3; ic++) + for (int iv = 0; iv < vmax; iv++) + cc(ic) += (w[iv] * v[iv]->m_color (ic)); + } + + if (v[0]->m_vertex_normal.numel () > 0) + { + for (int in = 0; in < 3; in++) + for (int iv = 0; iv < vmax; iv++) + vnn(in) += (w[iv] * v[iv]->m_vertex_normal (in)); + } + + if (v[0]->m_face_normal.numel () > 0) + { + for (int in = 0; in < 3; in++) + for (int iv = 0; iv < vmax; iv++) + fnn(in) += (w[iv] * v[iv]->m_face_normal (in)); + } + + for (int iv = 0; iv < vmax; iv++) + aa += (w[iv] * v[iv]->m_alpha); + + vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->m_ambient, v[0]->m_diffuse, + v[0]->m_specular, v[0]->m_specular_exp, + v[0]->m_specular_color_refl); + m_tmp_vdata.push_back (new_v); + + *out_data = new_v.get_rep (); + } + +private: + + // No copying! + + patch_tessellator (const patch_tessellator&) = delete; + + patch_tessellator& operator = (const patch_tessellator&) = delete; + + opengl_renderer *m_renderer; + int m_color_mode; + int m_light_mode; + bool m_face_lighting; + int m_index; + bool m_first; + std::list<vertex_data> m_tmp_vdata; +}; #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); +class +opengl_renderer::patch_tessellator +{ + // Dummy class. +}; #endif - } - - void - opengl_renderer::draw_axes_x_grid (const axes::properties& props) - { + +opengl_renderer::opengl_renderer (opengl_functions& glfcns) + : m_glfcns (glfcns), m_xmin (), m_xmax (), m_ymin (), m_ymax (), + m_zmin (), m_zmax (), m_devpixratio (1.0), m_xform (), m_toolkit (), + m_xZ1 (), m_xZ2 (), m_marker_id (), m_filled_marker_id (), + m_camera_pos (), m_camera_dir (), m_view_vector (), + m_interpreter ("none"), m_txt_renderer (), m_current_light (0), + m_max_lights (0), m_selecting (false), m_printing (false) +{ + // This constructor will fail if we don't have OpenGL or if the data + // types we assumed in our public interface aren't compatible with the + // OpenGL types. + #if defined (HAVE_OPENGL) - gh_manager& gh_mgr = __get_gh_manager__ (); - - int xstate = props.get_xstate (); - - if (xstate != AXE_DEPTH_DIR - && (props.is_visible () - || (m_selecting && props.pickableparts_is ("all")))) - { - int zstate = props.get_zstate (); - bool x2Dtop = props.get_x2Dtop (); - bool layer2Dtop = props.get_layer2Dtop (); - bool xyzSym = props.get_xyzSym (); - bool nearhoriz = props.get_nearhoriz (); - double xticklen = props.get_xticklen (); - double xtickoffset = props.get_xtickoffset (); - double fy = props.get_fy (); - double fz = props.get_fz (); - double x_min = props.get_x_min (); - double x_max = props.get_x_max (); - double y_min = props.get_y_min (); - double y_max = props.get_y_max (); - double yPlane = props.get_yPlane (); - double yPlaneN = props.get_yPlaneN (); - double ypTick = props.get_ypTick (); - double ypTickN = props.get_ypTickN (); - double zPlane = props.get_zPlane (); - double zPlaneN = props.get_zPlaneN (); - double zpTick = props.get_zpTick (); - double zpTickN = props.get_zpTickN (); - - // X ticks and grid properties - Matrix xticks = m_xform.xscale (props.get_xtick ().matrix_value ()); - Matrix xmticks = m_xform.xscale (props.get_xminortickvalues ().matrix_value ()); - bool do_xminortick = props.is_xminortick () && ! xticks.isempty (); - string_vector xticklabels = props.get_xticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool tick_along_z = nearhoriz || math::isinf (fy); - double linewidth = props.get_linewidth (); - std::string gridstyle = props.get_gridlinestyle (); - std::string minorgridstyle = props.get_minorgridlinestyle (); - Matrix gridcolor = props.get_gridcolor_rgb (); - Matrix minorgridcolor = props.get_minorgridcolor_rgb (); - double gridalpha = props.get_gridalpha (); - double minorgridalpha = props.get_minorgridalpha (); - bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); - bool do_xminorgrid = (props.is_xminorgrid () - && (minorgridstyle != "none") - && ! xticks.isempty ()); - bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D () - && ! props.yscale_is ("log"); - bool is_origin_low = is_origin && (y_min + y_max) < 0; - bool mirror = props.is_box () && xstate != AXE_ANY_DIR; - - // X grid - - // possibly use axis color for gridcolor & minorgridcolor - if (props.gridcolormode_is ("auto")) - if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) - gridcolor = props.get_xcolor_rgb (); - - if (props.minorgridcolormode_is ("auto")) - if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) - minorgridcolor = props.get_xcolor_rgb (); - - if (gridcolor.isempty ()) - do_xgrid = false; - - if (minorgridcolor.isempty ()) - do_xminorgrid = false; - - // set styles when drawing only minor grid - if (do_xminorgrid && ! do_xgrid) - { - gridstyle = minorgridstyle; - gridcolor = minorgridcolor; - gridalpha = minorgridalpha; - do_xgrid = true; - } - - // minor grid lines - if (do_xminorgrid) - render_grid (linewidth, - minorgridstyle, minorgridcolor, minorgridalpha, - xmticks, x_min, x_max, - yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, - 0, (zstate != AXE_DEPTH_DIR)); - - // grid lines - if (do_xgrid) - render_grid (linewidth, - gridstyle, gridcolor, gridalpha, - xticks, x_min, x_max, - yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, - 0, (zstate != AXE_DEPTH_DIR)); - - // Skip drawing axis, ticks, and ticklabels when color is "none" - if (props.xcolor_is ("none")) - return; - - set_color (props.get_xcolor_rgb ()); - - // axis line - double y_axis_pos = 0.; - if (is_origin) - { - y_axis_pos = math::max (math::min (0., y_max), y_min); - m_glfcns.glBegin (GL_LINES); - set_color (props.get_xcolor_rgb ()); - m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick); - m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick); - m_glfcns.glEnd (); - } - - // minor tick marks - if (do_xminortick) - { - if (tick_along_z) - render_tickmarks (xmticks, x_min, x_max, - is_origin ? y_axis_pos : ypTick, ypTick, - zpTick, zpTickN, 0., 0., - (is_origin_low ? -1. : 1.) * - math::signum (zpTick-zpTickN)*fz*xticklen/2, - 0, ! is_origin && mirror); - else - render_tickmarks (xmticks, x_min, x_max, - is_origin ? y_axis_pos : ypTick, ypTickN, - zpTick, zpTick, 0., - (is_origin_low ? -1. : 1.) * - math::signum (ypTick-ypTickN)*fy*xticklen/2, - 0., 0, ! is_origin && mirror); - } - - // tick marks - if (tick_along_z) - render_tickmarks (xticks, x_min, x_max, - is_origin ? y_axis_pos : ypTick, ypTick, - zpTick, zpTickN, 0., 0., - (is_origin_low ? -1. : 1.) * - math::signum (zpTick-zpTickN)*fz*xticklen, - 0, ! is_origin && mirror); - else - render_tickmarks (xticks, x_min, x_max, - is_origin ? y_axis_pos : ypTick, ypTickN, - zpTick, zpTick, 0., - (is_origin_low ? -1. : 1.) * - math::signum (ypTick-ypTickN)*fy*xticklen, - 0., 0, ! is_origin && mirror); - - // tick texts - if (xticklabels.numel () > 0) - { - int halign = (xstate == AXE_HORZ_DIR - ? 1 - : (xyzSym || is_origin_low ? 0 : 2)); - int valign = (xstate == AXE_VERT_DIR - ? 1 - : (x2Dtop || is_origin_low ? 0 : 2)); - - if (tick_along_z) - render_ticktexts (xticks, xticklabels, x_min, x_max, - is_origin ? y_axis_pos : ypTick, - zpTick + - (is_origin_low ? -1. : 1.) * - math::signum (zpTick-zpTickN)*fz*xtickoffset, - 0, halign, valign, wmax, hmax); - else - render_ticktexts (xticks, xticklabels, x_min, x_max, - (is_origin ? y_axis_pos : ypTick) + - (is_origin_low ? -1. : 1.) * - math::signum (ypTick-ypTickN)*fy*xtickoffset, - zpTick, 0, halign, valign, wmax, hmax); - } - - gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on"); - } - else - gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off"); + // Ensure that we can't request an image larger than OpenGL can handle. + // FIXME: should we check signed vs. unsigned? + + static bool ok = (sizeof (int) <= sizeof (GLsizei)); + + if (! ok) + error ("the size of GLsizei is smaller than the size of int"); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + err_disabled_feature ("opengl_renderer", "OpenGL"); + +#endif +} + +void +opengl_renderer::draw (const graphics_object& go, bool toplevel) +{ + if (! go.valid_object ()) + return; + + const base_properties& props = go.get_properties (); + + if (! m_toolkit) + m_toolkit = props.get_toolkit (); + + if (go.isa ("figure")) + draw_figure (dynamic_cast<const figure::properties&> (props)); + else if (go.isa ("axes")) + draw_axes (dynamic_cast<const axes::properties&> (props)); + else if (go.isa ("line")) + draw_line (dynamic_cast<const line::properties&> (props)); + else if (go.isa ("surface")) + draw_surface (dynamic_cast<const surface::properties&> (props)); + else if (go.isa ("patch")) + draw_patch (dynamic_cast<const patch::properties&> (props)); + else if (go.isa ("scatter")) + draw_scatter (dynamic_cast<const scatter::properties&> (props)); + else if (go.isa ("light")) + draw_light (dynamic_cast<const light::properties&> (props)); + else if (go.isa ("hggroup")) + draw_hggroup (dynamic_cast<const hggroup::properties&> (props)); + else if (go.isa ("text")) + draw_text (dynamic_cast<const text::properties&> (props)); + else if (go.isa ("image")) + draw_image (dynamic_cast<const image::properties&> (props)); + else if (go.isa ("uimenu") || go.isa ("uicontrol") + || go.isa ("uicontextmenu") || go.isa ("uitoolbar") + || go.isa ("uipushtool") || go.isa ("uitoggletool") + || go.isa ("uitable")) + ; // SKIP + else if (go.isa ("uipanel")) + { + if (toplevel) + draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go); + } + else if (go.isa ("uibuttongroup")) + { + if (toplevel) + draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go); + } + else + { + warning ("opengl_renderer: cannot render object of type '%s'", + props.graphics_object_name ().c_str ()); + } + +#if defined (HAVE_OPENGL) + + GLenum gl_error = m_glfcns.glGetError (); + if (gl_error) + warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object", + gluErrorString (gl_error), gl_error, + props.graphics_object_name ().c_str ()); + +#endif +} + +void +opengl_renderer::draw_figure (const figure::properties& props) +{ + m_printing = props.is___printing__ (); + + // Initialize OpenGL context + init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); + +#if defined (HAVE_OPENGL) + + props.set___gl_extensions__ (get_string (GL_EXTENSIONS)); + props.set___gl_renderer__ (get_string (GL_RENDERER)); + props.set___gl_vendor__ (get_string (GL_VENDOR)); + props.set___gl_version__ (get_string (GL_VERSION)); + +#endif + + // Draw children + + draw (props.get_all_children (), false); +} + +void +opengl_renderer::draw_uipanel (const uipanel::properties& props, + const graphics_object& go) +{ + graphics_object fig = go.get_ancestor ("figure"); + const figure::properties& figProps + = dynamic_cast<const figure::properties&> (fig.get_properties ()); + + // Initialize OpenGL context + + init_gl_context (figProps.is_graphicssmoothing (), + props.get_backgroundcolor_rgb ()); + + // Draw children + + draw (props.get_all_children (), false); +} + +void +opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props, + const graphics_object& go) +{ + graphics_object fig = go.get_ancestor ("figure"); + const figure::properties& figProps + = dynamic_cast<const figure::properties&> (fig.get_properties ()); + + // Initialize OpenGL context + + init_gl_context (figProps.is_graphicssmoothing (), + props.get_backgroundcolor_rgb ()); + + // Draw children + + draw (props.get_all_children (), false); +} + +void +opengl_renderer::init_gl_context (bool enhanced, const Matrix& c) +{ +#if defined (HAVE_OPENGL) + + // Initialize OpenGL context + + m_glfcns.glEnable (GL_DEPTH_TEST); + m_glfcns.glDepthFunc (GL_LEQUAL); + m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + m_glfcns.glAlphaFunc (GL_GREATER, 0.0f); + m_glfcns.glEnable (GL_NORMALIZE); + m_glfcns.glEnable (GL_BLEND); + + if (enhanced) + { + m_glfcns.glEnable (GL_MULTISAMPLE); + bool has_multisample = false; + if (! m_glfcns.glGetError ()) + { + GLint iMultiSample, iNumSamples; + m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample); + m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples); + if (iMultiSample == GL_TRUE && iNumSamples > 0) + has_multisample = true; + } + + if (! has_multisample) + { + // MultiSample not implemented. Use old-style anti-aliasing + m_glfcns.glDisable (GL_MULTISAMPLE); + // Disabling GL_MULTISAMPLE will raise a gl error if it is not + // implemented. Thus, call glGetError to reset the error state. + m_glfcns.glGetError (); + + m_glfcns.glEnable (GL_LINE_SMOOTH); + m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); + } + } + else + { + m_glfcns.glDisable (GL_LINE_SMOOTH); + } + + // Clear background + + if (c.numel () >= 3) + { + m_glfcns.glClearColor (c(0), c(1), c(2), 1); + m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + GLenum gl_error = m_glfcns.glGetError (); + if (gl_error) + warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context", + gluErrorString (gl_error), gl_error); + +#else + + octave_unused_parameter (enhanced); + octave_unused_parameter (c); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_axes_y_grid (const axes::properties& props) - { +} + +void +opengl_renderer::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) +{ #if defined (HAVE_OPENGL) - gh_manager& gh_mgr = __get_gh_manager__ (); - - int ystate = props.get_ystate (); - - if (ystate != AXE_DEPTH_DIR && props.is_visible () - && (props.is_visible () - || (m_selecting && props.pickableparts_is ("all")))) - { - int zstate = props.get_zstate (); - bool y2Dright = props.get_y2Dright (); - bool layer2Dtop = props.get_layer2Dtop (); - bool xyzSym = props.get_xyzSym (); - bool nearhoriz = props.get_nearhoriz (); - double yticklen = props.get_yticklen (); - double ytickoffset = props.get_ytickoffset (); - double fx = props.get_fx (); - double fz = props.get_fz (); - double xPlane = props.get_xPlane (); - double xPlaneN = props.get_xPlaneN (); - double xpTick = props.get_xpTick (); - double xpTickN = props.get_xpTickN (); - double y_min = props.get_y_min (); - double y_max = props.get_y_max (); - double x_min = props.get_x_min (); - double x_max = props.get_x_max (); - double zPlane = props.get_zPlane (); - double zPlaneN = props.get_zPlaneN (); - double zpTick = props.get_zpTick (); - double zpTickN = props.get_zpTickN (); - - // Y ticks and grid properties - Matrix yticks = m_xform.yscale (props.get_ytick ().matrix_value ()); - Matrix ymticks = m_xform.yscale (props.get_yminortickvalues ().matrix_value ()); - bool do_yminortick = props.is_yminortick () && ! yticks.isempty (); - string_vector yticklabels = props.get_yticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool tick_along_z = nearhoriz || math::isinf (fx); - double linewidth = props.get_linewidth (); - std::string gridstyle = props.get_gridlinestyle (); - std::string minorgridstyle = props.get_minorgridlinestyle (); - Matrix gridcolor = props.get_gridcolor_rgb (); - Matrix minorgridcolor = props.get_minorgridcolor_rgb (); - double gridalpha = props.get_gridalpha (); - double minorgridalpha = props.get_minorgridalpha (); - bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); - bool do_yminorgrid = (props.is_yminorgrid () - && (minorgridstyle != "none") - && ! yticks.isempty ()); - bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D () - && ! props.xscale_is ("log"); - bool is_origin_low = is_origin && (x_min + x_max) < 0; - bool mirror = props.is_box () && ystate != AXE_ANY_DIR - && (! props.has_property ("__plotyy_axes__")); - - // Y grid - - // possibly use axis color for gridcolor & minorgridcolor - if (props.gridcolormode_is ("auto")) - if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) - gridcolor = props.get_ycolor_rgb (); - - if (props.minorgridcolormode_is ("auto")) - if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) - minorgridcolor = props.get_ycolor_rgb (); - - if (gridcolor.isempty ()) - do_ygrid = false; - - if (minorgridcolor.isempty ()) - do_yminorgrid = false; - - // set styles when drawing only minor grid - if (do_yminorgrid && ! do_ygrid) - { - gridstyle = minorgridstyle; - gridcolor = minorgridcolor; - gridalpha = minorgridalpha; - do_ygrid = true; - } - - // minor grid lines - if (do_yminorgrid) - render_grid (linewidth, - minorgridstyle, minorgridcolor, minorgridalpha, - ymticks, y_min, y_max, - xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, - 1, (zstate != AXE_DEPTH_DIR)); - - // grid lines - if (do_ygrid) - render_grid (linewidth, - gridstyle, gridcolor, gridalpha, - yticks, y_min, y_max, - xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, - 1, (zstate != AXE_DEPTH_DIR)); - - // Skip drawing axis, ticks, and ticklabels when color is "none" - if (props.ycolor_is ("none")) - return; - - set_color (props.get_ycolor_rgb ()); - - // axis line - double x_axis_pos = 0.; - if (is_origin) - { - x_axis_pos = math::max (math::min (0., x_max), x_min); - m_glfcns.glBegin (GL_LINES); - set_color (props.get_ycolor_rgb ()); - m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick); - m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick); - m_glfcns.glEnd (); - } - - // minor tick marks - if (do_yminortick) - { - if (tick_along_z) - render_tickmarks (ymticks, y_min, y_max, - is_origin ? x_axis_pos : xpTick, xpTick, - zpTick, zpTickN, 0., 0., - (is_origin_low ? -1. : 1.) * - math::signum (zpTick-zpTickN)*fz*yticklen/2, - 1, ! is_origin && mirror); - else - render_tickmarks (ymticks, y_min, y_max, - is_origin ? x_axis_pos : xpTick, xpTickN, - zpTick, zpTick, - (is_origin_low ? -1. : 1.) * - math::signum (xpTick-xpTickN)*fx*yticklen/2, - 0., 0., 1, ! is_origin && mirror); - } - - // tick marks - if (tick_along_z) - render_tickmarks (yticks, y_min, y_max, - is_origin ? x_axis_pos : xpTick, xpTick, - zpTick, zpTickN, 0., 0., - (is_origin_low ? -1. : 1.) * - math::signum (zpTick-zpTickN)*fz*yticklen, - 1, ! is_origin && mirror); - else - render_tickmarks (yticks, y_min, y_max, - is_origin ? x_axis_pos : xpTick, xpTickN, - zpTick, zpTick, - (is_origin_low ? -1. : 1.) * - math::signum (xPlaneN-xPlane)*fx*yticklen, - 0., 0., 1, ! is_origin && mirror); - - // tick texts - if (yticklabels.numel () > 0) - { - int halign = (ystate == AXE_HORZ_DIR - ? 1 - : (! xyzSym || y2Dright || is_origin_low ? 0 : 2)); - int valign = (ystate == AXE_VERT_DIR - ? 1 - : (is_origin_low ? 0 : 2)); - - if (tick_along_z) - render_ticktexts (yticks, yticklabels, y_min, y_max, - is_origin ? x_axis_pos : xpTick, - zpTick + - (is_origin_low ? -1. : 1.) * - math::signum (zpTick-zpTickN)*fz*ytickoffset, - 1, halign, valign, wmax, hmax); - else - render_ticktexts (yticks, yticklabels, y_min, y_max, - (is_origin ? x_axis_pos : xpTick) + - (is_origin_low ? -1. : 1.) * - math::signum (xpTick-xpTickN)*fx*ytickoffset, - zpTick, 1, halign, valign, wmax, hmax); - } - - gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on"); - } - else - gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off"); + m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha); + set_linestyle (gridstyle, true, linewidth); + m_glfcns.glBegin (GL_LINES); + for (int i = 0; i < ticks.numel (); i++) + { + double val = ticks(i); + if (lim1 <= val && val <= lim2) + { + if (xyz == X_AXIS) + { + m_glfcns.glVertex3d (val, p1N, p2); + m_glfcns.glVertex3d (val, p1, p2); + if (is_3D) + { + m_glfcns.glVertex3d (val, p1, p2N); + m_glfcns.glVertex3d (val, p1, p2); + } + } + else if (xyz == Y_AXIS) + { + m_glfcns.glVertex3d (p1N, val, p2); + m_glfcns.glVertex3d (p1, val, p2); + if (is_3D) + { + m_glfcns.glVertex3d (p1, val, p2N); + m_glfcns.glVertex3d (p1, val, p2); + } + } + else if (xyz == Z_AXIS) + { + m_glfcns.glVertex3d (p1N, p2, val); + m_glfcns.glVertex3d (p1, p2, val); + m_glfcns.glVertex3d (p1, p2N, val); + m_glfcns.glVertex3d (p1, p2, val); + } + } + } + m_glfcns.glEnd (); + set_linestyle ("-"); // Disable LineStipple + double black[3] = {0, 0, 0}; + m_glfcns.glColor3dv (black); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (linewidth); + octave_unused_parameter (gridstyle); + octave_unused_parameter (gridcolor); + octave_unused_parameter (gridalpha); + octave_unused_parameter (ticks); + octave_unused_parameter (lim1); + octave_unused_parameter (lim2); + octave_unused_parameter (p1); + octave_unused_parameter (p1N); + octave_unused_parameter (p2); + octave_unused_parameter (p2N); + octave_unused_parameter (xyz); + octave_unused_parameter (is_3D); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::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) +{ +#if defined (HAVE_OPENGL) + + m_glfcns.glBegin (GL_LINES); + + for (int i = 0; i < ticks.numel (); i++) + { + double val = ticks(i); + + if (lim1 <= val && val <= lim2) + { + if (xyz == X_AXIS) + { + m_glfcns.glVertex3d (val, p1, p2); + m_glfcns.glVertex3d (val, p1+dy, p2+dz); + if (mirror) + { + m_glfcns.glVertex3d (val, p1N, p2N); + m_glfcns.glVertex3d (val, p1N-dy, p2N-dz); + } + } + else if (xyz == Y_AXIS) + { + m_glfcns.glVertex3d (p1, val, p2); + m_glfcns.glVertex3d (p1+dx, val, p2+dz); + if (mirror) + { + m_glfcns.glVertex3d (p1N, val, p2N); + m_glfcns.glVertex3d (p1N-dx, val, p2N-dz); + } + } + else if (xyz == Z_AXIS) + { + m_glfcns.glVertex3d (p1, p2, val); + m_glfcns.glVertex3d (p1+dx, p2+dy, val); + if (mirror) + { + m_glfcns.glVertex3d (p1N, p2N, val); + m_glfcns.glVertex3d (p1N-dx, p2N-dy, val); + } + } + } + } + + m_glfcns.glEnd (); + +#else + + octave_unused_parameter (ticks); + octave_unused_parameter (lim1); + octave_unused_parameter (lim2); + octave_unused_parameter (p1); + octave_unused_parameter (p1N); + octave_unused_parameter (p2); + octave_unused_parameter (p2N); + octave_unused_parameter (dx); + octave_unused_parameter (dy); + octave_unused_parameter (dz); + octave_unused_parameter (xyz); + octave_unused_parameter (mirror); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::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) +{ +#if defined (HAVE_OPENGL) + + int nticks = ticks.numel (); + int nlabels = ticklabels.numel (); + + if (nlabels == 0) + return; + + for (int i = 0; i < nticks; i++) + { + double val = ticks(i); + + if (lim1 <= val && val <= lim2) + { + Matrix b; + + std::string label (ticklabels(i % nlabels)); + label.erase (0, label.find_first_not_of (' ')); + label = label.substr (0, label.find_last_not_of (' ')+1); + + // FIXME: As tick text is transparent, shouldn't it be + // drawn after axes object, for correct rendering? + if (xyz == X_AXIS) + { + b = render_text (label, val, p1, p2, ha, va); + } + else if (xyz == Y_AXIS) + { + b = render_text (label, p1, val, p2, ha, va); + } + else if (xyz == Z_AXIS) + { + b = render_text (label, p1, p2, val, ha, va); + } + + wmax = std::max (wmax, static_cast<int> (b(2))); + hmax = std::max (hmax, static_cast<int> (b(3))); + } + } + +#else + + octave_unused_parameter (ticks); + octave_unused_parameter (ticklabels); + octave_unused_parameter (lim1); + octave_unused_parameter (lim2); + octave_unused_parameter (p1); + octave_unused_parameter (p2); + octave_unused_parameter (xyz); + octave_unused_parameter (ha); + octave_unused_parameter (va); + octave_unused_parameter (wmax); + octave_unused_parameter (hmax); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2) +{ +#if defined (HAVE_OPENGL) + + m_glfcns.glVertex2d (x1, y1); + m_glfcns.glVertex2d (x2, y1); + m_glfcns.glVertex2d (x2, y2); + m_glfcns.glVertex2d (x1, y2); + m_glfcns.glVertex2d (x1, y1); + +#else + + octave_unused_parameter (x1); + octave_unused_parameter (x2); + octave_unused_parameter (y1); + octave_unused_parameter (y2); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_axes_z_grid (const axes::properties& props) - { - gh_manager& gh_mgr = __get_gh_manager__ (); - - int zstate = props.get_zstate (); - - if (zstate != AXE_DEPTH_DIR && props.is_visible () - && (props.is_visible () - || (m_selecting && props.pickableparts_is ("all")))) - { - bool xySym = props.get_xySym (); - bool zSign = props.get_zSign (); - double zticklen = props.get_zticklen (); - double ztickoffset = props.get_ztickoffset (); - double fx = props.get_fx (); - double fy = props.get_fy (); - double xPlane = props.get_xPlane (); - double xPlaneN = props.get_xPlaneN (); - double yPlane = props.get_yPlane (); - double yPlaneN = props.get_yPlaneN (); - double z_min = props.get_z_min (); - double z_max = props.get_z_max (); - - // Z ticks and grid properties - Matrix zticks = m_xform.zscale (props.get_ztick ().matrix_value ()); - Matrix zmticks = m_xform.zscale (props.get_zminortickvalues ().matrix_value ()); - bool do_zminortick = props.is_zminortick () && ! zticks.isempty (); - string_vector zticklabels = props.get_zticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - double linewidth = props.get_linewidth (); - std::string gridstyle = props.get_gridlinestyle (); - std::string minorgridstyle = props.get_minorgridlinestyle (); - Matrix gridcolor = props.get_gridcolor_rgb (); - Matrix minorgridcolor = props.get_minorgridcolor_rgb (); - double gridalpha = props.get_gridalpha (); - double minorgridalpha = props.get_minorgridalpha (); - bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); - bool do_zminorgrid = (props.is_zminorgrid () - && (minorgridstyle != "none") - && ! zticks.isempty ()); - bool mirror = props.is_box () && zstate != AXE_ANY_DIR; - - // Z grid - - // possibly use axis color for gridcolor & minorgridcolor - if (props.gridcolormode_is ("auto")) - if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) - gridcolor = props.get_zcolor_rgb (); - - if (props.minorgridcolormode_is ("auto")) - if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) - minorgridcolor = props.get_zcolor_rgb (); - - if (gridcolor.isempty ()) - do_zgrid = false; - - if (minorgridcolor.isempty ()) - do_zminorgrid = false; - - // set styles when drawing only minor grid - if (do_zminorgrid && ! do_zgrid) - { - gridstyle = minorgridstyle; - gridcolor = minorgridcolor; - gridalpha = minorgridalpha; - do_zgrid = true; - } - - // minor grid lines - if (do_zminorgrid) - render_grid (linewidth, - minorgridstyle, minorgridcolor, minorgridalpha, - zmticks, z_min, z_max, - xPlane, xPlaneN, yPlane, yPlaneN, 2, true); - - // grid lines - if (do_zgrid) - render_grid (linewidth, - gridstyle, gridcolor, gridalpha, - zticks, z_min, z_max, - xPlane, xPlaneN, yPlane, yPlaneN, 2, true); - - // Skip drawing axis, ticks, and ticklabels when color is "none" - if (props.zcolor_is ("none")) - return; - - set_color (props.get_zcolor_rgb ()); - - // minor tick marks - if (do_zminortick) - { - if (xySym) - { - if (math::isinf (fy)) - render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane, - yPlane, yPlane, - math::signum (xPlaneN-xPlane)*fx*zticklen/2, - 0., 0., 2, mirror); - else - render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, - yPlane, yPlane, 0., - math::signum (yPlane-yPlaneN)*fy*zticklen/2, - 0., 2, false); - } - else - { - if (math::isinf (fx)) - render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, - yPlaneN, yPlane, 0., - math::signum (yPlaneN-yPlane)*fy*zticklen/2, - 0., 2, mirror); - else - render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, - yPlaneN, yPlaneN, - math::signum (xPlane-xPlaneN)*fx*zticklen/2, - 0., 0., 2, false); - } - } - - // tick marks - if (xySym) - { - if (math::isinf (fy)) - render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, - yPlane, yPlane, - math::signum (xPlaneN-xPlane)*fx*zticklen, - 0., 0., 2, mirror); - else - render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, - yPlane, yPlane, 0., - math::signum (yPlane-yPlaneN)*fy*zticklen, - 0., 2, false); - } - else - { - if (math::isinf (fx)) - render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, - yPlaneN, yPlane, 0., - math::signum (yPlaneN-yPlane)*fy*zticklen, - 0., 2, mirror); - else - render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, - yPlaneN, yPlane, - math::signum (xPlane-xPlaneN)*fx*zticklen, - 0., 0., 2, false); - } - - // tick texts - if (zticklabels.numel () > 0) - { - int halign = 2; - int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2)); - - if (xySym) - { - if (math::isinf (fy)) - render_ticktexts (zticks, zticklabels, z_min, z_max, - xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset, - yPlane, 2, halign, valign, wmax, hmax); - else - render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN, - yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset, - 2, halign, valign, wmax, hmax); - } - else - { - if (math::isinf (fx)) - render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane, - yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset, - 2, halign, valign, wmax, hmax); - else - render_ticktexts (zticks, zticklabels, z_min, z_max, - xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset, - yPlaneN, 2, halign, valign, wmax, hmax); - } - } - - gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on"); - } - else - gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off"); - } - - void - opengl_renderer::draw_axes_grids (const axes::properties& props) - { +} + +void +opengl_renderer::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) +{ #if defined (HAVE_OPENGL) - // Disable line smoothing for axes - GLboolean antialias; - - m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias); - - if (antialias == GL_TRUE) - m_glfcns.glDisable (GL_LINE_SMOOTH); - - set_linecap ("butt"); - set_linewidth (props.get_linewidth ()); - set_font (props); - set_interpreter (props.get_ticklabelinterpreter ()); - - draw_axes_x_grid (props); - draw_axes_y_grid (props); - draw_axes_z_grid (props); - - if (antialias == GL_TRUE) - m_glfcns.glEnable (GL_LINE_SMOOTH); + + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glPushMatrix (); + m_glfcns.glLoadIdentity (); + + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glPushMatrix (); + m_glfcns.glLoadIdentity (); + m_glfcns.glOrtho (0, width, height, 0, 1, -1); + + m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT); + m_glfcns.glDisable (GL_DEPTH_TEST); + + m_glfcns.glBegin (GL_POLYGON); + m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2), + overlayalpha); + draw_zoom_rect (x1, y1, x2, y2); + m_glfcns.glEnd (); + + m_glfcns.glLineWidth (borderwidth); + m_glfcns.glBegin (GL_LINE_STRIP); + m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2), + borderalpha); + draw_zoom_rect (x1, y1, x2, y2); + m_glfcns.glEnd (); + + m_glfcns.glPopAttrib (); + + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glPopMatrix (); + + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glPopMatrix (); + #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (width); + octave_unused_parameter (height); + octave_unused_parameter (x1); + octave_unused_parameter (x2); + octave_unused_parameter (y1); + octave_unused_parameter (y2); + octave_unused_parameter (overlaycolor); + octave_unused_parameter (overlayalpha); + octave_unused_parameter (bordercolor); + octave_unused_parameter (borderalpha); + octave_unused_parameter (borderwidth); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +uint8NDArray +opengl_renderer::get_pixels (int width, int height) +{ +#if defined (HAVE_OPENGL) + + m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1); + uint8NDArray pix(dim_vector (3, width, height), 0); + + m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, + pix.fortran_vec ()); + + // Permute and flip data + Array<octave_idx_type> perm (dim_vector (3, 1)); + perm(0) = 2; + perm(1) = 1; + perm(2) = 0; + + Array<idx_vector> idx (dim_vector (3, 1)); + idx(0) = idx_vector::make_range (height - 1, -1, height); + idx(1) = idx_vector::colon; + idx(2) = idx_vector::colon; + + return pix.permute (perm).index (idx); + +#else + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + octave_unused_parameter (width); + octave_unused_parameter (height); + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_all_lights (const base_properties& props, - std::list<graphics_object>& obj_list) - { +} + +void +opengl_renderer::finish (void) +{ +#if defined (HAVE_OPENGL) + + m_glfcns.glFinish (); + +#else + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::setup_opengl_transformation (const axes::properties& props) +{ #if defined (HAVE_OPENGL) - gh_manager& gh_mgr = __get_gh_manager__ (); - - Matrix children = props.get_all_children (); - - for (octave_idx_type i = children.numel () - 1; i >= 0; i--) - { - graphics_object go = gh_mgr.get_object (children(i)); - - base_properties p = go.get_properties (); - - if (p.is_visible () - || (m_selecting && p.pickableparts_is ("all"))) - { - if (go.isa ("light") && ! m_selecting) - { - if (m_current_light-GL_LIGHT0 < m_max_lights) - { - set_clipping (p.is_clipping ()); - draw (go); - m_current_light++; - } - } - else if (go.isa ("hggroup") - && ! (m_selecting && p.pickableparts_is ("none"))) - draw_all_lights (go.get_properties (), obj_list); - else if (! (m_selecting && p.pickableparts_is ("none"))) - obj_list.push_back (go); - } - } + + // setup OpenGL transformation + + Matrix x_zlim = props.get_transform_zlim (); + + // Expand the distance between the clipping planes symmetrically by + // an arbitrary factor (see bug #54551). + const double expansion_fac = 100.0; + // Also make sure that the distance between the clipping planes + // differs in single precision (see bug #58956). This factor is also + // arbitrary. Different values (>2) might also work. + const double single_prec_fac = 10.0; + + double avgZ = x_zlim(0) / 2.0 + x_zlim(1) / 2.0; + double span + = std::max (expansion_fac * (x_zlim(1)-x_zlim(0)), + single_prec_fac * std::abs (avgZ) + * std::numeric_limits<float>::epsilon ()); + m_xZ1 = avgZ - span; + m_xZ2 = avgZ + span; + + Matrix x_mat1 = props.get_opengl_matrix_1 (); + Matrix x_mat2 = props.get_opengl_matrix_2 (); + + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glLoadIdentity (); + m_glfcns.glScaled (1, 1, -1); + m_glfcns.glMultMatrixd (x_mat1.data ()); + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glLoadIdentity (); + + Matrix vp = get_viewport_scaled (); + m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2); + m_glfcns.glMultMatrixd (x_mat2.data ()); + m_glfcns.glMatrixMode (GL_MODELVIEW); + + m_glfcns.glClear (GL_DEPTH_BUFFER_BIT); + + // store axes transformation data + + m_xform = props.get_transform (); + #else - octave_unused_parameter (props); - octave_unused_parameter (obj_list); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_axes_planes (const axes::properties& props) +{ +#if defined (HAVE_OPENGL) + + Matrix axe_color = props.get_color_rgb (); + if (axe_color.isempty () || ! props.is_visible ()) + return; + + double xPlane = props.get_xPlane (); + double yPlane = props.get_yPlane (); + double zPlane = props.get_zPlane (); + double xPlaneN = props.get_xPlaneN (); + double yPlaneN = props.get_yPlaneN (); + double zPlaneN = props.get_zPlaneN (); + bool is2D = props.get_is2D (); + + // Axes planes + set_color (axe_color); + set_polygon_offset (true, 9.0); + + m_glfcns.glBegin (GL_QUADS); + + if (! is2D) + { + // X plane + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlane, zPlane); + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); + + // Y plane + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlane, zPlane); + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); + } + + // Z plane + m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); + m_glfcns.glVertex3d (xPlane, yPlane, zPlane); + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); + + m_glfcns.glEnd (); + + set_polygon_offset (false); + +#else + + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_axes_children (const axes::properties& props) - { +} + +void +opengl_renderer::draw_axes_boxes (const axes::properties& props) +{ #if defined (HAVE_OPENGL) - // list for non-light child objects - std::list<graphics_object> obj_list; - std::list<graphics_object>::iterator it; - - // 1st pass: draw light objects - - // FIXME: max_lights only needs to be set once. - // It would be better if this could be in the constructor for gl_renderer - // but this seems to lead to calls of OpenGL functions before the context - // is actually initialized. See bug #48669. - // Check actual maximum number of lights possible - init_maxlights (); - - // Start with the last element of the array of child objects to - // display them in the order they were added to the array. - - if (props.get_num_lights () > m_max_lights) - warning_with_id ("Octave:max-lights-exceeded", - "light: Maximum number of lights (%d) in these axes is " - "exceeded.", m_max_lights); - - m_current_light = GL_LIGHT0; - draw_all_lights (props, obj_list); - - // disable other OpenGL lights - for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++) - m_glfcns.glDisable (GL_LIGHT0 + i); - - // save camera position and set ambient light color before drawing - // other objects - m_view_vector = props.get_cameraposition ().matrix_value (); - - float cb[4] = { 1.0, 1.0, 1.0, 1.0 }; - ColumnVector ambient_color = props.get_ambientlightcolor_rgb (); - for (int i = 0; i < 3; i++) - cb[i] = ambient_color(i); - m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb); - - // 2nd pass: draw other objects (with units set to "data") - - it = obj_list.begin (); - while (it != obj_list.end ()) - { - graphics_object go = (*it); - - // FIXME: check whether object has "units" property and it is set - // to "data" - if (! go.isa ("text") || go.get ("units").string_value () == "data") - { - set_clipping (go.get_properties ().is_clipping ()); - draw (go); - - it = obj_list.erase (it); - } - else - it++; - } - - // 3rd pass: draw remaining objects - - m_glfcns.glDisable (GL_DEPTH_TEST); - - for (it = obj_list.begin (); it != obj_list.end (); it++) - { - graphics_object go = (*it); - - set_clipping (go.get_properties ().is_clipping ()); - draw (go); - } - - set_clipping (false); - - // FIXME: finalize rendering (transparency processing) - // FIXME: draw zoom box, if needed - -#else - - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::draw_axes (const axes::properties& props) - { -#if defined (HAVE_OPENGL) - - // Legends are not drawn when "visible" is "off". - if (! props.is_visible () && props.get_tag () == "legend") - return; - - // Don't draw the axes and its children if we are in selection and - // pickable parts is "none". - if (m_selecting && props.pickableparts_is ("none")) - return; - - static double floatmax = std::numeric_limits<float>::max (); - - double x_min = props.get_x_min (); - double x_max = props.get_x_max (); - double y_min = props.get_y_min (); - double y_max = props.get_y_max (); - double z_min = props.get_z_min (); - double z_max = props.get_z_max (); - - if (x_max > floatmax || y_max > floatmax || z_max > floatmax - || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax) - { - warning ("opengl_renderer: data values greater than float capacity. (1) Scale data, or (2) Use gnuplot"); - return; - } - - setup_opengl_transformation (props); - - // For 2D axes with only 2D primitives, draw from back to front without - // depth sorting - bool is2D = props.get_is2D (true); - if (is2D) - m_glfcns.glDisable (GL_DEPTH_TEST); - else - m_glfcns.glEnable (GL_DEPTH_TEST); - - draw_axes_planes (props); - - if (! is2D || props.layer_is ("bottom")) - { - draw_axes_grids (props); - if (props.get_tag () != "legend" || props.get_box () != "off") - draw_axes_boxes (props); - } - - set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max); - - draw_axes_children (props); - - if (is2D && props.layer_is ("top")) - { - draw_axes_grids (props); - if (props.get_tag () != "legend" || props.get_box () != "off") - draw_axes_boxes (props); - } + + if (! props.is_visible ()) + return; + + bool xySym = props.get_xySym (); + bool layer2Dtop = props.get_layer2Dtop (); + bool is2D = props.get_is2D (); + bool isXOrigin = props.xaxislocation_is ("origin") + && ! props.yscale_is ("log"); + bool isYOrigin = props.yaxislocation_is ("origin") + && ! props.xscale_is ("log"); + bool boxFull = (props.get_boxstyle () == "full"); + double linewidth = props.get_linewidth (); + double xPlane = props.get_xPlane (); + double yPlane = props.get_yPlane (); + double zPlane = props.get_zPlane (); + double xPlaneN = props.get_xPlaneN (); + double yPlaneN = props.get_yPlaneN (); + double zPlaneN = props.get_zPlaneN (); + double xpTick = props.get_xpTick (); + double ypTick = props.get_ypTick (); + double zpTick = props.get_zpTick (); + double xpTickN = props.get_xpTickN (); + double ypTickN = props.get_ypTickN (); + double zpTickN = props.get_zpTickN (); + + bool plotyy = (props.has_property ("__plotyy_axes__")); + + // Axes box + + set_linecap ("square"); + set_linestyle ("-", true, linewidth); + + m_glfcns.glBegin (GL_LINES); + + if (layer2Dtop) + std::swap (zpTick, zpTickN); + + // X box + Matrix color = props.get_xcolor_rgb (); + + if (! color.isempty ()) + { + set_color (color); + + if (! isXOrigin || props.is_box() || ! is2D) + { + m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick); + m_glfcns.glVertex3d (xPlane, ypTick, zpTick); + } + + if (props.is_box ()) + { + m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick); + m_glfcns.glVertex3d (xPlane, ypTickN, zpTick); + if (! is2D) + { + m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN); + m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN); + if (boxFull) + { + m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN); + m_glfcns.glVertex3d (xPlane, ypTick, zpTickN); + } + } + } + } + + // Y box + color = props.get_ycolor_rgb (); + + if (! color.isempty ()) + { + set_color (color); + if (! isYOrigin || props.is_box() || ! is2D) + { + m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick); + m_glfcns.glVertex3d (xpTick, yPlane, zpTick); + } + + if (props.is_box () && ! plotyy) + { + m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick); + m_glfcns.glVertex3d (xpTickN, yPlane, zpTick); + + if (! is2D) + { + m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN); + m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN); + if (boxFull) + { + m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN); + m_glfcns.glVertex3d (xpTick, yPlane, zpTickN); + } + } + } + } + + // Z box + color = props.get_zcolor_rgb (); + + if (! color.isempty () && ! is2D) + { + set_color (color); + + if (xySym) + { + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); + } + else + { + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); + } + + if (props.is_box ()) + { + m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlane, zPlane); + + if (xySym) + { + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); + m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); + } + else + { + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); + m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); + } + + if (boxFull) + { + m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN); + m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); + } + } + } + + m_glfcns.glEnd (); + + set_linestyle ("-"); // Disable LineStipple #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_line (const line::properties& props) - { +} + +void +opengl_renderer::draw_axes_x_grid (const axes::properties& props) +{ #if defined (HAVE_OPENGL) - bool draw_all = m_selecting && props.pickableparts_is ("all"); - - Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ()); - Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ()); - Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ()); - - bool has_z = (z.numel () > 0); - int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()), - (has_z ? z.numel () - : std::numeric_limits<int>::max ()))); - uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); - uint8_t clip_ok = 0x40; - - std::vector<uint8_t> clip (n); - - if (has_z) - for (int i = 0; i < n; i++) - clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask); - else - { - double z_mid = (m_zmin+m_zmax)/2; - - for (int i = 0; i < n; i++) - clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask); - } - - if (! props.linestyle_is ("none") && ! props.color_is ("none")) - { - set_color (props.get_color_rgb ()); - set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); - set_linewidth (props.get_linewidth ()); - set_linecap ("butt"); - set_linejoin (props.get_linejoin ()); - - if (has_z) - { - bool flag = false; - - for (int i = 1; i < n; i++) - { - if ((clip[i-1] & clip[i]) == clip_ok) - { - if (! flag) - { - flag = true; - m_glfcns.glBegin (GL_LINE_STRIP); - m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1)); - } - m_glfcns.glVertex3d (x(i), y(i), z(i)); - } - else if (flag) - { - flag = false; - m_glfcns.glEnd (); - } - } - - if (flag) - m_glfcns.glEnd (); - } - else - { - bool flag = false; - - for (int i = 1; i < n; i++) - { - if ((clip[i-1] & clip[i]) == clip_ok) - { - if (! flag) - { - flag = true; - m_glfcns.glBegin (GL_LINE_STRIP); - m_glfcns.glVertex2d (x(i-1), y(i-1)); - } - m_glfcns.glVertex2d (x(i), y(i)); - } - else if (flag) - { - flag = false; - m_glfcns.glEnd (); - } - } - - if (flag) - m_glfcns.glEnd (); - } - - set_linewidth (0.5f); - set_linestyle ("-"); - } - - set_clipping (false); - - if (! props.marker_is ("none") - && ! (props.markeredgecolor_is ("none") - && props.markerfacecolor_is ("none"))) - { - Matrix lc, fc; - - if (draw_all) - lc = Matrix (1, 3, 0.0); - else if (props.markeredgecolor_is ("auto")) - lc = props.get_color_rgb (); - else if (! props.markeredgecolor_is ("none")) - lc = props.get_markeredgecolor_rgb (); - - if (draw_all) - fc = Matrix (1, 3, 0.0); - if (props.markerfacecolor_is ("auto")) - fc = props.get_color_rgb (); - else if (! props.markerfacecolor_is ("none")) - fc = props.get_markerfacecolor_rgb (); - - init_marker (props.get_marker (), props.get_markersize (), - props.get_linewidth ()); - - for (int i = 0; i < n; i++) - { - if (clip[i] == clip_ok) - draw_marker (x(i), y(i), - has_z ? z(i) : 0.0, - lc, fc); - } - - end_marker (); - } - - set_clipping (props.is_clipping ()); + gh_manager& gh_mgr = __get_gh_manager__ (); + + int xstate = props.get_xstate (); + + if (xstate != AXE_DEPTH_DIR + && (props.is_visible () + || (m_selecting && props.pickableparts_is ("all")))) + { + int zstate = props.get_zstate (); + bool x2Dtop = props.get_x2Dtop (); + bool layer2Dtop = props.get_layer2Dtop (); + bool xyzSym = props.get_xyzSym (); + bool nearhoriz = props.get_nearhoriz (); + double xticklen = props.get_xticklen (); + double xtickoffset = props.get_xtickoffset (); + double fy = props.get_fy (); + double fz = props.get_fz (); + double x_min = props.get_x_min (); + double x_max = props.get_x_max (); + double y_min = props.get_y_min (); + double y_max = props.get_y_max (); + double yPlane = props.get_yPlane (); + double yPlaneN = props.get_yPlaneN (); + double ypTick = props.get_ypTick (); + double ypTickN = props.get_ypTickN (); + double zPlane = props.get_zPlane (); + double zPlaneN = props.get_zPlaneN (); + double zpTick = props.get_zpTick (); + double zpTickN = props.get_zpTickN (); + + // X ticks and grid properties + Matrix xticks = m_xform.xscale (props.get_xtick ().matrix_value ()); + Matrix xmticks = m_xform.xscale (props.get_xminortickvalues ().matrix_value ()); + bool do_xminortick = props.is_xminortick () && ! xticks.isempty (); + string_vector xticklabels = props.get_xticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool tick_along_z = nearhoriz || math::isinf (fy); + double linewidth = props.get_linewidth (); + std::string gridstyle = props.get_gridlinestyle (); + std::string minorgridstyle = props.get_minorgridlinestyle (); + Matrix gridcolor = props.get_gridcolor_rgb (); + Matrix minorgridcolor = props.get_minorgridcolor_rgb (); + double gridalpha = props.get_gridalpha (); + double minorgridalpha = props.get_minorgridalpha (); + bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); + bool do_xminorgrid = (props.is_xminorgrid () + && (minorgridstyle != "none") + && ! xticks.isempty ()); + bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D () + && ! props.yscale_is ("log"); + bool is_origin_low = is_origin && (y_min + y_max) < 0; + bool mirror = props.is_box () && xstate != AXE_ANY_DIR; + + // X grid + + // possibly use axis color for gridcolor & minorgridcolor + if (props.gridcolormode_is ("auto")) + if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) + gridcolor = props.get_xcolor_rgb (); + + if (props.minorgridcolormode_is ("auto")) + if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) + minorgridcolor = props.get_xcolor_rgb (); + + if (gridcolor.isempty ()) + do_xgrid = false; + + if (minorgridcolor.isempty ()) + do_xminorgrid = false; + + // set styles when drawing only minor grid + if (do_xminorgrid && ! do_xgrid) + { + gridstyle = minorgridstyle; + gridcolor = minorgridcolor; + gridalpha = minorgridalpha; + do_xgrid = true; + } + + // minor grid lines + if (do_xminorgrid) + render_grid (linewidth, + minorgridstyle, minorgridcolor, minorgridalpha, + xmticks, x_min, x_max, + yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, + 0, (zstate != AXE_DEPTH_DIR)); + + // grid lines + if (do_xgrid) + render_grid (linewidth, + gridstyle, gridcolor, gridalpha, + xticks, x_min, x_max, + yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, + 0, (zstate != AXE_DEPTH_DIR)); + + // Skip drawing axis, ticks, and ticklabels when color is "none" + if (props.xcolor_is ("none")) + return; + + set_color (props.get_xcolor_rgb ()); + + // axis line + double y_axis_pos = 0.; + if (is_origin) + { + y_axis_pos = math::max (math::min (0., y_max), y_min); + m_glfcns.glBegin (GL_LINES); + set_color (props.get_xcolor_rgb ()); + m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick); + m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick); + m_glfcns.glEnd (); + } + + // minor tick marks + if (do_xminortick) + { + if (tick_along_z) + render_tickmarks (xmticks, x_min, x_max, + is_origin ? y_axis_pos : ypTick, ypTick, + zpTick, zpTickN, 0., 0., + (is_origin_low ? -1. : 1.) * + math::signum (zpTick-zpTickN)*fz*xticklen/2, + 0, ! is_origin && mirror); + else + render_tickmarks (xmticks, x_min, x_max, + is_origin ? y_axis_pos : ypTick, ypTickN, + zpTick, zpTick, 0., + (is_origin_low ? -1. : 1.) * + math::signum (ypTick-ypTickN)*fy*xticklen/2, + 0., 0, ! is_origin && mirror); + } + + // tick marks + if (tick_along_z) + render_tickmarks (xticks, x_min, x_max, + is_origin ? y_axis_pos : ypTick, ypTick, + zpTick, zpTickN, 0., 0., + (is_origin_low ? -1. : 1.) * + math::signum (zpTick-zpTickN)*fz*xticklen, + 0, ! is_origin && mirror); + else + render_tickmarks (xticks, x_min, x_max, + is_origin ? y_axis_pos : ypTick, ypTickN, + zpTick, zpTick, 0., + (is_origin_low ? -1. : 1.) * + math::signum (ypTick-ypTickN)*fy*xticklen, + 0., 0, ! is_origin && mirror); + + // tick texts + if (xticklabels.numel () > 0) + { + int halign = (xstate == AXE_HORZ_DIR + ? 1 + : (xyzSym || is_origin_low ? 0 : 2)); + int valign = (xstate == AXE_VERT_DIR + ? 1 + : (x2Dtop || is_origin_low ? 0 : 2)); + + if (tick_along_z) + render_ticktexts (xticks, xticklabels, x_min, x_max, + is_origin ? y_axis_pos : ypTick, + zpTick + + (is_origin_low ? -1. : 1.) * + math::signum (zpTick-zpTickN)*fz*xtickoffset, + 0, halign, valign, wmax, hmax); + else + render_ticktexts (xticks, xticklabels, x_min, x_max, + (is_origin ? y_axis_pos : ypTick) + + (is_origin_low ? -1. : 1.) * + math::signum (ypTick-ypTickN)*fy*xtickoffset, + zpTick, 0, halign, valign, wmax, hmax); + } + + gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on"); + } + else + gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off"); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_surface (const surface::properties& props) - { +} + +void +opengl_renderer::draw_axes_y_grid (const axes::properties& props) +{ #if defined (HAVE_OPENGL) - bool draw_all = m_selecting && props.pickableparts_is ("all"); - - const Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ()); - const Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ()); - const Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ()); - - int zr = z.rows (); - int zc = z.columns (); - - NDArray c; - const NDArray vn = props.get_vertexnormals ().array_value (); - dim_vector vn_dims = vn.dims (); - bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc - && vn_dims(2) == 3); - const NDArray fn = props.get_facenormals ().array_value (); - dim_vector fn_dims = fn.dims (); - bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1 - && fn_dims(2) == 3); - - // FIXME: handle transparency - Matrix a; - - int fc_mode = (props.facecolor_is_rgb () ? 0 : - (props.facecolor_is ("flat") ? 1 : - (props.facecolor_is ("interp") ? 2 : - (props.facecolor_is ("texturemap") ? 3 : -1)))); - int fl_mode = (props.facelighting_is ("none") ? 0 : - (props.facelighting_is ("flat") ? - (has_face_normals ? 1 : 0) : - (has_vertex_normals ? 2 : 0))); - int fa_mode = (props.facealpha_is_double () ? 0 : - (props.facealpha_is ("flat") ? 1 : 2)); - int ec_mode = (props.edgecolor_is_rgb () ? 0 : - (props.edgecolor_is ("flat") ? 1 : - (props.edgecolor_is ("interp") ? 2 : -1))); - int el_mode = (props.edgelighting_is ("none") ? 0 : - (props.edgelighting_is ("flat") ? - (has_face_normals ? 1 : 0) : - (has_vertex_normals ? 2 : 0))); - int ea_mode = (props.edgealpha_is_double () ? 0 : - (props.edgealpha_is ("flat") ? 1 : 2)); - int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : - (props.backfacelighting_is ("reverselit") ? 1 : 2)); - bool do_lighting = props.get_do_lighting (); - - Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0) - : props.get_facecolor_rgb ()); - Matrix ecolor = props.get_edgecolor_rgb (); - double fa = 1.0; - - float as = props.get_ambientstrength (); - float ds = props.get_diffusestrength (); - float ss = props.get_specularstrength (); - float se = props.get_specularexponent () * 5; // to fit Matlab - float scr = props.get_specularcolorreflectance (); - float cb[4] = { 0.0, 0.0, 0.0, 1.0 }; - - opengl_texture tex (m_glfcns); - - int i1, i2, j1, j2; - bool x_mat = (x.rows () == z.rows ()); - bool y_mat = (y.columns () == z.columns ()); - - i1 = i2 = j1 = j2 = 0; - - if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0) - c = props.get_color_data ().array_value (); - - boolMatrix clip (z.dims (), false); - - for (int i = 0; i < zr; i++) - { - if (x_mat) - i1 = i; - - for (int j = 0; j < zc; j++) - { - if (y_mat) - j1 = j; - - clip(i,j) = is_nan_or_inf (x(i1,j), y(i,j1), z(i,j)); - } - } - - if (fa_mode > 0 || ea_mode > 0) - { - // FIXME: implement alphadata conversion - //a = props.get_alpha_data (); - } - - if (fl_mode > 0 || el_mode > 0) - m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); - - // FIXME: good candidate for caching, - // transferring pixel data to OpenGL is time consuming. - if (fc_mode == TEXTURE) - tex = opengl_texture::create (m_glfcns, props.get_color_data ()); - - if (draw_all || ! props.facecolor_is ("none")) - { - if (fa_mode == 0) - { - fa = props.get_facealpha_double (); - cb[3] = fa; - if (fc_mode == UNIFORM || fc_mode == TEXTURE) - { - m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); - if (fl_mode > 0) - { - for (int i = 0; i < 3; i++) - cb[i] = as * fcolor(i); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * fcolor(i); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * fcolor(i)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((fl_mode > 0) && do_lighting) - m_glfcns.glEnable (GL_LIGHTING); - m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD) - ? GL_SMOOTH : GL_FLAT); - set_polygon_offset (true, 1.0); - if (fc_mode == TEXTURE) - m_glfcns.glEnable (GL_TEXTURE_2D); - - for (int i = 1; i < zc; i++) - { - if (y_mat) - { - i1 = i-1; - i2 = i; - } - - for (int j = 1; j < zr; j++) - { - - if (clip(j-1, i-1) || clip(j, i-1) - || clip(j-1, i) || clip(j, i)) - continue; - - if (fc_mode == FLAT) - { - // "flat" only needs color at lower-left vertex - if (! math::isfinite (c(j-1,i-1))) - continue; - } - else if (fc_mode == INTERP) - { - // "interp" needs valid color at all 4 vertices - if (! (math::isfinite (c(j-1, i-1)) - && math::isfinite (c(j, i-1)) - && math::isfinite (c(j-1, i)) - && math::isfinite (c(j, i)))) - continue; - } - - if (x_mat) - { - j1 = j-1; - j2 = j; - } - - m_glfcns.glBegin (GL_QUADS); - - // Vertex 1 - if (fc_mode == TEXTURE) - tex.tex_coord (double (i-1) / (zc-1), - double (j-1) / (zr-1)); - else if (fc_mode > 0) - { - // FIXME: is there a smarter way to do this? - for (int k = 0; k < 3; k++) - cb[k] = c(j-1, i-1, k); - m_glfcns.glColor4fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j-1, i-1, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (fl_mode > 0) - set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn), - j-1, i-1); - - m_glfcns.glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1)); - - // Vertex 2 - if (fc_mode == TEXTURE) - tex.tex_coord (double (i) / (zc-1), - double (j-1) / (zr-1)); - else if (fc_mode == INTERP) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j-1, i, k); - m_glfcns.glColor4fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j-1, i, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if (fl_mode == GOURAUD) - set_normal (bfl_mode, vn, j-1, i); - - m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); - - // Vertex 3 - if (fc_mode == TEXTURE) - tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1)); - else if (fc_mode == INTERP) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j, i, k); - m_glfcns.glColor4fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (fl_mode == GOURAUD) - set_normal (bfl_mode, vn, j, i); - - m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); - - // Vertex 4 - if (fc_mode == TEXTURE) - tex.tex_coord (double (i-1) / (zc-1), - double (j) / (zr-1)); - else if (fc_mode == INTERP) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j, i-1, k); - m_glfcns.glColor4fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i-1, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (fl_mode == GOURAUD) - set_normal (bfl_mode, vn, j, i-1); - - m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); - - m_glfcns.glEnd (); - } - } - - set_polygon_offset (false); - if (fc_mode == TEXTURE) - m_glfcns.glDisable (GL_TEXTURE_2D); - - if ((fl_mode > 0) && do_lighting) - m_glfcns.glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement flat, interp and texturemap transparency - } - } - - if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")) - { - if (props.get_edgealpha_double () == 1) - { - cb[3] = 1.0; // edgealpha isn't implemented yet - if (ec_mode == UNIFORM) - { - m_glfcns.glColor3dv (ecolor.data ()); - if (el_mode > 0) - { - for (int i = 0; i < 3; i++) - cb[i] = as * ecolor(i); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * ecolor(i); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * ecolor(i)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((el_mode > 0) && do_lighting) - m_glfcns.glEnable (GL_LIGHTING); - m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) - ? GL_SMOOTH : GL_FLAT); - - set_linestyle (props.get_linestyle (), false, - props.get_linewidth ()); - set_linewidth (props.get_linewidth ()); - set_linecap ("butt"); - set_linejoin ("miter"); - - // Mesh along Y-axis - - if (props.meshstyle_is ("both") || props.meshstyle_is ("column")) - { - for (int i = 0; i < zc; i++) - { - if (y_mat) - { - i1 = i-1; - i2 = i; - } - - for (int j = 1; j < zr; j++) - { - if (clip(j-1,i) || clip(j,i)) - continue; - - if (ec_mode == FLAT) - { - // "flat" only needs color at lower-left vertex - if (! math::isfinite (c(j-1,i))) - continue; - } - else if (ec_mode == INTERP) - { - // "interp" needs valid color at both vertices - if (! (math::isfinite (c(j-1, i)) - && math::isfinite (c(j, i)))) - continue; - } - - if (x_mat) - { - j1 = j-1; - j2 = j; - } - - m_glfcns.glBegin (GL_LINES); - - // Vertex 1 - if (ec_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j-1, i, k); - m_glfcns.glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j-1, i, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, - cb); - } - } - if (el_mode > 0) - { - if (el_mode == GOURAUD) - set_normal (bfl_mode, vn, j-1, i); - else - set_normal (bfl_mode, fn, j-1, std::min (i, zc-2)); - } - - m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); - - // Vertex 2 - if (ec_mode == INTERP) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j, i, k); - m_glfcns.glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, - cb); - } - } - if (el_mode == GOURAUD) - set_normal (bfl_mode, vn, j, i); - - m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); - - m_glfcns.glEnd (); - } - } - } - - // Mesh along X-axis - - if (props.meshstyle_is ("both") || props.meshstyle_is ("row")) - { - for (int j = 0; j < zr; j++) - { - if (x_mat) - { - j1 = j-1; - j2 = j; - } - - for (int i = 1; i < zc; i++) - { - if (clip(j,i-1) || clip(j,i)) - continue; - - if (ec_mode == FLAT) - { - // "flat" only needs color at lower-left vertex - if (! math::isfinite (c(j,i-1))) - continue; - } - else if (ec_mode == INTERP) - { - // "interp" needs valid color at both vertices - if (! (math::isfinite (c(j, i-1)) - && math::isfinite (c(j, i)))) - continue; - } - - if (y_mat) - { - i1 = i-1; - i2 = i; - } - - m_glfcns.glBegin (GL_LINES); - - // Vertex 1 - if (ec_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j, i-1, k); - m_glfcns.glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i-1, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, - cb); - } - } - if (el_mode > 0) - { - if (el_mode == GOURAUD) - set_normal (bfl_mode, vn, j, i-1); - else - set_normal (bfl_mode, fn, std::min (j, zr-2), i-1); - } - - m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); - - // Vertex 2 - if (ec_mode == INTERP) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j, i, k); - m_glfcns.glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i, k); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, - cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i, k)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, - cb); - } - } - if (el_mode == GOURAUD) - set_normal (bfl_mode, vn, j, i); - - m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); - - m_glfcns.glEnd (); - } - } - } - - set_linestyle ("-"); // Disable LineStipple - set_linewidth (0.5f); - - if ((el_mode > 0) && do_lighting) - m_glfcns.glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement transparency - } - } - - if (! props.marker_is ("none") - && ! (props.markeredgecolor_is ("none") - && props.markerfacecolor_is ("none"))) - { - // FIXME: check how transparency should be handled in markers - // FIXME: check what to do with marker facecolor set to auto - // and facecolor set to none. - - bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); - bool do_face = draw_all || ! props.markerfacecolor_is ("none"); - - Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : - props.get_markeredgecolor_rgb ()); - Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : - props.get_markerfacecolor_rgb ()); - Matrix cc (1, 3, 0.0); - - if (mecolor.isempty () && props.markeredgecolor_is ("auto")) - { - mecolor = props.get_edgecolor_rgb (); - do_edge = ! props.edgecolor_is ("none"); - } - - if (mfcolor.isempty () && props.markerfacecolor_is ("auto")) - { - mfcolor = props.get_facecolor_rgb (); - do_face = ! props.facecolor_is ("none"); - } - - if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ()) - c = props.get_color_data ().array_value (); - - init_marker (props.get_marker (), props.get_markersize (), - props.get_linewidth ()); - - uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); - uint8_t clip_ok = 0x40; - - for (int i = 0; i < zc; i++) - { - if (y_mat) - i1 = i; - - for (int j = 0; j < zr; j++) - { - if (x_mat) - j1 = j; - - if ((clip_code (x(j1,i), y(j,i1), z(j,i)) & clip_mask) - != clip_ok) - continue; - - if ((do_edge && mecolor.isempty ()) - || (do_face && mfcolor.isempty ())) - { - if (! math::isfinite (c(j,i))) - continue; // Skip NaNs in color data - - for (int k = 0; k < 3; k++) - cc(k) = c(j,i,k); - } - - Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) - : Matrix ()); - Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) - : Matrix ()); - - draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc); - } - } - - end_marker (); - } + gh_manager& gh_mgr = __get_gh_manager__ (); + + int ystate = props.get_ystate (); + + if (ystate != AXE_DEPTH_DIR && props.is_visible () + && (props.is_visible () + || (m_selecting && props.pickableparts_is ("all")))) + { + int zstate = props.get_zstate (); + bool y2Dright = props.get_y2Dright (); + bool layer2Dtop = props.get_layer2Dtop (); + bool xyzSym = props.get_xyzSym (); + bool nearhoriz = props.get_nearhoriz (); + double yticklen = props.get_yticklen (); + double ytickoffset = props.get_ytickoffset (); + double fx = props.get_fx (); + double fz = props.get_fz (); + double xPlane = props.get_xPlane (); + double xPlaneN = props.get_xPlaneN (); + double xpTick = props.get_xpTick (); + double xpTickN = props.get_xpTickN (); + double y_min = props.get_y_min (); + double y_max = props.get_y_max (); + double x_min = props.get_x_min (); + double x_max = props.get_x_max (); + double zPlane = props.get_zPlane (); + double zPlaneN = props.get_zPlaneN (); + double zpTick = props.get_zpTick (); + double zpTickN = props.get_zpTickN (); + + // Y ticks and grid properties + Matrix yticks = m_xform.yscale (props.get_ytick ().matrix_value ()); + Matrix ymticks = m_xform.yscale (props.get_yminortickvalues ().matrix_value ()); + bool do_yminortick = props.is_yminortick () && ! yticks.isempty (); + string_vector yticklabels = props.get_yticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool tick_along_z = nearhoriz || math::isinf (fx); + double linewidth = props.get_linewidth (); + std::string gridstyle = props.get_gridlinestyle (); + std::string minorgridstyle = props.get_minorgridlinestyle (); + Matrix gridcolor = props.get_gridcolor_rgb (); + Matrix minorgridcolor = props.get_minorgridcolor_rgb (); + double gridalpha = props.get_gridalpha (); + double minorgridalpha = props.get_minorgridalpha (); + bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); + bool do_yminorgrid = (props.is_yminorgrid () + && (minorgridstyle != "none") + && ! yticks.isempty ()); + bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D () + && ! props.xscale_is ("log"); + bool is_origin_low = is_origin && (x_min + x_max) < 0; + bool mirror = props.is_box () && ystate != AXE_ANY_DIR + && (! props.has_property ("__plotyy_axes__")); + + // Y grid + + // possibly use axis color for gridcolor & minorgridcolor + if (props.gridcolormode_is ("auto")) + if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) + gridcolor = props.get_ycolor_rgb (); + + if (props.minorgridcolormode_is ("auto")) + if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) + minorgridcolor = props.get_ycolor_rgb (); + + if (gridcolor.isempty ()) + do_ygrid = false; + + if (minorgridcolor.isempty ()) + do_yminorgrid = false; + + // set styles when drawing only minor grid + if (do_yminorgrid && ! do_ygrid) + { + gridstyle = minorgridstyle; + gridcolor = minorgridcolor; + gridalpha = minorgridalpha; + do_ygrid = true; + } + + // minor grid lines + if (do_yminorgrid) + render_grid (linewidth, + minorgridstyle, minorgridcolor, minorgridalpha, + ymticks, y_min, y_max, + xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, + 1, (zstate != AXE_DEPTH_DIR)); + + // grid lines + if (do_ygrid) + render_grid (linewidth, + gridstyle, gridcolor, gridalpha, + yticks, y_min, y_max, + xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, + 1, (zstate != AXE_DEPTH_DIR)); + + // Skip drawing axis, ticks, and ticklabels when color is "none" + if (props.ycolor_is ("none")) + return; + + set_color (props.get_ycolor_rgb ()); + + // axis line + double x_axis_pos = 0.; + if (is_origin) + { + x_axis_pos = math::max (math::min (0., x_max), x_min); + m_glfcns.glBegin (GL_LINES); + set_color (props.get_ycolor_rgb ()); + m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick); + m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick); + m_glfcns.glEnd (); + } + + // minor tick marks + if (do_yminortick) + { + if (tick_along_z) + render_tickmarks (ymticks, y_min, y_max, + is_origin ? x_axis_pos : xpTick, xpTick, + zpTick, zpTickN, 0., 0., + (is_origin_low ? -1. : 1.) * + math::signum (zpTick-zpTickN)*fz*yticklen/2, + 1, ! is_origin && mirror); + else + render_tickmarks (ymticks, y_min, y_max, + is_origin ? x_axis_pos : xpTick, xpTickN, + zpTick, zpTick, + (is_origin_low ? -1. : 1.) * + math::signum (xpTick-xpTickN)*fx*yticklen/2, + 0., 0., 1, ! is_origin && mirror); + } + + // tick marks + if (tick_along_z) + render_tickmarks (yticks, y_min, y_max, + is_origin ? x_axis_pos : xpTick, xpTick, + zpTick, zpTickN, 0., 0., + (is_origin_low ? -1. : 1.) * + math::signum (zpTick-zpTickN)*fz*yticklen, + 1, ! is_origin && mirror); + else + render_tickmarks (yticks, y_min, y_max, + is_origin ? x_axis_pos : xpTick, xpTickN, + zpTick, zpTick, + (is_origin_low ? -1. : 1.) * + math::signum (xPlaneN-xPlane)*fx*yticklen, + 0., 0., 1, ! is_origin && mirror); + + // tick texts + if (yticklabels.numel () > 0) + { + int halign = (ystate == AXE_HORZ_DIR + ? 1 + : (! xyzSym || y2Dright || is_origin_low ? 0 : 2)); + int valign = (ystate == AXE_VERT_DIR + ? 1 + : (is_origin_low ? 0 : 2)); + + if (tick_along_z) + render_ticktexts (yticks, yticklabels, y_min, y_max, + is_origin ? x_axis_pos : xpTick, + zpTick + + (is_origin_low ? -1. : 1.) * + math::signum (zpTick-zpTickN)*fz*ytickoffset, + 1, halign, valign, wmax, hmax); + else + render_ticktexts (yticks, yticklabels, y_min, y_max, + (is_origin ? x_axis_pos : xpTick) + + (is_origin_low ? -1. : 1.) * + math::signum (xpTick-xpTickN)*fx*ytickoffset, + zpTick, 1, halign, valign, wmax, hmax); + } + + gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on"); + } + else + gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off"); + +#else + + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_axes_z_grid (const axes::properties& props) +{ + gh_manager& gh_mgr = __get_gh_manager__ (); + + int zstate = props.get_zstate (); + + if (zstate != AXE_DEPTH_DIR && props.is_visible () + && (props.is_visible () + || (m_selecting && props.pickableparts_is ("all")))) + { + bool xySym = props.get_xySym (); + bool zSign = props.get_zSign (); + double zticklen = props.get_zticklen (); + double ztickoffset = props.get_ztickoffset (); + double fx = props.get_fx (); + double fy = props.get_fy (); + double xPlane = props.get_xPlane (); + double xPlaneN = props.get_xPlaneN (); + double yPlane = props.get_yPlane (); + double yPlaneN = props.get_yPlaneN (); + double z_min = props.get_z_min (); + double z_max = props.get_z_max (); + + // Z ticks and grid properties + Matrix zticks = m_xform.zscale (props.get_ztick ().matrix_value ()); + Matrix zmticks = m_xform.zscale (props.get_zminortickvalues ().matrix_value ()); + bool do_zminortick = props.is_zminortick () && ! zticks.isempty (); + string_vector zticklabels = props.get_zticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + double linewidth = props.get_linewidth (); + std::string gridstyle = props.get_gridlinestyle (); + std::string minorgridstyle = props.get_minorgridlinestyle (); + Matrix gridcolor = props.get_gridcolor_rgb (); + Matrix minorgridcolor = props.get_minorgridcolor_rgb (); + double gridalpha = props.get_gridalpha (); + double minorgridalpha = props.get_minorgridalpha (); + bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); + bool do_zminorgrid = (props.is_zminorgrid () + && (minorgridstyle != "none") + && ! zticks.isempty ()); + bool mirror = props.is_box () && zstate != AXE_ANY_DIR; + + // Z grid + + // possibly use axis color for gridcolor & minorgridcolor + if (props.gridcolormode_is ("auto")) + if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) + gridcolor = props.get_zcolor_rgb (); + + if (props.minorgridcolormode_is ("auto")) + if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) + minorgridcolor = props.get_zcolor_rgb (); + + if (gridcolor.isempty ()) + do_zgrid = false; + + if (minorgridcolor.isempty ()) + do_zminorgrid = false; + + // set styles when drawing only minor grid + if (do_zminorgrid && ! do_zgrid) + { + gridstyle = minorgridstyle; + gridcolor = minorgridcolor; + gridalpha = minorgridalpha; + do_zgrid = true; + } + + // minor grid lines + if (do_zminorgrid) + render_grid (linewidth, + minorgridstyle, minorgridcolor, minorgridalpha, + zmticks, z_min, z_max, + xPlane, xPlaneN, yPlane, yPlaneN, 2, true); + + // grid lines + if (do_zgrid) + render_grid (linewidth, + gridstyle, gridcolor, gridalpha, + zticks, z_min, z_max, + xPlane, xPlaneN, yPlane, yPlaneN, 2, true); + + // Skip drawing axis, ticks, and ticklabels when color is "none" + if (props.zcolor_is ("none")) + return; + + set_color (props.get_zcolor_rgb ()); + + // minor tick marks + if (do_zminortick) + { + if (xySym) + { + if (math::isinf (fy)) + render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane, + yPlane, yPlane, + math::signum (xPlaneN-xPlane)*fx*zticklen/2, + 0., 0., 2, mirror); + else + render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, + yPlane, yPlane, 0., + math::signum (yPlane-yPlaneN)*fy*zticklen/2, + 0., 2, false); + } + else + { + if (math::isinf (fx)) + render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, + yPlaneN, yPlane, 0., + math::signum (yPlaneN-yPlane)*fy*zticklen/2, + 0., 2, mirror); + else + render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, + yPlaneN, yPlaneN, + math::signum (xPlane-xPlaneN)*fx*zticklen/2, + 0., 0., 2, false); + } + } + + // tick marks + if (xySym) + { + if (math::isinf (fy)) + render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, + yPlane, yPlane, + math::signum (xPlaneN-xPlane)*fx*zticklen, + 0., 0., 2, mirror); + else + render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, + yPlane, yPlane, 0., + math::signum (yPlane-yPlaneN)*fy*zticklen, + 0., 2, false); + } + else + { + if (math::isinf (fx)) + render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, + yPlaneN, yPlane, 0., + math::signum (yPlaneN-yPlane)*fy*zticklen, + 0., 2, mirror); + else + render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, + yPlaneN, yPlane, + math::signum (xPlane-xPlaneN)*fx*zticklen, + 0., 0., 2, false); + } + + // tick texts + if (zticklabels.numel () > 0) + { + int halign = 2; + int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2)); + + if (xySym) + { + if (math::isinf (fy)) + render_ticktexts (zticks, zticklabels, z_min, z_max, + xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset, + yPlane, 2, halign, valign, wmax, hmax); + else + render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN, + yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset, + 2, halign, valign, wmax, hmax); + } + else + { + if (math::isinf (fx)) + render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane, + yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset, + 2, halign, valign, wmax, hmax); + else + render_ticktexts (zticks, zticklabels, z_min, z_max, + xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset, + yPlaneN, 2, halign, valign, wmax, hmax); + } + } + + gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on"); + } + else + gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off"); +} + +void +opengl_renderer::draw_axes_grids (const axes::properties& props) +{ +#if defined (HAVE_OPENGL) + // Disable line smoothing for axes + GLboolean antialias; + + m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias); + + if (antialias == GL_TRUE) + m_glfcns.glDisable (GL_LINE_SMOOTH); + + set_linecap ("butt"); + set_linewidth (props.get_linewidth ()); + set_font (props); + set_interpreter (props.get_ticklabelinterpreter ()); + + draw_axes_x_grid (props); + draw_axes_y_grid (props); + draw_axes_z_grid (props); + + if (antialias == GL_TRUE) + m_glfcns.glEnable (GL_LINE_SMOOTH); +#else + + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_all_lights (const base_properties& props, + std::list<graphics_object>& obj_list) +{ +#if defined (HAVE_OPENGL) + gh_manager& gh_mgr = __get_gh_manager__ (); + + Matrix children = props.get_all_children (); + + for (octave_idx_type i = children.numel () - 1; i >= 0; i--) + { + graphics_object go = gh_mgr.get_object (children(i)); + + base_properties p = go.get_properties (); + + if (p.is_visible () + || (m_selecting && p.pickableparts_is ("all"))) + { + if (go.isa ("light") && ! m_selecting) + { + if (m_current_light-GL_LIGHT0 < m_max_lights) + { + set_clipping (p.is_clipping ()); + draw (go); + m_current_light++; + } + } + else if (go.isa ("hggroup") + && ! (m_selecting && p.pickableparts_is ("none"))) + draw_all_lights (go.get_properties (), obj_list); + else if (! (m_selecting && p.pickableparts_is ("none"))) + obj_list.push_back (go); + } + } +#else + + octave_unused_parameter (props); + octave_unused_parameter (obj_list); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_axes_children (const axes::properties& props) +{ +#if defined (HAVE_OPENGL) + // list for non-light child objects + std::list<graphics_object> obj_list; + std::list<graphics_object>::iterator it; + + // 1st pass: draw light objects + + // FIXME: max_lights only needs to be set once. + // It would be better if this could be in the constructor for gl_renderer + // but this seems to lead to calls of OpenGL functions before the context + // is actually initialized. See bug #48669. + // Check actual maximum number of lights possible + init_maxlights (); + + // Start with the last element of the array of child objects to + // display them in the order they were added to the array. + + if (props.get_num_lights () > m_max_lights) + warning_with_id ("Octave:max-lights-exceeded", + "light: Maximum number of lights (%d) in these axes is " + "exceeded.", m_max_lights); + + m_current_light = GL_LIGHT0; + draw_all_lights (props, obj_list); + + // disable other OpenGL lights + for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++) + m_glfcns.glDisable (GL_LIGHT0 + i); + + // save camera position and set ambient light color before drawing + // other objects + m_view_vector = props.get_cameraposition ().matrix_value (); + + float cb[4] = { 1.0, 1.0, 1.0, 1.0 }; + ColumnVector ambient_color = props.get_ambientlightcolor_rgb (); + for (int i = 0; i < 3; i++) + cb[i] = ambient_color(i); + m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb); + + // 2nd pass: draw other objects (with units set to "data") + + it = obj_list.begin (); + while (it != obj_list.end ()) + { + graphics_object go = (*it); + + // FIXME: check whether object has "units" property and it is set + // to "data" + if (! go.isa ("text") || go.get ("units").string_value () == "data") + { + set_clipping (go.get_properties ().is_clipping ()); + draw (go); + + it = obj_list.erase (it); + } + else + it++; + } + + // 3rd pass: draw remaining objects + + m_glfcns.glDisable (GL_DEPTH_TEST); + + for (it = obj_list.begin (); it != obj_list.end (); it++) + { + graphics_object go = (*it); + + set_clipping (go.get_properties ().is_clipping ()); + draw (go); + } + + set_clipping (false); + + // FIXME: finalize rendering (transparency processing) + // FIXME: draw zoom box, if needed + +#else + + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_axes (const axes::properties& props) +{ +#if defined (HAVE_OPENGL) + + // Legends are not drawn when "visible" is "off". + if (! props.is_visible () && props.get_tag () == "legend") + return; + + // Don't draw the axes and its children if we are in selection and + // pickable parts is "none". + if (m_selecting && props.pickableparts_is ("none")) + return; + + static double floatmax = std::numeric_limits<float>::max (); + + double x_min = props.get_x_min (); + double x_max = props.get_x_max (); + double y_min = props.get_y_min (); + double y_max = props.get_y_max (); + double z_min = props.get_z_min (); + double z_max = props.get_z_max (); + + if (x_max > floatmax || y_max > floatmax || z_max > floatmax + || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax) + { + warning ("opengl_renderer: data values greater than float capacity. (1) Scale data, or (2) Use gnuplot"); + return; + } + + setup_opengl_transformation (props); + + // For 2D axes with only 2D primitives, draw from back to front without + // depth sorting + bool is2D = props.get_is2D (true); + if (is2D) + m_glfcns.glDisable (GL_DEPTH_TEST); + else + m_glfcns.glEnable (GL_DEPTH_TEST); + + draw_axes_planes (props); + + if (! is2D || props.layer_is ("bottom")) + { + draw_axes_grids (props); + if (props.get_tag () != "legend" || props.get_box () != "off") + draw_axes_boxes (props); + } + + set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max); + + draw_axes_children (props); + + if (is2D && props.layer_is ("top")) + { + draw_axes_grids (props); + if (props.get_tag () != "legend" || props.get_box () != "off") + draw_axes_boxes (props); + } #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::draw_line (const line::properties& props) +{ +#if defined (HAVE_OPENGL) + + bool draw_all = m_selecting && props.pickableparts_is ("all"); + + Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ()); + Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ()); + Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ()); + + bool has_z = (z.numel () > 0); + int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()), + (has_z ? z.numel () + : std::numeric_limits<int>::max ()))); + uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); + uint8_t clip_ok = 0x40; + + std::vector<uint8_t> clip (n); + + if (has_z) + for (int i = 0; i < n; i++) + clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask); + else + { + double z_mid = (m_zmin+m_zmax)/2; + + for (int i = 0; i < n; i++) + clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask); + } + + if (! props.linestyle_is ("none") && ! props.color_is ("none")) + { + set_color (props.get_color_rgb ()); + set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); + set_linewidth (props.get_linewidth ()); + set_linecap ("butt"); + set_linejoin (props.get_linejoin ()); + + if (has_z) + { + bool flag = false; + + for (int i = 1; i < n; i++) + { + if ((clip[i-1] & clip[i]) == clip_ok) + { + if (! flag) + { + flag = true; + m_glfcns.glBegin (GL_LINE_STRIP); + m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1)); + } + m_glfcns.glVertex3d (x(i), y(i), z(i)); + } + else if (flag) + { + flag = false; + m_glfcns.glEnd (); + } + } + + if (flag) + m_glfcns.glEnd (); + } + else + { + bool flag = false; + + for (int i = 1; i < n; i++) + { + if ((clip[i-1] & clip[i]) == clip_ok) + { + if (! flag) + { + flag = true; + m_glfcns.glBegin (GL_LINE_STRIP); + m_glfcns.glVertex2d (x(i-1), y(i-1)); + } + m_glfcns.glVertex2d (x(i), y(i)); + } + else if (flag) + { + flag = false; + m_glfcns.glEnd (); + } + } + + if (flag) + m_glfcns.glEnd (); + } + + set_linewidth (0.5f); + set_linestyle ("-"); + } + + set_clipping (false); + + if (! props.marker_is ("none") + && ! (props.markeredgecolor_is ("none") + && props.markerfacecolor_is ("none"))) + { + Matrix lc, fc; + + if (draw_all) + lc = Matrix (1, 3, 0.0); + else if (props.markeredgecolor_is ("auto")) + lc = props.get_color_rgb (); + else if (! props.markeredgecolor_is ("none")) + lc = props.get_markeredgecolor_rgb (); + + if (draw_all) + fc = Matrix (1, 3, 0.0); + if (props.markerfacecolor_is ("auto")) + fc = props.get_color_rgb (); + else if (! props.markerfacecolor_is ("none")) + fc = props.get_markerfacecolor_rgb (); + + init_marker (props.get_marker (), props.get_markersize (), + props.get_linewidth ()); + + for (int i = 0; i < n; i++) + { + if (clip[i] == clip_ok) + draw_marker (x(i), y(i), + has_z ? z(i) : 0.0, + lc, fc); + } + + end_marker (); + } + + set_clipping (props.is_clipping ()); + +#else + + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - // FIXME: global optimization (rendering, data structures...), - // there is probably a smarter/faster/less-memory-consuming way to do this. - void - opengl_renderer::draw_patch (const patch::properties& props) - { +} + +void +opengl_renderer::draw_surface (const surface::properties& props) +{ #if defined (HAVE_OPENGL) - // Do not render if the patch has incoherent data - std::string msg; - if (props.has_bad_data (msg)) - { - warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); - return; - } - - bool draw_all = m_selecting && props.pickableparts_is ("all"); - const Matrix f = props.get_faces ().matrix_value (); - const Matrix v = m_xform.scale (props.get_vertices ().matrix_value ()); - Matrix c; - Matrix a; - double fa = 1.0; - - int nv = v.rows (); - int nf = f.rows (); - int fcmax = f.columns (); - - bool has_z = (v.columns () > 2); - bool has_facecolor = false; - bool has_facealpha = false; - - int fc_mode = ((props.facecolor_is ("none") - || props.facecolor_is_rgb () || draw_all) ? 0 : - (props.facecolor_is ("flat") ? 1 : 2)); - int fl_mode = (props.facelighting_is ("none") ? 0 : - (props.facelighting_is ("flat") ? 1 : 2)); - int fa_mode = (props.facealpha_is_double () ? 0 : - (props.facealpha_is ("flat") ? 1 : 2)); - int ec_mode = ((props.edgecolor_is ("none") - || props.edgecolor_is_rgb ()) ? 0 : - (props.edgecolor_is ("flat") ? 1 : 2)); - int el_mode = (props.edgelighting_is ("none") ? 0 : - (props.edgelighting_is ("flat") ? 1 : 2)); - int ea_mode = (props.edgealpha_is_double () ? 0 : - (props.edgealpha_is ("flat") ? 1 : 2)); - int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : - (props.backfacelighting_is ("reverselit") ? 1 : 2)); - bool do_lighting = props.get_do_lighting (); - - Matrix fcolor = props.get_facecolor_rgb (); - Matrix ecolor = props.get_edgecolor_rgb (); - - float as = props.get_ambientstrength (); - float ds = props.get_diffusestrength (); - float ss = props.get_specularstrength (); - float se = props.get_specularexponent () * 5; // to fit Matlab - float scr = props.get_specularcolorreflectance (); - - const Matrix vn = props.get_vertexnormals ().matrix_value (); - bool has_vertex_normals = (vn.rows () == nv); - const Matrix fn = props.get_facenormals ().matrix_value (); - bool has_face_normals = (fn.rows () == nf); - - boolMatrix clip (1, nv, false); - - if (has_z) - for (int i = 0; i < nv; i++) - clip(i) = is_nan_or_inf (v(i,0), v(i,1), v(i,2)); - else - for (int i = 0; i < nv; i++) - clip(i) = is_nan_or_inf (v(i,0), v(i,1), 0); - - boolMatrix clip_f (1, nf, false); - Array<int> count_f (dim_vector (nf, 1), 0); - - for (int i = 0; i < nf; i++) - { - bool fclip = false; - int count = 0; - - for (int j = 0; j < fcmax && ! math::isnan (f(i,j)); j++, count++) - fclip = (fclip || clip(int (f(i,j) - 1))); - - clip_f(i) = fclip; - count_f(i) = count; - } - - if (draw_all || fc_mode > 0 || ec_mode > 0) - { - if (draw_all) - c = Matrix (1, 3, 0.0); - else - c = props.get_color_data ().matrix_value (); - - if (c.rows () == 1) - { - // Single color specifications, we can simplify a little bit - - if (draw_all || fc_mode > 0) - { - fcolor = c; - fc_mode = UNIFORM; - } - - if (draw_all || ec_mode > 0) - { - ecolor = c; - ec_mode = UNIFORM; - } - - c = Matrix (); - } - else - has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ())); - } - - if (fa_mode > 0 || ea_mode > 0) - { - // FIXME: retrieve alpha data from patch object - //a = props.get_alpha_data (); - has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ())); - } - - if (fa_mode == 0) - fa = props.get_facealpha_double (); - - octave_idx_type fr = f.rows (); - std::vector<vertex_data> vdata (f.numel ()); - - for (int i = 0; i < nf; i++) - for (int j = 0; j < count_f(i); j++) + bool draw_all = m_selecting && props.pickableparts_is ("all"); + + const Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ()); + const Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ()); + const Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ()); + + int zr = z.rows (); + int zc = z.columns (); + + NDArray c; + const NDArray vn = props.get_vertexnormals ().array_value (); + dim_vector vn_dims = vn.dims (); + bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc + && vn_dims(2) == 3); + const NDArray fn = props.get_facenormals ().array_value (); + dim_vector fn_dims = fn.dims (); + bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1 + && fn_dims(2) == 3); + + // FIXME: handle transparency + Matrix a; + + int fc_mode = (props.facecolor_is_rgb () ? 0 : + (props.facecolor_is ("flat") ? 1 : + (props.facecolor_is ("interp") ? 2 : + (props.facecolor_is ("texturemap") ? 3 : -1)))); + int fl_mode = (props.facelighting_is ("none") ? 0 : + (props.facelighting_is ("flat") ? + (has_face_normals ? 1 : 0) : + (has_vertex_normals ? 2 : 0))); + int fa_mode = (props.facealpha_is_double () ? 0 : + (props.facealpha_is ("flat") ? 1 : 2)); + int ec_mode = (props.edgecolor_is_rgb () ? 0 : + (props.edgecolor_is ("flat") ? 1 : + (props.edgecolor_is ("interp") ? 2 : -1))); + int el_mode = (props.edgelighting_is ("none") ? 0 : + (props.edgelighting_is ("flat") ? + (has_face_normals ? 1 : 0) : + (has_vertex_normals ? 2 : 0))); + int ea_mode = (props.edgealpha_is_double () ? 0 : + (props.edgealpha_is ("flat") ? 1 : 2)); + int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : + (props.backfacelighting_is ("reverselit") ? 1 : 2)); + bool do_lighting = props.get_do_lighting (); + + Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0) + : props.get_facecolor_rgb ()); + Matrix ecolor = props.get_edgecolor_rgb (); + double fa = 1.0; + + float as = props.get_ambientstrength (); + float ds = props.get_diffusestrength (); + float ss = props.get_specularstrength (); + float se = props.get_specularexponent () * 5; // to fit Matlab + float scr = props.get_specularcolorreflectance (); + float cb[4] = { 0.0, 0.0, 0.0, 1.0 }; + + opengl_texture tex (m_glfcns); + + int i1, i2, j1, j2; + bool x_mat = (x.rows () == z.rows ()); + bool y_mat = (y.columns () == z.columns ()); + + i1 = i2 = j1 = j2 = 0; + + if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0) + c = props.get_color_data ().array_value (); + + boolMatrix clip (z.dims (), false); + + for (int i = 0; i < zr; i++) + { + if (x_mat) + i1 = i; + + for (int j = 0; j < zc; j++) + { + if (y_mat) + j1 = j; + + clip(i, j) = is_nan_or_inf (x(i1, j), y(i, j1), z(i, j)); + } + } + + if (fa_mode > 0 || ea_mode > 0) + { + // FIXME: implement alphadata conversion + //a = props.get_alpha_data (); + } + + if (fl_mode > 0 || el_mode > 0) + m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); + + // FIXME: good candidate for caching, + // transferring pixel data to OpenGL is time consuming. + if (fc_mode == TEXTURE) + tex = opengl_texture::create (m_glfcns, props.get_color_data ()); + + if (draw_all || ! props.facecolor_is ("none")) + { + if (fa_mode == 0) { - int idx = int (f(i,j) - 1); - - Matrix vv (1, 3, 0.0); - Matrix cc; - Matrix vnn (1, 3, 0.0); - Matrix fnn (1, 3, 0.0); - double aa = 1.0; - - vv(0) = v(idx,0); vv(1) = v(idx,1); - if (has_z) - vv(2) = v(idx,2); - if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals) + fa = props.get_facealpha_double (); + cb[3] = fa; + if (fc_mode == UNIFORM || fc_mode == TEXTURE) { - double dir = 1.0; - if (bfl_mode > 0) - dir = ((fn(i,0) * m_view_vector(0) - + fn(i,1) * m_view_vector(1) - + fn(i,2) * m_view_vector(2) < 0) - ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); - fnn(0) = dir * fn(i,0); - fnn(1) = dir * fn(i,1); - fnn(2) = dir * fn(i,2); + m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); + if (fl_mode > 0) + { + for (int i = 0; i < 3; i++) + cb[i] = as * fcolor(i); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * fcolor(i); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * fcolor(i)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } } - if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals) + + if ((fl_mode > 0) && do_lighting) + m_glfcns.glEnable (GL_LIGHTING); + m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD) + ? GL_SMOOTH : GL_FLAT); + set_polygon_offset (true, 1.0); + if (fc_mode == TEXTURE) + m_glfcns.glEnable (GL_TEXTURE_2D); + + for (int i = 1; i < zc; i++) { - double dir = 1.0; - if (bfl_mode > 0) - dir = ((vn(idx,0) * m_view_vector(0) - + vn(idx,1) * m_view_vector(1) - + vn(idx,2) * m_view_vector(2) < 0) - ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); - vnn(0) = dir * vn(idx,0); - vnn(1) = dir * vn(idx,1); - vnn(2) = dir * vn(idx,2); + if (y_mat) + { + i1 = i-1; + i2 = i; + } + + for (int j = 1; j < zr; j++) + { + + if (clip(j-1, i-1) || clip(j, i-1) + || clip(j-1, i) || clip(j, i)) + continue; + + if (fc_mode == FLAT) + { + // "flat" only needs color at lower-left vertex + if (! math::isfinite (c(j-1, i-1))) + continue; + } + else if (fc_mode == INTERP) + { + // "interp" needs valid color at all 4 vertices + if (! (math::isfinite (c(j-1, i-1)) + && math::isfinite (c(j, i-1)) + && math::isfinite (c(j-1, i)) + && math::isfinite (c(j, i)))) + continue; + } + + if (x_mat) + { + j1 = j-1; + j2 = j; + } + + m_glfcns.glBegin (GL_QUADS); + + // Vertex 1 + if (fc_mode == TEXTURE) + tex.tex_coord (double (i-1) / (zc-1), + double (j-1) / (zr-1)); + else if (fc_mode > 0) + { + // FIXME: is there a smarter way to do this? + for (int k = 0; k < 3; k++) + cb[k] = c(j-1, i-1, k); + m_glfcns.glColor4fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j-1, i-1, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (fl_mode > 0) + set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn), + j-1, i-1); + + m_glfcns.glVertex3d (x(j1, i-1), y(j-1, i1), z(j-1, i-1)); + + // Vertex 2 + if (fc_mode == TEXTURE) + tex.tex_coord (double (i) / (zc-1), + double (j-1) / (zr-1)); + else if (fc_mode == INTERP) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j-1, i, k); + m_glfcns.glColor4fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j-1, i, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if (fl_mode == GOURAUD) + set_normal (bfl_mode, vn, j-1, i); + + m_glfcns.glVertex3d (x(j1, i), y(j-1, i2), z(j-1, i)); + + // Vertex 3 + if (fc_mode == TEXTURE) + tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1)); + else if (fc_mode == INTERP) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j, i, k); + m_glfcns.glColor4fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (fl_mode == GOURAUD) + set_normal (bfl_mode, vn, j, i); + + m_glfcns.glVertex3d (x(j2, i), y(j, i2), z(j, i)); + + // Vertex 4 + if (fc_mode == TEXTURE) + tex.tex_coord (double (i-1) / (zc-1), + double (j) / (zr-1)); + else if (fc_mode == INTERP) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j, i-1, k); + m_glfcns.glColor4fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i-1, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (fl_mode == GOURAUD) + set_normal (bfl_mode, vn, j, i-1); + + m_glfcns.glVertex3d (x(j2, i-1), y(j, i1), z(j, i-1)); + + m_glfcns.glEnd (); + } } - if (c.numel () > 0) - { - cc.resize (1, 3); - if (has_facecolor) - cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2); - else - cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2); - } - if (fa_mode == 0) - aa = fa; - else if (a.numel () > 0) - { - if (has_facealpha) - aa = a(i); - else - aa = a(idx); - } - - vdata[i+j*fr] - = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr); + + set_polygon_offset (false); + if (fc_mode == TEXTURE) + m_glfcns.glDisable (GL_TEXTURE_2D); + + if ((fl_mode > 0) && do_lighting) + m_glfcns.glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement flat, interp and texturemap transparency } - - if (fl_mode > 0 || el_mode > 0) - m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); - - if (draw_all || ! props.facecolor_is ("none")) - { - // FIXME: adapt to double-radio property - if (fa_mode == 0) - { - if (fc_mode == UNIFORM) - { - m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); - if (fl_mode > 0) - { - float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; - - for (int i = 0; i < 3; i++) - cb[i] = as * fcolor(i); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * fcolor(i); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * fcolor(i)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((fl_mode > 0) && do_lighting) - m_glfcns.glEnable (GL_LIGHTING); - - // NOTE: Push filled part of patch backwards to avoid Z-fighting - // with tessellator outline. A value of 1.0 seems to work fine. - // Value can't be too large or the patch will be pushed below the - // axes planes at +2.5. - patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0); - - std::vector<octave_idx_type>::const_iterator it; - octave_idx_type i_start, i_end; - - for (int i = 0; i < nf; i++) - { - if (clip_f(i)) - continue; - - bool is_non_planar = false; - if (props.m_coplanar_last_idx.size () > 0 - && props.m_coplanar_last_idx[i].size () > 1) - { - is_non_planar = true; - it = props.m_coplanar_last_idx[i].end (); - it--; - } - - // loop over planar subsets of face - do - { - if (is_non_planar) - { - i_end = *it; - if (it == props.m_coplanar_last_idx[i].begin ()) - i_start = 0; - else - { - it--; - i_start = *it - 1; - } - } - else - { - i_end = count_f(i) - 1; - i_start = 0; - } - - tess.begin_polygon (true); - tess.begin_contour (); - - // Add vertices in reverse order for Matlab compatibility - for (int j = i_end; j > i_start; j--) - { - vertex_data::vertex_data_rep *vv - = vdata[i+j*fr].get_rep (); - - tess.add_vertex (vv->m_coords.fortran_vec (), vv); - } - - if (count_f(i) > 0) - { - vertex_data::vertex_data_rep *vv = vdata[i].get_rep (); - - if (fc_mode == FLAT) - { - // For "flat" shading, use color of 1st vertex. - Matrix col = vv->m_color; - - if (col.numel () == 3) - { - m_glfcns.glColor4d (col(0), col(1), col(2), fa); - if (fl_mode > 0) - { - float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - - for (int k = 0; k < 3; k++) - cb[k] = (vv->m_ambient * col(k)); - m_glfcns.glMaterialfv (LIGHT_MODE, - GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = (vv->m_diffuse * col(k)); - m_glfcns.glMaterialfv (LIGHT_MODE, - GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = vv->m_specular * - (vv->m_specular_color_refl - + (1-vv->m_specular_color_refl) * - col(k)); - m_glfcns.glMaterialfv (LIGHT_MODE, - GL_SPECULAR, cb); - } - } - } - - tess.add_vertex (vv->m_coords.fortran_vec (), vv); - } - - tess.end_contour (); - tess.end_polygon (); - } while (i_start > 0); - } - - if ((fl_mode > 0) && do_lighting) - m_glfcns.glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement flat and interp transparency - } - } - - if (draw_all - || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))) - { - // FIXME: adapt to double-radio property - if (props.get_edgealpha_double () == 1) - { - if (ec_mode == UNIFORM) - { - m_glfcns.glColor3dv (ecolor.data ()); - if (el_mode > 0) - { - // edge lighting only uses ambient light - float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = (as * ecolor(i)); - m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - } - } - - if ((el_mode > 0) && do_lighting) - m_glfcns.glEnable (GL_LIGHTING); - - double linewidth = props.get_linewidth (); - set_linestyle (props.get_linestyle (), false, linewidth); - set_linewidth (linewidth); - set_linecap ("butt"); - set_linejoin ("miter"); - - // NOTE: patch contour cannot be offset. Offset must occur with - // the filled portion of the patch above. The tessellator uses - // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL - // automatically sets the glType to GL_LINE_LOOP. This primitive is - // not supported by glPolygonOffset which is used to do Z offsets. - patch_tessellator tess (this, ec_mode, el_mode, false); - - for (int i = 0; i < nf; i++) - { - bool is_non_planar = false; - if (props.m_coplanar_last_idx.size () > 0 - && props.m_coplanar_last_idx[i].size () > 1) - is_non_planar = true; - if (clip_f(i) || is_non_planar) - { - // This is an unclosed contour or a non-planar face. - // Draw it as a line. - bool flag = false; - - m_glfcns.glShadeModel ((ec_mode == INTERP - || el_mode == GOURAUD) - ? GL_SMOOTH : GL_FLAT); - - // Add vertices in reverse order for Matlab compatibility - for (int j = count_f(i)-1; j >= 0; j--) - { - if (! clip(int (f(i,j) - 1))) - { - vertex_data::vertex_data_rep *vv - = vdata[i+j*fr].get_rep (); - const Matrix m = vv->m_coords; - if (! flag) - { - flag = true; - m_glfcns.glBegin (GL_LINE_STRIP); - } - if (ec_mode != UNIFORM) - { - Matrix col = vv->m_color; - - if (col.numel () == 3) - m_glfcns.glColor3dv (col.data ()); - } - m_glfcns.glVertex3d (m(0), m(1), m(2)); - } - else if (flag) - { - flag = false; - m_glfcns.glEnd (); - } - } - // Do loop body with vertex N to "close" GL_LINE_STRIP - // from vertex 0 to vertex N. - int j = count_f(i)-1; - if (flag && ! clip(int (f(i,j) - 1))) - { - vertex_data::vertex_data_rep *vv - = vdata[i+j*fr].get_rep (); - const Matrix m = vv->m_coords; - if (ec_mode != UNIFORM) - { - Matrix col = vv->m_color; - - if (col.numel () == 3) - m_glfcns.glColor3dv (col.data ()); - } - m_glfcns.glVertex3d (m(0), m(1), m(2)); - } - - if (flag) + } + + if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")) + { + if (props.get_edgealpha_double () == 1) + { + cb[3] = 1.0; // edgealpha isn't implemented yet + if (ec_mode == UNIFORM) + { + m_glfcns.glColor3dv (ecolor.data ()); + if (el_mode > 0) + { + for (int i = 0; i < 3; i++) + cb[i] = as * ecolor(i); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * ecolor(i); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * ecolor(i)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if ((el_mode > 0) && do_lighting) + m_glfcns.glEnable (GL_LIGHTING); + m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) + ? GL_SMOOTH : GL_FLAT); + + set_linestyle (props.get_linestyle (), false, + props.get_linewidth ()); + set_linewidth (props.get_linewidth ()); + set_linecap ("butt"); + set_linejoin ("miter"); + + // Mesh along Y-axis + + if (props.meshstyle_is ("both") || props.meshstyle_is ("column")) + { + for (int i = 0; i < zc; i++) + { + if (y_mat) + { + i1 = i-1; + i2 = i; + } + + for (int j = 1; j < zr; j++) + { + if (clip(j-1, i) || clip(j, i)) + continue; + + if (ec_mode == FLAT) + { + // "flat" only needs color at lower-left vertex + if (! math::isfinite (c(j-1, i))) + continue; + } + else if (ec_mode == INTERP) + { + // "interp" needs valid color at both vertices + if (! (math::isfinite (c(j-1, i)) + && math::isfinite (c(j, i)))) + continue; + } + + if (x_mat) + { + j1 = j-1; + j2 = j; + } + + m_glfcns.glBegin (GL_LINES); + + // Vertex 1 + if (ec_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j-1, i, k); + m_glfcns.glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j-1, i, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, + cb); + } + } + if (el_mode > 0) + { + if (el_mode == GOURAUD) + set_normal (bfl_mode, vn, j-1, i); + else + set_normal (bfl_mode, fn, j-1, std::min (i, zc-2)); + } + + m_glfcns.glVertex3d (x(j1, i), y(j-1, i2), z(j-1, i)); + + // Vertex 2 + if (ec_mode == INTERP) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j, i, k); + m_glfcns.glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, + cb); + } + } + if (el_mode == GOURAUD) + set_normal (bfl_mode, vn, j, i); + + m_glfcns.glVertex3d (x(j2, i), y(j, i2), z(j, i)); + m_glfcns.glEnd (); - } - else // Normal edge contour drawn with tessellator - { - tess.begin_polygon (false); - tess.begin_contour (); - - for (int j = count_f(i)-1; j >= 0; j--) - { - vertex_data::vertex_data_rep *vv - = vdata[i+j*fr].get_rep (); - tess.add_vertex (vv->m_coords.fortran_vec (), vv); - } - - tess.end_contour (); - tess.end_polygon (); - } - } - - set_linestyle ("-"); // Disable LineStipple - set_linewidth (0.5f); - - if ((el_mode > 0) && do_lighting) - m_glfcns.glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement transparency - } - } - - if (! props.marker_is ("none") - && ! (props.markeredgecolor_is ("none") - && props.markerfacecolor_is ("none"))) - { - bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); - bool do_face = draw_all || ! props.markerfacecolor_is ("none"); - - Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : - props.get_markeredgecolor_rgb ()); - Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : - props.get_markerfacecolor_rgb ()); - - bool has_markerfacecolor = draw_all || false; - - if ((mecolor.isempty () && ! props.markeredgecolor_is ("none")) - || (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))) - { - Matrix mc = props.get_color_data ().matrix_value (); - - if (mc.rows () == 1) - { - // Single color specifications, we can simplify a little bit - if (mfcolor.isempty () && ! props.markerfacecolor_is ("none")) - mfcolor = mc; - - if (mecolor.isempty () && ! props.markeredgecolor_is ("none")) - mecolor = mc; - } - else - { - if (c.isempty ()) - c = props.get_color_data ().matrix_value (); - has_markerfacecolor = ((c.numel () > 0) - && (c.rows () == f.rows ())); - } - } - - init_marker (props.get_marker (), props.get_markersize (), - props.get_linewidth ()); - - uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); - uint8_t clip_ok = 0x40; - - for (int i = 0; i < nf; i++) - for (int j = 0; j < count_f(i); j++) + } + } + } + + // Mesh along X-axis + + if (props.meshstyle_is ("both") || props.meshstyle_is ("row")) { - int idx = int (f(i,j) - 1); - - if ((clip_code (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0)) - & clip_mask) != clip_ok) + for (int j = 0; j < zr; j++) + { + if (x_mat) + { + j1 = j-1; + j2 = j; + } + + for (int i = 1; i < zc; i++) + { + if (clip(j, i-1) || clip(j, i)) + continue; + + if (ec_mode == FLAT) + { + // "flat" only needs color at lower-left vertex + if (! math::isfinite (c(j, i-1))) + continue; + } + else if (ec_mode == INTERP) + { + // "interp" needs valid color at both vertices + if (! (math::isfinite (c(j, i-1)) + && math::isfinite (c(j, i)))) + continue; + } + + if (y_mat) + { + i1 = i-1; + i2 = i; + } + + m_glfcns.glBegin (GL_LINES); + + // Vertex 1 + if (ec_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j, i-1, k); + m_glfcns.glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i-1, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, + cb); + } + } + if (el_mode > 0) + { + if (el_mode == GOURAUD) + set_normal (bfl_mode, vn, j, i-1); + else + set_normal (bfl_mode, fn, std::min (j, zr-2), i-1); + } + + m_glfcns.glVertex3d (x(j2, i-1), y(j, i1), z(j, i-1)); + + // Vertex 2 + if (ec_mode == INTERP) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j, i, k); + m_glfcns.glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i, k); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, + cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i, k)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, + cb); + } + } + if (el_mode == GOURAUD) + set_normal (bfl_mode, vn, j, i); + + m_glfcns.glVertex3d (x(j2, i), y(j, i2), z(j, i)); + + m_glfcns.glEnd (); + } + } + } + + set_linestyle ("-"); // Disable LineStipple + set_linewidth (0.5f); + + if ((el_mode > 0) && do_lighting) + m_glfcns.glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement transparency + } + } + + if (! props.marker_is ("none") + && ! (props.markeredgecolor_is ("none") + && props.markerfacecolor_is ("none"))) + { + // FIXME: check how transparency should be handled in markers + // FIXME: check what to do with marker facecolor set to auto + // and facecolor set to none. + + bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); + bool do_face = draw_all || ! props.markerfacecolor_is ("none"); + + Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markeredgecolor_rgb ()); + Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markerfacecolor_rgb ()); + Matrix cc (1, 3, 0.0); + + if (mecolor.isempty () && props.markeredgecolor_is ("auto")) + { + mecolor = props.get_edgecolor_rgb (); + do_edge = ! props.edgecolor_is ("none"); + } + + if (mfcolor.isempty () && props.markerfacecolor_is ("auto")) + { + mfcolor = props.get_facecolor_rgb (); + do_face = ! props.facecolor_is ("none"); + } + + if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ()) + c = props.get_color_data ().array_value (); + + init_marker (props.get_marker (), props.get_markersize (), + props.get_linewidth ()); + + uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); + uint8_t clip_ok = 0x40; + + for (int i = 0; i < zc; i++) + { + if (y_mat) + i1 = i; + + for (int j = 0; j < zr; j++) + { + if (x_mat) + j1 = j; + + if ((clip_code (x(j1, i), y(j, i1), z(j, i)) & clip_mask) + != clip_ok) continue; - Matrix cc; - if (c.numel () > 0) + if ((do_edge && mecolor.isempty ()) + || (do_face && mfcolor.isempty ())) { - cc.resize (1, 3); - if (has_markerfacecolor) - cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2); - else - cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2); + if (! math::isfinite (c(j, i))) + continue; // Skip NaNs in color data + + for (int k = 0; k < 3; k++) + cc(k) = c(j, i, k); } Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) - : Matrix ()); + : Matrix ()); Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) - : Matrix ()); - - draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc); + : Matrix ()); + + draw_marker (x(j1, i), y(j, i1), z(j, i), lc, fc); } - - end_marker (); - } + } + + end_marker (); + } #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_scatter (const scatter::properties& props) - { +} + +// FIXME: global optimization (rendering, data structures...), +// there is probably a smarter/faster/less-memory-consuming way to do this. +void +opengl_renderer::draw_patch (const patch::properties& props) +{ #if defined (HAVE_OPENGL) - // Do not render if the scatter object has incoherent data - std::string msg; - if (props.has_bad_data (msg)) - { - warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); - return; - } - - bool draw_all = m_selecting; - - if (draw_all || (! props.marker_is ("none") - && ! (props.markeredgecolor_is ("none") - && props.markerfacecolor_is ("none")))) + // Do not render if the patch has incoherent data + std::string msg; + if (props.has_bad_data (msg)) + { + warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); + return; + } + + bool draw_all = m_selecting && props.pickableparts_is ("all"); + const Matrix f = props.get_faces ().matrix_value (); + const Matrix v = m_xform.scale (props.get_vertices ().matrix_value ()); + Matrix c; + Matrix a; + double fa = 1.0; + + int nv = v.rows (); + int nf = f.rows (); + int fcmax = f.columns (); + + bool has_z = (v.columns () > 2); + bool has_facecolor = false; + bool has_facealpha = false; + + int fc_mode = ((props.facecolor_is ("none") + || props.facecolor_is_rgb () || draw_all) ? 0 : + (props.facecolor_is ("flat") ? 1 : 2)); + int fl_mode = (props.facelighting_is ("none") ? 0 : + (props.facelighting_is ("flat") ? 1 : 2)); + int fa_mode = (props.facealpha_is_double () ? 0 : + (props.facealpha_is ("flat") ? 1 : 2)); + int ec_mode = ((props.edgecolor_is ("none") + || props.edgecolor_is_rgb ()) ? 0 : + (props.edgecolor_is ("flat") ? 1 : 2)); + int el_mode = (props.edgelighting_is ("none") ? 0 : + (props.edgelighting_is ("flat") ? 1 : 2)); + int ea_mode = (props.edgealpha_is_double () ? 0 : + (props.edgealpha_is ("flat") ? 1 : 2)); + int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : + (props.backfacelighting_is ("reverselit") ? 1 : 2)); + bool do_lighting = props.get_do_lighting (); + + Matrix fcolor = props.get_facecolor_rgb (); + Matrix ecolor = props.get_edgecolor_rgb (); + + float as = props.get_ambientstrength (); + float ds = props.get_diffusestrength (); + float ss = props.get_specularstrength (); + float se = props.get_specularexponent () * 5; // to fit Matlab + float scr = props.get_specularcolorreflectance (); + + const Matrix vn = props.get_vertexnormals ().matrix_value (); + bool has_vertex_normals = (vn.rows () == nv); + const Matrix fn = props.get_facenormals ().matrix_value (); + bool has_face_normals = (fn.rows () == nf); + + boolMatrix clip (1, nv, false); + + if (has_z) + for (int i = 0; i < nv; i++) + clip(i) = is_nan_or_inf (v(i, 0), v(i, 1), v(i, 2)); + else + for (int i = 0; i < nv; i++) + clip(i) = is_nan_or_inf (v(i, 0), v(i, 1), 0); + + boolMatrix clip_f (1, nf, false); + Array<int> count_f (dim_vector (nf, 1), 0); + + for (int i = 0; i < nf; i++) + { + bool fclip = false; + int count = 0; + + for (int j = 0; j < fcmax && ! math::isnan (f(i, j)); j++, count++) + fclip = (fclip || clip(int (f(i, j) - 1))); + + clip_f(i) = fclip; + count_f(i) = count; + } + + if (draw_all || fc_mode > 0 || ec_mode > 0) + { + if (draw_all) + c = Matrix (1, 3, 0.0); + else + c = props.get_color_data ().matrix_value (); + + if (c.rows () == 1) + { + // Single color specifications, we can simplify a little bit + + if (draw_all || fc_mode > 0) + { + fcolor = c; + fc_mode = UNIFORM; + } + + if (draw_all || ec_mode > 0) + { + ecolor = c; + ec_mode = UNIFORM; + } + + c = Matrix (); + } + else + has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ())); + } + + if (fa_mode > 0 || ea_mode > 0) + { + // FIXME: retrieve alpha data from patch object + //a = props.get_alpha_data (); + has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ())); + } + + if (fa_mode == 0) + fa = props.get_facealpha_double (); + + octave_idx_type fr = f.rows (); + std::vector<vertex_data> vdata (f.numel ()); + + for (int i = 0; i < nf; i++) + for (int j = 0; j < count_f(i); j++) { - bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); - bool do_face = draw_all || ! props.markerfacecolor_is ("none"); - - const Matrix x = props.get_xdata ().matrix_value (); - const Matrix y = props.get_ydata ().matrix_value (); - const Matrix z = props.get_zdata ().matrix_value (); - const Matrix c = props.get_color_data ().matrix_value (); - const Matrix s = props.get_sizedata ().matrix_value (); - - int np = x.rows (); - bool has_z = ! z.isempty (); - - // If markeredgecolor is "flat", mecolor is empty - Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : - props.get_markeredgecolor_rgb ()); - Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : - props.get_markerfacecolor_rgb ()); - const double mea = props.get_markeredgealpha (); - const double mfa = props.get_markerfacealpha (); - - if (props.markerfacecolor_is ("auto")) + int idx = int (f(i, j) - 1); + + Matrix vv (1, 3, 0.0); + Matrix cc; + Matrix vnn (1, 3, 0.0); + Matrix fnn (1, 3, 0.0); + double aa = 1.0; + + vv(0) = v(idx, 0); vv(1) = v(idx, 1); + if (has_z) + vv(2) = v(idx, 2); + if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals) + { + double dir = 1.0; + if (bfl_mode > 0) + dir = ((fn(i, 0) * m_view_vector(0) + + fn(i, 1) * m_view_vector(1) + + fn(i, 2) * m_view_vector(2) < 0) + ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); + fnn(0) = dir * fn(i, 0); + fnn(1) = dir * fn(i, 1); + fnn(2) = dir * fn(i, 2); + } + if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals) + { + double dir = 1.0; + if (bfl_mode > 0) + dir = ((vn(idx, 0) * m_view_vector(0) + + vn(idx, 1) * m_view_vector(1) + + vn(idx, 2) * m_view_vector(2) < 0) + ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); + vnn(0) = dir * vn(idx, 0); + vnn(1) = dir * vn(idx, 1); + vnn(2) = dir * vn(idx, 2); + } + if (c.numel () > 0) + { + cc.resize (1, 3); + if (has_facecolor) + cc(0) = c(i, 0), cc(1) = c(i, 1), cc(2) = c(i, 2); + else + cc(0) = c(idx, 0), cc(1) = c(idx, 1), cc(2) = c(idx, 2); + } + if (fa_mode == 0) + aa = fa; + else if (a.numel () > 0) { - gh_manager& gh_mgr = __get_gh_manager__ (); - graphics_object go = gh_mgr.get_object (props.get___myhandle__ ()); - graphics_object ax = go.get_ancestor ("axes"); - const axes::properties& ax_props - = dynamic_cast<const axes::properties&> (ax.get_properties ()); - - mfcolor = ax_props.get_color ().matrix_value (); + if (has_facealpha) + aa = a(i); + else + aa = a(idx); } - init_marker (props.get_marker (), std::sqrt (s(0)), - props.get_linewidth ()); - - uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); - uint8_t clip_ok = 0x40; - - Matrix cc; - if (! c.isempty ()) + vdata[i+j*fr] + = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr); + } + + if (fl_mode > 0 || el_mode > 0) + m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); + + if (draw_all || ! props.facecolor_is ("none")) + { + // FIXME: adapt to double-radio property + if (fa_mode == 0) + { + if (fc_mode == UNIFORM) + { + m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); + if (fl_mode > 0) + { + float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; + + for (int i = 0; i < 3; i++) + cb[i] = as * fcolor(i); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * fcolor(i); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * fcolor(i)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if ((fl_mode > 0) && do_lighting) + m_glfcns.glEnable (GL_LIGHTING); + + // NOTE: Push filled part of patch backwards to avoid Z-fighting + // with tessellator outline. A value of 1.0 seems to work fine. + // Value can't be too large or the patch will be pushed below the + // axes planes at +2.5. + patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0); + + std::vector<octave_idx_type>::const_iterator it; + octave_idx_type i_start, i_end; + + for (int i = 0; i < nf; i++) + { + if (clip_f(i)) + continue; + + bool is_non_planar = false; + if (props.m_coplanar_last_idx.size () > 0 + && props.m_coplanar_last_idx[i].size () > 1) + { + is_non_planar = true; + it = props.m_coplanar_last_idx[i].end (); + it--; + } + + // loop over planar subsets of face + do + { + if (is_non_planar) + { + i_end = *it; + if (it == props.m_coplanar_last_idx[i].begin ()) + i_start = 0; + else + { + it--; + i_start = *it - 1; + } + } + else + { + i_end = count_f(i) - 1; + i_start = 0; + } + + tess.begin_polygon (true); + tess.begin_contour (); + + // Add vertices in reverse order for Matlab compatibility + for (int j = i_end; j > i_start; j--) + { + vertex_data::vertex_data_rep *vv + = vdata[i+j*fr].get_rep (); + + tess.add_vertex (vv->m_coords.fortran_vec (), vv); + } + + if (count_f(i) > 0) + { + vertex_data::vertex_data_rep *vv = vdata[i].get_rep (); + + if (fc_mode == FLAT) + { + // For "flat" shading, use color of 1st vertex. + Matrix col = vv->m_color; + + if (col.numel () == 3) + { + m_glfcns.glColor4d (col(0), col(1), col(2), fa); + if (fl_mode > 0) + { + float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + for (int k = 0; k < 3; k++) + cb[k] = (vv->m_ambient * col(k)); + m_glfcns.glMaterialfv (LIGHT_MODE, + GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = (vv->m_diffuse * col(k)); + m_glfcns.glMaterialfv (LIGHT_MODE, + GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = vv->m_specular * + (vv->m_specular_color_refl + + (1-vv->m_specular_color_refl) * + col(k)); + m_glfcns.glMaterialfv (LIGHT_MODE, + GL_SPECULAR, cb); + } + } + } + + tess.add_vertex (vv->m_coords.fortran_vec (), vv); + } + + tess.end_contour (); + tess.end_polygon (); + } + while (i_start > 0); + } + + if ((fl_mode > 0) && do_lighting) + m_glfcns.glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement flat and interp transparency + } + } + + if (draw_all + || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))) + { + // FIXME: adapt to double-radio property + if (props.get_edgealpha_double () == 1) + { + if (ec_mode == UNIFORM) + { + m_glfcns.glColor3dv (ecolor.data ()); + if (el_mode > 0) + { + // edge lighting only uses ambient light + float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = (as * ecolor(i)); + m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + } + } + + if ((el_mode > 0) && do_lighting) + m_glfcns.glEnable (GL_LIGHTING); + + double linewidth = props.get_linewidth (); + set_linestyle (props.get_linestyle (), false, linewidth); + set_linewidth (linewidth); + set_linecap ("butt"); + set_linejoin ("miter"); + + // NOTE: patch contour cannot be offset. Offset must occur with + // the filled portion of the patch above. The tessellator uses + // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL + // automatically sets the glType to GL_LINE_LOOP. This primitive is + // not supported by glPolygonOffset which is used to do Z offsets. + patch_tessellator tess (this, ec_mode, el_mode, false); + + for (int i = 0; i < nf; i++) + { + bool is_non_planar = false; + if (props.m_coplanar_last_idx.size () > 0 + && props.m_coplanar_last_idx[i].size () > 1) + is_non_planar = true; + if (clip_f(i) || is_non_planar) + { + // This is an unclosed contour or a non-planar face. + // Draw it as a line. + bool flag = false; + + m_glfcns.glShadeModel ((ec_mode == INTERP + || el_mode == GOURAUD) + ? GL_SMOOTH : GL_FLAT); + + // Add vertices in reverse order for Matlab compatibility + for (int j = count_f(i)-1; j >= 0; j--) + { + if (! clip(int (f(i, j) - 1))) + { + vertex_data::vertex_data_rep *vv + = vdata[i+j*fr].get_rep (); + const Matrix m = vv->m_coords; + if (! flag) + { + flag = true; + m_glfcns.glBegin (GL_LINE_STRIP); + } + if (ec_mode != UNIFORM) + { + Matrix col = vv->m_color; + + if (col.numel () == 3) + m_glfcns.glColor3dv (col.data ()); + } + m_glfcns.glVertex3d (m(0), m(1), m(2)); + } + else if (flag) + { + flag = false; + m_glfcns.glEnd (); + } + } + // Do loop body with vertex N to "close" GL_LINE_STRIP + // from vertex 0 to vertex N. + int j = count_f(i)-1; + if (flag && ! clip(int (f(i, j) - 1))) + { + vertex_data::vertex_data_rep *vv + = vdata[i+j*fr].get_rep (); + const Matrix m = vv->m_coords; + if (ec_mode != UNIFORM) + { + Matrix col = vv->m_color; + + if (col.numel () == 3) + m_glfcns.glColor3dv (col.data ()); + } + m_glfcns.glVertex3d (m(0), m(1), m(2)); + } + + if (flag) + m_glfcns.glEnd (); + } + else // Normal edge contour drawn with tessellator + { + tess.begin_polygon (false); + tess.begin_contour (); + + for (int j = count_f(i)-1; j >= 0; j--) + { + vertex_data::vertex_data_rep *vv + = vdata[i+j*fr].get_rep (); + tess.add_vertex (vv->m_coords.fortran_vec (), vv); + } + + tess.end_contour (); + tess.end_polygon (); + } + } + + set_linestyle ("-"); // Disable LineStipple + set_linewidth (0.5f); + + if ((el_mode > 0) && do_lighting) + m_glfcns.glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement transparency + } + } + + if (! props.marker_is ("none") + && ! (props.markeredgecolor_is ("none") + && props.markerfacecolor_is ("none"))) + { + bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); + bool do_face = draw_all || ! props.markerfacecolor_is ("none"); + + Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markeredgecolor_rgb ()); + Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markerfacecolor_rgb ()); + + bool has_markerfacecolor = draw_all || false; + + if ((mecolor.isempty () && ! props.markeredgecolor_is ("none")) + || (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))) + { + Matrix mc = props.get_color_data ().matrix_value (); + + if (mc.rows () == 1) + { + // Single color specifications, we can simplify a little bit + if (mfcolor.isempty () && ! props.markerfacecolor_is ("none")) + mfcolor = mc; + + if (mecolor.isempty () && ! props.markeredgecolor_is ("none")) + mecolor = mc; + } + else + { + if (c.isempty ()) + c = props.get_color_data ().matrix_value (); + has_markerfacecolor = ((c.numel () > 0) + && (c.rows () == f.rows ())); + } + } + + init_marker (props.get_marker (), props.get_markersize (), + props.get_linewidth ()); + + uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); + uint8_t clip_ok = 0x40; + + for (int i = 0; i < nf; i++) + for (int j = 0; j < count_f(i); j++) { - if (c.rows () == 1) - cc = c; - else + int idx = int (f(i, j) - 1); + + if ((clip_code (v(idx, 0), v(idx, 1), (has_z ? v(idx, 2) : 0)) + & clip_mask) != clip_ok) + continue; + + Matrix cc; + if (c.numel () > 0) { cc.resize (1, 3); - cc(0) = c(0,0); - cc(1) = c(0,1); - cc(2) = c(0,2); - } - } - - for (int i = 0; i < np; i++) - { - if ((clip_code (x(i), y(i), (has_z ? z(i) : 0.0)) & clip_mask) - != clip_ok) - continue; - - if (c.rows () > 1) - { - cc(0) = c(i,0); - cc(1) = c(i,1); - cc(2) = c(i,2); + if (has_markerfacecolor) + cc(0) = c(i, 0), cc(1) = c(i, 1), cc(2) = c(i, 2); + else + cc(0) = c(idx, 0), cc(1) = c(idx, 1), cc(2) = c(idx, 2); } Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) - : Matrix ()); + : Matrix ()); Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) - : Matrix ()); - - if (s.numel () > 1) - change_marker (props.get_marker (), std::sqrt (s(i))); - - draw_marker (x(i), y(i), (has_z ? z(i) : 0.0), lc, fc, mea, mfa); + : Matrix ()); + + draw_marker (v(idx, 0), v(idx, 1), (has_z ? v(idx, 2) : 0), lc, fc); } - end_marker (); - } + end_marker (); + } #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::draw_light (const light::properties& props) - { -#if defined (HAVE_OPENGL) - - // enable light source - m_glfcns.glEnable (m_current_light); - - // light position - float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local - Matrix lpos = props.get_position ().matrix_value (); - for (int i = 0; i < 3; i++) - pos[i] = lpos(i); - if (props.style_is ("local")) - pos[3] = 1; - m_glfcns.glLightfv (m_current_light, GL_POSITION, pos); - - // light color - float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning) - Matrix lcolor = props.get_color ().matrix_value (); - for (int i = 0; i < 3; i++) - col[i] = lcolor(i); - m_glfcns.glLightfv (m_current_light, GL_DIFFUSE, col); - m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col); - -#else - - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_hggroup (const hggroup::properties& props) - { - draw (props.get_children ()); - } - - void - opengl_renderer::set_ortho_coordinates (void) - { +} + +void +opengl_renderer::draw_scatter (const scatter::properties& props) +{ #if defined (HAVE_OPENGL) - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glPushMatrix (); - m_glfcns.glLoadIdentity (); - - Matrix vp = get_viewport_scaled (); - m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2); - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glPushMatrix (); - m_glfcns.glLoadIdentity (); + // Do not render if the scatter object has incoherent data + std::string msg; + if (props.has_bad_data (msg)) + { + warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); + return; + } + + bool draw_all = m_selecting; + + if (draw_all || (! props.marker_is ("none") + && ! (props.markeredgecolor_is ("none") + && props.markerfacecolor_is ("none")))) + { + bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); + bool do_face = draw_all || ! props.markerfacecolor_is ("none"); + + const Matrix x = props.get_xdata ().matrix_value (); + const Matrix y = props.get_ydata ().matrix_value (); + const Matrix z = props.get_zdata ().matrix_value (); + const Matrix c = props.get_color_data ().matrix_value (); + const Matrix s = props.get_sizedata ().matrix_value (); + + int np = x.rows (); + bool has_z = ! z.isempty (); + + // If markeredgecolor is "flat", mecolor is empty + Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markeredgecolor_rgb ()); + Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markerfacecolor_rgb ()); + const double mea = props.get_markeredgealpha (); + const double mfa = props.get_markerfacealpha (); + + if (props.markerfacecolor_is ("auto")) + { + gh_manager& gh_mgr = __get_gh_manager__ (); + graphics_object go = gh_mgr.get_object (props.get___myhandle__ ()); + graphics_object ax = go.get_ancestor ("axes"); + const axes::properties& ax_props + = dynamic_cast<const axes::properties&> (ax.get_properties ()); + + mfcolor = ax_props.get_color ().matrix_value (); + } + + init_marker (props.get_marker (), std::sqrt (s(0)), + props.get_linewidth ()); + + uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); + uint8_t clip_ok = 0x40; + + Matrix cc; + if (! c.isempty ()) + { + if (c.rows () == 1) + cc = c; + else + { + cc.resize (1, 3); + cc(0) = c(0, 0); + cc(1) = c(0, 1); + cc(2) = c(0, 2); + } + } + + for (int i = 0; i < np; i++) + { + if ((clip_code (x(i), y(i), (has_z ? z(i) : 0.0)) & clip_mask) + != clip_ok) + continue; + + if (c.rows () > 1) + { + cc(0) = c(i, 0); + cc(1) = c(i, 1); + cc(2) = c(i, 2); + } + + Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) + : Matrix ()); + Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) + : Matrix ()); + + if (s.numel () > 1) + change_marker (props.get_marker (), std::sqrt (s(i))); + + draw_marker (x(i), y(i), (has_z ? z(i) : 0.0), lc, fc, mea, mfa); + } + + end_marker (); + } #else - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::restore_previous_coordinates (void) - { +} + +void +opengl_renderer::draw_light (const light::properties& props) +{ #if defined (HAVE_OPENGL) - // Restore previous coordinate system - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glPopMatrix(); - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glPopMatrix(); + // enable light source + m_glfcns.glEnable (m_current_light); + + // light position + float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local + Matrix lpos = props.get_position ().matrix_value (); + for (int i = 0; i < 3; i++) + pos[i] = lpos(i); + if (props.style_is ("local")) + pos[3] = 1; + m_glfcns.glLightfv (m_current_light, GL_POSITION, pos); + + // light color + float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning) + Matrix lcolor = props.get_color ().matrix_value (); + for (int i = 0; i < 3; i++) + col[i] = lcolor(i); + m_glfcns.glLightfv (m_current_light, GL_DIFFUSE, col); + m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col); #else - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_text (const text::properties& props) - { +} + +void +opengl_renderer::draw_hggroup (const hggroup::properties& props) +{ + draw (props.get_children ()); +} + +void +opengl_renderer::set_ortho_coordinates (void) +{ #if defined (HAVE_OPENGL) - if (props.get_string ().isempty () || props.color_is ("none")) - return; - - Matrix pos = m_xform.scale (props.get_data_position ()); - - // Handle clipping manually when drawing text in ortho coordinates - if (! props.is_clipping () - || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40)) - { - set_clipping (false); - - draw_text_background (props); - - set_font (props); - - render_text (props.get_pixels (), props.get_extent_matrix (), - pos(0), pos(1), pos(2), props.get_rotation ()); - - set_clipping (props.is_clipping ()); - } + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glPushMatrix (); + m_glfcns.glLoadIdentity (); + + Matrix vp = get_viewport_scaled (); + m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2); + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glPushMatrix (); + m_glfcns.glLoadIdentity (); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +void +opengl_renderer::restore_previous_coordinates (void) +{ +#if defined (HAVE_OPENGL) + + // Restore previous coordinate system + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glPopMatrix(); + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glPopMatrix(); + +#else + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_text_background (const text::properties& props, - bool /*do_rotate*/) - { +} + +void +opengl_renderer::draw_text (const text::properties& props) +{ #if defined (HAVE_OPENGL) - Matrix bgcol = props.get_backgroundcolor_rgb (); - Matrix ecol = props.get_edgecolor_rgb (); - - if (bgcol.isempty () && ecol.isempty ()) - return; - - Matrix pos = props.get_data_position (); - ColumnVector pixpos = get_transform ().transform (pos(0), pos(1), - pos(2), true); - - // Save current transform matrices and set orthogonal window coordinates - set_ortho_coordinates (); - - // Translate coordinates so that the text anchor is (0,0) - m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); - - // FIXME: Only multiples of 90° are handled by the text renderer. - // Handle others here. - double rotation = props.get_rotation (); - - m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); - - double m = points_to_pixels (props.get_margin ()); - const Matrix bbox = props.get_extent_matrix (); - double x0 = bbox (0) / m_devpixratio - m; - double x1 = x0 + bbox(2) / m_devpixratio + 2 * m; - double y0 = -(bbox (1) / m_devpixratio - m); - double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m); - - if (! bgcol.isempty ()) - { - m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2)); - - bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST); - if (depth_test) - set_polygon_offset (true, 4.0); - - m_glfcns.glBegin (GL_QUADS); - m_glfcns.glVertex2d (x0, y0); - m_glfcns.glVertex2d (x1, y0); - m_glfcns.glVertex2d (x1, y1); - m_glfcns.glVertex2d (x0, y1); - m_glfcns.glEnd (); - - if (depth_test) - set_polygon_offset (false); - } - - if (! ecol.isempty ()) - { - m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2)); - - set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); - set_linewidth (props.get_linewidth ()); - - m_glfcns.glBegin (GL_LINE_STRIP); - m_glfcns.glVertex2d (x0, y0); - m_glfcns.glVertex2d (x1, y0); - m_glfcns.glVertex2d (x1, y1); - m_glfcns.glVertex2d (x0, y1); - m_glfcns.glVertex2d (x0, y0); - m_glfcns.glEnd (); - - set_linestyle ("-"); - } - - restore_previous_coordinates (); + if (props.get_string ().isempty () || props.color_is ("none")) + return; + + Matrix pos = m_xform.scale (props.get_data_position ()); + + // Handle clipping manually when drawing text in ortho coordinates + if (! props.is_clipping () + || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40)) + { + set_clipping (false); + + draw_text_background (props); + + set_font (props); + + render_text (props.get_pixels (), props.get_extent_matrix (), + pos(0), pos(1), pos(2), props.get_rotation ()); + + set_clipping (props.is_clipping ()); + } #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_image (const image::properties& props) - { +} + +void +opengl_renderer::draw_text_background (const text::properties& props, + bool /*do_rotate*/) +{ #if defined (HAVE_OPENGL) - octave_value cdata = props.get_color_data (); - Matrix x = props.get_xdata ().matrix_value (); - Matrix y = props.get_ydata ().matrix_value (); - - draw_texture_image (cdata, x, y); + Matrix bgcol = props.get_backgroundcolor_rgb (); + Matrix ecol = props.get_edgecolor_rgb (); + + if (bgcol.isempty () && ecol.isempty ()) + return; + + Matrix pos = props.get_data_position (); + ColumnVector pixpos = get_transform ().transform (pos(0), pos(1), + pos(2), true); + + // Save current transform matrices and set orthogonal window coordinates + set_ortho_coordinates (); + + // Translate coordinates so that the text anchor is (0,0) + m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); + + // FIXME: Only multiples of 90° are handled by the text renderer. + // Handle others here. + double rotation = props.get_rotation (); + + m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); + + double m = points_to_pixels (props.get_margin ()); + const Matrix bbox = props.get_extent_matrix (); + double x0 = bbox (0) / m_devpixratio - m; + double x1 = x0 + bbox(2) / m_devpixratio + 2 * m; + double y0 = -(bbox (1) / m_devpixratio - m); + double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m); + + if (! bgcol.isempty ()) + { + m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2)); + + bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST); + if (depth_test) + set_polygon_offset (true, 4.0); + + m_glfcns.glBegin (GL_QUADS); + m_glfcns.glVertex2d (x0, y0); + m_glfcns.glVertex2d (x1, y0); + m_glfcns.glVertex2d (x1, y1); + m_glfcns.glVertex2d (x0, y1); + m_glfcns.glEnd (); + + if (depth_test) + set_polygon_offset (false); + } + + if (! ecol.isempty ()) + { + m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2)); + + set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); + set_linewidth (props.get_linewidth ()); + + m_glfcns.glBegin (GL_LINE_STRIP); + m_glfcns.glVertex2d (x0, y0); + m_glfcns.glVertex2d (x1, y0); + m_glfcns.glVertex2d (x1, y1); + m_glfcns.glVertex2d (x0, y1); + m_glfcns.glVertex2d (x0, y0); + m_glfcns.glEnd (); + + set_linestyle ("-"); + } + + restore_previous_coordinates (); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_texture_image (const octave_value cdata, Matrix x, - Matrix y, bool ortho) - { +} + +void +opengl_renderer::draw_image (const image::properties& props) +{ #if defined (HAVE_OPENGL) - dim_vector dv (cdata.dims ()); - int h = dv(0); - int w = dv(1); - double x0, x1, y0, y1; - - double dx = 1.0; - if (w > 1) - dx = (x(1) - x(0)) / (w - 1); - - x0 = x(0)-dx/2; - x1 = x(1)+dx/2; - - double dy = 1.0; - if (h > 1) - dy = (y(1) - y(0)) / (h - 1); - - y0 = y(0)-dy/2; - y1 = y(1)+dy/2; - - // Expect RGB data - if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4)) - { - opengl_texture tex = opengl_texture::create (m_glfcns, cdata); - if (tex.is_valid ()) - { - m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0); - - m_glfcns.glEnable (GL_TEXTURE_2D); - - m_glfcns.glBegin (GL_QUADS); - - tex.tex_coord (0.0, 0.0); - if (ortho) - m_glfcns.glVertex2d (x0, y0); - else - m_glfcns.glVertex3d (x0, y0, 0.0); - - tex.tex_coord (1.0, 0.0); - if (ortho) - m_glfcns.glVertex2d (x1, y0); - else - m_glfcns.glVertex3d (x1, y0, 0.0); - - tex.tex_coord (1.0, 1.0); - if (ortho) - m_glfcns.glVertex2d (x1, y1); - else - m_glfcns.glVertex3d (x1, y1, 0.0); - - tex.tex_coord (0.0, 1.0); - if (ortho) - m_glfcns.glVertex2d (x0, y1); - else - m_glfcns.glVertex3d (x0, y1, 0.0); - - m_glfcns.glEnd (); - m_glfcns.glDisable (GL_TEXTURE_2D); - } - } - else - warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); + octave_value cdata = props.get_color_data (); + Matrix x = props.get_xdata ().matrix_value (); + Matrix y = props.get_ydata ().matrix_value (); + + draw_texture_image (cdata, x, y); #else - octave_unused_parameter (cdata); - octave_unused_parameter (x); - octave_unused_parameter (y); - octave_unused_parameter (ortho); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void opengl_renderer::draw (const Matrix& hlist, bool toplevel) - { - int len = hlist.numel (); - - gh_manager& gh_mgr = __get_gh_manager__ (); - - for (int i = len-1; i >= 0; i--) - { - graphics_object obj = gh_mgr.get_object (hlist(i)); - - if (obj) - draw (obj, toplevel); - } - } - - void - opengl_renderer::set_viewport (int w, int h) - { +} + +void +opengl_renderer::draw_texture_image (const octave_value cdata, Matrix x, + Matrix y, bool ortho) +{ #if defined (HAVE_OPENGL) - m_glfcns.glViewport (0, 0, w, h); + dim_vector dv (cdata.dims ()); + int h = dv(0); + int w = dv(1); + double x0, x1, y0, y1; + + double dx = 1.0; + if (w > 1) + dx = (x(1) - x(0)) / (w - 1); + + x0 = x(0)-dx/2; + x1 = x(1)+dx/2; + + double dy = 1.0; + if (h > 1) + dy = (y(1) - y(0)) / (h - 1); + + y0 = y(0)-dy/2; + y1 = y(1)+dy/2; + + // Expect RGB data + if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4)) + { + opengl_texture tex = opengl_texture::create (m_glfcns, cdata); + if (tex.is_valid ()) + { + m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0); + + m_glfcns.glEnable (GL_TEXTURE_2D); + + m_glfcns.glBegin (GL_QUADS); + + tex.tex_coord (0.0, 0.0); + if (ortho) + m_glfcns.glVertex2d (x0, y0); + else + m_glfcns.glVertex3d (x0, y0, 0.0); + + tex.tex_coord (1.0, 0.0); + if (ortho) + m_glfcns.glVertex2d (x1, y0); + else + m_glfcns.glVertex3d (x1, y0, 0.0); + + tex.tex_coord (1.0, 1.0); + if (ortho) + m_glfcns.glVertex2d (x1, y1); + else + m_glfcns.glVertex3d (x1, y1, 0.0); + + tex.tex_coord (0.0, 1.0); + if (ortho) + m_glfcns.glVertex2d (x0, y1); + else + m_glfcns.glVertex3d (x0, y1, 0.0); + + m_glfcns.glEnd (); + m_glfcns.glDisable (GL_TEXTURE_2D); + } + } + else + warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); #else - octave_unused_parameter (w); - octave_unused_parameter (h); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (cdata); + octave_unused_parameter (x); + octave_unused_parameter (y); + octave_unused_parameter (ortho); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - Matrix - opengl_renderer::get_viewport_scaled (void) const - { - Matrix retval (1, 4, 0.0); +} + +void opengl_renderer::draw (const Matrix& hlist, bool toplevel) +{ + int len = hlist.numel (); + + gh_manager& gh_mgr = __get_gh_manager__ (); + + for (int i = len-1; i >= 0; i--) + { + graphics_object obj = gh_mgr.get_object (hlist(i)); + + if (obj) + draw (obj, toplevel); + } +} + +void +opengl_renderer::set_viewport (int w, int h) +{ +#if defined (HAVE_OPENGL) + + m_glfcns.glViewport (0, 0, w, h); + +#else + + octave_unused_parameter (w); + octave_unused_parameter (h); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + +#endif +} + +Matrix +opengl_renderer::get_viewport_scaled (void) const +{ + Matrix retval (1, 4, 0.0); #if defined (HAVE_OPENGL) #if defined (HAVE_FRAMEWORK_OPENGL) - GLint vp[4]; + GLint vp[4]; #else - int vp[4]; + int vp[4]; #endif - m_glfcns.glGetIntegerv (GL_VIEWPORT, vp); - - for (int i = 0; i < 4; i++) - retval(i) = static_cast<double> (vp[i]) / m_devpixratio; + m_glfcns.glGetIntegerv (GL_VIEWPORT, vp); + + for (int i = 0; i < 4; i++) + retval(i) = static_cast<double> (vp[i]) / m_devpixratio; #else - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - return retval; - } - - void - opengl_renderer::set_color (const Matrix& c) - { + return retval; +} + +void +opengl_renderer::set_color (const Matrix& c) +{ #if defined (HAVE_OPENGL) - m_glfcns.glColor3dv (c.data ()); - - if (! c.isempty ()) - m_txt_renderer.set_color (c); + m_glfcns.glColor3dv (c.data ()); + + if (! c.isempty ()) + m_txt_renderer.set_color (c); #else - octave_unused_parameter (c); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (c); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::set_font (const base_properties& props) - { - bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on"; - m_txt_renderer.set_anti_aliasing (do_anti_alias); - m_txt_renderer.set_font (props.get ("fontname").string_value (), - props.get ("fontweight").string_value (), - props.get ("fontangle").string_value (), - props.get ("__fontsize_points__").double_value () - * m_devpixratio); - } - - void - opengl_renderer::set_polygon_offset (bool on, float offset) - { +} + +void +opengl_renderer::set_font (const base_properties& props) +{ + bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on"; + m_txt_renderer.set_anti_aliasing (do_anti_alias); + m_txt_renderer.set_font (props.get ("fontname").string_value (), + props.get ("fontweight").string_value (), + props.get ("fontangle").string_value (), + props.get ("__fontsize_points__").double_value () + * m_devpixratio); +} + +void +opengl_renderer::set_polygon_offset (bool on, float offset) +{ #if defined (HAVE_OPENGL) - if (on) - { - m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL); - m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE); - m_glfcns.glPolygonOffset (offset, offset); - } - else - { - m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL); - m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE); - } + if (on) + { + m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL); + m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE); + m_glfcns.glPolygonOffset (offset, offset); + } + else + { + m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL); + m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE); + } #else - octave_unused_parameter (on); - octave_unused_parameter (offset); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (on); + octave_unused_parameter (offset); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::set_linewidth (float w) - { +} + +void +opengl_renderer::set_linewidth (float w) +{ #if defined (HAVE_OPENGL) - // Measure LineWidth in points. See bug #53056. - m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio); + // Measure LineWidth in points. See bug #53056. + m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio); #else - octave_unused_parameter (w); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (w); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::set_linestyle (const std::string& s, bool use_stipple, - double linewidth) - { +} + +void +opengl_renderer::set_linestyle (const std::string& s, bool use_stipple, + double linewidth) +{ #if defined (HAVE_OPENGL) - // Measure LineWidth in points. See bug #53056. - int factor = math::round (points_to_pixels (linewidth) * m_devpixratio); - if (factor < 1) - factor = 1; - - uint16_t pattern = 0xFFFF; - - bool solid = false; - - if (s == "-") - solid = true; - else if (s == ":") - { - if (factor > 1) - pattern = 0x5555; - else - pattern = 0x1111; - } - else if (s == "--") - { - if (factor > 1) - pattern = 0x0F0F; - else - pattern = 0x01FF; - } - else if (s == "-.") - { - if (factor > 1) - pattern = 0x6F6F; - else - pattern = 0x18FF; - } - else - pattern = 0x0000; - - m_glfcns.glLineStipple (factor, pattern); - - if (solid && ! use_stipple) - m_glfcns.glDisable (GL_LINE_STIPPLE); - else - m_glfcns.glEnable (GL_LINE_STIPPLE); + // Measure LineWidth in points. See bug #53056. + int factor = math::round (points_to_pixels (linewidth) * m_devpixratio); + if (factor < 1) + factor = 1; + + uint16_t pattern = 0xFFFF; + + bool solid = false; + + if (s == "-") + solid = true; + else if (s == ":") + { + if (factor > 1) + pattern = 0x5555; + else + pattern = 0x1111; + } + else if (s == "--") + { + if (factor > 1) + pattern = 0x0F0F; + else + pattern = 0x01FF; + } + else if (s == "-.") + { + if (factor > 1) + pattern = 0x6F6F; + else + pattern = 0x18FF; + } + else + pattern = 0x0000; + + m_glfcns.glLineStipple (factor, pattern); + + if (solid && ! use_stipple) + m_glfcns.glDisable (GL_LINE_STIPPLE); + else + m_glfcns.glEnable (GL_LINE_STIPPLE); #else - octave_unused_parameter (s); - octave_unused_parameter (use_stipple); - octave_unused_parameter (linewidth); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (s); + octave_unused_parameter (use_stipple); + octave_unused_parameter (linewidth); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2, - double z1, double z2) - { +} + +void +opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2, + double z1, double z2) +{ #if defined (HAVE_OPENGL) - double dx = (x2-x1); - double dy = (y2-y1); - double dz = (z2-z1); - - x1 -= 0.001*dx; x2 += 0.001*dx; - y1 -= 0.001*dy; y2 += 0.001*dy; - z1 -= 0.001*dz; z2 += 0.001*dz; - - ColumnVector p (4, 0.0); - - p(0) = -1; p(3) = x2; - m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ()); - p(0) = 1; p(3) = -x1; - m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ()); - p(0) = 0; p(1) = -1; p(3) = y2; - m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ()); - p(1) = 1; p(3) = -y1; - m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ()); - p(1) = 0; p(2) = -1; p(3) = z2; - m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ()); - p(2) = 1; p(3) = -z1; - m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ()); - - m_xmin = x1; m_xmax = x2; - m_ymin = y1; m_ymax = y2; - m_zmin = z1; m_zmax = z2; + double dx = (x2-x1); + double dy = (y2-y1); + double dz = (z2-z1); + + x1 -= 0.001*dx; x2 += 0.001*dx; + y1 -= 0.001*dy; y2 += 0.001*dy; + z1 -= 0.001*dz; z2 += 0.001*dz; + + ColumnVector p (4, 0.0); + + p(0) = -1; p(3) = x2; + m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ()); + p(0) = 1; p(3) = -x1; + m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ()); + p(0) = 0; p(1) = -1; p(3) = y2; + m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ()); + p(1) = 1; p(3) = -y1; + m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ()); + p(1) = 0; p(2) = -1; p(3) = z2; + m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ()); + p(2) = 1; p(3) = -z1; + m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ()); + + m_xmin = x1; m_xmax = x2; + m_ymin = y1; m_ymax = y2; + m_zmin = z1; m_zmax = z2; #else - octave_unused_parameter (x1); - octave_unused_parameter (x2); - octave_unused_parameter (y1); - octave_unused_parameter (y2); - octave_unused_parameter (z1); - octave_unused_parameter (z2); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (x1); + octave_unused_parameter (x2); + octave_unused_parameter (y1); + octave_unused_parameter (y2); + octave_unused_parameter (z1); + octave_unused_parameter (z2); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::set_clipping (bool enable) - { +} + +void +opengl_renderer::set_clipping (bool enable) +{ #if defined (HAVE_OPENGL) - bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE); - - if (enable != has_clipping) - { - if (enable) - for (int i = 0; i < 6; i++) - m_glfcns.glEnable (GL_CLIP_PLANE0+i); - else - for (int i = 0; i < 6; i++) - m_glfcns.glDisable (GL_CLIP_PLANE0+i); - } + bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE); + + if (enable != has_clipping) + { + if (enable) + for (int i = 0; i < 6; i++) + m_glfcns.glEnable (GL_CLIP_PLANE0+i); + else + for (int i = 0; i < 6; i++) + m_glfcns.glDisable (GL_CLIP_PLANE0+i); + } #else - octave_unused_parameter (enable); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (enable); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::init_marker (const std::string& m, double size, float width) - { +} + +void +opengl_renderer::init_marker (const std::string& m, double size, float width) +{ #if defined (HAVE_OPENGL) - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glPushMatrix (); - m_glfcns.glLoadIdentity (); - - Matrix vp = get_viewport_scaled (); - m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2); - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glPushMatrix (); - - set_clipping (false); - set_linewidth (width); - - m_marker_id = make_marker_list (m, size, false); - m_filled_marker_id = make_marker_list (m, size, true); + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glPushMatrix (); + m_glfcns.glLoadIdentity (); + + Matrix vp = get_viewport_scaled (); + m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2); + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glPushMatrix (); + + set_clipping (false); + set_linewidth (width); + + m_marker_id = make_marker_list (m, size, false); + m_filled_marker_id = make_marker_list (m, size, true); #else - octave_unused_parameter (m); - octave_unused_parameter (size); - octave_unused_parameter (width); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (m); + octave_unused_parameter (size); + octave_unused_parameter (width); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::change_marker (const std::string& m, double size) - { +} + +void +opengl_renderer::change_marker (const std::string& m, double size) +{ #if defined (HAVE_OPENGL) - m_marker_id = make_marker_list (m, size, false); - m_filled_marker_id = make_marker_list (m, size, true); + m_marker_id = make_marker_list (m, size, false); + m_filled_marker_id = make_marker_list (m, size, true); #else - octave_unused_parameter (m); - octave_unused_parameter (size); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (m); + octave_unused_parameter (size); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::end_marker (void) - { +} + +void +opengl_renderer::end_marker (void) +{ #if defined (HAVE_OPENGL) - m_glfcns.glDeleteLists (m_marker_id, 1); - m_glfcns.glDeleteLists (m_filled_marker_id, 1); - - m_glfcns.glMatrixMode (GL_MODELVIEW); - m_glfcns.glPopMatrix (); - m_glfcns.glMatrixMode (GL_PROJECTION); - m_glfcns.glPopMatrix (); - set_linewidth (0.5f); + m_glfcns.glDeleteLists (m_marker_id, 1); + m_glfcns.glDeleteLists (m_filled_marker_id, 1); + + m_glfcns.glMatrixMode (GL_MODELVIEW); + m_glfcns.glPopMatrix (); + m_glfcns.glMatrixMode (GL_PROJECTION); + m_glfcns.glPopMatrix (); + set_linewidth (0.5f); #else - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::draw_marker (double x, double y, double z, - const Matrix& lc, const Matrix& fc, - const double la, const double fa) - { +} + +void +opengl_renderer::draw_marker (double x, double y, double z, + const Matrix& lc, const Matrix& fc, + const double la, const double fa) +{ #if defined (HAVE_OPENGL) - ColumnVector tmp = m_xform.transform (x, y, z, false); - - m_glfcns.glLoadIdentity (); - m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2)); - - if (m_filled_marker_id > 0 && fc.numel () > 0) - { - m_glfcns.glColor4d (fc(0), fc(1), fc(2), fa); - set_polygon_offset (true, -1.0); - m_glfcns.glCallList (m_filled_marker_id); - if (lc.numel () > 0) - { - m_glfcns.glColor4d (lc(0), lc(1), lc(2), la); - m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - m_glfcns.glEdgeFlag (GL_TRUE); - set_polygon_offset (true, -2.0); - m_glfcns.glCallList (m_filled_marker_id); - m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - } - set_polygon_offset (false); - } - else if (m_marker_id > 0 && lc.numel () > 0) - { - m_glfcns.glColor4d (lc(0), lc(1), lc(2), la); - m_glfcns.glCallList (m_marker_id); - } + ColumnVector tmp = m_xform.transform (x, y, z, false); + + m_glfcns.glLoadIdentity (); + m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2)); + + if (m_filled_marker_id > 0 && fc.numel () > 0) + { + m_glfcns.glColor4d (fc(0), fc(1), fc(2), fa); + set_polygon_offset (true, -1.0); + m_glfcns.glCallList (m_filled_marker_id); + if (lc.numel () > 0) + { + m_glfcns.glColor4d (lc(0), lc(1), lc(2), la); + m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + m_glfcns.glEdgeFlag (GL_TRUE); + set_polygon_offset (true, -2.0); + m_glfcns.glCallList (m_filled_marker_id); + m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + } + set_polygon_offset (false); + } + else if (m_marker_id > 0 && lc.numel () > 0) + { + m_glfcns.glColor4d (lc(0), lc(1), lc(2), la); + m_glfcns.glCallList (m_marker_id); + } #else - octave_unused_parameter (x); - octave_unused_parameter (y); - octave_unused_parameter (z); - octave_unused_parameter (lc); - octave_unused_parameter (fc); - octave_unused_parameter (la); - octave_unused_parameter (fa); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (x); + octave_unused_parameter (y); + octave_unused_parameter (z); + octave_unused_parameter (lc); + octave_unused_parameter (fc); + octave_unused_parameter (la); + octave_unused_parameter (fa); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::init_maxlights (void) - { +} + +void +opengl_renderer::init_maxlights (void) +{ #if defined (HAVE_OPENGL) - // Check actual maximum number of lights possible - if (m_max_lights == 0) - { - GLint max_lights; - m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights); - m_max_lights = max_lights; - } + // Check actual maximum number of lights possible + if (m_max_lights == 0) + { + GLint max_lights; + m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights); + m_max_lights = max_lights; + } #else - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - std::string - opengl_renderer::get_string (unsigned int id) const - { +} + +std::string +opengl_renderer::get_string (unsigned int id) const +{ #if defined (HAVE_OPENGL) - // This is kind of ugly, but glGetString returns a pointer to GLubyte - // and there is no std::string constructor that matches. Is there a - // better way? - - std::ostringstream buf; - - buf << m_glfcns.glGetString (static_cast<GLenum> (id)); - - return std::string (buf.str ()); + // This is kind of ugly, but glGetString returns a pointer to GLubyte + // and there is no std::string constructor that matches. Is there a + // better way? + + std::ostringstream buf; + + buf << m_glfcns.glGetString (static_cast<GLenum> (id)); + + return std::string (buf.str ()); #else - octave_unused_parameter (id); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - return std::string (); + octave_unused_parameter (id); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); + return std::string (); #endif - } - - void - opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i) - { +} + +void +opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i) +{ #if defined (HAVE_OPENGL) - double x = n(j,i,0); - double y = n(j,i,1); - double z = n(j,i,2); - - double d = sqrt (x*x + y*y + z*z); - - double dir = 1.0; - - if (bfl_mode > 0) - dir = ((x*m_view_vector(0) + y*m_view_vector(1) + z*m_view_vector(2) < 0) - ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); - - m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d); + double x = n(j, i, 0); + double y = n(j, i, 1); + double z = n(j, i, 2); + + double d = sqrt (x*x + y*y + z*z); + + double dir = 1.0; + + if (bfl_mode > 0) + dir = ((x*m_view_vector(0) + y*m_view_vector(1) + z*m_view_vector(2) < 0) + ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); + + m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d); #else - octave_unused_parameter (bfl_mode); - octave_unused_parameter (n); - octave_unused_parameter (j); - octave_unused_parameter (i); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (bfl_mode); + octave_unused_parameter (n); + octave_unused_parameter (j); + octave_unused_parameter (i); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - double - opengl_renderer::points_to_pixels (const double val) const - { - gh_manager& gh_mgr = __get_gh_manager__ (); - - // FIXME: Does making this static cause problems if figure is moved to a - // 2nd monitor with a different value for "screenpixelsperinch"? - static const double pix_per_pts = - gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0; - - double retval = val; - - if (! m_printing) - retval *= pix_per_pts; - - return retval; - } - - unsigned int - opengl_renderer::make_marker_list (const std::string& marker, double size, - bool filled) const - { +} + +double +opengl_renderer::points_to_pixels (const double val) const +{ + gh_manager& gh_mgr = __get_gh_manager__ (); + + // FIXME: Does making this static cause problems if figure is moved to a + // 2nd monitor with a different value for "screenpixelsperinch"? + static const double pix_per_pts = + gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0; + + double retval = val; + + if (! m_printing) + retval *= pix_per_pts; + + return retval; +} + +unsigned int +opengl_renderer::make_marker_list (const std::string& marker, double size, + bool filled) const +{ #if defined (HAVE_OPENGL) - char c = marker[0]; - - if (filled && (c == '+' || c == 'x' || c == '*' || c == '.' - || c == '|' || c == '_')) - return 0; - - unsigned int ID = m_glfcns.glGenLists (1); - - // FIXME: See bug #53056 (measure LineWidth in points). - double sz = points_to_pixels (size); - - // constants for the * marker - const double sqrt2d4 = 0.35355339059327; - double tt = sz*sqrt2d4; - - m_glfcns.glNewList (ID, GL_COMPILE); - - switch (marker[0]) + char c = marker[0]; + + if (filled && (c == '+' || c == 'x' || c == '*' || c == '.' + || c == '|' || c == '_')) + return 0; + + unsigned int ID = m_glfcns.glGenLists (1); + + // FIXME: See bug #53056 (measure LineWidth in points). + double sz = points_to_pixels (size); + + // constants for the * marker + const double sqrt2d4 = 0.35355339059327; + double tt = sz*sqrt2d4; + + m_glfcns.glNewList (ID, GL_COMPILE); + + switch (marker[0]) + { + case '+': + m_glfcns.glBegin (GL_LINES); + m_glfcns.glVertex2d (-sz/2, 0); + m_glfcns.glVertex2d (sz/2, 0); + m_glfcns.glVertex2d (0, -sz/2); + m_glfcns.glVertex2d (0, sz/2); + m_glfcns.glEnd (); + break; + case '|': + m_glfcns.glBegin (GL_LINES); + m_glfcns.glVertex2d (0, -sz/2); + m_glfcns.glVertex2d (0, sz/2); + m_glfcns.glEnd (); + break; + case '_': + m_glfcns.glBegin (GL_LINES); + m_glfcns.glVertex2d (-sz/2, 0); + m_glfcns.glVertex2d (sz/2, 0); + m_glfcns.glEnd (); + break; + case 'x': + m_glfcns.glBegin (GL_LINES); + m_glfcns.glVertex2d (-sz/2, -sz/2); + m_glfcns.glVertex2d (sz/2, sz/2); + m_glfcns.glVertex2d (-sz/2, sz/2); + m_glfcns.glVertex2d (sz/2, -sz/2); + m_glfcns.glEnd (); + break; + case '*': + m_glfcns.glBegin (GL_LINES); + m_glfcns.glVertex2d (-sz/2, 0); + m_glfcns.glVertex2d (sz/2, 0); + m_glfcns.glVertex2d (0, -sz/2); + m_glfcns.glVertex2d (0, sz/2); + m_glfcns.glVertex2d (-tt, -tt); + m_glfcns.glVertex2d (+tt, +tt); + m_glfcns.glVertex2d (-tt, +tt); + m_glfcns.glVertex2d (+tt, -tt); + m_glfcns.glEnd (); + break; + case '.': { - case '+': - m_glfcns.glBegin (GL_LINES); - m_glfcns.glVertex2d (-sz/2, 0); - m_glfcns.glVertex2d (sz/2, 0); - m_glfcns.glVertex2d (0, -sz/2); - m_glfcns.glVertex2d (0, sz/2); - m_glfcns.glEnd (); - break; - case '|': - m_glfcns.glBegin (GL_LINES); - m_glfcns.glVertex2d (0, -sz/2); - m_glfcns.glVertex2d (0, sz/2); - m_glfcns.glEnd (); - break; - case '_': - m_glfcns.glBegin (GL_LINES); - m_glfcns.glVertex2d (-sz/2, 0); - m_glfcns.glVertex2d (sz/2, 0); - m_glfcns.glEnd (); - break; - case 'x': - m_glfcns.glBegin (GL_LINES); - m_glfcns.glVertex2d (-sz/2, -sz/2); - m_glfcns.glVertex2d (sz/2, sz/2); - m_glfcns.glVertex2d (-sz/2, sz/2); - m_glfcns.glVertex2d (sz/2, -sz/2); - m_glfcns.glEnd (); - break; - case '*': - m_glfcns.glBegin (GL_LINES); - m_glfcns.glVertex2d (-sz/2, 0); - m_glfcns.glVertex2d (sz/2, 0); - m_glfcns.glVertex2d (0, -sz/2); - m_glfcns.glVertex2d (0, sz/2); - m_glfcns.glVertex2d (-tt, -tt); - m_glfcns.glVertex2d (+tt, +tt); - m_glfcns.glVertex2d (-tt, +tt); - m_glfcns.glVertex2d (+tt, -tt); + // The dot marker is special and is drawn at 1/3rd the specified size + + // Ensure that something is drawn even at very small markersizes + if (sz > 0 && sz < 3) + sz = 3; + + int div = static_cast<int> (M_PI * sz / 12); + if (! (div % 2)) + div += 1; // ensure odd number for left/right symmetry + div = std::max (div, 3); // ensure at least a few vertices are drawn + double ang_step = M_PI / div; + + m_glfcns.glBegin (GL_POLYGON); + for (double ang = 0; ang < 2*M_PI; ang += ang_step) + m_glfcns.glVertex2d (sz/6* cos (ang), sz/6*sin (ang)); m_glfcns.glEnd (); - break; - case '.': - { - // The dot marker is special and is drawn at 1/3rd the specified size - - // Ensure that something is drawn even at very small markersizes - if (sz > 0 && sz < 3) - sz = 3; - - int div = static_cast<int> (M_PI * sz / 12); - if (! (div % 2)) - div += 1; // ensure odd number for left/right symmetry - div = std::max (div, 3); // ensure at least a few vertices are drawn - double ang_step = M_PI / div; - - m_glfcns.glBegin (GL_POLYGON); - for (double ang = 0; ang < 2*M_PI; ang += ang_step) - m_glfcns.glVertex2d (sz/6*cos (ang), sz/6*sin (ang)); - m_glfcns.glEnd (); - } - break; - case 's': + } + break; + case 's': + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + m_glfcns.glVertex2d (-sz/2, -sz/2); + m_glfcns.glVertex2d (-sz/2, sz/2); + m_glfcns.glVertex2d (sz/2, sz/2); + m_glfcns.glVertex2d (sz/2, -sz/2); + m_glfcns.glEnd (); + break; + case 'o': + { + int div = static_cast<int> (M_PI * sz / 4); + if (! (div % 2)) + div += 1; // ensure odd number for left/right symmetry + div = std::max (div, 5); // ensure at least a few vertices are drawn + double ang_step = M_PI / div; + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - m_glfcns.glVertex2d (-sz/2, -sz/2); - m_glfcns.glVertex2d (-sz/2, sz/2); - m_glfcns.glVertex2d (sz/2, sz/2); - m_glfcns.glVertex2d (sz/2, -sz/2); - m_glfcns.glEnd (); - break; - case 'o': - { - int div = static_cast<int> (M_PI * sz / 4); - if (! (div % 2)) - div += 1; // ensure odd number for left/right symmetry - div = std::max (div, 5); // ensure at least a few vertices are drawn - double ang_step = M_PI / div; - - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - for (double ang = 0; ang < 2*M_PI; ang += ang_step) - m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang)); - m_glfcns.glEnd (); - } - break; - case 'd': - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - m_glfcns.glVertex2d (0, -sz/2); - m_glfcns.glVertex2d (sz/2, 0); - m_glfcns.glVertex2d (0, sz/2); - m_glfcns.glVertex2d (-sz/2, 0); - m_glfcns.glEnd (); - break; - case 'v': - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - m_glfcns.glVertex2d (0, sz/2); - m_glfcns.glVertex2d (sz/2, -sz/2); - m_glfcns.glVertex2d (-sz/2, -sz/2); + for (double ang = 0; ang < 2*M_PI; ang += ang_step) + m_glfcns.glVertex2d (sz/2* cos (ang), sz/2*sin (ang)); m_glfcns.glEnd (); - break; - case '^': - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - m_glfcns.glVertex2d (0, -sz/2); - m_glfcns.glVertex2d (-sz/2, sz/2); - m_glfcns.glVertex2d (sz/2, sz/2); - m_glfcns.glEnd (); - break; - case '>': + } + break; + case 'd': + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + m_glfcns.glVertex2d (0, -sz/2); + m_glfcns.glVertex2d (sz/2, 0); + m_glfcns.glVertex2d (0, sz/2); + m_glfcns.glVertex2d (-sz/2, 0); + m_glfcns.glEnd (); + break; + case 'v': + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + m_glfcns.glVertex2d (0, sz/2); + m_glfcns.glVertex2d (sz/2, -sz/2); + m_glfcns.glVertex2d (-sz/2, -sz/2); + m_glfcns.glEnd (); + break; + case '^': + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + m_glfcns.glVertex2d (0, -sz/2); + m_glfcns.glVertex2d (-sz/2, sz/2); + m_glfcns.glVertex2d (sz/2, sz/2); + m_glfcns.glEnd (); + break; + case '>': + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + m_glfcns.glVertex2d (sz/2, 0); + m_glfcns.glVertex2d (-sz/2, sz/2); + m_glfcns.glVertex2d (-sz/2, -sz/2); + m_glfcns.glEnd (); + break; + case '<': + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + m_glfcns.glVertex2d (-sz/2, 0); + m_glfcns.glVertex2d (sz/2, -sz/2); + m_glfcns.glVertex2d (sz/2, sz/2); + m_glfcns.glEnd (); + break; + case 'p': + { + double ang, r, dr; + dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02; + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - m_glfcns.glVertex2d (sz/2, 0); - m_glfcns.glVertex2d (-sz/2, sz/2); - m_glfcns.glVertex2d (-sz/2, -sz/2); - m_glfcns.glEnd (); - break; - case '<': - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - m_glfcns.glVertex2d (-sz/2, 0); - m_glfcns.glVertex2d (sz/2, -sz/2); - m_glfcns.glVertex2d (sz/2, sz/2); + for (int i = 0; i < 2*5; i++) + { + ang = (-0.5 + double (i+1) / 5) * M_PI; + r = 1.0 - (dr * fmod (double (i+1), 2.0)); + m_glfcns.glVertex2d (sz/2*r* cos (ang), sz/2*r*sin (ang)); + } m_glfcns.glEnd (); - break; - case 'p': - { - double ang, r, dr; - dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02; - - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - for (int i = 0; i < 2*5; i++) - { - ang = (-0.5 + double (i+1) / 5) * M_PI; - r = 1.0 - (dr * fmod (double (i+1), 2.0)); - m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); - } - m_glfcns.glEnd (); - } - break; - case 'h': - { - double ang, r, dr; - dr = 1.0 - 0.5/sin (M_PI/3)*1.02; - - m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - for (int i = 0; i < 2*6; i++) - { - ang = (0.5 + double (i+1) / 6.0) * M_PI; - r = 1.0 - (dr * fmod (double (i+1), 2.0)); - m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); - } - m_glfcns.glEnd (); - } - break; - default: - warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ()); - break; } - - m_glfcns.glEndList (); - - return ID; + break; + case 'h': + { + double ang, r, dr; + dr = 1.0 - 0.5/sin (M_PI/3)*1.02; + + m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + for (int i = 0; i < 2*6; i++) + { + ang = (0.5 + double (i+1) / 6.0) * M_PI; + r = 1.0 - (dr * fmod (double (i+1), 2.0)); + m_glfcns.glVertex2d (sz/2*r* cos (ang), sz/2*r*sin (ang)); + } + m_glfcns.glEnd (); + } + break; + default: + warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ()); + break; + } + + m_glfcns.glEndList (); + + return ID; #else - octave_unused_parameter (marker); - octave_unused_parameter (size); - octave_unused_parameter (filled); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (marker); + octave_unused_parameter (size); + octave_unused_parameter (filled); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::text_to_pixels (const std::string& txt, - uint8NDArray& pixels, - Matrix& bbox, - int halign, int valign, double rotation) - { - m_txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign, - rotation, m_interpreter); - } - - void - opengl_renderer::text_to_strlist (const std::string& txt, - std::list<text_renderer::string>& lst, - Matrix& bbox, - int halign, int valign, double rotation) - { - m_txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign, - rotation, m_interpreter); - } - - Matrix - opengl_renderer::render_text (const std::string& txt, - double x, double y, double z, - int halign, int valign, double rotation) - { +} + +void +opengl_renderer::text_to_pixels (const std::string& txt, + uint8NDArray& pixels, + Matrix& bbox, + int halign, int valign, double rotation) +{ + m_txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign, + rotation, m_interpreter); +} + +void +opengl_renderer::text_to_strlist (const std::string& txt, + std::list<text_renderer::string>& lst, + Matrix& bbox, + int halign, int valign, double rotation) +{ + m_txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign, + rotation, m_interpreter); +} + +Matrix +opengl_renderer::render_text (const std::string& txt, + double x, double y, double z, + int halign, int valign, double rotation) +{ #if defined (HAVE_OPENGL) - Matrix bbox (1, 4, 0.0); - - if (txt.empty ()) - return bbox; - - if (m_txt_renderer.ok ()) - { - uint8NDArray pixels; - text_to_pixels (txt, pixels, bbox, halign, valign, rotation); - - render_text (pixels, bbox, x, y, z, rotation); - } - + Matrix bbox (1, 4, 0.0); + + if (txt.empty ()) return bbox; + if (m_txt_renderer.ok ()) + { + uint8NDArray pixels; + text_to_pixels (txt, pixels, bbox, halign, valign, rotation); + + render_text (pixels, bbox, x, y, z, rotation); + } + + return bbox; + #else - octave_unused_parameter (txt); - octave_unused_parameter (x); - octave_unused_parameter (y); - octave_unused_parameter (z); - octave_unused_parameter (halign); - octave_unused_parameter (valign); - octave_unused_parameter (rotation); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (txt); + octave_unused_parameter (x); + octave_unused_parameter (y); + octave_unused_parameter (z); + octave_unused_parameter (halign); + octave_unused_parameter (valign); + octave_unused_parameter (rotation); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } - - void - opengl_renderer::render_text (uint8NDArray pixels, Matrix bbox, - double x, double y, double z, double rotation) - { +} + +void +opengl_renderer::render_text (uint8NDArray pixels, Matrix bbox, + double x, double y, double z, double rotation) +{ #if defined (HAVE_OPENGL) - // Transform data coordinates to screen pixel ortho coordinates - ColumnVector pixpos = get_transform ().transform (x, y, z, false); - Matrix xdata(1, 2, bbox(0) / m_devpixratio); - xdata(1) += (bbox(2) - 1) / m_devpixratio; - Matrix ydata(1, 2, -bbox(1) / m_devpixratio); - ydata(1) -= (bbox(3) - 1) / m_devpixratio; - - bool blend = m_glfcns.glIsEnabled (GL_BLEND); - m_glfcns.glEnable (GL_BLEND); - m_glfcns.glEnable (GL_ALPHA_TEST); - - set_ortho_coordinates (); - - // Translate coordinates so that the text anchor is (0,0) - m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); - - m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); - - // Permute pixels returned by freetype - Array<octave_idx_type> perm (dim_vector (3, 1)); - perm(0) = 2; - perm(1) = 1; - perm(2) = 0; - draw_texture_image (pixels.permute (perm), - xdata, ydata, true); - - restore_previous_coordinates (); - - m_glfcns.glDisable (GL_ALPHA_TEST); - - if (! blend) - m_glfcns.glDisable (GL_BLEND); + // Transform data coordinates to screen pixel ortho coordinates + ColumnVector pixpos = get_transform ().transform (x, y, z, false); + Matrix xdata(1, 2, bbox(0) / m_devpixratio); + xdata(1) += (bbox(2) - 1) / m_devpixratio; + Matrix ydata(1, 2, -bbox(1) / m_devpixratio); + ydata(1) -= (bbox(3) - 1) / m_devpixratio; + + bool blend = m_glfcns.glIsEnabled (GL_BLEND); + m_glfcns.glEnable (GL_BLEND); + m_glfcns.glEnable (GL_ALPHA_TEST); + + set_ortho_coordinates (); + + // Translate coordinates so that the text anchor is (0,0) + m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); + + m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); + + // Permute pixels returned by freetype + Array<octave_idx_type> perm (dim_vector (3, 1)); + perm(0) = 2; + perm(1) = 1; + perm(2) = 0; + draw_texture_image (pixels.permute (perm), + xdata, ydata, true); + + restore_previous_coordinates (); + + m_glfcns.glDisable (GL_ALPHA_TEST); + + if (! blend) + m_glfcns.glDisable (GL_BLEND); #else - octave_unused_parameter (pixels); - octave_unused_parameter (bbox); - octave_unused_parameter (x); - octave_unused_parameter (y); - octave_unused_parameter (z); - octave_unused_parameter (rotation); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (pixels); + octave_unused_parameter (bbox); + octave_unused_parameter (x); + octave_unused_parameter (y); + octave_unused_parameter (z); + octave_unused_parameter (rotation); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif - } +} OCTAVE_END_NAMESPACE(octave)