Mercurial > jwe > octave
view libinterp/corefcn/gl-render.cc @ 30258:20fd3c03fd74
Add new plot marker styles '|' and '_' (bug #61350)
* NEWS: Announce addition.
* gl-render.cc (make_marker_list): Add instructions for drawing '|' and '_' markers.
* graphics.cc (radio_values::radio_values): Add special case in constructor from
string of options to parse '|||' and return value '|'.
* graphics.in.h (line, patch, scatter, surface): Add '|' and '_' to marker
property for graphics objects which have this property.
* __pltopt__.m: Add '|' and '_' to list of possible markers in input validation.
* __gnuplot_draw_axes__.m: Add FIXME note about implementing these two markers
in gnuplot. There doesn't seem to be an easy way to do this as it is not one
of the defined point styles.
author | Rik <rik@octave.org> |
---|---|
date | Wed, 27 Oct 2021 15:07:52 -0700 |
parents | 5c9f5353eef3 |
children | 796f54d4ddbf |
line wrap: on
line source
//////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2021 The Octave Project Developers // // See the file COPYRIGHT.md in the top-level directory of this // distribution or <https://octave.org/copyright/>. // // This file is part of Octave. // // Octave is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Octave is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Octave; see the file COPYING. If not, see // <https://www.gnu.org/licenses/>. // //////////////////////////////////////////////////////////////////////// #if defined (HAVE_CONFIG_H) # include "config.h" #endif #include <limits> #include <memory> #include <sstream> #if defined (HAVE_WINDOWS_H) # define WIN32_LEAN_AND_MEAN # include <windows.h> #endif #include "lo-mappers.h" #include "oct-locbuf.h" #include "errwarn.h" #include "gl-render.h" #include "interpreter-private.h" #include "oct-opengl.h" #include "text-renderer.h" namespace octave { #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; } #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. #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 { 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) { gluTessProperty (m_glu_tess, GLU_TESS_BOUNDARY_ONLY, (filled ? GL_FALSE : GL_TRUE)); m_fill = filled; gluTessBeginPolygon (m_glu_tess, this); } 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; }; class vertex_data { public: class vertex_data_rep { 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 ())) { Matrix col = v->m_color; if (col.numel () == 3) { glfcns.glColor4d (col(0), col(1), col(2), v->m_alpha); if (m_light_mode > 0) { // 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); } } } 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 class opengl_renderer::patch_tessellator { // Dummy class. }; #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) { 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::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) 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 } 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) { #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) { #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 } 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_boxes (const axes::properties& props) { #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 ()) { 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 (); #endif } void opengl_renderer::draw_axes_x_grid (const axes::properties& props) { #if defined (HAVE_OPENGL) gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_axes_x_grid"); 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 (); #endif } void opengl_renderer::draw_axes_y_grid (const axes::properties& props) { #if defined (HAVE_OPENGL) gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_axes_y_grid"); 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__ ("opengl_renderer::draw_axes_z_grid"); 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__ ("opengl_renderer::draw_axes_all_lights"); 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 (); #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 } void opengl_renderer::draw_surface (const surface::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 (); } #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) { #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++) { 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) { 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); } 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++) { 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); 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 fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) : Matrix ()); draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc); } 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_scatter (const scatter::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")))) { 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__ ("opengl_renderer::draw_scatter"); 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 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 (); #endif } void opengl_renderer::draw_hggroup (const hggroup::properties& props) { draw (props.get_children ()); } void opengl_renderer::set_ortho_coordinates (void) { #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 (); #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::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 (const text::properties& props) { #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 ()); } #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_text_background (const text::properties& props, bool /*do_rotate*/) { #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 (); #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_image (const image::properties& props) { #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); #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_texture_image (const octave_value cdata, Matrix x, Matrix y, bool ortho) { #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)"); #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 (); #endif } void opengl_renderer::draw (const Matrix& hlist, bool toplevel) { int len = hlist.numel (); gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw"); 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]; #else 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; #else // 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) { #if defined (HAVE_OPENGL) 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 (); #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) { #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); } #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 (); #endif } 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); #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 (); #endif } 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); #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 (); #endif } 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; #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 (); #endif } 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); } #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 (); #endif } 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); #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 (); #endif } 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); #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 (); #endif } 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); #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_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); } #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 (); #endif } 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; } #else // 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 { #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 ()); #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 (); #endif } 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); #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 (); #endif } double opengl_renderer::points_to_pixels (const double val) const { gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::points_to_pixels"); // 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]) { 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 '.': { // 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': 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); 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); 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; #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 (); #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) { #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); } 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 (); #endif } 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); #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 (); #endif } }