Mercurial > octave
changeset 22331:b81b08cc4c83
maint: Indent namespaces in more files.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 17 Aug 2016 11:43:27 -0400 |
parents | 53e246fd8124 |
children | 1d85005fc6b7 |
files | libinterp/corefcn/base-text-renderer.h libinterp/corefcn/ft-text-renderer.cc libinterp/corefcn/gl-render.cc libinterp/corefcn/gl-render.h libinterp/corefcn/gl2ps-print.cc libinterp/corefcn/text-renderer.cc libinterp/corefcn/text-renderer.h libinterp/octave-value/ov-java.cc |
diffstat | 8 files changed, 5652 insertions(+), 5670 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/base-text-renderer.h Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/base-text-renderer.h Wed Aug 17 11:43:27 2016 -0400 @@ -37,50 +37,48 @@ namespace octave { + class + base_text_renderer : public text_processor + { + public: -class -base_text_renderer : public text_processor -{ -public: + base_text_renderer (void) : text_processor () { } - base_text_renderer (void) : text_processor () { } - - virtual ~base_text_renderer (void) { } + virtual ~base_text_renderer (void) { } - virtual Matrix - get_extent (text_element *elt, double rotation) = 0; + virtual Matrix + get_extent (text_element *elt, double rotation) = 0; - virtual Matrix - get_extent (const std::string& txt, double rotation, - const caseless_str& interpreter) = 0; + virtual Matrix + get_extent (const std::string& txt, double rotation, + const caseless_str& interpreter) = 0; - virtual void - set_font (const std::string& name, const std::string& weight, - const std::string& angle, double size) = 0; + virtual void + set_font (const std::string& name, const std::string& weight, + const std::string& angle, double size) = 0; - virtual void set_color (const Matrix& c) = 0; + virtual void set_color (const Matrix& c) = 0; - virtual void - text_to_pixels (const std::string& txt, uint8NDArray& pxls, - Matrix& bbox, int halign, int valign, double rotation, - const caseless_str& interpreter, - bool handle_rotation) = 0; + virtual void + text_to_pixels (const std::string& txt, uint8NDArray& pxls, + Matrix& bbox, int halign, int valign, double rotation, + const caseless_str& interpreter, + bool handle_rotation) = 0; - virtual void - text_to_strlist (const std::string& txt, - std::list<text_renderer::string>& lst, - Matrix& box, int halign, int valign, double rotation, - const caseless_str& interpreter = "tex") = 0; - -private: + virtual void + text_to_strlist (const std::string& txt, + std::list<text_renderer::string>& lst, + Matrix& box, int halign, int valign, double rotation, + const caseless_str& interpreter = "tex") = 0; - // No copying! + private: - base_text_renderer (const base_text_renderer&); + // No copying! - base_text_renderer& operator = (const base_text_renderer&); -}; + base_text_renderer (const base_text_renderer&); + base_text_renderer& operator = (const base_text_renderer&); + }; } #endif
--- a/libinterp/corefcn/ft-text-renderer.cc Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/ft-text-renderer.cc Wed Aug 17 11:43:27 2016 -0400 @@ -89,7 +89,6 @@ namespace octave { - class ft_manager { @@ -324,1053 +323,1051 @@ namespace octave { - -class -OCTINTERP_API -ft_text_renderer : public base_text_renderer -{ -public: - - enum - { - MODE_BBOX = 0, - MODE_RENDER = 1 - }; - - enum - { - ROTATION_0 = 0, - ROTATION_90 = 1, - ROTATION_180 = 2, - ROTATION_270 = 3 - }; - -public: - - ft_text_renderer (void) - : base_text_renderer (), font (), bbox (1, 4, 0.0), halign (0), - xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX), - color (dim_vector (1, 3), 0) - { } - - ~ft_text_renderer (void) { } - - void visit (text_element_string& e); - - void visit (text_element_list& e); - - void visit (text_element_subscript& e); - - void visit (text_element_superscript& e); - - void visit (text_element_color& e); - - void visit (text_element_fontsize& e); - - void visit (text_element_fontname& e); - - void visit (text_element_fontstyle& e); - - void visit (text_element_symbol& e); - - void visit (text_element_combined& e); - - void reset (void); - - uint8NDArray get_pixels (void) const { return pixels; } - - Matrix get_boundingbox (void) const { return bbox; } - - uint8NDArray render (text_element *elt, Matrix& box, - int rotation = ROTATION_0); - - Matrix get_extent (text_element *elt, double rotation = 0.0); - Matrix get_extent (const std::string& txt, double rotation, - const caseless_str& interpreter); - - void set_font (const std::string& name, const std::string& weight, - const std::string& angle, double size); - - void set_color (const Matrix& c); - - void set_mode (int m); - - void text_to_pixels (const std::string& txt, - uint8NDArray& pxls, Matrix& bbox, - int halign, int valign, double rotation, - const caseless_str& interpreter, - bool handle_rotation); - -private: - - int rotation_to_mode (double rotation) const; - - // No copying! - - ft_text_renderer (const ft_text_renderer&); - - ft_text_renderer& operator = (const ft_text_renderer&); - - // Class to hold information about fonts and a strong - // reference to the font objects loaded by FreeType. - - class ft_font : public text_renderer::font + class + OCTINTERP_API + ft_text_renderer : public base_text_renderer { public: - ft_font (void) - : text_renderer::font (), face (0) { } + enum + { + MODE_BBOX = 0, + MODE_RENDER = 1 + }; + + enum + { + ROTATION_0 = 0, + ROTATION_90 = 1, + ROTATION_180 = 2, + ROTATION_270 = 3 + }; + + public: + + ft_text_renderer (void) + : base_text_renderer (), font (), bbox (1, 4, 0.0), halign (0), + xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX), + color (dim_vector (1, 3), 0) + { } + + ~ft_text_renderer (void) { } + + void visit (text_element_string& e); + + void visit (text_element_list& e); + + void visit (text_element_subscript& e); + + void visit (text_element_superscript& e); - ft_font (const std::string& nm, const std::string& wt, - const std::string& ang, double sz, FT_Face f = 0) - : text_renderer::font (nm, wt, ang, sz), face (f) - { } + void visit (text_element_color& e); + + void visit (text_element_fontsize& e); + + void visit (text_element_fontname& e); + + void visit (text_element_fontstyle& e); + + void visit (text_element_symbol& e); + + void visit (text_element_combined& e); + + void reset (void); + + uint8NDArray get_pixels (void) const { return pixels; } - ft_font (const ft_font& ft); + Matrix get_boundingbox (void) const { return bbox; } + + uint8NDArray render (text_element *elt, Matrix& box, + int rotation = ROTATION_0); + + Matrix get_extent (text_element *elt, double rotation = 0.0); + Matrix get_extent (const std::string& txt, double rotation, + const caseless_str& interpreter); + + void set_font (const std::string& name, const std::string& weight, + const std::string& angle, double size); + + void set_color (const Matrix& c); + + void set_mode (int m); - ~ft_font (void) + void text_to_pixels (const std::string& txt, + uint8NDArray& pxls, Matrix& bbox, + int halign, int valign, double rotation, + const caseless_str& interpreter, + bool handle_rotation); + + private: + + int rotation_to_mode (double rotation) const; + + // No copying! + + ft_text_renderer (const ft_text_renderer&); + + ft_text_renderer& operator = (const ft_text_renderer&); + + // Class to hold information about fonts and a strong + // reference to the font objects loaded by FreeType. + + class ft_font : public text_renderer::font { - if (face) - FT_Done_Face (face); - } + public: + + ft_font (void) + : text_renderer::font (), face (0) { } + + ft_font (const std::string& nm, const std::string& wt, + const std::string& ang, double sz, FT_Face f = 0) + : text_renderer::font (nm, wt, ang, sz), face (f) + { } + + ft_font (const ft_font& ft); - ft_font& operator = (const ft_font& ft); + ~ft_font (void) + { + if (face) + FT_Done_Face (face); + } + + ft_font& operator = (const ft_font& ft); + + bool is_valid (void) const { return get_face (); } + + FT_Face get_face (void) const; + + private: + + mutable FT_Face face; + }; - bool is_valid (void) const { return get_face (); } + void push_new_line (void); + + void update_line_bbox (void); + + void compute_bbox (void); + + int compute_line_xoffset (const Matrix& lb) const; - FT_Face get_face (void) const; + FT_UInt process_character (FT_ULong code, FT_UInt previous = 0); + + public: + + void text_to_strlist (const std::string& txt, + std::list<text_renderer::string>& lst, Matrix& bbox, + int halign, int valign, double rotation, + const caseless_str& interp); private: - mutable FT_Face face; + // The current font used by the renderer. + ft_font font; + + // Used to stored the bounding box corresponding to the rendered text. + // The bounding box has the form [x, y, w, h] where x and y represent the + // coordinates of the bottom left corner relative to the anchor point of + // the text (== start of text on the baseline). Due to font descent or + // multiple lines, the value y is usually negative. + Matrix bbox; + + // Used to stored the rendered text. It's a 3D matrix with size MxNx4 + // where M and N are the width and height of the bounding box. + uint8NDArray pixels; + + // Used to store the bounding box of each line. This is used to layout + // multiline text properly. + std::list<Matrix> line_bbox; + + // The current horizontal alignment. This is used to align multi-line text. + int halign; + + // The X offset for the next glyph. + int xoffset; + + // The Y offset of the baseline for the current line. + int line_yoffset; + + // The Y offset of the baseline for the next glyph. The offset is relative + // to line_yoffset. The total Y offset is computed with: + // line_yoffset + yoffset. + int yoffset; + + // The current mode of the rendering process (box computing or rendering). + int mode; + + // The base color of the rendered text. + uint8NDArray color; + + // A list of parsed strings to be used for printing. + std::list<text_renderer::string> strlist; + + // The X offset of the baseline for the current line. + int line_xoffset; + }; - void push_new_line (void); + void + ft_text_renderer::set_font (const std::string& name, const std::string& weight, + const std::string& angle, double size) + { + // FIXME: take "fontunits" into account + + font = ft_font (name, weight, angle, size, 0); + } + + void + ft_text_renderer::push_new_line (void) + { + switch (mode) + { + case MODE_BBOX: + { + // Create a new bbox entry based on the current font. + + FT_Face face = font.get_face (); - void update_line_bbox (void); + if (face) + { + int asc = face->size->metrics.ascender >> 6; + int desc = face->size->metrics.descender >> 6; + int h = face->size->metrics.height >> 6; + + Matrix bb (1, 5, 0.0); + + bb(1) = desc; + bb(3) = asc - desc; + bb(4) = h; + + line_bbox.push_back (bb); + + xoffset = yoffset = 0; + } + } + break; + + case MODE_RENDER: + { + // Move to the next line bbox, adjust xoffset based on alignment + // and yoffset based on the old and new line bbox. - void compute_bbox (void); + Matrix old_bbox = line_bbox.front (); + line_bbox.pop_front (); + Matrix new_bbox = line_bbox.front (); + + xoffset = line_xoffset = compute_line_xoffset (new_bbox); + line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3))); + yoffset = 0; + } + break; + } + } - int compute_line_xoffset (const Matrix& lb) const; + int + ft_text_renderer::compute_line_xoffset (const Matrix& lb) const + { + if (! bbox.is_empty ()) + { + switch (halign) + { + case 0: + return 0; + case 1: + return (bbox(2) - lb(2)) / 2; + case 2: + return (bbox(2) - lb(2)); + } + } + + return 0; + } + + void + ft_text_renderer::compute_bbox (void) + { + // Stack the various line bbox together and compute the final + // bounding box for the entire text string. + + bbox = Matrix (); + + switch (line_bbox.size ()) + { + case 0: + break; + + case 1: + bbox = line_bbox.front ().extract (0, 0, 0, 3); + break; - FT_UInt process_character (FT_ULong code, FT_UInt previous = 0); + default: + for (std::list<Matrix>::const_iterator it = line_bbox.begin (); + it != line_bbox.end (); ++it) + { + if (bbox.is_empty ()) + bbox = it->extract (0, 0, 0, 3); + else + { + bbox(1) -= (*it)(3); + bbox(3) += (*it)(3); + bbox(2) = octave::math::max (bbox(2), (*it)(2)); + } + } + break; + } + } -public: + void + ft_text_renderer::update_line_bbox (void) + { + // Called after a font change, when in MODE_BBOX mode, to update the + // current line bbox with the new font metrics. This also includes the + // current yoffset, that is the offset of the current glyph's baseline + // the line's baseline. + + if (mode == MODE_BBOX) + { + int asc = font.get_face ()->size->metrics.ascender >> 6; + int desc = font.get_face ()->size->metrics.descender >> 6; + + Matrix& bb = line_bbox.back (); + + if ((yoffset + desc) < bb(1)) + { + // The new font goes below the bottom of the current bbox. + + int delta = bb(1) - (yoffset + desc); + + bb(1) -= delta; + bb(3) += delta; + } + + if ((yoffset + asc) > (bb(1) + bb(3))) + { + // The new font goes above the top of the current bbox. + + int delta = (yoffset + asc) - (bb(1) + bb(3)); - void text_to_strlist (const std::string& txt, - std::list<text_renderer::string>& lst, Matrix& bbox, - int halign, int valign, double rotation, - const caseless_str& interp); + bb(3) += delta; + } + } + } + + void + ft_text_renderer::set_mode (int m) + { + mode = m; + + switch (mode) + { + case MODE_BBOX: + xoffset = line_yoffset = yoffset = 0; + bbox = Matrix (1, 4, 0.0); + line_bbox.clear (); + push_new_line (); + break; + + case MODE_RENDER: + if (bbox.numel () != 4) + { + ::warning ("ft_text_renderer: invalid bounding box, cannot render"); -private: + xoffset = line_yoffset = yoffset = 0; + pixels = uint8NDArray (); + } + else + { + dim_vector d (4, octave_idx_type (bbox(2)), + octave_idx_type (bbox(3))); + pixels = uint8NDArray (d, static_cast<uint8_t> (0)); + xoffset = compute_line_xoffset (line_bbox.front ()); + line_yoffset = -bbox(1)-1; + yoffset = 0; + } + break; + + default: + error ("ft_text_renderer: invalid mode '%d'", mode); + break; + } + } + + FT_UInt + ft_text_renderer::process_character (FT_ULong code, FT_UInt previous) + { + FT_Face face = font.get_face (); + FT_UInt glyph_index = 0; + + if (face) + { + glyph_index = FT_Get_Char_Index (face, code); - // The current font used by the renderer. - ft_font font; + if (code != '\n' + && (! glyph_index + || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))) + { + glyph_index = 0; + warn_missing_glyph (code); + } + else + { + switch (mode) + { + case MODE_RENDER: + if (code == '\n') + { + glyph_index = FT_Get_Char_Index (face, ' '); + if (! glyph_index + || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) + { + glyph_index = 0; + warn_missing_glyph (' '); + } + else + push_new_line (); + } + else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) + { + glyph_index = 0; + warn_glyph_render (code); + } + else + { + FT_Bitmap& bitmap = face->glyph->bitmap; + int x0, y0; + + if (previous) + { + FT_Vector delta; + + FT_Get_Kerning (face, previous, glyph_index, + FT_KERNING_DEFAULT, &delta); + xoffset += (delta.x >> 6); + } + + x0 = xoffset + face->glyph->bitmap_left; + y0 = line_yoffset + yoffset + face->glyph->bitmap_top; + + // 'w' seems to have a negative -1 + // face->glyph->bitmap_left, this is so we don't + // index out of bound, and assumes we've allocated + // the right amount of horizontal space in the bbox. + if (x0 < 0) + x0 = 0; - // Used to stored the bounding box corresponding to the rendered text. - // The bounding box has the form [x, y, w, h] where x and y represent the - // coordinates of the bottom left corner relative to the anchor point of - // the text (== start of text on the baseline). Due to font descent or - // multiple lines, the value y is usually negative. - Matrix bbox; + for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++) + for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++) + { + unsigned char pix = bitmap.buffer[r*bitmap.width+c]; + if (x0+c < 0 || x0+c >= pixels.dim2 () + || y0-r < 0 || y0-r >= pixels.dim3 ()) + { + //::warning ("ft_text_renderer: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)", + // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ()); + } + else if (pixels(3, x0+c, y0-r).value () == 0) + { + pixels(0, x0+c, y0-r) = color(0); + pixels(1, x0+c, y0-r) = color(1); + pixels(2, x0+c, y0-r) = color(2); + pixels(3, x0+c, y0-r) = pix; + } + } + + xoffset += (face->glyph->advance.x >> 6); + } + break; + + case MODE_BBOX: + if (code == '\n') + { + glyph_index = FT_Get_Char_Index (face, ' '); + if (! glyph_index + || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) + { + glyph_index = 0; + warn_missing_glyph (' '); + } + else + push_new_line (); + } + else + { + Matrix& bb = line_bbox.back (); + + // If we have a previous glyph, use kerning information. + // This usually means moving a bit backward before adding + // the next glyph. That is, "delta.x" is usually < 0. + if (previous) + { + FT_Vector delta; + + FT_Get_Kerning (face, previous, glyph_index, + FT_KERNING_DEFAULT, &delta); + + xoffset += (delta.x >> 6); + } - // Used to stored the rendered text. It's a 3D matrix with size MxNx4 - // where M and N are the width and height of the bounding box. - uint8NDArray pixels; + // Extend current X offset box by the width of the current + // glyph. Then extend the line bounding box if necessary. + + xoffset += (face->glyph->advance.x >> 6); + bb(2) = octave::math::max (bb(2), xoffset); + } + break; + } + } + } + + return glyph_index; + } + + void + ft_text_renderer::text_to_strlist (const std::string& txt, + std::list<text_renderer::string>& lst, + Matrix& box, + int ha, int va, double rot, + const caseless_str& interp) + { + uint8NDArray pxls; + + // First run text_to_pixels which will also build the string list + + text_to_pixels (txt, pxls, box, ha, va, rot, interp, false); + + lst = strlist; + } + + void + ft_text_renderer::visit (text_element_string& e) + { + if (font.is_valid ()) + { + FT_UInt glyph_index, previous = 0; + + std::string str = e.string_value (); + size_t n = str.length (); + size_t curr = 0; + size_t idx = 0; + mbstate_t ps; + memset (&ps, 0, sizeof (ps)); // Initialize state to 0. + wchar_t wc; + + text_renderer::string fs (str, font, xoffset, yoffset); + + while (n > 0) + { + size_t r = std::mbrtowc (&wc, str.data () + curr, n, &ps); - // Used to store the bounding box of each line. This is used to layout - // multiline text properly. - std::list<Matrix> line_bbox; + if (r > 0 + && r != static_cast<size_t> (-1) + && r != static_cast<size_t> (-2)) + { + n -= r; + curr += r; + + if (wc == L'\n') + { + // Finish previous string in srtlist before processing + // the newline character + fs.set_y (line_yoffset + yoffset); + fs.set_color (color); + std::string s = str.substr (idx, curr - idx - 1); + if (! s.empty ()) + { + fs.set_string (s); + strlist.push_back (fs); + } + } + + glyph_index = process_character (wc, previous); - // The current horizontal alignment. This is used to align multi-line text. - int halign; + if (wc == L'\n') + { + previous = 0; + // Start a new string in strlist + idx = curr; + fs = text_renderer::string (str.substr (idx), font, + line_xoffset, yoffset); + + } + else + previous = glyph_index; + } + else + { + if (r != 0) + ::warning ("ft_text_renderer: failed to decode string `%s' with " + "locale `%s'", str.c_str (), + std::setlocale (LC_CTYPE, 0)); + break; + } + } + + if (! fs.get_string ().empty ()) + { + fs.set_y (line_yoffset + yoffset); + fs.set_color (color); + strlist.push_back (fs); + } + } + } + + void + ft_text_renderer::visit (text_element_list& e) + { + // Save and restore (after processing the list) the current font and color. - // The X offset for the next glyph. - int xoffset; + ft_font saved_font (font); + uint8NDArray saved_color (color); + + text_processor::visit (e); + + font = saved_font; + color = saved_color; + } + + void + ft_text_renderer::visit (text_element_subscript& e) + { + ft_font saved_font (font); + int saved_line_yoffset = line_yoffset; + int saved_yoffset = yoffset; + + set_font (font.get_name (), font.get_weight (), font.get_angle (), + font.get_size () - 2); + + if (font.is_valid ()) + { + int h = font.get_face ()->size->metrics.height >> 6; + + // Shifting the baseline by 2/3 the font height seems to produce + // decent result. + yoffset -= (h * 2) / 3; + + if (mode == MODE_BBOX) + update_line_bbox (); + } + + text_processor::visit (e); + + font = saved_font; + // If line_yoffset changed, this means we moved to a new line; hence yoffset + // cannot be restored, because the saved value is not relevant anymore. + if (line_yoffset == saved_line_yoffset) + yoffset = saved_yoffset; + } + + void + ft_text_renderer::visit (text_element_superscript& e) + { + ft_font saved_font (font); + int saved_line_yoffset = line_yoffset; + int saved_yoffset = yoffset; - // The Y offset of the baseline for the current line. - int line_yoffset; + set_font (font.get_name (), font.get_weight (), font.get_angle (), + font.get_size () - 2); + + if (saved_font.is_valid ()) + { + int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6; + + // Shifting the baseline by 2/3 base font ascender seems to produce + // decent result. + yoffset += (s_asc * 2) / 3; + + if (mode == MODE_BBOX) + update_line_bbox (); + } + + text_processor::visit (e); + + font = saved_font; + // If line_yoffset changed, this means we moved to a new line; hence yoffset + // cannot be restored, because the saved value is not relevant anymore. + if (line_yoffset == saved_line_yoffset) + yoffset = saved_yoffset; + } + + void + ft_text_renderer::visit (text_element_color& e) + { + if (mode == MODE_RENDER) + set_color (e.get_color ()); + } + + void + ft_text_renderer::visit (text_element_fontsize& e) + { + double sz = e.get_fontsize (); + + // FIXME: Matlab documentation says that the font size is expressed + // in the text object FontUnit. + + set_font (font.get_name (), font.get_weight (), font.get_angle (), sz); + + if (mode == MODE_BBOX) + update_line_bbox (); + } + + void + ft_text_renderer::visit (text_element_fontname& e) + { + set_font (e.get_fontname (), font.get_weight (), font.get_angle (), + font.get_size ()); - // The Y offset of the baseline for the next glyph. The offset is relative - // to line_yoffset. The total Y offset is computed with: - // line_yoffset + yoffset. - int yoffset; + if (mode == MODE_BBOX) + update_line_bbox (); + } + + void + ft_text_renderer::visit (text_element_fontstyle& e) + { + switch (e.get_fontstyle ()) + { + case text_element_fontstyle::normal: + set_font (font.get_name (), "normal", "normal", font.get_size ()); + break; + + case text_element_fontstyle::bold: + set_font (font.get_name (), "bold", "normal", font.get_size ()); + break; + + case text_element_fontstyle::italic: + set_font (font.get_name (), "normal", "italic", font.get_size ()); + break; + + case text_element_fontstyle::oblique: + set_font (font.get_name (), "normal", "oblique", font.get_size ()); + break; + } + + if (mode == MODE_BBOX) + update_line_bbox (); + } + + void + ft_text_renderer::visit (text_element_symbol& e) + { + uint32_t code = e.get_symbol_code (); + + text_renderer::string fs (std::string ("-"), font, xoffset, yoffset); + + if (code != text_element_symbol::invalid_code && font.is_valid ()) + { + process_character (code); + fs.set_code (code); + } + else if (font.is_valid ()) + ::warning ("ignoring unknown symbol: %d", e.get_symbol ()); - // The current mode of the rendering process (box computing or rendering). - int mode; + if (fs.get_code ()) + { + fs.set_y (line_yoffset + yoffset); + fs.set_color (color); + strlist.push_back (fs); + } + } + + void + ft_text_renderer::visit (text_element_combined& e) + { + int saved_xoffset = xoffset; + int max_xoffset = xoffset; + + for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it) + { + xoffset = saved_xoffset; + (*it)->accept (*this); + max_xoffset = octave::math::max (xoffset, max_xoffset); + } + + xoffset = max_xoffset; + } - // The base color of the rendered text. - uint8NDArray color; + void + ft_text_renderer::reset (void) + { + set_mode (MODE_BBOX); + set_color (Matrix (1, 3, 0.0)); + } - // A list of parsed strings to be used for printing. - std::list<text_renderer::string> strlist; + void + ft_text_renderer::set_color (const Matrix& c) + { + if (c.numel () == 3) + { + color(0) = static_cast<uint8_t> (c(0)*255); + color(1) = static_cast<uint8_t> (c(1)*255); + color(2) = static_cast<uint8_t> (c(2)*255); + } + else + ::warning ("ft_text_renderer::set_color: invalid color"); + } + + uint8NDArray + ft_text_renderer::render (text_element *elt, Matrix& box, int rotation) + { + set_mode (MODE_BBOX); + elt->accept (*this); + compute_bbox (); + box = bbox; + + set_mode (MODE_RENDER); + // Clear the list of parsed strings + strlist.clear (); + + if (pixels.numel () > 0) + { + elt->accept (*this); - // The X offset of the baseline for the current line. - int line_xoffset; + switch (rotation) + { + case ROTATION_0: + break; + + case ROTATION_90: + { + Array<octave_idx_type> perm (dim_vector (3, 1)); + perm(0) = 0; + perm(1) = 2; + perm(2) = 1; + pixels = pixels.permute (perm); + + Array<idx_vector> idx (dim_vector (3, 1)); + idx(0) = idx_vector (':'); + idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); + idx(2) = idx_vector (':'); + pixels = uint8NDArray (pixels.index (idx)); + } + break; -}; + case ROTATION_180: + { + Array<idx_vector> idx (dim_vector (3, 1)); + idx(0) = idx_vector (':'); + idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); + idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); + pixels = uint8NDArray (pixels.index (idx)); + } + break; + + case ROTATION_270: + { + Array<octave_idx_type> perm (dim_vector (3, 1)); + perm(0) = 0; + perm(1) = 2; + perm(2) = 1; + pixels = pixels.permute (perm); + + Array<idx_vector> idx (dim_vector (3, 1)); + idx(0) = idx_vector (':'); + idx(1) = idx_vector (':'); + idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); + pixels = uint8NDArray (pixels.index (idx)); + } + break; + } + } -void -ft_text_renderer::set_font (const std::string& name, const std::string& weight, - const std::string& angle, double size) -{ - // FIXME: take "fontunits" into account + return pixels; + } + + // Note: + // x-extent accurately measures width of glyphs. + // y-extent is overly large because it is measured from baseline-to-baseline. + // Calling routines, such as ylabel, may need to account for this mismatch. + + Matrix + ft_text_renderer::get_extent (text_element *elt, double rotation) + { + set_mode (MODE_BBOX); + elt->accept (*this); + compute_bbox (); + + Matrix extent (1, 2, 0.0); + + switch (rotation_to_mode (rotation)) + { + case ROTATION_0: + case ROTATION_180: + extent(0) = bbox(2); + extent(1) = bbox(3); + break; + + case ROTATION_90: + case ROTATION_270: + extent(0) = bbox(3); + extent(1) = bbox(2); + } + + return extent; + } + + Matrix + ft_text_renderer::get_extent (const std::string& txt, double rotation, + const caseless_str& interpreter) + { + text_element *elt = text_parser::parse (txt, interpreter); + Matrix extent = get_extent (elt, rotation); + delete elt; + + return extent; + } + + int + ft_text_renderer::rotation_to_mode (double rotation) const + { + // Clip rotation to range [0, 360] + while (rotation < 0) + rotation += 360.0; + while (rotation > 360.0) + rotation -= 360.0; - font = ft_font (name, weight, angle, size, 0); -} + if (rotation == 0.0) + return ROTATION_0; + else if (rotation == 90.0) + return ROTATION_90; + else if (rotation == 180.0) + return ROTATION_180; + else if (rotation == 270.0) + return ROTATION_270; + else + return ROTATION_0; + } + + void + ft_text_renderer::text_to_pixels (const std::string& txt, + uint8NDArray& pxls, Matrix& box, + int _halign, int valign, double rotation, + const caseless_str& interpreter, + bool handle_rotation) + { + int rot_mode = rotation_to_mode (rotation); + + halign = _halign; + + text_element *elt = text_parser::parse (txt, interpreter); + pxls = render (elt, box, rot_mode); + delete elt; + + if (pxls.is_empty ()) + return; // nothing to render + + switch (halign) + { + case 1: + box(0) = -box(2)/2; + break; + + case 2: + box(0) = -box(2); + break; + + default: + box(0) = 0; + break; + } + + switch (valign) + { + case 1: + box(1) = -box(3)/2; + break; -void -ft_text_renderer::push_new_line (void) -{ - switch (mode) - { - case MODE_BBOX: + case 2: + box(1) = -box(3); + break; + + case 3: + break; + + case 4: + box(1) = -box(3)-box(1); + break; + + default: + box(1) = 0; + break; + } + + if (handle_rotation) { - // Create a new bbox entry based on the current font. + switch (rot_mode) + { + case ROTATION_90: + std::swap (box(0), box(1)); + std::swap (box(2), box(3)); + box(0) = -box(0)-box(2); + break; + + case ROTATION_180: + box(0) = -box(0)-box(2); + box(1) = -box(1)-box(3); + break; - FT_Face face = font.get_face (); + case ROTATION_270: + std::swap (box(0), box(1)); + std::swap (box(2), box(3)); + box(1) = -box(1)-box(3); + break; + } + } + } + + ft_text_renderer::ft_font::ft_font (const ft_font& ft) + : text_renderer::font (ft), face (0) + { +#if defined (HAVE_FT_REFERENCE_FACE) + FT_Face ft_face = ft.get_face (); + + if (ft_face && FT_Reference_Face (ft_face) == 0) + face = ft_face; +#endif + } + + ft_text_renderer::ft_font& + ft_text_renderer::ft_font::operator = (const ft_font& ft) + { + if (&ft != this) + { + text_renderer::font::operator = (ft); if (face) { - int asc = face->size->metrics.ascender >> 6; - int desc = face->size->metrics.descender >> 6; - int h = face->size->metrics.height >> 6; - - Matrix bb (1, 5, 0.0); - - bb(1) = desc; - bb(3) = asc - desc; - bb(4) = h; - - line_bbox.push_back (bb); - - xoffset = yoffset = 0; + FT_Done_Face (face); + face = 0; } - } - break; - - case MODE_RENDER: - { - // Move to the next line bbox, adjust xoffset based on alignment - // and yoffset based on the old and new line bbox. - - Matrix old_bbox = line_bbox.front (); - line_bbox.pop_front (); - Matrix new_bbox = line_bbox.front (); - - xoffset = line_xoffset = compute_line_xoffset (new_bbox); - line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3))); - yoffset = 0; - } - break; - } -} - -int -ft_text_renderer::compute_line_xoffset (const Matrix& lb) const -{ - if (! bbox.is_empty ()) - { - switch (halign) - { - case 0: - return 0; - case 1: - return (bbox(2) - lb(2)) / 2; - case 2: - return (bbox(2) - lb(2)); - } - } - - return 0; -} - -void -ft_text_renderer::compute_bbox (void) -{ - // Stack the various line bbox together and compute the final - // bounding box for the entire text string. - - bbox = Matrix (); - - switch (line_bbox.size ()) - { - case 0: - break; - - case 1: - bbox = line_bbox.front ().extract (0, 0, 0, 3); - break; - - default: - for (std::list<Matrix>::const_iterator it = line_bbox.begin (); - it != line_bbox.end (); ++it) - { - if (bbox.is_empty ()) - bbox = it->extract (0, 0, 0, 3); - else - { - bbox(1) -= (*it)(3); - bbox(3) += (*it)(3); - bbox(2) = octave::math::max (bbox(2), (*it)(2)); - } - } - break; - } -} - -void -ft_text_renderer::update_line_bbox (void) -{ - // Called after a font change, when in MODE_BBOX mode, to update the - // current line bbox with the new font metrics. This also includes the - // current yoffset, that is the offset of the current glyph's baseline - // the line's baseline. - - if (mode == MODE_BBOX) - { - int asc = font.get_face ()->size->metrics.ascender >> 6; - int desc = font.get_face ()->size->metrics.descender >> 6; - - Matrix& bb = line_bbox.back (); - - if ((yoffset + desc) < bb(1)) - { - // The new font goes below the bottom of the current bbox. - - int delta = bb(1) - (yoffset + desc); - - bb(1) -= delta; - bb(3) += delta; - } - - if ((yoffset + asc) > (bb(1) + bb(3))) - { - // The new font goes above the top of the current bbox. - - int delta = (yoffset + asc) - (bb(1) + bb(3)); - - bb(3) += delta; - } - } -} - -void -ft_text_renderer::set_mode (int m) -{ - mode = m; - - switch (mode) - { - case MODE_BBOX: - xoffset = line_yoffset = yoffset = 0; - bbox = Matrix (1, 4, 0.0); - line_bbox.clear (); - push_new_line (); - break; - - case MODE_RENDER: - if (bbox.numel () != 4) - { - ::warning ("ft_text_renderer: invalid bounding box, cannot render"); - - xoffset = line_yoffset = yoffset = 0; - pixels = uint8NDArray (); - } - else - { - dim_vector d (4, octave_idx_type (bbox(2)), - octave_idx_type (bbox(3))); - pixels = uint8NDArray (d, static_cast<uint8_t> (0)); - xoffset = compute_line_xoffset (line_bbox.front ()); - line_yoffset = -bbox(1)-1; - yoffset = 0; - } - break; - - default: - error ("ft_text_renderer: invalid mode '%d'", mode); - break; - } -} - -FT_UInt -ft_text_renderer::process_character (FT_ULong code, FT_UInt previous) -{ - FT_Face face = font.get_face (); - FT_UInt glyph_index = 0; - - if (face) - { - glyph_index = FT_Get_Char_Index (face, code); - - if (code != '\n' - && (! glyph_index - || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))) - { - glyph_index = 0; - warn_missing_glyph (code); - } - else - { - switch (mode) - { - case MODE_RENDER: - if (code == '\n') - { - glyph_index = FT_Get_Char_Index (face, ' '); - if (! glyph_index - || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) - { - glyph_index = 0; - warn_missing_glyph (' '); - } - else - push_new_line (); - } - else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) - { - glyph_index = 0; - warn_glyph_render (code); - } - else - { - FT_Bitmap& bitmap = face->glyph->bitmap; - int x0, y0; - - if (previous) - { - FT_Vector delta; - - FT_Get_Kerning (face, previous, glyph_index, - FT_KERNING_DEFAULT, &delta); - xoffset += (delta.x >> 6); - } - - x0 = xoffset + face->glyph->bitmap_left; - y0 = line_yoffset + yoffset + face->glyph->bitmap_top; - - // 'w' seems to have a negative -1 - // face->glyph->bitmap_left, this is so we don't - // index out of bound, and assumes we've allocated - // the right amount of horizontal space in the bbox. - if (x0 < 0) - x0 = 0; - - for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++) - for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++) - { - unsigned char pix = bitmap.buffer[r*bitmap.width+c]; - if (x0+c < 0 || x0+c >= pixels.dim2 () - || y0-r < 0 || y0-r >= pixels.dim3 ()) - { - //::warning ("ft_text_renderer: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)", - // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ()); - } - else if (pixels(3, x0+c, y0-r).value () == 0) - { - pixels(0, x0+c, y0-r) = color(0); - pixels(1, x0+c, y0-r) = color(1); - pixels(2, x0+c, y0-r) = color(2); - pixels(3, x0+c, y0-r) = pix; - } - } - - xoffset += (face->glyph->advance.x >> 6); - } - break; - - case MODE_BBOX: - if (code == '\n') - { - glyph_index = FT_Get_Char_Index (face, ' '); - if (! glyph_index - || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) - { - glyph_index = 0; - warn_missing_glyph (' '); - } - else - push_new_line (); - } - else - { - Matrix& bb = line_bbox.back (); - - // If we have a previous glyph, use kerning information. - // This usually means moving a bit backward before adding - // the next glyph. That is, "delta.x" is usually < 0. - if (previous) - { - FT_Vector delta; - - FT_Get_Kerning (face, previous, glyph_index, - FT_KERNING_DEFAULT, &delta); - - xoffset += (delta.x >> 6); - } - - // Extend current X offset box by the width of the current - // glyph. Then extend the line bounding box if necessary. - - xoffset += (face->glyph->advance.x >> 6); - bb(2) = octave::math::max (bb(2), xoffset); - } - break; - } - } - } - - return glyph_index; -} - -void -ft_text_renderer::text_to_strlist (const std::string& txt, - std::list<text_renderer::string>& lst, - Matrix& box, - int ha, int va, double rot, - const caseless_str& interp) -{ - uint8NDArray pxls; - - // First run text_to_pixels which will also build the string list - - text_to_pixels (txt, pxls, box, ha, va, rot, interp, false); - - lst = strlist; -} - -void -ft_text_renderer::visit (text_element_string& e) -{ - if (font.is_valid ()) - { - FT_UInt glyph_index, previous = 0; - - std::string str = e.string_value (); - size_t n = str.length (); - size_t curr = 0; - size_t idx = 0; - mbstate_t ps; - memset (&ps, 0, sizeof (ps)); // Initialize state to 0. - wchar_t wc; - - text_renderer::string fs (str, font, xoffset, yoffset); - - while (n > 0) - { - size_t r = std::mbrtowc (&wc, str.data () + curr, n, &ps); - - if (r > 0 - && r != static_cast<size_t> (-1) - && r != static_cast<size_t> (-2)) - { - n -= r; - curr += r; - - if (wc == L'\n') - { - // Finish previous string in srtlist before processing - // the newline character - fs.set_y (line_yoffset + yoffset); - fs.set_color (color); - std::string s = str.substr (idx, curr - idx - 1); - if (! s.empty ()) - { - fs.set_string (s); - strlist.push_back (fs); - } - } - - glyph_index = process_character (wc, previous); - - if (wc == L'\n') - { - previous = 0; - // Start a new string in strlist - idx = curr; - fs = text_renderer::string (str.substr (idx), font, - line_xoffset, yoffset); - - } - else - previous = glyph_index; - } - else - { - if (r != 0) - ::warning ("ft_text_renderer: failed to decode string `%s' with " - "locale `%s'", str.c_str (), - std::setlocale (LC_CTYPE, 0)); - break; - } - } - - if (! fs.get_string ().empty ()) - { - fs.set_y (line_yoffset + yoffset); - fs.set_color (color); - strlist.push_back (fs); - } - } -} - -void -ft_text_renderer::visit (text_element_list& e) -{ - // Save and restore (after processing the list) the current font and color. - - ft_font saved_font (font); - uint8NDArray saved_color (color); - - text_processor::visit (e); - - font = saved_font; - color = saved_color; -} - -void -ft_text_renderer::visit (text_element_subscript& e) -{ - ft_font saved_font (font); - int saved_line_yoffset = line_yoffset; - int saved_yoffset = yoffset; - - set_font (font.get_name (), font.get_weight (), font.get_angle (), - font.get_size () - 2); - - if (font.is_valid ()) - { - int h = font.get_face ()->size->metrics.height >> 6; - - // Shifting the baseline by 2/3 the font height seems to produce - // decent result. - yoffset -= (h * 2) / 3; - - if (mode == MODE_BBOX) - update_line_bbox (); - } - - text_processor::visit (e); - - font = saved_font; - // If line_yoffset changed, this means we moved to a new line; hence yoffset - // cannot be restored, because the saved value is not relevant anymore. - if (line_yoffset == saved_line_yoffset) - yoffset = saved_yoffset; -} - -void -ft_text_renderer::visit (text_element_superscript& e) -{ - ft_font saved_font (font); - int saved_line_yoffset = line_yoffset; - int saved_yoffset = yoffset; - - set_font (font.get_name (), font.get_weight (), font.get_angle (), - font.get_size () - 2); - - if (saved_font.is_valid ()) - { - int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6; - - // Shifting the baseline by 2/3 base font ascender seems to produce - // decent result. - yoffset += (s_asc * 2) / 3; - - if (mode == MODE_BBOX) - update_line_bbox (); - } - - text_processor::visit (e); - - font = saved_font; - // If line_yoffset changed, this means we moved to a new line; hence yoffset - // cannot be restored, because the saved value is not relevant anymore. - if (line_yoffset == saved_line_yoffset) - yoffset = saved_yoffset; -} - -void -ft_text_renderer::visit (text_element_color& e) -{ - if (mode == MODE_RENDER) - set_color (e.get_color ()); -} - -void -ft_text_renderer::visit (text_element_fontsize& e) -{ - double sz = e.get_fontsize (); - - // FIXME: Matlab documentation says that the font size is expressed - // in the text object FontUnit. - - set_font (font.get_name (), font.get_weight (), font.get_angle (), sz); - - if (mode == MODE_BBOX) - update_line_bbox (); -} - -void -ft_text_renderer::visit (text_element_fontname& e) -{ - set_font (e.get_fontname (), font.get_weight (), font.get_angle (), - font.get_size ()); - - if (mode == MODE_BBOX) - update_line_bbox (); -} - -void -ft_text_renderer::visit (text_element_fontstyle& e) -{ - switch (e.get_fontstyle ()) - { - case text_element_fontstyle::normal: - set_font (font.get_name (), "normal", "normal", font.get_size ()); - break; - - case text_element_fontstyle::bold: - set_font (font.get_name (), "bold", "normal", font.get_size ()); - break; - - case text_element_fontstyle::italic: - set_font (font.get_name (), "normal", "italic", font.get_size ()); - break; - - case text_element_fontstyle::oblique: - set_font (font.get_name (), "normal", "oblique", font.get_size ()); - break; - } - - if (mode == MODE_BBOX) - update_line_bbox (); -} - -void -ft_text_renderer::visit (text_element_symbol& e) -{ - uint32_t code = e.get_symbol_code (); - - text_renderer::string fs (std::string ("-"), font, xoffset, yoffset); - - if (code != text_element_symbol::invalid_code && font.is_valid ()) - { - process_character (code); - fs.set_code (code); - } - else if (font.is_valid ()) - ::warning ("ignoring unknown symbol: %d", e.get_symbol ()); - - if (fs.get_code ()) - { - fs.set_y (line_yoffset + yoffset); - fs.set_color (color); - strlist.push_back (fs); - } -} - -void -ft_text_renderer::visit (text_element_combined& e) -{ - int saved_xoffset = xoffset; - int max_xoffset = xoffset; - - for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it) - { - xoffset = saved_xoffset; - (*it)->accept (*this); - max_xoffset = octave::math::max (xoffset, max_xoffset); - } - - xoffset = max_xoffset; -} - -void -ft_text_renderer::reset (void) -{ - set_mode (MODE_BBOX); - set_color (Matrix (1, 3, 0.0)); -} - -void -ft_text_renderer::set_color (const Matrix& c) -{ - if (c.numel () == 3) - { - color(0) = static_cast<uint8_t> (c(0)*255); - color(1) = static_cast<uint8_t> (c(1)*255); - color(2) = static_cast<uint8_t> (c(2)*255); - } - else - ::warning ("ft_text_renderer::set_color: invalid color"); -} - -uint8NDArray -ft_text_renderer::render (text_element *elt, Matrix& box, int rotation) -{ - set_mode (MODE_BBOX); - elt->accept (*this); - compute_bbox (); - box = bbox; - - set_mode (MODE_RENDER); - // Clear the list of parsed strings - strlist.clear (); - - if (pixels.numel () > 0) - { - elt->accept (*this); - - switch (rotation) - { - case ROTATION_0: - break; - - case ROTATION_90: - { - Array<octave_idx_type> perm (dim_vector (3, 1)); - perm(0) = 0; - perm(1) = 2; - perm(2) = 1; - pixels = pixels.permute (perm); - - Array<idx_vector> idx (dim_vector (3, 1)); - idx(0) = idx_vector (':'); - idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); - idx(2) = idx_vector (':'); - pixels = uint8NDArray (pixels.index (idx)); - } - break; - - case ROTATION_180: - { - Array<idx_vector> idx (dim_vector (3, 1)); - idx(0) = idx_vector (':'); - idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); - idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); - pixels = uint8NDArray (pixels.index (idx)); - } - break; - - case ROTATION_270: - { - Array<octave_idx_type> perm (dim_vector (3, 1)); - perm(0) = 0; - perm(1) = 2; - perm(2) = 1; - pixels = pixels.permute (perm); - - Array<idx_vector> idx (dim_vector (3, 1)); - idx(0) = idx_vector (':'); - idx(1) = idx_vector (':'); - idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); - pixels = uint8NDArray (pixels.index (idx)); - } - break; - } - } - - return pixels; -} - -// Note: -// x-extent accurately measures width of glyphs. -// y-extent is overly large because it is measured from baseline-to-baseline. -// Calling routines, such as ylabel, may need to account for this mismatch. - -Matrix -ft_text_renderer::get_extent (text_element *elt, double rotation) -{ - set_mode (MODE_BBOX); - elt->accept (*this); - compute_bbox (); - - Matrix extent (1, 2, 0.0); - - switch (rotation_to_mode (rotation)) - { - case ROTATION_0: - case ROTATION_180: - extent(0) = bbox(2); - extent(1) = bbox(3); - break; - - case ROTATION_90: - case ROTATION_270: - extent(0) = bbox(3); - extent(1) = bbox(2); - } - - return extent; -} - -Matrix -ft_text_renderer::get_extent (const std::string& txt, double rotation, - const caseless_str& interpreter) -{ - text_element *elt = text_parser::parse (txt, interpreter); - Matrix extent = get_extent (elt, rotation); - delete elt; - - return extent; -} - -int -ft_text_renderer::rotation_to_mode (double rotation) const -{ - // Clip rotation to range [0, 360] - while (rotation < 0) - rotation += 360.0; - while (rotation > 360.0) - rotation -= 360.0; - - if (rotation == 0.0) - return ROTATION_0; - else if (rotation == 90.0) - return ROTATION_90; - else if (rotation == 180.0) - return ROTATION_180; - else if (rotation == 270.0) - return ROTATION_270; - else - return ROTATION_0; -} - -void -ft_text_renderer::text_to_pixels (const std::string& txt, - uint8NDArray& pxls, Matrix& box, - int _halign, int valign, double rotation, - const caseless_str& interpreter, - bool handle_rotation) -{ - int rot_mode = rotation_to_mode (rotation); - - halign = _halign; - - text_element *elt = text_parser::parse (txt, interpreter); - pxls = render (elt, box, rot_mode); - delete elt; - - if (pxls.is_empty ()) - return; // nothing to render - - switch (halign) - { - case 1: - box(0) = -box(2)/2; - break; - - case 2: - box(0) = -box(2); - break; - - default: - box(0) = 0; - break; - } - - switch (valign) - { - case 1: - box(1) = -box(3)/2; - break; - - case 2: - box(1) = -box(3); - break; - - case 3: - break; - - case 4: - box(1) = -box(3)-box(1); - break; - - default: - box(1) = 0; - break; - } - - if (handle_rotation) - { - switch (rot_mode) - { - case ROTATION_90: - std::swap (box(0), box(1)); - std::swap (box(2), box(3)); - box(0) = -box(0)-box(2); - break; - - case ROTATION_180: - box(0) = -box(0)-box(2); - box(1) = -box(1)-box(3); - break; - - case ROTATION_270: - std::swap (box(0), box(1)); - std::swap (box(2), box(3)); - box(1) = -box(1)-box(3); - break; - } - } -} - -ft_text_renderer::ft_font::ft_font (const ft_font& ft) - : text_renderer::font (ft), face (0) -{ -#if defined (HAVE_FT_REFERENCE_FACE) - FT_Face ft_face = ft.get_face (); - - if (ft_face && FT_Reference_Face (ft_face) == 0) - face = ft_face; -#endif -} - -ft_text_renderer::ft_font& -ft_text_renderer::ft_font::operator = (const ft_font& ft) -{ - if (&ft != this) - { - text_renderer::font::operator = (ft); - - if (face) - { - FT_Done_Face (face); - face = 0; - } #if defined (HAVE_FT_REFERENCE_FACE) - FT_Face ft_face = ft.get_face (); + FT_Face ft_face = ft.get_face (); - if (ft_face && FT_Reference_Face (ft_face) == 0) - face = ft_face; + if (ft_face && FT_Reference_Face (ft_face) == 0) + face = ft_face; #endif - } + } - return *this; -} + return *this; + } -FT_Face -ft_text_renderer::ft_font::get_face (void) const -{ - if (! face && ! name.empty ()) - { - face = ft_manager::get_font (name, weight, angle, size); + FT_Face + ft_text_renderer::ft_font::get_face (void) const + { + if (! face && ! name.empty ()) + { + face = ft_manager::get_font (name, weight, angle, size); - if (face) - { - if (FT_Set_Char_Size (face, 0, size*64, 0, 0)) - ::warning ("ft_text_renderer: unable to set font size to %g", size); - } - else - ::warning ("ft_text_renderer: unable to load appropriate font"); - } + if (face) + { + if (FT_Set_Char_Size (face, 0, size*64, 0, 0)) + ::warning ("ft_text_renderer: unable to set font size to %g", size); + } + else + ::warning ("ft_text_renderer: unable to load appropriate font"); + } - return face; -} - + return face; + } } #endif
--- a/libinterp/corefcn/gl-render.cc Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/gl-render.cc Wed Aug 17 11:43:27 2016 -0400 @@ -55,3965 +55,3963 @@ namespace octave { - #define LIGHT_MODE GL_FRONT_AND_BACK -// Use symbolic names for axes -enum -{ - X_AXIS, - Y_AXIS, - Z_AXIS -}; - -// Use symbolic names for color mode -enum -{ - UNIFORM, - FLAT, - INTERP, - TEXTURE -}; - -// Use symbolic names for lighting -enum -{ - NONE, - //FLAT, // Already declared in anonymous enum for color mode - GOURAUD = 2 -}; - -// Win32 API requires the CALLBACK attributes for -// GLU callback functions. Define it to empty on -// other platforms. + // Use symbolic names for axes + enum + { + X_AXIS, + Y_AXIS, + Z_AXIS + }; + + // Use symbolic names for color mode + enum + { + UNIFORM, + FLAT, + INTERP, + TEXTURE + }; + + // Use symbolic names for lighting + enum + { + NONE, + //FLAT, // Already declared in anonymous enum for color mode + GOURAUD = 2 + }; + + // Win32 API requires the CALLBACK attributes for + // GLU callback functions. Define it to empty on + // other platforms. #if ! defined (CALLBACK) # define CALLBACK #endif -class -opengl_texture -{ -protected: - class texture_rep + class + opengl_texture { + protected: + class texture_rep + { + public: + texture_rep (void) + : id (), w (), h (), tw (), th (), tx (), ty (), + valid (false), count (1) + { } + + texture_rep (GLuint id_arg, int w_arg, int h_arg, int tw_arg, int th_arg) + : id (id_arg), w (w_arg), h (h_arg), tw (tw_arg), th (th_arg), + tx (double(w)/tw), ty (double(h)/th), valid (true), + count (1) { } + + ~texture_rep (void) + { + if (valid) + glDeleteTextures (1, &id); + } + + void bind (int mode) const + { if (valid) glBindTexture (mode, id); } + + void tex_coord (double q, double r) const + { if (valid) glTexCoord2d (q*tx, r*ty); } + + GLuint id; + int w, h; + int tw, th; + double tx, ty; + bool valid; + octave_refcount<int> count; + }; + + texture_rep *rep; + + private: + opengl_texture (texture_rep *_rep) : rep (_rep) { } + public: - texture_rep (void) - : id (), w (), h (), tw (), th (), tx (), ty (), - valid (false), count (1) - { } - - texture_rep (GLuint id_arg, int w_arg, int h_arg, int tw_arg, int th_arg) - : id (id_arg), w (w_arg), h (h_arg), tw (tw_arg), th (th_arg), - tx (double(w)/tw), ty (double(h)/th), valid (true), - count (1) { } - - ~texture_rep (void) + opengl_texture (void) : rep (new texture_rep ()) { } + + opengl_texture (const opengl_texture& tx) + : rep (tx.rep) + { + rep->count++; + } + + ~opengl_texture (void) { - if (valid) - glDeleteTextures (1, &id); + if (--rep->count == 0) + delete rep; } - void bind (int mode) const - { if (valid) glBindTexture (mode, id); } + opengl_texture& operator = (const opengl_texture& tx) + { + if (--rep->count == 0) + delete rep; + + rep = tx.rep; + rep->count++; + + return *this; + } + + static opengl_texture create (const octave_value& data); + + void bind (int mode = GL_TEXTURE_2D) const + { rep->bind (mode); } void tex_coord (double q, double r) const - { if (valid) glTexCoord2d (q*tx, r*ty); } - - GLuint id; - int w, h; - int tw, th; - double tx, ty; - bool valid; - octave_refcount<int> count; + { rep->tex_coord (q, r); } + + bool is_valid (void) const + { return rep->valid; } }; - texture_rep *rep; - -private: - opengl_texture (texture_rep *_rep) : rep (_rep) { } - -public: - opengl_texture (void) : rep (new texture_rep ()) { } - - opengl_texture (const opengl_texture& tx) - : rep (tx.rep) + opengl_texture + opengl_texture::create (const octave_value& data) { - rep->count++; - } - - ~opengl_texture (void) - { - if (--rep->count == 0) - delete rep; - } - - opengl_texture& operator = (const opengl_texture& tx) - { - if (--rep->count == 0) - delete rep; - - rep = tx.rep; - rep->count++; - - return *this; + opengl_texture retval; + + dim_vector dv (data.dims ()); + + // Expect RGB data + if (dv.ndims () == 3 && dv(2) == 3) + { + // 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); + GLuint id; + bool ok = true; + + tw = next_power_of_2 (w); + th = next_power_of_2 (h); + + glGenTextures (1, &id); + glBindTexture (GL_TEXTURE_2D, id); + + if (data.is_double_type ()) + { + const NDArray xdata = data.array_value (); + + OCTAVE_LOCAL_BUFFER (float, 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); + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a); + } + else if (data.is_uint8_type ()) + { + const uint8NDArray xdata = data.uint8_array_value (); + + OCTAVE_LOCAL_BUFFER (octave_uint8, 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); + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, + GL_RGB, GL_UNSIGNED_BYTE, a); + } + else + { + ok = false; + warning ("opengl_texture::create: invalid texture data type (double or uint8 required)"); + } + + if (ok) + { + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + if (glGetError () != GL_NO_ERROR) + warning ("opengl_texture::create: OpenGL error while generating texture data"); + else + retval = opengl_texture (new texture_rep (id, w, h, tw, th)); + } + } + else + warning ("opengl_texture::create: invalid texture data size"); + + return retval; } - static opengl_texture create (const octave_value& data); - - void bind (int mode = GL_TEXTURE_2D) const - { rep->bind (mode); } - - void tex_coord (double q, double r) const - { rep->tex_coord (q, r); } - - bool is_valid (void) const - { return rep->valid; } -}; - -opengl_texture -opengl_texture::create (const octave_value& data) -{ - opengl_texture retval; - - dim_vector dv (data.dims ()); - - // Expect RGB data - if (dv.ndims () == 3 && dv(2) == 3) + class + opengl_tesselator + { + public: +#if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS) + typedef GLvoid (CALLBACK *fcn) (...); +#else + typedef void (CALLBACK *fcn) (void); +#endif + + public: + + opengl_tesselator (void) : glu_tess (0), fill () { init (); } + + virtual ~opengl_tesselator (void) + { if (glu_tess) gluDeleteTess (glu_tess); } + + void begin_polygon (bool filled = true) + { + gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY, + (filled ? GL_FALSE : GL_TRUE)); + fill = filled; + gluTessBeginPolygon (glu_tess, this); + } + + void end_polygon (void) const + { gluTessEndPolygon (glu_tess); } + + void begin_contour (void) const + { gluTessBeginContour (glu_tess); } + + void end_contour (void) const + { gluTessEndContour (glu_tess); } + + void add_vertex (double *loc, void *data) const + { gluTessVertex (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 tesselation error (%d)", err); } + + virtual void init (void) + { + glu_tess = gluNewTess (); + + gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA, + reinterpret_cast<fcn> (tess_begin)); + gluTessCallback (glu_tess, GLU_TESS_END_DATA, + reinterpret_cast<fcn> (tess_end)); + gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA, + reinterpret_cast<fcn> (tess_vertex)); + gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA, + reinterpret_cast<fcn> (tess_combine)); + gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA, + reinterpret_cast<fcn> (tess_edge_flag)); + gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA, + reinterpret_cast<fcn> (tess_error)); + } + + bool is_filled (void) const { return fill; } + + private: + static void CALLBACK tess_begin (GLenum type, void *t) + { reinterpret_cast<opengl_tesselator *> (t)->begin (type); } + + static void CALLBACK tess_end (void *t) + { reinterpret_cast<opengl_tesselator *> (t)->end (); } + + static void CALLBACK tess_vertex (void *v, void *t) + { reinterpret_cast<opengl_tesselator *> (t)->vertex (v); } + + static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4], + void **out, void *t) + { reinterpret_cast<opengl_tesselator *> (t)->combine (c, v, w, out); } + + static void CALLBACK tess_edge_flag (GLboolean flag, void *t) + { reinterpret_cast<opengl_tesselator *> (t)->edge_flag (flag); } + + static void CALLBACK tess_error (GLenum err, void *t) + { reinterpret_cast<opengl_tesselator *> (t)->error (err); } + + private: + + // No copying! + + opengl_tesselator (const opengl_tesselator&); + + opengl_tesselator operator = (const opengl_tesselator&); + + GLUtesselator *glu_tess; + bool fill; + }; + + class + vertex_data + { + public: + class vertex_data_rep { - // 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); - GLuint id; - bool ok = true; - - tw = next_power_of_2 (w); - th = next_power_of_2 (h); - - glGenTextures (1, &id); - glBindTexture (GL_TEXTURE_2D, id); - - if (data.is_double_type ()) + public: + Matrix coords; + Matrix color; + Matrix normal; + double alpha; + float ambient; + float diffuse; + float specular; + float specular_exp; + float specular_color_refl; + + // reference counter + octave_refcount<int> count; + + vertex_data_rep (void) + : coords (), color (), normal (), alpha (), + ambient (), diffuse (), specular (), specular_exp (), + specular_color_refl (), count (1) { } + + vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& n, + double a, float as, float ds, float ss, float se, + float scr) + : coords (c), color (col), normal (n), alpha (a), + ambient (as), diffuse (ds), specular (ss), specular_exp (se), + specular_color_refl (scr), count (1) { } + }; + + private: + vertex_data_rep *rep; + + vertex_data_rep *nil_rep (void) const + { + static vertex_data_rep *nr = new vertex_data_rep (); + + return nr; + } + + public: + vertex_data (void) : rep (nil_rep ()) + { rep->count++; } + + vertex_data (const vertex_data& v) : rep (v.rep) + { rep->count++; } + + vertex_data (const Matrix& c, const Matrix& col, const Matrix& n, + double a, float as, float ds, float ss, float se, + float scr) + : rep (new vertex_data_rep (c, col, n, a, as, ds, ss, se, scr)) + { } + + vertex_data (vertex_data_rep *new_rep) + : rep (new_rep) { } + + ~vertex_data (void) + { + if (--rep->count == 0) + delete rep; + } + + vertex_data& operator = (const vertex_data& v) + { + if (--rep->count == 0) + delete rep; + + rep = v.rep; + rep->count++; + + return *this; + } + + vertex_data_rep *get_rep (void) const { return rep; } + }; + + class + opengl_renderer::patch_tesselator : public opengl_tesselator + { + public: + patch_tesselator (opengl_renderer *r, int cmode, int lmode, float idx = 0.0) + : opengl_tesselator (), renderer (r), + color_mode (cmode), light_mode (lmode), index (idx), + first (true), tmp_vdata () + { } + + protected: + void begin (GLenum type) + { + //printf ("patch_tesselator::begin (%d)\n", type); + first = true; + + if (color_mode == INTERP || light_mode == GOURAUD) + glShadeModel (GL_SMOOTH); + else + glShadeModel (GL_FLAT); + + if (is_filled ()) + renderer->set_polygon_offset (true, index); + + glBegin (type); + } + + void end (void) + { + //printf ("patch_tesselator::end\n"); + glEnd (); + renderer->set_polygon_offset (false); + } + + void vertex (void *data) + { + vertex_data::vertex_data_rep *v + = reinterpret_cast<vertex_data::vertex_data_rep *> (data); + //printf ("patch_tesselator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->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 (color_mode == INTERP || (color_mode == FLAT && ! is_filled ())) { - const NDArray xdata = data.array_value (); - - OCTAVE_LOCAL_BUFFER (float, a, (3*tw*th)); - - for (int i = 0; i < h; i++) + Matrix col = v->color; + + if (col.numel () == 3) { - for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) + glColor3dv (col.data ()); + if (light_mode > 0) { - a[idx] = xdata(i,j,0); - a[idx+1] = xdata(i,j,1); - a[idx+2] = xdata(i,j,2); - } - } - - glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a); - } - else if (data.is_uint8_type ()) - { - const uint8NDArray xdata = data.uint8_array_value (); - - OCTAVE_LOCAL_BUFFER (octave_uint8, 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); + float buf[4] = { 0, 0, 0, 1 }; + + for (int k = 0; k < 3; k++) + buf[k] = (v->ambient * col(k)); + glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf); + + for (int k = 0; k < 3; k++) + buf[k] = (v->diffuse * col(k)); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf); + + for (int k = 0; k < 3; k++) + buf[k] = v->specular * (v->specular_color_refl + + (1 - v->specular_color_refl) * col(k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf); + } } - - glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, - GL_RGB, GL_UNSIGNED_BYTE, a); } - else - { - ok = false; - warning ("opengl_texture::create: invalid texture data type (double or uint8 required)"); - } - - if (ok) - { - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - if (glGetError () != GL_NO_ERROR) - warning ("opengl_texture::create: OpenGL error while generating texture data"); - else - retval = opengl_texture (new texture_rep (id, w, h, tw, th)); - } + + if (light_mode > 0 && (first || light_mode == GOURAUD)) + glNormal3dv (v->normal.data ()); + + glVertex3dv (v->coords.data ()); + + first = false; } - else - warning ("opengl_texture::create: invalid texture data size"); - - return retval; -} - -class -opengl_tesselator -{ -public: -#if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS) - typedef GLvoid (CALLBACK *fcn) (...); -#else - typedef void (CALLBACK *fcn) (void); -#endif - -public: - - opengl_tesselator (void) : glu_tess (0), fill () { init (); } - - virtual ~opengl_tesselator (void) - { if (glu_tess) gluDeleteTess (glu_tess); } - - void begin_polygon (bool filled = true) - { - gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY, - (filled ? GL_FALSE : GL_TRUE)); - fill = filled; - gluTessBeginPolygon (glu_tess, this); - } - - void end_polygon (void) const - { gluTessEndPolygon (glu_tess); } - - void begin_contour (void) const - { gluTessBeginContour (glu_tess); } - - void end_contour (void) const - { gluTessEndContour (glu_tess); } - - void add_vertex (double *loc, void *data) const - { gluTessVertex (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 tesselation error (%d)", err); } - - virtual void init (void) - { - glu_tess = gluNewTess (); - - gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA, - reinterpret_cast<fcn> (tess_begin)); - gluTessCallback (glu_tess, GLU_TESS_END_DATA, - reinterpret_cast<fcn> (tess_end)); - gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA, - reinterpret_cast<fcn> (tess_vertex)); - gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA, - reinterpret_cast<fcn> (tess_combine)); - gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA, - reinterpret_cast<fcn> (tess_edge_flag)); - gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA, - reinterpret_cast<fcn> (tess_error)); - } - - bool is_filled (void) const { return fill; } - -private: - static void CALLBACK tess_begin (GLenum type, void *t) - { reinterpret_cast<opengl_tesselator *> (t)->begin (type); } - - static void CALLBACK tess_end (void *t) - { reinterpret_cast<opengl_tesselator *> (t)->end (); } - - static void CALLBACK tess_vertex (void *v, void *t) - { reinterpret_cast<opengl_tesselator *> (t)->vertex (v); } - - static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4], - void **out, void *t) - { reinterpret_cast<opengl_tesselator *> (t)->combine (c, v, w, out); } - - static void CALLBACK tess_edge_flag (GLboolean flag, void *t) - { reinterpret_cast<opengl_tesselator *> (t)->edge_flag (flag); } - - static void CALLBACK tess_error (GLenum err, void *t) - { reinterpret_cast<opengl_tesselator *> (t)->error (err); } - -private: - - // No copying! - - opengl_tesselator (const opengl_tesselator&); - - opengl_tesselator operator = (const opengl_tesselator&); - - GLUtesselator *glu_tess; - bool fill; -}; - -class -vertex_data -{ -public: - class vertex_data_rep - { - public: - Matrix coords; - Matrix color; - Matrix normal; - double alpha; - float ambient; - float diffuse; - float specular; - float specular_exp; - float specular_color_refl; - - // reference counter - octave_refcount<int> count; - - vertex_data_rep (void) - : coords (), color (), normal (), alpha (), - ambient (), diffuse (), specular (), specular_exp (), - specular_color_refl (), count (1) { } - - vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& n, - double a, float as, float ds, float ss, float se, - float scr) - : coords (c), color (col), normal (n), alpha (a), - ambient (as), diffuse (ds), specular (ss), specular_exp (se), - specular_color_refl (scr), count (1) { } + + void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data) + { + //printf ("patch_tesselator::combine\n"); + + 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 nn (1, 3, 0.0); + double aa = 0.0; + + vv(0) = xyz[0]; + vv(1) = xyz[1]; + vv(2) = xyz[2]; + + if (v[0]->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]->color (ic)); + } + + if (v[0]->normal.numel () > 0) + { + for (int in = 0; in < 3; in++) + for (int iv = 0; iv < vmax; iv++) + nn(in) += (w[iv] * v[iv]->normal (in)); + } + + for (int iv = 0; iv < vmax; iv++) + aa += (w[iv] * v[iv]->alpha); + + vertex_data new_v (vv, cc, nn, aa, v[0]->ambient, v[0]->diffuse, + v[0]->specular, v[0]->specular_exp, v[0]->specular_color_refl); + tmp_vdata.push_back (new_v); + + *out_data = new_v.get_rep (); + } + + private: + + // No copying! + + patch_tesselator (const patch_tesselator&); + + patch_tesselator& operator = (const patch_tesselator&); + + opengl_renderer *renderer; + int color_mode; + int light_mode; + int index; + bool first; + std::list<vertex_data> tmp_vdata; }; -private: - vertex_data_rep *rep; - - vertex_data_rep *nil_rep (void) const - { - static vertex_data_rep *nr = new vertex_data_rep (); - - return nr; - } - -public: - vertex_data (void) : rep (nil_rep ()) - { rep->count++; } - - vertex_data (const vertex_data& v) : rep (v.rep) - { rep->count++; } - - vertex_data (const Matrix& c, const Matrix& col, const Matrix& n, - double a, float as, float ds, float ss, float se, - float scr) - : rep (new vertex_data_rep (c, col, n, a, as, ds, ss, se, scr)) - { } - - vertex_data (vertex_data_rep *new_rep) - : rep (new_rep) { } - - ~vertex_data (void) - { - if (--rep->count == 0) - delete rep; - } - - vertex_data& operator = (const vertex_data& v) - { - if (--rep->count == 0) - delete rep; - - rep = v.rep; - rep->count++; - - return *this; - } - - vertex_data_rep *get_rep (void) const { return rep; } -}; - -class -opengl_renderer::patch_tesselator : public opengl_tesselator -{ -public: - patch_tesselator (opengl_renderer *r, int cmode, int lmode, float idx = 0.0) - : opengl_tesselator (), renderer (r), - color_mode (cmode), light_mode (lmode), index (idx), - first (true), tmp_vdata () - { } - -protected: - void begin (GLenum type) - { - //printf ("patch_tesselator::begin (%d)\n", type); - first = true; - - if (color_mode == INTERP || light_mode == GOURAUD) - glShadeModel (GL_SMOOTH); - else - glShadeModel (GL_FLAT); - - if (is_filled ()) - renderer->set_polygon_offset (true, index); - - glBegin (type); - } - - void end (void) - { - //printf ("patch_tesselator::end\n"); - glEnd (); - renderer->set_polygon_offset (false); - } - - void vertex (void *data) - { - vertex_data::vertex_data_rep *v - = reinterpret_cast<vertex_data::vertex_data_rep *> (data); - //printf ("patch_tesselator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->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 (color_mode == INTERP || (color_mode == FLAT && ! is_filled ())) - { - Matrix col = v->color; - - if (col.numel () == 3) - { - glColor3dv (col.data ()); - if (light_mode > 0) - { - float buf[4] = { 0, 0, 0, 1 }; - - for (int k = 0; k < 3; k++) - buf[k] = (v->ambient * col(k)); - glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf); - - for (int k = 0; k < 3; k++) - buf[k] = (v->diffuse * col(k)); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf); - - for (int k = 0; k < 3; k++) - buf[k] = v->specular * (v->specular_color_refl + - (1 - v->specular_color_refl) * col(k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf); - - } - } - } - - if (light_mode > 0 && (first || light_mode == GOURAUD)) - glNormal3dv (v->normal.data ()); - - glVertex3dv (v->coords.data ()); - - first = false; - } - - void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data) - { - //printf ("patch_tesselator::combine\n"); - - 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 nn (1, 3, 0.0); - double aa = 0.0; - - vv(0) = xyz[0]; - vv(1) = xyz[1]; - vv(2) = xyz[2]; - - if (v[0]->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]->color (ic)); - } - - if (v[0]->normal.numel () > 0) - { - for (int in = 0; in < 3; in++) - for (int iv = 0; iv < vmax; iv++) - nn(in) += (w[iv] * v[iv]->normal (in)); - } - - for (int iv = 0; iv < vmax; iv++) - aa += (w[iv] * v[iv]->alpha); - - vertex_data new_v (vv, cc, nn, aa, v[0]->ambient, v[0]->diffuse, - v[0]->specular, v[0]->specular_exp, v[0]->specular_color_refl); - tmp_vdata.push_back (new_v); - - *out_data = new_v.get_rep (); - } - -private: - - // No copying! - - patch_tesselator (const patch_tesselator&); - - patch_tesselator& operator = (const patch_tesselator&); - - opengl_renderer *renderer; - int color_mode; - int light_mode; - int index; - bool first; - std::list<vertex_data> tmp_vdata; -}; - #else -class -opengl_renderer::patch_tesselator -{ - // Dummy class. -}; + class + opengl_renderer::patch_tesselator + { + // Dummy class. + }; #endif #if defined (HAVE_OPENGL) -static int -get_maxlights (void) -{ - - static int max_lights = 0; - - // Check actual maximum number of lights possible - if (max_lights == 0) - { - for (max_lights = 0; max_lights < GL_MAX_LIGHTS; max_lights++) - { - glDisable (GL_LIGHT0 + max_lights); - if (glGetError ()) - break; - } - } - - return max_lights; - -} + static int + get_maxlights (void) + { + + static int max_lights = 0; + + // Check actual maximum number of lights possible + if (max_lights == 0) + { + for (max_lights = 0; max_lights < GL_MAX_LIGHTS; max_lights++) + { + glDisable (GL_LIGHT0 + max_lights); + if (glGetError ()) + break; + } + } + + return max_lights; + + } #endif -opengl_renderer::opengl_renderer (void) - : toolkit (), xform (), xmin (), xmax (), ymin (), ymax (), - zmin (), zmax (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (), - camera_pos (), camera_dir (), interpreter ("none"), txt_renderer () -{ - // 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. + opengl_renderer::opengl_renderer (void) + : toolkit (), xform (), xmin (), xmax (), ymin (), ymax (), + zmin (), zmax (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (), + camera_pos (), camera_dir (), interpreter ("none"), txt_renderer () + { + // 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"); - - // Check actual maximum number of lights possible - max_lights = get_maxlights (); + // 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"); + + // Check actual maximum number of lights possible + max_lights = get_maxlights (); #else - err_disabled_feature ("opengl_renderer", "OpenGL"); + 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 (! toolkit) - 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 ("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")) - ; // 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 ()); - } -} + } + + void + opengl_renderer::draw (const graphics_object& go, bool toplevel) + { + if (! go.valid_object ()) + return; + + const base_properties& props = go.get_properties (); + + if (! toolkit) + 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 ("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")) + ; // 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) + + static std::string + gl_get_string (GLenum id) + { + // 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 << glGetString (id); + return std::string (buf.str ()); + } + +#endif + + void + opengl_renderer::draw_figure (const figure::properties& props) + { + // Initialize OpenGL context + + init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); #if defined (HAVE_OPENGL) -static std::string -gl_get_string (GLenum id) -{ - // 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 << glGetString (id); - return std::string (buf.str ()); -} + props.set___gl_extensions__ (gl_get_string (GL_EXTENSIONS)); + props.set___gl_renderer__ (gl_get_string (GL_RENDERER)); + props.set___gl_vendor__ (gl_get_string (GL_VENDOR)); + props.set___gl_version__ (gl_get_string (GL_VERSION)); #endif -void -opengl_renderer::draw_figure (const figure::properties& props) -{ - // Initialize OpenGL context - - init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); - + // 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) - props.set___gl_extensions__ (gl_get_string (GL_EXTENSIONS)); - props.set___gl_renderer__ (gl_get_string (GL_RENDERER)); - props.set___gl_vendor__ (gl_get_string (GL_VENDOR)); - props.set___gl_version__ (gl_get_string (GL_VERSION)); + // Initialize OpenGL context + + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LEQUAL); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc (GL_GREATER, 0.0f); + glEnable (GL_NORMALIZE); + + if (enhanced) + { + glEnable (GL_BLEND); + glEnable (GL_MULTISAMPLE); + GLint iMultiSample, iNumSamples; + glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample); + glGetIntegerv (GL_SAMPLES, &iNumSamples); + if (iMultiSample != GL_TRUE || iNumSamples == 0) + { + // MultiSample not implemented. Use old-style anti-aliasing + glDisable (GL_MULTISAMPLE); + glEnable (GL_LINE_SMOOTH); + glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); + } + } + else + { + glDisable (GL_BLEND); + glDisable (GL_LINE_SMOOTH); + } + + // Clear background + + if (c.numel () >= 3) + { + glClearColor (c(0), c(1), c(2), 1); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + +#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 - - // 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) -{ + } + + void + opengl_renderer::render_grid (const std::string& gridstyle, + const Matrix& ticks, double lim1, double lim2, + double p1, double p1N, double p2, double p2N, + int xyz, bool is_3D) + { #if defined (HAVE_OPENGL) - // Initialize OpenGL context - - glEnable (GL_DEPTH_TEST); - glDepthFunc (GL_LEQUAL); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glAlphaFunc (GL_GREATER, 0.0f); - glEnable (GL_NORMALIZE); - - if (enhanced) - { - glEnable (GL_BLEND); - glEnable (GL_MULTISAMPLE); - GLint iMultiSample, iNumSamples; - glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample); - glGetIntegerv (GL_SAMPLES, &iNumSamples); - if (iMultiSample != GL_TRUE || iNumSamples == 0) - { - // MultiSample not implemented. Use old-style anti-aliasing - glDisable (GL_MULTISAMPLE); - glEnable (GL_LINE_SMOOTH); - glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); - } - } - else - { - glDisable (GL_BLEND); - glDisable (GL_LINE_SMOOTH); - } - - // Clear background - - if (c.numel () >= 3) - { - glClearColor (c(0), c(1), c(2), 1); - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } + set_linestyle (gridstyle, true); + glBegin (GL_LINES); + for (int i = 0; i < ticks.numel (); i++) + { + double val = ticks(i); + if (lim1 <= val && val <= lim2) + { + if (xyz == X_AXIS) + { + glVertex3d (val, p1N, p2); + glVertex3d (val, p1, p2); + if (is_3D) + { + glVertex3d (val, p1, p2N); + glVertex3d (val, p1, p2); + } + } + else if (xyz == Y_AXIS) + { + glVertex3d (p1N, val, p2); + glVertex3d (p1, val, p2); + if (is_3D) + { + glVertex3d (p1, val, p2N); + glVertex3d (p1, val, p2); + } + } + else if (xyz == Z_AXIS) + { + glVertex3d (p1N, p2, val); + glVertex3d (p1, p2, val); + glVertex3d (p1, p2N, val); + glVertex3d (p1, p2, val); + } + } + } + glEnd (); + set_linestyle ("-", true); + +#else + + octave_unused_parameter (gridstyle); + 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) + + glBegin (GL_LINES); + + for (int i = 0; i < ticks.numel (); i++) + { + double val = ticks(i); + + if (lim1 <= val && val <= lim2) + { + if (xyz == X_AXIS) + { + glVertex3d (val, p1, p2); + glVertex3d (val, p1+dy, p2+dz); + if (mirror) + { + glVertex3d (val, p1N, p2N); + glVertex3d (val, p1N-dy, p2N-dz); + } + } + else if (xyz == Y_AXIS) + { + glVertex3d (p1, val, p2); + glVertex3d (p1+dx, val, p2+dz); + if (mirror) + { + glVertex3d (p1N, val, p2N); + glVertex3d (p1N-dx, val, p2N-dz); + } + } + else if (xyz == Z_AXIS) + { + glVertex3d (p1, p2, val); + glVertex3d (p1+dx, p2+dy, val); + if (mirror) + { + glVertex3d (p1N, p2N, val); + glVertex3d (p1N-dx, p2N-dy, val); + } + } + } + } + + glEnd (); #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 (); + 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::finish (void) + { +#if defined (HAVE_OPENGL) + + 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::render_grid (const std::string& gridstyle, - const Matrix& ticks, double lim1, double lim2, - double p1, double p1N, double p2, double p2N, - int xyz, bool is_3D) -{ + } + + void + opengl_renderer::setup_opengl_transformation (const axes::properties& props) + { #if defined (HAVE_OPENGL) - set_linestyle (gridstyle, true); - glBegin (GL_LINES); - for (int i = 0; i < ticks.numel (); i++) - { - double val = ticks(i); - if (lim1 <= val && val <= lim2) - { - if (xyz == X_AXIS) - { - glVertex3d (val, p1N, p2); - glVertex3d (val, p1, p2); - if (is_3D) - { - glVertex3d (val, p1, p2N); - glVertex3d (val, p1, p2); - } - } - else if (xyz == Y_AXIS) - { - glVertex3d (p1N, val, p2); - glVertex3d (p1, val, p2); - if (is_3D) - { - glVertex3d (p1, val, p2N); - glVertex3d (p1, val, p2); - } - } - else if (xyz == Z_AXIS) - { - glVertex3d (p1N, p2, val); - glVertex3d (p1, p2, val); - glVertex3d (p1, p2N, val); - glVertex3d (p1, p2, val); - } - } - } - glEnd (); - set_linestyle ("-", true); + // setup OpenGL transformation + + Matrix x_zlim = props.get_transform_zlim (); + + xZ1 = x_zlim(0)-(x_zlim(1)-x_zlim(0))/2; + xZ2 = x_zlim(1)+(x_zlim(1)-x_zlim(0))/2; + + Matrix x_mat1 = props.get_opengl_matrix_1 (); + Matrix x_mat2 = props.get_opengl_matrix_2 (); + +#if defined (HAVE_FRAMEWORK_OPENGL) + GLint vw[4]; +#else + int vw[4]; +#endif + + glGetIntegerv (GL_VIEWPORT, vw); + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glScaled (1, 1, -1); + glMultMatrixd (x_mat1.data ()); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2); + glMultMatrixd (x_mat2.data ()); + glMatrixMode (GL_MODELVIEW); + + glClear (GL_DEPTH_BUFFER_BIT); + + // store axes transformation data + + xform = props.get_transform (); #else - octave_unused_parameter (gridstyle); - 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 (); + 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.is_empty () || ! 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, 2.5); + + glBegin (GL_QUADS); + + if (! is2d) + { + // X plane + glVertex3d (xPlane, yPlaneN, zPlaneN); + glVertex3d (xPlane, yPlane, zPlaneN); + glVertex3d (xPlane, yPlane, zPlane); + glVertex3d (xPlane, yPlaneN, zPlane); + + // Y plane + glVertex3d (xPlaneN, yPlane, zPlaneN); + glVertex3d (xPlane, yPlane, zPlaneN); + glVertex3d (xPlane, yPlane, zPlane); + glVertex3d (xPlaneN, yPlane, zPlane); + } + + // Z plane + glVertex3d (xPlaneN, yPlaneN, zPlane); + glVertex3d (xPlane, yPlaneN, zPlane); + glVertex3d (xPlane, yPlane, zPlane); + glVertex3d (xPlaneN, yPlane, zPlane); + + 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::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) -{ + } + + void + opengl_renderer::draw_axes_boxes (const axes::properties& props) + { #if defined (HAVE_OPENGL) - glBegin (GL_LINES); - - for (int i = 0; i < ticks.numel (); i++) - { - double val = ticks(i); - - if (lim1 <= val && val <= lim2) - { - if (xyz == X_AXIS) - { - glVertex3d (val, p1, p2); - glVertex3d (val, p1+dy, p2+dz); - if (mirror) - { - glVertex3d (val, p1N, p2N); - glVertex3d (val, p1N-dy, p2N-dz); - } - } - else if (xyz == Y_AXIS) - { - glVertex3d (p1, val, p2); - glVertex3d (p1+dx, val, p2+dz); - if (mirror) - { - glVertex3d (p1N, val, p2N); - glVertex3d (p1N-dx, val, p2N-dz); - } - } - else if (xyz == Z_AXIS) - { - glVertex3d (p1, p2, val); - glVertex3d (p1+dx, p2+dy, val); - if (mirror) - { - glVertex3d (p1N, p2N, val); - glVertex3d (p1N-dx, p2N-dy, val); - } - } - } - } - - glEnd (); + if (! props.is_visible ()) + return; + + bool xySym = props.get_xySym (); + bool layer2Dtop = props.get_layer2Dtop (); + bool is2d = props.get_is2D (); + 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_linestyle ("-", true); + set_linewidth (props.get_linewidth ()); + + glBegin (GL_LINES); + + if (layer2Dtop) + std::swap (zpTick, zpTickN); + + // X box + set_color (props.get_xcolor_rgb ()); + glVertex3d (xPlaneN, ypTick, zpTick); + glVertex3d (xPlane, ypTick, zpTick); + + if (props.is_box ()) + { + glVertex3d (xPlaneN, ypTickN, zpTick); + glVertex3d (xPlane, ypTickN, zpTick); + if (! is2d) + { + glVertex3d (xPlaneN, ypTickN, zpTickN); + glVertex3d (xPlane, ypTickN, zpTickN); + glVertex3d (xPlaneN, ypTick, zpTickN); + glVertex3d (xPlane, ypTick, zpTickN); + } + } + + // Y box + set_color (props.get_ycolor_rgb ()); + glVertex3d (xpTick, yPlaneN, zpTick); + glVertex3d (xpTick, yPlane, zpTick); + + if (props.is_box () && ! plotyy) + { + glVertex3d (xpTickN, yPlaneN, zpTick); + glVertex3d (xpTickN, yPlane, zpTick); + + if (! is2d) + { + glVertex3d (xpTickN, yPlaneN, zpTickN); + glVertex3d (xpTickN, yPlane, zpTickN); + glVertex3d (xpTick, yPlaneN, zpTickN); + glVertex3d (xpTick, yPlane, zpTickN); + } + } + + // Z box + if (! is2d) + { + set_color (props.get_zcolor_rgb ()); + + if (xySym) + { + glVertex3d (xPlaneN, yPlane, zPlaneN); + glVertex3d (xPlaneN, yPlane, zPlane); + } + else + { + glVertex3d (xPlane, yPlaneN, zPlaneN); + glVertex3d (xPlane, yPlaneN, zPlane); + } + + if (props.is_box ()) + { + glVertex3d (xPlane, yPlane, zPlaneN); + glVertex3d (xPlane, yPlane, zPlane); + + if (xySym) + { + glVertex3d (xPlane, yPlaneN, zPlaneN); + glVertex3d (xPlane, yPlaneN, zPlane); + } + else + { + glVertex3d (xPlaneN, yPlane, zPlaneN); + glVertex3d (xPlaneN, yPlane, zPlane); + } + + glVertex3d (xPlaneN, yPlaneN, zPlaneN); + glVertex3d (xPlaneN, yPlaneN, zPlane); + } + } + + 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::finish (void) -{ -#if defined (HAVE_OPENGL) - - glFinish (); - -#else - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::setup_opengl_transformation (const axes::properties& props) -{ + } + + void + opengl_renderer::draw_axes_x_grid (const axes::properties& props) + { + int xstate = props.get_xstate (); + + if (props.is_visible () && xstate != AXE_DEPTH_DIR) + { + 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 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 grid + + std::string gridstyle = props.get_gridlinestyle (); + std::string minorgridstyle = props.get_minorgridlinestyle (); + bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); + bool do_xminorgrid = (props.is_xminorgrid () + && (minorgridstyle != "none")); + bool do_xminortick = props.is_xminortick (); + Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); + Matrix xmticks = xform.xscale (props.get_xmtick ().matrix_value ()); + string_vector xticklabels = props.get_xticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool tick_along_z = nearhoriz || octave::math::isinf (fy); + bool mirror = props.is_box () && xstate != AXE_ANY_DIR; + + set_color (props.get_xcolor_rgb ()); + + // grid lines + if (do_xgrid) + render_grid (gridstyle, xticks, x_min, x_max, + yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, + zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); + + // tick marks + if (tick_along_z) + { + render_tickmarks (xticks, x_min, x_max, ypTick, ypTick, + zpTick, zpTickN, 0., 0., + octave::math::signum (zpTick-zpTickN)*fz*xticklen, + 0, mirror); + } + else + { + render_tickmarks (xticks, x_min, x_max, ypTick, ypTickN, + zpTick, zpTick, 0., + octave::math::signum (ypTick-ypTickN)*fy*xticklen, + 0., 0, mirror); + } + + // tick texts + if (xticklabels.numel () > 0) + { + int halign = (xstate == AXE_HORZ_DIR ? 1 : (xyzSym ? 0 : 2)); + int valign = (xstate == AXE_VERT_DIR ? 1 : (x2Dtop ? 0 : 2)); + + if (tick_along_z) + render_ticktexts (xticks, xticklabels, x_min, x_max, ypTick, + zpTick+octave::math::signum (zpTick-zpTickN)*fz*xtickoffset, + 0, halign, valign, wmax, hmax); + else + render_ticktexts (xticks, xticklabels, x_min, x_max, + ypTick+octave::math::signum (ypTick-ypTickN)*fy*xtickoffset, + zpTick, 0, halign, valign, wmax, hmax); + } + + // minor grid lines + if (do_xminorgrid) + render_grid (minorgridstyle, xmticks, x_min, x_max, + yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, + zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); + + // minor tick marks + if (do_xminortick) + { + if (tick_along_z) + render_tickmarks (xmticks, x_min, x_max, ypTick, ypTick, + zpTick, zpTickN, 0., 0., + octave::math::signum (zpTick-zpTickN)*fz*xticklen/2, + 0, mirror); + else + render_tickmarks (xmticks, x_min, x_max, ypTick, ypTickN, + zpTick, zpTick, 0., + octave::math::signum (ypTick-ypTickN)*fy*xticklen/2, + 0., 0, mirror); + } + + gh_manager::get_object (props.get_xlabel ()).set ("visible", "on"); + } + else + gh_manager::get_object (props.get_xlabel ()).set ("visible", "off"); + } + + void + opengl_renderer::draw_axes_y_grid (const axes::properties& props) + { + int ystate = props.get_ystate (); + + if (ystate != AXE_DEPTH_DIR && props.is_visible ()) + { + 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 zPlane = props.get_zPlane (); + double zPlaneN = props.get_zPlaneN (); + double zpTick = props.get_zpTick (); + double zpTickN = props.get_zpTickN (); + + // Y grid + + std::string gridstyle = props.get_gridlinestyle (); + std::string minorgridstyle = props.get_minorgridlinestyle (); + bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); + bool do_yminorgrid = (props.is_yminorgrid () + && (minorgridstyle != "none")); + bool do_yminortick = props.is_yminortick (); + Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); + Matrix ymticks = xform.yscale (props.get_ymtick ().matrix_value ()); + string_vector yticklabels = props.get_yticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool tick_along_z = nearhoriz || octave::math::isinf (fx); + bool mirror = props.is_box () && ystate != AXE_ANY_DIR + && (! props.has_property ("__plotyy_axes__")); + + set_color (props.get_ycolor_rgb ()); + + // grid lines + if (do_ygrid) + render_grid (gridstyle, yticks, y_min, y_max, + xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, + zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); + + // tick marks + if (tick_along_z) + render_tickmarks (yticks, y_min, y_max, xpTick, xpTick, + zpTick, zpTickN, 0., 0., + octave::math::signum (zpTick-zpTickN)*fz*yticklen, + 1, mirror); + else + render_tickmarks (yticks, y_min, y_max, xpTick, xpTickN, + zpTick, zpTick, + octave::math::signum (xPlaneN-xPlane)*fx*yticklen, + 0., 0., 1, mirror); + + // tick texts + if (yticklabels.numel () > 0) + { + int halign = (ystate == AXE_HORZ_DIR + ? 1 : (! xyzSym || y2Dright ? 0 : 2)); + int valign = (ystate == AXE_VERT_DIR ? 1 : 2); + + if (tick_along_z) + render_ticktexts (yticks, yticklabels, y_min, y_max, xpTick, + zpTick+octave::math::signum (zpTick-zpTickN)*fz*ytickoffset, + 1, halign, valign, wmax, hmax); + else + render_ticktexts (yticks, yticklabels, y_min, y_max, + xpTick+octave::math::signum (xpTick-xpTickN)*fx*ytickoffset, + zpTick, 1, halign, valign, wmax, hmax); + } + + // minor grid lines + if (do_yminorgrid) + render_grid (minorgridstyle, ymticks, y_min, y_max, + xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, + zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); + + // minor tick marks + if (do_yminortick) + { + if (tick_along_z) + render_tickmarks (ymticks, y_min, y_max, xpTick, xpTick, + zpTick, zpTickN, 0., 0., + octave::math::signum (zpTick-zpTickN)*fz*yticklen/2, + 1, mirror); + else + render_tickmarks (ymticks, y_min, y_max, xpTick, xpTickN, + zpTick, zpTick, + octave::math::signum (xpTick-xpTickN)*fx*yticklen/2, + 0., 0., 1, mirror); + } + + gh_manager::get_object (props.get_ylabel ()).set ("visible", "on"); + } + else + gh_manager::get_object (props.get_ylabel ()).set ("visible", "off"); + } + + void + opengl_renderer::draw_axes_z_grid (const axes::properties& props) + { + int zstate = props.get_zstate (); + + if (zstate != AXE_DEPTH_DIR && props.is_visible ()) + { + 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 Grid + + std::string gridstyle = props.get_gridlinestyle (); + std::string minorgridstyle = props.get_minorgridlinestyle (); + bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); + bool do_zminorgrid = (props.is_zminorgrid () + && (minorgridstyle != "none")); + bool do_zminortick = props.is_zminortick (); + Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); + Matrix zmticks = xform.zscale (props.get_zmtick ().matrix_value ()); + string_vector zticklabels = props.get_zticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool mirror = props.is_box () && zstate != AXE_ANY_DIR; + + set_color (props.get_zcolor_rgb ()); + + // grid lines + if (do_zgrid) + render_grid (gridstyle, zticks, z_min, z_max, + xPlane, xPlaneN, yPlane, yPlaneN, 2, true); + + // tick marks + if (xySym) + { + if (octave::math::isinf (fy)) + render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, + yPlane, yPlane, + octave::math::signum (xPlaneN-xPlane)*fx*zticklen, + 0., 0., 2, mirror); + else + render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, + yPlane, yPlane, 0., + octave::math::signum (yPlane-yPlaneN)*fy*zticklen, + 0., 2, false); + } + else + { + if (octave::math::isinf (fx)) + render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, + yPlaneN, yPlane, 0., + octave::math::signum (yPlaneN-yPlane)*fy*zticklen, + 0., 2, mirror); + else + render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, + yPlaneN, yPlane, + octave::math::signum (xPlane-xPlaneN)*fx*zticklen, + 0., 0., 2, false); + } + + // FIXME: tick texts + if (zticklabels.numel () > 0) + { + int halign = 2; + int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2)); + + if (xySym) + { + if (octave::math::isinf (fy)) + render_ticktexts (zticks, zticklabels, z_min, z_max, + xPlaneN+octave::math::signum (xPlaneN-xPlane)*fx*ztickoffset, + yPlane, 2, halign, valign, wmax, hmax); + else + render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN, + yPlane+octave::math::signum (yPlane-yPlaneN)*fy*ztickoffset, + 2, halign, valign, wmax, hmax); + } + else + { + if (octave::math::isinf (fx)) + render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane, + yPlaneN+octave::math::signum (yPlaneN-yPlane)*fy*ztickoffset, + 2, halign, valign, wmax, hmax); + else + render_ticktexts (zticks, zticklabels, z_min, z_max, + xPlane+octave::math::signum (xPlane-xPlaneN)*fx*ztickoffset, + yPlaneN, 2, halign, valign, wmax, hmax); + } + } + + // minor grid lines + if (do_zminorgrid) + render_grid (minorgridstyle, zmticks, z_min, z_max, + xPlane, xPlaneN, yPlane, yPlaneN, 2, true); + + // minor tick marks + if (do_zminortick) + { + if (xySym) + { + if (octave::math::isinf (fy)) + render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane, + yPlane, yPlane, + octave::math::signum (xPlaneN-xPlane)*fx*zticklen/2, + 0., 0., 2, mirror); + else + render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, + yPlane, yPlane, 0., + octave::math::signum (yPlane-yPlaneN)*fy*zticklen/2, + 0., 2, false); + } + else + { + if (octave::math::isinf (fx)) + render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, + yPlaneN, yPlane, 0., + octave::math::signum (yPlaneN-yPlane)*fy*zticklen/2, + 0., 2, mirror); + else + render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, + yPlaneN, yPlaneN, + octave::math::signum (xPlane-xPlaneN)*fx*zticklen/2, + 0., 0., 2, false); + } + } + + gh_manager::get_object (props.get_zlabel ()).set ("visible", "on"); + } + else + gh_manager::get_object (props.get_zlabel ()).set ("visible", "off"); + } + + void + opengl_renderer::draw_all_lights (const base_properties& props, std::list<graphics_object>& obj_list) + { #if defined (HAVE_OPENGL) - - // setup OpenGL transformation - - Matrix x_zlim = props.get_transform_zlim (); - - xZ1 = x_zlim(0)-(x_zlim(1)-x_zlim(0))/2; - xZ2 = x_zlim(1)+(x_zlim(1)-x_zlim(0))/2; - - Matrix x_mat1 = props.get_opengl_matrix_1 (); - Matrix x_mat2 = props.get_opengl_matrix_2 (); - -#if defined (HAVE_FRAMEWORK_OPENGL) - GLint vw[4]; -#else - int vw[4]; -#endif - - glGetIntegerv (GL_VIEWPORT, vw); - - glMatrixMode (GL_MODELVIEW); - glLoadIdentity (); - glScaled (1, 1, -1); - glMultMatrixd (x_mat1.data ()); - glMatrixMode (GL_PROJECTION); - glLoadIdentity (); - glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2); - glMultMatrixd (x_mat2.data ()); - glMatrixMode (GL_MODELVIEW); - - glClear (GL_DEPTH_BUFFER_BIT); - - // store axes transformation data - - xform = props.get_transform (); - + Matrix children = props.get_all_children (); + + for (octave_idx_type i = children.numel () - 1; i >= 0; i--) + { + graphics_object go = gh_manager::get_object (children(i)); + + if (go.get_properties ().is_visible ()) + { + if (go.isa ("light")) + { + if (num_lights < max_lights) + { + current_light = GL_LIGHT0 + num_lights; + set_clipping (go.get_properties ().is_clipping ()); + draw (go); + num_lights++; + } + else + warning_with_id ("Octave:max-lights-exceeded", + "light: Maximum number of lights (%d) in these axes is " + "exceeded.", max_lights); + } + else if (go.isa ("hggroup")) + draw_all_lights (go.get_properties (), obj_list); + else + obj_list.push_back (go); + } + } #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.is_empty () || ! 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, 2.5); - - glBegin (GL_QUADS); - - if (! is2d) - { - // X plane - glVertex3d (xPlane, yPlaneN, zPlaneN); - glVertex3d (xPlane, yPlane, zPlaneN); - glVertex3d (xPlane, yPlane, zPlane); - glVertex3d (xPlane, yPlaneN, zPlane); - - // Y plane - glVertex3d (xPlaneN, yPlane, zPlaneN); - glVertex3d (xPlane, yPlane, zPlaneN); - glVertex3d (xPlane, yPlane, zPlane); - glVertex3d (xPlaneN, yPlane, zPlane); - } - - // Z plane - glVertex3d (xPlaneN, yPlaneN, zPlane); - glVertex3d (xPlane, yPlaneN, zPlane); - glVertex3d (xPlane, yPlane, zPlane); - glVertex3d (xPlaneN, yPlane, zPlane); - - 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 (); + 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_boxes (const axes::properties& props) -{ + } + + void + opengl_renderer::draw_axes_children (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 (); - 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_linestyle ("-", true); - set_linewidth (props.get_linewidth ()); - - glBegin (GL_LINES); - - if (layer2Dtop) - std::swap (zpTick, zpTickN); - - // X box - set_color (props.get_xcolor_rgb ()); - glVertex3d (xPlaneN, ypTick, zpTick); - glVertex3d (xPlane, ypTick, zpTick); - - if (props.is_box ()) - { - glVertex3d (xPlaneN, ypTickN, zpTick); - glVertex3d (xPlane, ypTickN, zpTick); - if (! is2d) - { - glVertex3d (xPlaneN, ypTickN, zpTickN); - glVertex3d (xPlane, ypTickN, zpTickN); - glVertex3d (xPlaneN, ypTick, zpTickN); - glVertex3d (xPlane, ypTick, zpTickN); - } - } - - // Y box - set_color (props.get_ycolor_rgb ()); - glVertex3d (xpTick, yPlaneN, zpTick); - glVertex3d (xpTick, yPlane, zpTick); - - if (props.is_box () && ! plotyy) - { - glVertex3d (xpTickN, yPlaneN, zpTick); - glVertex3d (xpTickN, yPlane, zpTick); - - if (! is2d) - { - glVertex3d (xpTickN, yPlaneN, zpTickN); - glVertex3d (xpTickN, yPlane, zpTickN); - glVertex3d (xpTick, yPlaneN, zpTickN); - glVertex3d (xpTick, yPlane, zpTickN); - } - } - - // Z box - if (! is2d) - { - set_color (props.get_zcolor_rgb ()); - - if (xySym) - { - glVertex3d (xPlaneN, yPlane, zPlaneN); - glVertex3d (xPlaneN, yPlane, zPlane); - } - else - { - glVertex3d (xPlane, yPlaneN, zPlaneN); - glVertex3d (xPlane, yPlaneN, zPlane); - } - - if (props.is_box ()) - { - glVertex3d (xPlane, yPlane, zPlaneN); - glVertex3d (xPlane, yPlane, zPlane); - - if (xySym) - { - glVertex3d (xPlane, yPlaneN, zPlaneN); - glVertex3d (xPlane, yPlaneN, zPlane); - } - else - { - glVertex3d (xPlaneN, yPlane, zPlaneN); - glVertex3d (xPlaneN, yPlane, zPlane); - } - - glVertex3d (xPlaneN, yPlaneN, zPlaneN); - glVertex3d (xPlaneN, yPlaneN, zPlane); - } - } - - glEnd (); + // list for non-light child objects + std::list<graphics_object> obj_list; + std::list<graphics_object>::iterator it; + + // 1st pass: draw light objects + + // Start with the last element of the array of child objects to + // display them in the order they were added to the array. + + num_lights = 0; + draw_all_lights (props, obj_list); + + // disable other OpenGL lights + for (int i = num_lights; i < max_lights; i++) + glDisable (GL_LIGHT0 + i); + + // save camera position and set ambient light color before drawing + // other objects + 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); + 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 + + 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); + } + + glEnable (GL_DEPTH_TEST); + + 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 (); + 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) -{ - int xstate = props.get_xstate (); - - if (props.is_visible () && xstate != AXE_DEPTH_DIR) - { - 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 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 grid - - std::string gridstyle = props.get_gridlinestyle (); - std::string minorgridstyle = props.get_minorgridlinestyle (); - bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); - bool do_xminorgrid = (props.is_xminorgrid () - && (minorgridstyle != "none")); - bool do_xminortick = props.is_xminortick (); - Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); - Matrix xmticks = xform.xscale (props.get_xmtick ().matrix_value ()); - string_vector xticklabels = props.get_xticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool tick_along_z = nearhoriz || octave::math::isinf (fy); - bool mirror = props.is_box () && xstate != AXE_ANY_DIR; - - set_color (props.get_xcolor_rgb ()); - - // grid lines - if (do_xgrid) - render_grid (gridstyle, xticks, x_min, x_max, - yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, - zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); - - // tick marks - if (tick_along_z) - { - render_tickmarks (xticks, x_min, x_max, ypTick, ypTick, - zpTick, zpTickN, 0., 0., - octave::math::signum (zpTick-zpTickN)*fz*xticklen, - 0, mirror); - } - else - { - render_tickmarks (xticks, x_min, x_max, ypTick, ypTickN, - zpTick, zpTick, 0., - octave::math::signum (ypTick-ypTickN)*fy*xticklen, - 0., 0, mirror); - } - - // tick texts - if (xticklabels.numel () > 0) - { - int halign = (xstate == AXE_HORZ_DIR ? 1 : (xyzSym ? 0 : 2)); - int valign = (xstate == AXE_VERT_DIR ? 1 : (x2Dtop ? 0 : 2)); - - if (tick_along_z) - render_ticktexts (xticks, xticklabels, x_min, x_max, ypTick, - zpTick+octave::math::signum (zpTick-zpTickN)*fz*xtickoffset, - 0, halign, valign, wmax, hmax); - else - render_ticktexts (xticks, xticklabels, x_min, x_max, - ypTick+octave::math::signum (ypTick-ypTickN)*fy*xtickoffset, - zpTick, 0, halign, valign, wmax, hmax); - } - - // minor grid lines - if (do_xminorgrid) - render_grid (minorgridstyle, xmticks, x_min, x_max, - yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, - zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); - - // minor tick marks - if (do_xminortick) - { - if (tick_along_z) - render_tickmarks (xmticks, x_min, x_max, ypTick, ypTick, - zpTick, zpTickN, 0., 0., - octave::math::signum (zpTick-zpTickN)*fz*xticklen/2, - 0, mirror); - else - render_tickmarks (xmticks, x_min, x_max, ypTick, ypTickN, - zpTick, zpTick, 0., - octave::math::signum (ypTick-ypTickN)*fy*xticklen/2, - 0., 0, mirror); - } - - gh_manager::get_object (props.get_xlabel ()).set ("visible", "on"); - } - else - gh_manager::get_object (props.get_xlabel ()).set ("visible", "off"); -} - -void -opengl_renderer::draw_axes_y_grid (const axes::properties& props) -{ - int ystate = props.get_ystate (); - - if (ystate != AXE_DEPTH_DIR && props.is_visible ()) - { - 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 zPlane = props.get_zPlane (); - double zPlaneN = props.get_zPlaneN (); - double zpTick = props.get_zpTick (); - double zpTickN = props.get_zpTickN (); - - // Y grid - - std::string gridstyle = props.get_gridlinestyle (); - std::string minorgridstyle = props.get_minorgridlinestyle (); - bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); - bool do_yminorgrid = (props.is_yminorgrid () - && (minorgridstyle != "none")); - bool do_yminortick = props.is_yminortick (); - Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); - Matrix ymticks = xform.yscale (props.get_ymtick ().matrix_value ()); - string_vector yticklabels = props.get_yticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool tick_along_z = nearhoriz || octave::math::isinf (fx); - bool mirror = props.is_box () && ystate != AXE_ANY_DIR - && (! props.has_property ("__plotyy_axes__")); - - set_color (props.get_ycolor_rgb ()); - - // grid lines - if (do_ygrid) - render_grid (gridstyle, yticks, y_min, y_max, - xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, - zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); - - // tick marks - if (tick_along_z) - render_tickmarks (yticks, y_min, y_max, xpTick, xpTick, - zpTick, zpTickN, 0., 0., - octave::math::signum (zpTick-zpTickN)*fz*yticklen, - 1, mirror); - else - render_tickmarks (yticks, y_min, y_max, xpTick, xpTickN, - zpTick, zpTick, - octave::math::signum (xPlaneN-xPlane)*fx*yticklen, - 0., 0., 1, mirror); - - // tick texts - if (yticklabels.numel () > 0) - { - int halign = (ystate == AXE_HORZ_DIR - ? 1 : (! xyzSym || y2Dright ? 0 : 2)); - int valign = (ystate == AXE_VERT_DIR ? 1 : 2); - - if (tick_along_z) - render_ticktexts (yticks, yticklabels, y_min, y_max, xpTick, - zpTick+octave::math::signum (zpTick-zpTickN)*fz*ytickoffset, - 1, halign, valign, wmax, hmax); - else - render_ticktexts (yticks, yticklabels, y_min, y_max, - xpTick+octave::math::signum (xpTick-xpTickN)*fx*ytickoffset, - zpTick, 1, halign, valign, wmax, hmax); - } - - // minor grid lines - if (do_yminorgrid) - render_grid (minorgridstyle, ymticks, y_min, y_max, - xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, - zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); - - // minor tick marks - if (do_yminortick) - { - if (tick_along_z) - render_tickmarks (ymticks, y_min, y_max, xpTick, xpTick, - zpTick, zpTickN, 0., 0., - octave::math::signum (zpTick-zpTickN)*fz*yticklen/2, - 1, mirror); - else - render_tickmarks (ymticks, y_min, y_max, xpTick, xpTickN, - zpTick, zpTick, - octave::math::signum (xpTick-xpTickN)*fx*yticklen/2, - 0., 0., 1, mirror); - } - - gh_manager::get_object (props.get_ylabel ()).set ("visible", "on"); - } - else - gh_manager::get_object (props.get_ylabel ()).set ("visible", "off"); -} - -void -opengl_renderer::draw_axes_z_grid (const axes::properties& props) -{ - int zstate = props.get_zstate (); - - if (zstate != AXE_DEPTH_DIR && props.is_visible ()) - { - 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 Grid - - std::string gridstyle = props.get_gridlinestyle (); - std::string minorgridstyle = props.get_minorgridlinestyle (); - bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); - bool do_zminorgrid = (props.is_zminorgrid () - && (minorgridstyle != "none")); - bool do_zminortick = props.is_zminortick (); - Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); - Matrix zmticks = xform.zscale (props.get_zmtick ().matrix_value ()); - string_vector zticklabels = props.get_zticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool mirror = props.is_box () && zstate != AXE_ANY_DIR; - - set_color (props.get_zcolor_rgb ()); - - // grid lines - if (do_zgrid) - render_grid (gridstyle, zticks, z_min, z_max, - xPlane, xPlaneN, yPlane, yPlaneN, 2, true); - - // tick marks - if (xySym) - { - if (octave::math::isinf (fy)) - render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, - yPlane, yPlane, - octave::math::signum (xPlaneN-xPlane)*fx*zticklen, - 0., 0., 2, mirror); - else - render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, - yPlane, yPlane, 0., - octave::math::signum (yPlane-yPlaneN)*fy*zticklen, - 0., 2, false); - } - else - { - if (octave::math::isinf (fx)) - render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, - yPlaneN, yPlane, 0., - octave::math::signum (yPlaneN-yPlane)*fy*zticklen, - 0., 2, mirror); - else - render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, - yPlaneN, yPlane, - octave::math::signum (xPlane-xPlaneN)*fx*zticklen, - 0., 0., 2, false); - } - - // FIXME: tick texts - if (zticklabels.numel () > 0) - { - int halign = 2; - int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2)); - - if (xySym) - { - if (octave::math::isinf (fy)) - render_ticktexts (zticks, zticklabels, z_min, z_max, - xPlaneN+octave::math::signum (xPlaneN-xPlane)*fx*ztickoffset, - yPlane, 2, halign, valign, wmax, hmax); - else - render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN, - yPlane+octave::math::signum (yPlane-yPlaneN)*fy*ztickoffset, - 2, halign, valign, wmax, hmax); - } - else - { - if (octave::math::isinf (fx)) - render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane, - yPlaneN+octave::math::signum (yPlaneN-yPlane)*fy*ztickoffset, - 2, halign, valign, wmax, hmax); - else - render_ticktexts (zticks, zticklabels, z_min, z_max, - xPlane+octave::math::signum (xPlane-xPlaneN)*fx*ztickoffset, - yPlaneN, 2, halign, valign, wmax, hmax); - } - } - - // minor grid lines - if (do_zminorgrid) - render_grid (minorgridstyle, zmticks, z_min, z_max, - xPlane, xPlaneN, yPlane, yPlaneN, 2, true); - - // minor tick marks - if (do_zminortick) - { - if (xySym) - { - if (octave::math::isinf (fy)) - render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane, - yPlane, yPlane, - octave::math::signum (xPlaneN-xPlane)*fx*zticklen/2, - 0., 0., 2, mirror); - else - render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, - yPlane, yPlane, 0., - octave::math::signum (yPlane-yPlaneN)*fy*zticklen/2, - 0., 2, false); - } - else - { - if (octave::math::isinf (fx)) - render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, - yPlaneN, yPlane, 0., - octave::math::signum (yPlaneN-yPlane)*fy*zticklen/2, - 0., 2, mirror); - else - render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, - yPlaneN, yPlaneN, - octave::math::signum (xPlane-xPlaneN)*fx*zticklen/2, - 0., 0., 2, false); - } - } - - gh_manager::get_object (props.get_zlabel ()).set ("visible", "on"); - } - else - gh_manager::get_object (props.get_zlabel ()).set ("visible", "off"); -} - -void -opengl_renderer::draw_all_lights (const base_properties& props, std::list<graphics_object>& obj_list) -{ + } + + void + opengl_renderer::draw_axes (const axes::properties& props) + { #if defined (HAVE_OPENGL) - Matrix children = props.get_all_children (); - - for (octave_idx_type i = children.numel () - 1; i >= 0; i--) - { - graphics_object go = gh_manager::get_object (children(i)); - - if (go.get_properties ().is_visible ()) - { - if (go.isa ("light")) - { - if (num_lights < max_lights) - { - current_light = GL_LIGHT0 + num_lights; - set_clipping (go.get_properties ().is_clipping ()); - draw (go); - num_lights++; - } - else - warning_with_id ("Octave:max-lights-exceeded", - "light: Maximum number of lights (%d) in these axes is " - "exceeded.", max_lights); - } - else if (go.isa ("hggroup")) - draw_all_lights (go.get_properties (), obj_list); - else - obj_list.push_back (go); - } - } + + // Legends are not drawn when "visible" is "off". + if (! props.is_visible () && props.get_tag () == "legend") + 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); + + // Disable line smoothing for axes + GLboolean antialias; + glGetBooleanv (GL_LINE_SMOOTH, &antialias); + if (antialias == GL_TRUE) + glDisable (GL_LINE_SMOOTH); + + // draw axes object + draw_axes_planes (props); + if (props.get_tag () != "legend" || props.get_box () != "off") + draw_axes_boxes (props); + + set_font (props); + set_interpreter (props.get_ticklabelinterpreter ()); + + draw_axes_x_grid (props); + draw_axes_y_grid (props); + draw_axes_z_grid (props); + + set_linestyle ("-"); + + set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max); + + // Re-enable line smoothing for children + if (antialias == GL_TRUE) + glEnable (GL_LINE_SMOOTH); + + draw_axes_children (props); + #else - octave_unused_parameter (props); - octave_unused_parameter (obj_list); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::draw_axes_children (const axes::properties& props) -{ + } + + void + opengl_renderer::draw_line (const line::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 - - // Start with the last element of the array of child objects to - // display them in the order they were added to the array. - - num_lights = 0; - draw_all_lights (props, obj_list); - - // disable other OpenGL lights - for (int i = num_lights; i < max_lights; i++) - glDisable (GL_LIGHT0 + i); - - // save camera position and set ambient light color before drawing - // other objects - 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); - 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 - - 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); - } - - glEnable (GL_DEPTH_TEST); - - set_clipping (false); - - // FIXME: finalize rendering (transparency processing) - // FIXME: draw zoom box, if needed + + Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); + Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); + Matrix z = 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 ()))); + octave_uint8 clip_mask = (props.is_clipping () ? 0x7F : 0x40), clip_ok (0x40); + + std::vector<octave_uint8> 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 = (zmin+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); + set_linewidth (props.get_linewidth ()); + + 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; + glBegin (GL_LINE_STRIP); + glVertex3d (x(i-1), y(i-1), z(i-1)); + } + glVertex3d (x(i), y(i), z(i)); + } + else if (flag) + { + flag = false; + glEnd (); + } + } + + if (flag) + glEnd (); + } + else + { + bool flag = false; + + for (int i = 1; i < n; i++) + { + if ((clip[i-1] & clip[i]) == clip_ok) + { + if (! flag) + { + flag = true; + glBegin (GL_LINE_STRIP); + glVertex2d (x(i-1), y(i-1)); + } + glVertex2d (x(i), y(i)); + } + else if (flag) + { + flag = false; + glEnd (); + } + } + + if (flag) + glEnd (); + } + + set_linewidth (0.5); + set_linestyle ("-"); + } + + set_clipping (false); + + if (! props.marker_is ("none") + && ! (props.markeredgecolor_is ("none") + && props.markerfacecolor_is ("none"))) + { + Matrix lc, fc; + + if (props.markeredgecolor_is ("auto")) + lc = props.get_color_rgb (); + else if (! props.markeredgecolor_is ("none")) + lc = props.get_markeredgecolor_rgb (); + + 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_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; - - 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); - - // Disable line smoothing for axes - GLboolean antialias; - glGetBooleanv (GL_LINE_SMOOTH, &antialias); - if (antialias == GL_TRUE) - glDisable (GL_LINE_SMOOTH); - - // draw axes object - draw_axes_planes (props); - if (props.get_tag () != "legend" || props.get_box () != "off") - draw_axes_boxes (props); - - set_font (props); - set_interpreter (props.get_ticklabelinterpreter ()); - - draw_axes_x_grid (props); - draw_axes_y_grid (props); - draw_axes_z_grid (props); - - set_linestyle ("-"); - - set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max); - - // Re-enable line smoothing for children - if (antialias == GL_TRUE) - glEnable (GL_LINE_SMOOTH); - - draw_axes_children (props); - -#else - - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::draw_line (const line::properties& props) -{ + } + + void + opengl_renderer::draw_surface (const surface::properties& props) + { #if defined (HAVE_OPENGL) - Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); - Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); - Matrix z = 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 ()))); - octave_uint8 clip_mask = (props.is_clipping () ? 0x7F : 0x40), clip_ok (0x40); - - std::vector<octave_uint8> 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 = (zmin+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); - set_linewidth (props.get_linewidth ()); - - 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; - glBegin (GL_LINE_STRIP); - glVertex3d (x(i-1), y(i-1), z(i-1)); - } - glVertex3d (x(i), y(i), z(i)); - } - else if (flag) - { - flag = false; - glEnd (); - } - } - - if (flag) - glEnd (); - } - else - { - bool flag = false; - - for (int i = 1; i < n; i++) - { - if ((clip[i-1] & clip[i]) == clip_ok) - { - if (! flag) - { - flag = true; - glBegin (GL_LINE_STRIP); - glVertex2d (x(i-1), y(i-1)); - } - glVertex2d (x(i), y(i)); - } - else if (flag) - { - flag = false; - glEnd (); - } - } - - if (flag) - glEnd (); - } - - set_linewidth (0.5); - set_linestyle ("-"); - } - - set_clipping (false); - - if (! props.marker_is ("none") - && ! (props.markeredgecolor_is ("none") - && props.markerfacecolor_is ("none"))) - { - Matrix lc, fc; - - if (props.markeredgecolor_is ("auto")) - lc = props.get_color_rgb (); - else if (! props.markeredgecolor_is ("none")) - lc = props.get_markeredgecolor_rgb (); - - 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 ()); + const Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); + const Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); + const Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); + + int zr = z.rows (); + int zc = z.columns (); + + NDArray c; + const NDArray n = props.get_vertexnormals ().array_value (); + + // 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") ? 1 : 2)); + 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") ? 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)); + + Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0) + : 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 (); + float cb[4] = { 0.0, 0.0, 0.0, 1.0 }; + + opengl_texture tex; + + 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) + 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 (props.get_color_data ()); + + if (! props.facecolor_is ("none")) + { + if (props.get_facealpha_double () == 1) + { + if (fc_mode == UNIFORM || fc_mode == TEXTURE) + { + glColor3dv (fcolor.data ()); + if (fl_mode > 0) + { + for (int i = 0; i < 3; i++) + cb[i] = as * fcolor(i); + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * fcolor(i); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * fcolor(i)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if ((fl_mode > 0) && (num_lights > 0)) + glEnable (GL_LIGHTING); + glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD) ? GL_SMOOTH + : GL_FLAT); + set_polygon_offset (true, 1); + if (fc_mode == TEXTURE) + 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 (! octave::math::finite (c(j-1,i-1))) + continue; + } + else if (fc_mode == INTERP) + { + // "interp" needs valid color at all 4 vertices + if (! (octave::math::finite (c(j-1, i-1)) && octave::math::finite (c(j, i-1)) + && octave::math::finite (c(j-1, i)) && octave::math::finite (c(j, i)))) + continue; + } + + if (x_mat) + { + j1 = j-1; + j2 = j; + } + + 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); + glColor3fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j-1, i-1, k); + 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)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (fl_mode > 0) + set_normal (bfl_mode, n, j-1, i-1); + + 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); + glColor3fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j-1, i, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if (fl_mode == GOURAUD) + set_normal (bfl_mode, n, j-1, i); + + 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); + glColor3fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (fl_mode == GOURAUD) + set_normal (bfl_mode, n, j, i); + + 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); + glColor3fv (cb); + + if (fl_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i-1, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (fl_mode == GOURAUD) + set_normal (bfl_mode, n, j, i-1); + + glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); + + glEnd (); + } + } + + set_polygon_offset (false); + if (fc_mode == TEXTURE) + glDisable (GL_TEXTURE_2D); + + if ((fl_mode > 0) && (num_lights > 0)) + glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement transparency + } + } + + if (! props.edgecolor_is ("none")) + { + if (props.get_edgealpha_double () == 1) + { + if (ec_mode == UNIFORM) + { + glColor3dv (ecolor.data ()); + if (fl_mode > 0) + { + for (int i = 0; i < 3; i++) + cb[i] = as * ecolor(i); + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * ecolor(i); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * ecolor(i)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if ((el_mode > 0) && (num_lights > 0)) + glEnable (GL_LIGHTING); + glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) ? GL_SMOOTH + : GL_FLAT); + + set_linestyle (props.get_linestyle (), false); + set_linewidth (props.get_linewidth ()); + + // 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 (! octave::math::finite (c(j-1,i))) + continue; + } + else if (ec_mode == INTERP) + { + // "interp" needs valid color at both vertices + if (! (octave::math::finite (c(j-1, i)) && octave::math::finite (c(j, i)))) + continue; + } + + if (x_mat) + { + j1 = j-1; + j2 = j; + } + + glBegin (GL_LINES); + + // Vertex 1 + if (ec_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j-1, i, k); + glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j-1, i, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (el_mode > 0) + set_normal (bfl_mode, n, j-1, i); + + 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); + glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (el_mode == GOURAUD) + set_normal (bfl_mode, n, j, i); + + glVertex3d (x(j2,i), y(j,i2), z(j,i)); + + 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 (! octave::math::finite (c(j,i-1))) + continue; + } + else if (ec_mode == INTERP) + { + // "interp" needs valid color at both vertices + if (! (octave::math::finite (c(j, i-1)) && octave::math::finite (c(j, i)))) + continue; + } + + if (y_mat) + { + i1 = i-1; + i2 = i; + } + + glBegin (GL_LINES); + + // Vertex 1 + if (ec_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] = c(j, i-1, k); + glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i-1, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (el_mode > 0) + set_normal (bfl_mode, n, j, i-1); + + 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); + glColor3fv (cb); + + if (el_mode > 0) + { + for (int k = 0; k < 3; k++) + cb[k] *= as; + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ds * c(j, i, k); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = ss * (scr + (1-scr) * c(j, i, k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + if (el_mode == GOURAUD) + set_normal (bfl_mode, n, j, i); + + glVertex3d (x(j2,i), y(j,i2), z(j,i)); + + glEnd (); + } + } + } + + set_linestyle ("-"); + set_linewidth (0.5); + + if ((el_mode > 0) && (num_lights > 0)) + 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 = ! props.markeredgecolor_is ("none"); + bool do_face = ! props.markerfacecolor_is ("none"); + + Matrix mecolor = props.get_markeredgecolor_rgb (); + Matrix mfcolor = props.get_markerfacecolor_rgb (); + Matrix cc (1, 3, 0.0); + + if (mecolor.is_empty () && props.markeredgecolor_is ("auto")) + { + mecolor = props.get_edgecolor_rgb (); + do_edge = ! props.edgecolor_is ("none"); + } + + if (mfcolor.is_empty () && props.markerfacecolor_is ("auto")) + { + mfcolor = props.get_facecolor_rgb (); + do_face = ! props.facecolor_is ("none"); + } + + if ((mecolor.is_empty () || mfcolor.is_empty ()) && c.is_empty ()) + c = props.get_color_data ().array_value (); + + init_marker (props.get_marker (), props.get_markersize (), + props.get_linewidth ()); + + for (int i = 0; i < zc; i++) + { + if (y_mat) + i1 = i; + + for (int j = 0; j < zr; j++) + { + if (clip(j,i)) + continue; + + if (x_mat) + j1 = j; + + if ((do_edge && mecolor.is_empty ()) + || (do_face && mfcolor.is_empty ())) + { + if (! octave::math::finite (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.is_empty () ? cc : mecolor) + : Matrix ()); + Matrix fc = (do_face ? (mfcolor.is_empty () ? 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 (); + 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) -{ + } + + // 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) - const Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); - const Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); - const Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); - - int zr = z.rows (); - int zc = z.columns (); - - NDArray c; - const NDArray n = props.get_vertexnormals ().array_value (); - - // 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") ? 1 : 2)); - 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") ? 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)); - - Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0) - : 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 (); - float cb[4] = { 0.0, 0.0, 0.0, 1.0 }; - - opengl_texture tex; - - 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++) + // 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; + } + + const Matrix f = props.get_faces ().matrix_value (); + const Matrix v = xform.scale (props.get_vertices ().matrix_value ()); + Matrix c; + const Matrix n = props.get_vertexnormals ().matrix_value (); + Matrix a; + + 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; + // FIXME: remove when patch object has normal computation (patch #8951) + bool has_normals = (n.rows () == nv); + + int fc_mode = ((props.facecolor_is ("none") + || props.facecolor_is_rgb ()) ? 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)); + + 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 (); + + 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 && ! octave::math::isnan (f(i,j)); j++, count++) + fclip = (fclip || clip(int (f(i,j) - 1))); + + clip_f(i) = fclip; + count_f(i) = count; + } + + if (fc_mode > 0 || ec_mode > 0) + { + c = props.get_color_data ().matrix_value (); + + if (c.rows () == 1) + { + // Single color specifications, we can simplify a little bit + + if (fc_mode > 0) + { + fcolor = c; + fc_mode = UNIFORM; + } + + if (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 ())); + } + + 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++) { - 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) - 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 (props.get_color_data ()); - - if (! props.facecolor_is ("none")) - { - if (props.get_facealpha_double () == 1) - { - if (fc_mode == UNIFORM || fc_mode == TEXTURE) - { - glColor3dv (fcolor.data ()); - if (fl_mode > 0) - { - for (int i = 0; i < 3; i++) - cb[i] = as * fcolor(i); - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * fcolor(i); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * fcolor(i)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((fl_mode > 0) && (num_lights > 0)) - glEnable (GL_LIGHTING); - glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD) ? GL_SMOOTH - : GL_FLAT); - set_polygon_offset (true, 1); - if (fc_mode == TEXTURE) - glEnable (GL_TEXTURE_2D); - - for (int i = 1; i < zc; i++) + int idx = int (f(i,j) - 1); + + Matrix vv (1, 3, 0.0); + Matrix cc; + Matrix nn (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 (has_normals) { - 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 (! octave::math::finite (c(j-1,i-1))) - continue; - } - else if (fc_mode == INTERP) - { - // "interp" needs valid color at all 4 vertices - if (! (octave::math::finite (c(j-1, i-1)) && octave::math::finite (c(j, i-1)) - && octave::math::finite (c(j-1, i)) && octave::math::finite (c(j, i)))) - continue; - } - - if (x_mat) - { - j1 = j-1; - j2 = j; - } - - 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); - glColor3fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j-1, i-1, k); - 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)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (fl_mode > 0) - set_normal (bfl_mode, n, j-1, i-1); - - 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); - glColor3fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j-1, i, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if (fl_mode == GOURAUD) - set_normal (bfl_mode, n, j-1, i); - - 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); - glColor3fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (fl_mode == GOURAUD) - set_normal (bfl_mode, n, j, i); - - 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); - glColor3fv (cb); - - if (fl_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i-1, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (fl_mode == GOURAUD) - set_normal (bfl_mode, n, j, i-1); - - glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); - - glEnd (); - } + double dir = 1.0; + if (bfl_mode > 0) + dir = ((n(idx,0) * view_vector(0) + + n(idx,1) * view_vector(1) + + n(idx,2) * view_vector(2) < 0) + ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); + nn(0) = dir * n(idx,0); + nn(1) = dir * n(idx,1); + nn(2) = dir * n(idx,2); } - - set_polygon_offset (false); - if (fc_mode == TEXTURE) - glDisable (GL_TEXTURE_2D); - - if ((fl_mode > 0) && (num_lights > 0)) - glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement transparency + 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 (a.numel () > 0) + { + if (has_facealpha) + aa = a(i); + else + aa = a(idx); + } + + vdata[i+j*fr] = vertex_data (vv, cc, nn, aa, as, ds, ss, se, scr); } - } - - if (! props.edgecolor_is ("none")) - { - if (props.get_edgealpha_double () == 1) - { - if (ec_mode == UNIFORM) - { - glColor3dv (ecolor.data ()); - if (fl_mode > 0) - { - for (int i = 0; i < 3; i++) - cb[i] = as * ecolor(i); - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * ecolor(i); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * ecolor(i)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((el_mode > 0) && (num_lights > 0)) - glEnable (GL_LIGHTING); - glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) ? GL_SMOOTH - : GL_FLAT); - - set_linestyle (props.get_linestyle (), false); - set_linewidth (props.get_linewidth ()); - - // Mesh along Y-axis - - if (props.meshstyle_is ("both") || props.meshstyle_is ("column")) + + if (fl_mode > 0 || el_mode > 0) + glMaterialf (LIGHT_MODE, GL_SHININESS, se); + + if (! props.facecolor_is ("none")) + { + // FIXME: adapt to double-radio property + if (props.get_facealpha_double () == 1) + { + if (fc_mode == UNIFORM) + { + glColor3dv (fcolor.data ()); + if (fl_mode > 0) + { + float cb[4] = { 0, 0, 0, 1 }; + + for (int i = 0; i < 3; i++) + cb[i] = as * fcolor(i); + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * fcolor(i); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * fcolor(i)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if ((fl_mode > 0) && (num_lights > 0) && has_normals) + glEnable (GL_LIGHTING); + + // NOTE: Push filled part of patch backwards to avoid Z-fighting with + // tesselator 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_tesselator tess (this, fc_mode, fl_mode, 1.0); + + for (int i = 0; i < nf; i++) + { + if (clip_f(i)) + continue; + + tess.begin_polygon (true); + tess.begin_contour (); + + // Add vertices in reverse order for Matlab compatibility + 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->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->color; + + if (col.numel () == 3) + { + glColor3dv (col.data ()); + if (fl_mode > 0) + { + float cb[4] = { 0, 0, 0, 1 }; + + for (int k = 0; k < 3; k++) + cb[k] = (vv->ambient * col(k)); + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int k = 0; k < 3; k++) + cb[k] = (vv->diffuse * col(k)); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int k = 0; k < 3; k++) + cb[k] = vv->specular * (vv->specular_color_refl + + (1-vv->specular_color_refl) * col(k)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + } + + tess.add_vertex (vv->coords.fortran_vec (), vv); + } + + tess.end_contour (); + tess.end_polygon (); + } + + if ((fl_mode > 0) && (num_lights > 0) && has_normals) + glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement transparency + } + } + + if (! props.edgecolor_is ("none")) + { + // FIXME: adapt to double-radio property + if (props.get_edgealpha_double () == 1) + { + if (ec_mode == UNIFORM) + { + glColor3dv (ecolor.data ()); + if (el_mode > 0) + { + float cb[4] = { 0, 0, 0, 1 }; + + for (int i = 0; i < 3; i++) + cb[i] = (as * ecolor(i)); + glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ds * ecolor(i); + glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); + + for (int i = 0; i < 3; i++) + cb[i] = ss * (scr + (1-scr) * ecolor(i)); + glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); + } + } + + if ((el_mode > 0) && (num_lights > 0) && has_normals) + glEnable (GL_LIGHTING); + + set_linestyle (props.get_linestyle (), false); + set_linewidth (props.get_linewidth ()); + + // NOTE: patch contour cannot be offset. Offset must occur with the + // filled portion of the patch above. The tesselator 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_tesselator tess (this, ec_mode, el_mode); + + for (int i = 0; i < nf; i++) + { + if (clip_f(i)) + { + // This is an unclosed contour. Draw it as a line. + bool flag = false; + + 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->coords; + if (! flag) + { + flag = true; + glBegin (GL_LINE_STRIP); + } + if (ec_mode != UNIFORM) + { + Matrix col = vv->color; + + if (col.numel () == 3) + glColor3dv (col.data ()); + } + glVertex3d (m(0), m(1), m(2)); + } + else if (flag) + { + flag = false; + 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->coords; + if (ec_mode != UNIFORM) + { + Matrix col = vv->color; + + if (col.numel () == 3) + glColor3dv (col.data ()); + } + glVertex3d (m(0), m(1), m(2)); + } + + if (flag) + glEnd (); + } + else // Normal edge contour drawn with tesselator + { + 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->coords.fortran_vec (), vv); + } + + tess.end_contour (); + tess.end_polygon (); + } + } + + set_linestyle ("-"); + set_linewidth (0.5); + + if ((el_mode > 0) && (num_lights > 0) && has_normals) + glDisable (GL_LIGHTING); + } + else + { + // FIXME: implement transparency + } + } + + if (! props.marker_is ("none") + && ! (props.markeredgecolor_is ("none") + && props.markerfacecolor_is ("none"))) + { + bool do_edge = ! props.markeredgecolor_is ("none"); + bool do_face = ! props.markerfacecolor_is ("none"); + + Matrix mecolor = props.get_markeredgecolor_rgb (); + Matrix mfcolor = props.get_markerfacecolor_rgb (); + + bool has_markerfacecolor = false; + + if ((mecolor.is_empty () && ! props.markeredgecolor_is ("none")) + || (mfcolor.is_empty () && ! 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.is_empty () && ! props.markerfacecolor_is ("none")) + mfcolor = mc; + + if (mecolor.is_empty () && ! props.markeredgecolor_is ("none")) + mecolor = mc; + } + else + { + if (c.is_empty ()) + 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 ()); + + for (int i = 0; i < nf; i++) + for (int j = 0; j < count_f(i); j++) { - 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 (! octave::math::finite (c(j-1,i))) - continue; - } - else if (ec_mode == INTERP) - { - // "interp" needs valid color at both vertices - if (! (octave::math::finite (c(j-1, i)) && octave::math::finite (c(j, i)))) - continue; - } - - if (x_mat) - { - j1 = j-1; - j2 = j; - } - - glBegin (GL_LINES); - - // Vertex 1 - if (ec_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j-1, i, k); - glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j-1, i, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (el_mode > 0) - set_normal (bfl_mode, n, j-1, i); - - 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); - glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (el_mode == GOURAUD) - set_normal (bfl_mode, n, j, i); - - glVertex3d (x(j2,i), y(j,i2), z(j,i)); - - glEnd (); - } - } - } - - // Mesh along X-axis - - if (props.meshstyle_is ("both") || props.meshstyle_is ("row")) - { - for (int j = 0; j < zr; j++) + int idx = int (f(i,j) - 1); + + if (clip(idx)) + continue; + + Matrix cc; + if (c.numel () > 0) { - 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 (! octave::math::finite (c(j,i-1))) - continue; - } - else if (ec_mode == INTERP) - { - // "interp" needs valid color at both vertices - if (! (octave::math::finite (c(j, i-1)) && octave::math::finite (c(j, i)))) - continue; - } - - if (y_mat) - { - i1 = i-1; - i2 = i; - } - - glBegin (GL_LINES); - - // Vertex 1 - if (ec_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] = c(j, i-1, k); - glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i-1, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (el_mode > 0) - set_normal (bfl_mode, n, j, i-1); - - 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); - glColor3fv (cb); - - if (el_mode > 0) - { - for (int k = 0; k < 3; k++) - cb[k] *= as; - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ds * c(j, i, k); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = ss * (scr + (1-scr) * c(j, i, k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - if (el_mode == GOURAUD) - set_normal (bfl_mode, n, j, i); - - glVertex3d (x(j2,i), y(j,i2), z(j,i)); - - glEnd (); - } - } - } - - set_linestyle ("-"); - set_linewidth (0.5); - - if ((el_mode > 0) && (num_lights > 0)) - 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 = ! props.markeredgecolor_is ("none"); - bool do_face = ! props.markerfacecolor_is ("none"); - - Matrix mecolor = props.get_markeredgecolor_rgb (); - Matrix mfcolor = props.get_markerfacecolor_rgb (); - Matrix cc (1, 3, 0.0); - - if (mecolor.is_empty () && props.markeredgecolor_is ("auto")) - { - mecolor = props.get_edgecolor_rgb (); - do_edge = ! props.edgecolor_is ("none"); - } - - if (mfcolor.is_empty () && props.markerfacecolor_is ("auto")) - { - mfcolor = props.get_facecolor_rgb (); - do_face = ! props.facecolor_is ("none"); - } - - if ((mecolor.is_empty () || mfcolor.is_empty ()) && c.is_empty ()) - c = props.get_color_data ().array_value (); - - init_marker (props.get_marker (), props.get_markersize (), - props.get_linewidth ()); - - for (int i = 0; i < zc; i++) - { - if (y_mat) - i1 = i; - - for (int j = 0; j < zr; j++) - { - if (clip(j,i)) - continue; - - if (x_mat) - j1 = j; - - if ((do_edge && mecolor.is_empty ()) - || (do_face && mfcolor.is_empty ())) - { - if (! octave::math::finite (c(j,i))) - continue; // Skip NaNs in color data - - for (int k = 0; k < 3; k++) - cc(k) = c(j,i,k); + 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.is_empty () ? cc : mecolor) - : Matrix ()); + : Matrix ()); Matrix fc = (do_face ? (mfcolor.is_empty () ? cc : mfcolor) - : Matrix ()); - - draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc); + : Matrix ()); + + draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc); } - } - - end_marker (); - } + + end_marker (); + } #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -// FIXME: global optimization (rendering, data structures...), -// there is probably a smarter/faster/less-memory-consuming way to do this. -void -opengl_renderer::draw_patch (const patch::properties &props) -{ + } + + void + opengl_renderer::draw_light (const light::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; - } - - const Matrix f = props.get_faces ().matrix_value (); - const Matrix v = xform.scale (props.get_vertices ().matrix_value ()); - Matrix c; - const Matrix n = props.get_vertexnormals ().matrix_value (); - Matrix a; - - 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; - // FIXME: remove when patch object has normal computation (patch #8951) - bool has_normals = (n.rows () == nv); - - int fc_mode = ((props.facecolor_is ("none") - || props.facecolor_is_rgb ()) ? 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)); - - 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 (); - - 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 && ! octave::math::isnan (f(i,j)); j++, count++) - fclip = (fclip || clip(int (f(i,j) - 1))); - - clip_f(i) = fclip; - count_f(i) = count; - } - - if (fc_mode > 0 || ec_mode > 0) - { - c = props.get_color_data ().matrix_value (); - - if (c.rows () == 1) - { - // Single color specifications, we can simplify a little bit - - if (fc_mode > 0) - { - fcolor = c; - fc_mode = UNIFORM; - } - - if (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 ())); - } - - 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 nn (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 (has_normals) - { - double dir = 1.0; - if (bfl_mode > 0) - dir = ((n(idx,0) * view_vector(0) - + n(idx,1) * view_vector(1) - + n(idx,2) * view_vector(2) < 0) - ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); - nn(0) = dir * n(idx,0); - nn(1) = dir * n(idx,1); - nn(2) = dir * n(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 (a.numel () > 0) - { - if (has_facealpha) - aa = a(i); - else - aa = a(idx); - } - - vdata[i+j*fr] = vertex_data (vv, cc, nn, aa, as, ds, ss, se, scr); - } - - if (fl_mode > 0 || el_mode > 0) - glMaterialf (LIGHT_MODE, GL_SHININESS, se); - - if (! props.facecolor_is ("none")) - { - // FIXME: adapt to double-radio property - if (props.get_facealpha_double () == 1) - { - if (fc_mode == UNIFORM) - { - glColor3dv (fcolor.data ()); - if (fl_mode > 0) - { - float cb[4] = { 0, 0, 0, 1 }; - - for (int i = 0; i < 3; i++) - cb[i] = as * fcolor(i); - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * fcolor(i); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * fcolor(i)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((fl_mode > 0) && (num_lights > 0) && has_normals) - glEnable (GL_LIGHTING); - - // NOTE: Push filled part of patch backwards to avoid Z-fighting with - // tesselator 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_tesselator tess (this, fc_mode, fl_mode, 1.0); - - for (int i = 0; i < nf; i++) - { - if (clip_f(i)) - continue; - - tess.begin_polygon (true); - tess.begin_contour (); - - // Add vertices in reverse order for Matlab compatibility - 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->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->color; - - if (col.numel () == 3) - { - glColor3dv (col.data ()); - if (fl_mode > 0) - { - float cb[4] = { 0, 0, 0, 1 }; - - for (int k = 0; k < 3; k++) - cb[k] = (vv->ambient * col(k)); - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int k = 0; k < 3; k++) - cb[k] = (vv->diffuse * col(k)); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int k = 0; k < 3; k++) - cb[k] = vv->specular * (vv->specular_color_refl - + (1-vv->specular_color_refl) * col(k)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - } - - tess.add_vertex (vv->coords.fortran_vec (), vv); - } - - tess.end_contour (); - tess.end_polygon (); - } - - if ((fl_mode > 0) && (num_lights > 0) && has_normals) - glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement transparency - } - } - - if (! props.edgecolor_is ("none")) - { - // FIXME: adapt to double-radio property - if (props.get_edgealpha_double () == 1) - { - if (ec_mode == UNIFORM) - { - glColor3dv (ecolor.data ()); - if (el_mode > 0) - { - float cb[4] = { 0, 0, 0, 1 }; - - for (int i = 0; i < 3; i++) - cb[i] = (as * ecolor(i)); - glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ds * ecolor(i); - glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); - - for (int i = 0; i < 3; i++) - cb[i] = ss * (scr + (1-scr) * ecolor(i)); - glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); - } - } - - if ((el_mode > 0) && (num_lights > 0) && has_normals) - glEnable (GL_LIGHTING); - - set_linestyle (props.get_linestyle (), false); - set_linewidth (props.get_linewidth ()); - - // NOTE: patch contour cannot be offset. Offset must occur with the - // filled portion of the patch above. The tesselator 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_tesselator tess (this, ec_mode, el_mode); - - for (int i = 0; i < nf; i++) - { - if (clip_f(i)) - { - // This is an unclosed contour. Draw it as a line. - bool flag = false; - - 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->coords; - if (! flag) - { - flag = true; - glBegin (GL_LINE_STRIP); - } - if (ec_mode != UNIFORM) - { - Matrix col = vv->color; - - if (col.numel () == 3) - glColor3dv (col.data ()); - } - glVertex3d (m(0), m(1), m(2)); - } - else if (flag) - { - flag = false; - 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->coords; - if (ec_mode != UNIFORM) - { - Matrix col = vv->color; - - if (col.numel () == 3) - glColor3dv (col.data ()); - } - glVertex3d (m(0), m(1), m(2)); - } - - if (flag) - glEnd (); - } - else // Normal edge contour drawn with tesselator - { - 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->coords.fortran_vec (), vv); - } - - tess.end_contour (); - tess.end_polygon (); - } - } - - set_linestyle ("-"); - set_linewidth (0.5); - - if ((el_mode > 0) && (num_lights > 0) && has_normals) - glDisable (GL_LIGHTING); - } - else - { - // FIXME: implement transparency - } - } - - if (! props.marker_is ("none") - && ! (props.markeredgecolor_is ("none") - && props.markerfacecolor_is ("none"))) - { - bool do_edge = ! props.markeredgecolor_is ("none"); - bool do_face = ! props.markerfacecolor_is ("none"); - - Matrix mecolor = props.get_markeredgecolor_rgb (); - Matrix mfcolor = props.get_markerfacecolor_rgb (); - - bool has_markerfacecolor = false; - - if ((mecolor.is_empty () && ! props.markeredgecolor_is ("none")) - || (mfcolor.is_empty () && ! 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.is_empty () && ! props.markerfacecolor_is ("none")) - mfcolor = mc; - - if (mecolor.is_empty () && ! props.markeredgecolor_is ("none")) - mecolor = mc; - } - else - { - if (c.is_empty ()) - 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 ()); - - 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(idx)) - 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.is_empty () ? cc : mecolor) - : Matrix ()); - Matrix fc = (do_face ? (mfcolor.is_empty () ? cc : mfcolor) - : Matrix ()); - - draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc); - } - - end_marker (); - } + // enable light source + glEnable (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; + glLightfv (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); + glLightfv (current_light, GL_DIFFUSE, col); + glLightfv (current_light, GL_SPECULAR, col); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::draw_light (const light::properties &props) -{ + } + + void + opengl_renderer::draw_hggroup (const hggroup::properties &props) + { + draw (props.get_children ()); + } + + void + opengl_renderer::draw_text (const text::properties& props) + { #if defined (HAVE_OPENGL) - // enable light source - glEnable (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; - glLightfv (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); - glLightfv (current_light, GL_DIFFUSE, col); - glLightfv (current_light, GL_SPECULAR, col); + if (props.get_string ().is_empty ()) + return; + + set_font (props); + + Matrix pos = xform.scale (props.get_data_position ()); + const Matrix bbox = props.get_extent_matrix (); + + // FIXME: handle margin and surrounding box + bool blend = glIsEnabled (GL_BLEND); + + glEnable (GL_BLEND); + glEnable (GL_ALPHA_TEST); + glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0); + glBitmap (0, 0, 0, 0, bbox(0), bbox(1), 0); + glDrawPixels (bbox(2), bbox(3), + GL_RGBA, GL_UNSIGNED_BYTE, props.get_pixels ().data ()); + glDisable (GL_ALPHA_TEST); + if (! blend) + glDisable (GL_BLEND); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::draw_hggroup (const hggroup::properties &props) -{ - draw (props.get_children ()); -} - -void -opengl_renderer::draw_text (const text::properties& props) -{ + } + + void + opengl_renderer::draw_image (const image::properties& props) + { #if defined (HAVE_OPENGL) - if (props.get_string ().is_empty ()) - return; - - set_font (props); - - Matrix pos = xform.scale (props.get_data_position ()); - const Matrix bbox = props.get_extent_matrix (); - - // FIXME: handle margin and surrounding box - bool blend = glIsEnabled (GL_BLEND); - - glEnable (GL_BLEND); - glEnable (GL_ALPHA_TEST); - glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0); - glBitmap (0, 0, 0, 0, bbox(0), bbox(1), 0); - glDrawPixels (bbox(2), bbox(3), - GL_RGBA, GL_UNSIGNED_BYTE, props.get_pixels ().data ()); - glDisable (GL_ALPHA_TEST); - if (! blend) - glDisable (GL_BLEND); + octave_value cdata = props.get_color_data (); + dim_vector dv (cdata.dims ()); + int h = dv(0); + int w = dv(1); + + Matrix x = props.get_xdata ().matrix_value (); + Matrix y = props.get_ydata ().matrix_value (); + + // Someone wants us to draw an empty image? No way. + if (x.is_empty () || y.is_empty ()) + return; + + if (w > 1 && x(1) == x(0)) + x(1) = x(1) + (w-1); + + if (h > 1 && y(1) == y(0)) + y(1) = y(1) + (h-1); + + const ColumnVector p0 = xform.transform (x(0), y(0), 0); + const ColumnVector p1 = xform.transform (x(1), y(1), 0); + + if (octave::math::isnan (p0(0)) || octave::math::isnan (p0(1)) || octave::math::isnan (p1(0)) || octave::math::isnan (p1(1))) + { + warning ("opengl_renderer: image X,Y data too large to draw"); + return; + } + + // image pixel size in screen pixel units + float pix_dx, pix_dy; + // image pixel size in normalized units + float nor_dx, nor_dy; + + if (w > 1) + { + pix_dx = (p1(0) - p0(0))/(w-1); + nor_dx = (x(1) - x(0))/(w-1); + } + else + { + const ColumnVector p1w = xform.transform (x(1) + 1, y(1), 0); + pix_dx = p1w(0) - p0(0); + nor_dx = 1; + } + + if (h > 1) + { + pix_dy = (p1(1) - p0(1))/(h-1); + nor_dy = (y(1) - y(0))/(h-1); + } + else + { + const ColumnVector p1h = xform.transform (x(1), y(1) + 1, 0); + pix_dy = p1h(1) - p0(1); + nor_dy = 1; + } + + // OpenGL won't draw any of the image if its origin is outside the + // viewport/clipping plane so we must do the clipping ourselves. + + int j0, j1, i0, i1; + j0 = 0, j1 = w; + i0 = 0, i1 = h; + + float im_xmin = x(0) - nor_dx/2; + float im_xmax = x(1) + nor_dx/2; + float im_ymin = y(0) - nor_dy/2; + float im_ymax = y(1) + nor_dy/2; + if (props.is_clipping ()) // clip to axes + { + if (im_xmin < xmin) + j0 += (xmin - im_xmin)/nor_dx + 1; + if (im_xmax > xmax) + j1 -= (im_xmax - xmax)/nor_dx; + + if (im_ymin < ymin) + i0 += (ymin - im_ymin)/nor_dy + 1; + if (im_ymax > ymax) + i1 -= (im_ymax - ymax)/nor_dy; + } + else // clip to viewport + { + GLfloat vp[4]; + glGetFloatv (GL_VIEWPORT, vp); + // FIXME: actually add the code to do it! + + } + + if (i0 >= i1 || j0 >= j1) + return; + + glPixelZoom (pix_dx, -pix_dy); + glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0); + + // by default this is 4 + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + + // Expect RGB data + if (dv.ndims () == 3 && dv(2) == 3) + { + if (cdata.is_double_type ()) + { + const NDArray xcdata = cdata.array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + a[idx] = xcdata(i,j,0); + a[idx+1] = xcdata(i,j,1); + a[idx+2] = xcdata(i,j,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_single_type ()) + { + const FloatNDArray xcdata = cdata.float_array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + a[idx] = xcdata(i,j,0); + a[idx+1] = xcdata(i,j,1); + a[idx+2] = xcdata(i,j,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_uint8_type ()) + { + const uint8NDArray xcdata = cdata.uint8_array_value (); + + OCTAVE_LOCAL_BUFFER (GLubyte, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + a[idx] = xcdata(i,j,0); + a[idx+1] = xcdata(i,j,1); + a[idx+2] = xcdata(i,j,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_uint16_type ()) + { + const uint16NDArray xcdata = cdata.uint16_array_value (); + + OCTAVE_LOCAL_BUFFER (GLushort, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + a[idx] = xcdata(i,j,0); + a[idx+1] = xcdata(i,j,1); + a[idx+2] = xcdata(i,j,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else + warning ("opengl_renderer: invalid image data type (expected double, single, uint8, or uint16)"); + } + else + warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); + + glPixelZoom (1, 1); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (props); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::draw_image (const image::properties& props) -{ + } + + void + opengl_renderer::set_viewport (int w, int h) + { #if defined (HAVE_OPENGL) - octave_value cdata = props.get_color_data (); - dim_vector dv (cdata.dims ()); - int h = dv(0); - int w = dv(1); - - Matrix x = props.get_xdata ().matrix_value (); - Matrix y = props.get_ydata ().matrix_value (); - - // Someone wants us to draw an empty image? No way. - if (x.is_empty () || y.is_empty ()) - return; - - if (w > 1 && x(1) == x(0)) - x(1) = x(1) + (w-1); - - if (h > 1 && y(1) == y(0)) - y(1) = y(1) + (h-1); - - const ColumnVector p0 = xform.transform (x(0), y(0), 0); - const ColumnVector p1 = xform.transform (x(1), y(1), 0); - - if (octave::math::isnan (p0(0)) || octave::math::isnan (p0(1)) || octave::math::isnan (p1(0)) || octave::math::isnan (p1(1))) - { - warning ("opengl_renderer: image X,Y data too large to draw"); - return; - } - - // image pixel size in screen pixel units - float pix_dx, pix_dy; - // image pixel size in normalized units - float nor_dx, nor_dy; - - if (w > 1) - { - pix_dx = (p1(0) - p0(0))/(w-1); - nor_dx = (x(1) - x(0))/(w-1); - } - else - { - const ColumnVector p1w = xform.transform (x(1) + 1, y(1), 0); - pix_dx = p1w(0) - p0(0); - nor_dx = 1; - } - - if (h > 1) - { - pix_dy = (p1(1) - p0(1))/(h-1); - nor_dy = (y(1) - y(0))/(h-1); - } - else - { - const ColumnVector p1h = xform.transform (x(1), y(1) + 1, 0); - pix_dy = p1h(1) - p0(1); - nor_dy = 1; - } - - // OpenGL won't draw any of the image if its origin is outside the - // viewport/clipping plane so we must do the clipping ourselves. - - int j0, j1, i0, i1; - j0 = 0, j1 = w; - i0 = 0, i1 = h; - - float im_xmin = x(0) - nor_dx/2; - float im_xmax = x(1) + nor_dx/2; - float im_ymin = y(0) - nor_dy/2; - float im_ymax = y(1) + nor_dy/2; - if (props.is_clipping ()) // clip to axes - { - if (im_xmin < xmin) - j0 += (xmin - im_xmin)/nor_dx + 1; - if (im_xmax > xmax) - j1 -= (im_xmax - xmax)/nor_dx; - - if (im_ymin < ymin) - i0 += (ymin - im_ymin)/nor_dy + 1; - if (im_ymax > ymax) - i1 -= (im_ymax - ymax)/nor_dy; - } - else // clip to viewport - { - GLfloat vp[4]; - glGetFloatv (GL_VIEWPORT, vp); - // FIXME: actually add the code to do it! - - } - - if (i0 >= i1 || j0 >= j1) - return; - - glPixelZoom (pix_dx, -pix_dy); - glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0); - - // by default this is 4 - glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - - // Expect RGB data - if (dv.ndims () == 3 && dv(2) == 3) - { - if (cdata.is_double_type ()) - { - const NDArray xcdata = cdata.array_value (); - - OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - a[idx] = xcdata(i,j,0); - a[idx+1] = xcdata(i,j,1); - a[idx+2] = xcdata(i,j,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else if (cdata.is_single_type ()) - { - const FloatNDArray xcdata = cdata.float_array_value (); - - OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - a[idx] = xcdata(i,j,0); - a[idx+1] = xcdata(i,j,1); - a[idx+2] = xcdata(i,j,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else if (cdata.is_uint8_type ()) - { - const uint8NDArray xcdata = cdata.uint8_array_value (); - - OCTAVE_LOCAL_BUFFER (GLubyte, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - a[idx] = xcdata(i,j,0); - a[idx+1] = xcdata(i,j,1); - a[idx+2] = xcdata(i,j,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else if (cdata.is_uint16_type ()) - { - const uint16NDArray xcdata = cdata.uint16_array_value (); - - OCTAVE_LOCAL_BUFFER (GLushort, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - a[idx] = xcdata(i,j,0); - a[idx+1] = xcdata(i,j,1); - a[idx+2] = xcdata(i,j,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else - warning ("opengl_renderer: invalid image data type (expected double, single, uint8, or uint16)"); - } - else - warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); - - glPixelZoom (1, 1); + glViewport (0, 0, w, h); #else - octave_unused_parameter (props); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (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 -} - -void -opengl_renderer::set_viewport (int w, int h) -{ + } + + void + opengl_renderer::draw_pixels (int width, int height, const float *data) + { #if defined (HAVE_OPENGL) - glViewport (0, 0, w, h); + glDrawPixels (width, height, GL_RGB, GL_FLOAT, data); #else - octave_unused_parameter (w); - octave_unused_parameter (h); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (width); + octave_unused_parameter (height); + octave_unused_parameter (data); + + // 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_pixels (int width, int height, const float *data) -{ + } + + void + opengl_renderer::draw_pixels (int width, int height, const uint8_t *data) + { #if defined (HAVE_OPENGL) - glDrawPixels (width, height, GL_RGB, GL_FLOAT, data); - -#else - - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (data); - - // 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_pixels (int width, int height, const uint8_t *data) -{ -#if defined (HAVE_OPENGL) - - glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_BYTE, data); + glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_BYTE, data); #else - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (data); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (width); + octave_unused_parameter (height); + octave_unused_parameter (data); + + // 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_pixels (int width, int height, const uint16_t *data) -{ + } + + void + opengl_renderer::draw_pixels (int width, int height, const uint16_t *data) + { #if defined (HAVE_OPENGL) - glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_SHORT, data); + glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_SHORT, data); #else - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (data); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (width); + octave_unused_parameter (height); + octave_unused_parameter (data); + + // 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_color (const Matrix& c) -{ + } + + void + opengl_renderer::set_color (const Matrix& c) + { #if defined (HAVE_OPENGL) - glColor3dv (c.data ()); - - txt_renderer.set_color (c); + glColor3dv (c.data ()); + + txt_renderer.set_color (c); #else - octave_unused_parameter (c); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (c); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::set_font (const base_properties& props) -{ - 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 ()); -} - -void -opengl_renderer::set_polygon_offset (bool on, float offset) -{ + } + + void + opengl_renderer::set_font (const base_properties& props) + { + 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 ()); + } + + void + opengl_renderer::set_polygon_offset (bool on, float offset) + { #if defined (HAVE_OPENGL) - if (on) - { - glEnable (GL_POLYGON_OFFSET_FILL); - glEnable (GL_POLYGON_OFFSET_LINE); - glPolygonOffset (offset, offset); - } - else - { - glDisable (GL_POLYGON_OFFSET_FILL); - glDisable (GL_POLYGON_OFFSET_LINE); - } + if (on) + { + glEnable (GL_POLYGON_OFFSET_FILL); + glEnable (GL_POLYGON_OFFSET_LINE); + glPolygonOffset (offset, offset); + } + else + { + glDisable (GL_POLYGON_OFFSET_FILL); + glDisable (GL_POLYGON_OFFSET_LINE); + } #else - octave_unused_parameter (on); - octave_unused_parameter (offset); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (on); + octave_unused_parameter (offset); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::set_linewidth (float w) -{ + } + + void + opengl_renderer::set_linewidth (float w) + { #if defined (HAVE_OPENGL) - glLineWidth (w); + glLineWidth (w); #else - octave_unused_parameter (w); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (w); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::set_linestyle (const std::string& s, bool use_stipple) -{ + } + + void + opengl_renderer::set_linestyle (const std::string& s, bool use_stipple) + { #if defined (HAVE_OPENGL) - bool solid = false; - - if (s == "-") - { - glLineStipple (1, static_cast<unsigned short> (0xFFFF)); - solid = true; - } - else if (s == ":") - glLineStipple (1, static_cast<unsigned short> (0x8888)); - else if (s == "--") - glLineStipple (1, static_cast<unsigned short> (0xF0F0)); - else if (s == "-.") - glLineStipple (1, static_cast<unsigned short> (0x020F)); - else - glLineStipple (1, static_cast<unsigned short> (0x0000)); - - if (solid && ! use_stipple) - glDisable (GL_LINE_STIPPLE); - else - glEnable (GL_LINE_STIPPLE); + bool solid = false; + + if (s == "-") + { + glLineStipple (1, static_cast<unsigned short> (0xFFFF)); + solid = true; + } + else if (s == ":") + glLineStipple (1, static_cast<unsigned short> (0x8888)); + else if (s == "--") + glLineStipple (1, static_cast<unsigned short> (0xF0F0)); + else if (s == "-.") + glLineStipple (1, static_cast<unsigned short> (0x020F)); + else + glLineStipple (1, static_cast<unsigned short> (0x0000)); + + if (solid && ! use_stipple) + glDisable (GL_LINE_STIPPLE); + else + glEnable (GL_LINE_STIPPLE); #else - octave_unused_parameter (s); - octave_unused_parameter (use_stipple); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (s); + octave_unused_parameter (use_stipple); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2, - double z1, double z2) -{ + } + + void + opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2, + double z1, double z2) + { #if defined (HAVE_OPENGL) - double dx = (x2-x1); - double dy = (y2-y1); - double dz = (z2-z1); - - x1 -= 0.001*dx; x2 += 0.001*dx; - y1 -= 0.001*dy; y2 += 0.001*dy; - z1 -= 0.001*dz; z2 += 0.001*dz; - - ColumnVector p (4, 0.0); - - p(0) = -1; p(3) = x2; - glClipPlane (GL_CLIP_PLANE0, p.data ()); - p(0) = 1; p(3) = -x1; - glClipPlane (GL_CLIP_PLANE1, p.data ()); - p(0) = 0; p(1) = -1; p(3) = y2; - glClipPlane (GL_CLIP_PLANE2, p.data ()); - p(1) = 1; p(3) = -y1; - glClipPlane (GL_CLIP_PLANE3, p.data ()); - p(1) = 0; p(2) = -1; p(3) = z2; - glClipPlane (GL_CLIP_PLANE4, p.data ()); - p(2) = 1; p(3) = -z1; - glClipPlane (GL_CLIP_PLANE5, p.data ()); - - xmin = x1; xmax = x2; - ymin = y1; ymax = y2; - zmin = z1; zmax = z2; + double dx = (x2-x1); + double dy = (y2-y1); + double dz = (z2-z1); + + x1 -= 0.001*dx; x2 += 0.001*dx; + y1 -= 0.001*dy; y2 += 0.001*dy; + z1 -= 0.001*dz; z2 += 0.001*dz; + + ColumnVector p (4, 0.0); + + p(0) = -1; p(3) = x2; + glClipPlane (GL_CLIP_PLANE0, p.data ()); + p(0) = 1; p(3) = -x1; + glClipPlane (GL_CLIP_PLANE1, p.data ()); + p(0) = 0; p(1) = -1; p(3) = y2; + glClipPlane (GL_CLIP_PLANE2, p.data ()); + p(1) = 1; p(3) = -y1; + glClipPlane (GL_CLIP_PLANE3, p.data ()); + p(1) = 0; p(2) = -1; p(3) = z2; + glClipPlane (GL_CLIP_PLANE4, p.data ()); + p(2) = 1; p(3) = -z1; + glClipPlane (GL_CLIP_PLANE5, p.data ()); + + xmin = x1; xmax = x2; + ymin = y1; ymax = y2; + zmin = z1; zmax = z2; #else - octave_unused_parameter (x1); - octave_unused_parameter (x2); - octave_unused_parameter (y1); - octave_unused_parameter (y2); - octave_unused_parameter (z1); - octave_unused_parameter (z2); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (x1); + octave_unused_parameter (x2); + octave_unused_parameter (y1); + octave_unused_parameter (y2); + octave_unused_parameter (z1); + octave_unused_parameter (z2); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::set_clipping (bool enable) -{ + } + + void + opengl_renderer::set_clipping (bool enable) + { #if defined (HAVE_OPENGL) - bool has_clipping = (glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE); - - if (enable != has_clipping) - { - if (enable) - for (int i = 0; i < 6; i++) - glEnable (GL_CLIP_PLANE0+i); - else - for (int i = 0; i < 6; i++) - glDisable (GL_CLIP_PLANE0+i); - } + bool has_clipping = (glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE); + + if (enable != has_clipping) + { + if (enable) + for (int i = 0; i < 6; i++) + glEnable (GL_CLIP_PLANE0+i); + else + for (int i = 0; i < 6; i++) + glDisable (GL_CLIP_PLANE0+i); + } #else - octave_unused_parameter (enable); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (enable); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::init_marker (const std::string& m, double size, float width) -{ + } + + void + opengl_renderer::init_marker (const std::string& m, double size, float width) + { #if defined (HAVE_OPENGL) # if defined (HAVE_FRAMEWORK_OPENGL) - GLint vw[4]; + GLint vw[4]; # else - int vw[4]; + int vw[4]; # endif - glGetIntegerv (GL_VIEWPORT, vw); - - glMatrixMode (GL_PROJECTION); - glPushMatrix (); - glLoadIdentity (); - glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2); - glMatrixMode (GL_MODELVIEW); - glPushMatrix (); - - set_clipping (false); - set_linewidth (width); - - marker_id = make_marker_list (m, size, false); - filled_marker_id = make_marker_list (m, size, true); + glGetIntegerv (GL_VIEWPORT, vw); + + glMatrixMode (GL_PROJECTION); + glPushMatrix (); + glLoadIdentity (); + glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2); + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + + set_clipping (false); + set_linewidth (width); + + marker_id = make_marker_list (m, size, false); + filled_marker_id = make_marker_list (m, size, true); #else - octave_unused_parameter (m); - octave_unused_parameter (size); - octave_unused_parameter (width); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (m); + octave_unused_parameter (size); + octave_unused_parameter (width); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::end_marker (void) -{ + } + + void + opengl_renderer::end_marker (void) + { #if defined (HAVE_OPENGL) - glDeleteLists (marker_id, 1); - glDeleteLists (filled_marker_id, 1); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix (); - glMatrixMode (GL_PROJECTION); - glPopMatrix (); - set_linewidth (0.5f); + glDeleteLists (marker_id, 1); + glDeleteLists (filled_marker_id, 1); + + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + glMatrixMode (GL_PROJECTION); + glPopMatrix (); + set_linewidth (0.5f); #else - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::draw_marker (double x, double y, double z, - const Matrix& lc, const Matrix& fc) -{ + } + + void + opengl_renderer::draw_marker (double x, double y, double z, + const Matrix& lc, const Matrix& fc) + { #if defined (HAVE_OPENGL) - ColumnVector tmp = xform.transform (x, y, z, false); - - glLoadIdentity (); - glTranslated (tmp(0), tmp(1), -tmp(2)); - - if (filled_marker_id > 0 && fc.numel () > 0) - { - glColor3dv (fc.data ()); - set_polygon_offset (true, -1.0); - glCallList (filled_marker_id); - if (lc.numel () > 0) - { - glColor3dv (lc.data ()); - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - glEdgeFlag (GL_TRUE); - set_polygon_offset (true, -2.0); - glCallList (filled_marker_id); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - } - set_polygon_offset (false); - } - else if (marker_id > 0 && lc.numel () > 0) - { - glColor3dv (lc.data ()); - glCallList (marker_id); - } + ColumnVector tmp = xform.transform (x, y, z, false); + + glLoadIdentity (); + glTranslated (tmp(0), tmp(1), -tmp(2)); + + if (filled_marker_id > 0 && fc.numel () > 0) + { + glColor3dv (fc.data ()); + set_polygon_offset (true, -1.0); + glCallList (filled_marker_id); + if (lc.numel () > 0) + { + glColor3dv (lc.data ()); + glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + glEdgeFlag (GL_TRUE); + set_polygon_offset (true, -2.0); + glCallList (filled_marker_id); + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + } + set_polygon_offset (false); + } + else if (marker_id > 0 && lc.numel () > 0) + { + glColor3dv (lc.data ()); + glCallList (marker_id); + } #else - octave_unused_parameter (x); - octave_unused_parameter (y); - octave_unused_parameter (z); - octave_unused_parameter (lc); - octave_unused_parameter (fc); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (x); + octave_unused_parameter (y); + octave_unused_parameter (z); + octave_unused_parameter (lc); + octave_unused_parameter (fc); + + // 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_normal (int bfl_mode, const NDArray& n, int j, int i) -{ + } + + void + opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i) + { #if defined (HAVE_OPENGL) - double x = n(j,i,0); - double y = n(j,i,1); - double z = n(j,i,2); - - double d = sqrt (x*x + y*y + z*z); - - double dir = 1.0; - - if (bfl_mode > 0) - dir = ((x * view_vector(0) + y * view_vector(1) + z * view_vector(2) < 0) - ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); - - glNormal3d (dir*x/d, dir*y/d, dir*z/d); + double x = n(j,i,0); + double y = n(j,i,1); + double z = n(j,i,2); + + double d = sqrt (x*x + y*y + z*z); + + double dir = 1.0; + + if (bfl_mode > 0) + dir = ((x * view_vector(0) + y * view_vector(1) + z * view_vector(2) < 0) + ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); + + glNormal3d (dir*x/d, dir*y/d, dir*z/d); #else - octave_unused_parameter (bfl_mode); - octave_unused_parameter (n); - octave_unused_parameter (j); - octave_unused_parameter (i); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (bfl_mode); + octave_unused_parameter (n); + octave_unused_parameter (j); + octave_unused_parameter (i); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -unsigned int -opengl_renderer::make_marker_list (const std::string& marker, double size, - bool filled) const -{ + } + + 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 == '.')) - return 0; - - unsigned int ID = glGenLists (1); - double sz = size * toolkit.get_screen_resolution () / 72.0; - - // constants for the * marker - const double sqrt2d4 = 0.35355339059327; - double tt = sz*sqrt2d4; - - glNewList (ID, GL_COMPILE); - - switch (marker[0]) - { - case '+': - glBegin (GL_LINES); - glVertex2d (-sz/2, 0); - glVertex2d (sz/2, 0); - glVertex2d (0, -sz/2); - glVertex2d (0, sz/2); - glEnd (); - break; - case 'x': - glBegin (GL_LINES); - glVertex2d (-sz/2, -sz/2); - glVertex2d (sz/2, sz/2); - glVertex2d (-sz/2, sz/2); - glVertex2d (sz/2, -sz/2); - glEnd (); - break; - case '*': - glBegin (GL_LINES); - glVertex2d (-sz/2, 0); - glVertex2d (sz/2, 0); - glVertex2d (0, -sz/2); - glVertex2d (0, sz/2); - glVertex2d (-tt, -tt); - glVertex2d (+tt, +tt); - glVertex2d (-tt, +tt); - glVertex2d (+tt, -tt); - 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; - - glBegin (GL_POLYGON); - for (double ang = 0; ang < 2*M_PI; ang += ang_step) - glVertex2d (sz/6*cos (ang), sz/6*sin (ang)); - glEnd (); - } - break; - case 's': - glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - glVertex2d (-sz/2, -sz/2); - glVertex2d (-sz/2, sz/2); - glVertex2d (sz/2, sz/2); - glVertex2d (sz/2, -sz/2); - glEnd (); - break; - case 'o': + char c = marker[0]; + + if (filled && (c == '+' || c == 'x' || c == '*' || c == '.')) + return 0; + + unsigned int ID = glGenLists (1); + double sz = size * toolkit.get_screen_resolution () / 72.0; + + // constants for the * marker + const double sqrt2d4 = 0.35355339059327; + double tt = sz*sqrt2d4; + + glNewList (ID, GL_COMPILE); + + switch (marker[0]) { - 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; - + case '+': + glBegin (GL_LINES); + glVertex2d (-sz/2, 0); + glVertex2d (sz/2, 0); + glVertex2d (0, -sz/2); + glVertex2d (0, sz/2); + glEnd (); + break; + case 'x': + glBegin (GL_LINES); + glVertex2d (-sz/2, -sz/2); + glVertex2d (sz/2, sz/2); + glVertex2d (-sz/2, sz/2); + glVertex2d (sz/2, -sz/2); + glEnd (); + break; + case '*': + glBegin (GL_LINES); + glVertex2d (-sz/2, 0); + glVertex2d (sz/2, 0); + glVertex2d (0, -sz/2); + glVertex2d (0, sz/2); + glVertex2d (-tt, -tt); + glVertex2d (+tt, +tt); + glVertex2d (-tt, +tt); + glVertex2d (+tt, -tt); + 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; + + glBegin (GL_POLYGON); + for (double ang = 0; ang < 2*M_PI; ang += ang_step) + glVertex2d (sz/6*cos (ang), sz/6*sin (ang)); + glEnd (); + } + break; + case 's': glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - for (double ang = 0; ang < 2*M_PI; ang += ang_step) - glVertex2d (sz/2*cos (ang), sz/2*sin (ang)); + glVertex2d (-sz/2, -sz/2); + glVertex2d (-sz/2, sz/2); + glVertex2d (sz/2, sz/2); + glVertex2d (sz/2, -sz/2); + 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; + + glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + for (double ang = 0; ang < 2*M_PI; ang += ang_step) + glVertex2d (sz/2*cos (ang), sz/2*sin (ang)); + glEnd (); + } + break; + case 'd': + glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + glVertex2d (0, -sz/2); + glVertex2d (sz/2, 0); + glVertex2d (0, sz/2); + glVertex2d (-sz/2, 0); + glEnd (); + break; + case 'v': + glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + glVertex2d (0, sz/2); + glVertex2d (sz/2, -sz/2); + glVertex2d (-sz/2, -sz/2); + glEnd (); + break; + case '^': + glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + glVertex2d (0, -sz/2); + glVertex2d (-sz/2, sz/2); + glVertex2d (sz/2, sz/2); glEnd (); + break; + case '>': + glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + glVertex2d (sz/2, 0); + glVertex2d (-sz/2, sz/2); + glVertex2d (-sz/2, -sz/2); + glEnd (); + break; + case '<': + glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); + glVertex2d (-sz/2, 0); + glVertex2d (sz/2, -sz/2); + glVertex2d (sz/2, sz/2); + glEnd (); + break; + case 'p': + { + double ang, r, dr; + dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02; + + 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)); + glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); + } + glEnd (); + } + break; + case 'h': + { + double ang, r, dr; + dr = 1.0 - 0.5/sin (M_PI/3)*1.02; + + 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)); + glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); + } + glEnd (); + } + break; + default: + warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ()); + break; } - break; - case 'd': - glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - glVertex2d (0, -sz/2); - glVertex2d (sz/2, 0); - glVertex2d (0, sz/2); - glVertex2d (-sz/2, 0); - glEnd (); - break; - case 'v': - glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - glVertex2d (0, sz/2); - glVertex2d (sz/2, -sz/2); - glVertex2d (-sz/2, -sz/2); - glEnd (); - break; - case '^': - glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - glVertex2d (0, -sz/2); - glVertex2d (-sz/2, sz/2); - glVertex2d (sz/2, sz/2); - glEnd (); - break; - case '>': - glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - glVertex2d (sz/2, 0); - glVertex2d (-sz/2, sz/2); - glVertex2d (-sz/2, -sz/2); - glEnd (); - break; - case '<': - glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); - glVertex2d (-sz/2, 0); - glVertex2d (sz/2, -sz/2); - glVertex2d (sz/2, sz/2); - glEnd (); - break; - case 'p': - { - double ang, r, dr; - dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02; - - 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)); - glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); - } - glEnd (); - } - break; - case 'h': - { - double ang, r, dr; - dr = 1.0 - 0.5/sin (M_PI/3)*1.02; - - 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)); - glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); - } - glEnd (); - } - break; - default: - warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ()); - break; - } - - glEndList (); - - return ID; + + glEndList (); + + return ID; #else - octave_unused_parameter (marker); - octave_unused_parameter (size); - octave_unused_parameter (filled); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (marker); + octave_unused_parameter (size); + octave_unused_parameter (filled); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif -} - -void -opengl_renderer::text_to_pixels (const std::string& txt, - uint8NDArray& pixels, - Matrix& bbox, - int halign, int valign, double rotation) -{ - txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign, - rotation, 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) -{ - txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign, - rotation, interpreter); -} - -Matrix -opengl_renderer::render_text (const std::string& txt, - double x, double y, double z, - int halign, int valign, double rotation) -{ + } + + void + opengl_renderer::text_to_pixels (const std::string& txt, + uint8NDArray& pixels, + Matrix& bbox, + int halign, int valign, double rotation) + { + txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign, + rotation, 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) + { + txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign, + rotation, 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 ()) + Matrix bbox (1, 4, 0.0); + + if (txt.empty ()) + return bbox; + + if (txt_renderer.ok ()) + { + uint8NDArray pixels; + text_to_pixels (txt, pixels, bbox, halign, valign, rotation); + + bool blend = glIsEnabled (GL_BLEND); + + glEnable (GL_BLEND); + glEnable (GL_ALPHA_TEST); + glRasterPos3d (x, y, z); + glBitmap(0, 0, 0, 0, bbox(0), bbox(1), 0); + glDrawPixels (bbox(2), bbox(3), + GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ()); + glDisable (GL_ALPHA_TEST); + + if (! blend) + glDisable (GL_BLEND); + } + return bbox; - if (txt_renderer.ok ()) - { - uint8NDArray pixels; - text_to_pixels (txt, pixels, bbox, halign, valign, rotation); - - bool blend = glIsEnabled (GL_BLEND); - - glEnable (GL_BLEND); - glEnable (GL_ALPHA_TEST); - glRasterPos3d (x, y, z); - glBitmap(0, 0, 0, 0, bbox(0), bbox(1), 0); - glDrawPixels (bbox(2), bbox(3), - GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ()); - glDisable (GL_ALPHA_TEST); - - if (! blend) - glDisable (GL_BLEND); - } - - return bbox; - #else - octave_unused_parameter (txt); - octave_unused_parameter (x); - octave_unused_parameter (y); - octave_unused_parameter (z); - octave_unused_parameter (halign); - octave_unused_parameter (valign); - octave_unused_parameter (rotation); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); + octave_unused_parameter (txt); + octave_unused_parameter (x); + octave_unused_parameter (y); + octave_unused_parameter (z); + octave_unused_parameter (halign); + octave_unused_parameter (valign); + octave_unused_parameter (rotation); + + // This shouldn't happen because construction of opengl_renderer + // objects is supposed to be impossible if OpenGL is not available. + + panic_impossible (); #endif + } } - -}
--- a/libinterp/corefcn/gl-render.h Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/gl-render.h Wed Aug 17 11:43:27 2016 -0400 @@ -30,185 +30,183 @@ namespace octave { - -class -OCTINTERP_API -opengl_renderer -{ -public: - - opengl_renderer (void); - - virtual ~opengl_renderer (void) { } - - virtual void draw (const graphics_object& go, bool toplevel = true); - - virtual void draw (const Matrix& hlist, bool toplevel = false) + class + OCTINTERP_API + opengl_renderer { - int len = hlist.numel (); + public: + + opengl_renderer (void); + + virtual ~opengl_renderer (void) { } - for (int i = len-1; i >= 0; i--) - { - graphics_object obj = gh_manager::get_object (hlist(i)); + virtual void draw (const graphics_object& go, bool toplevel = true); + + virtual void draw (const Matrix& hlist, bool toplevel = false) + { + int len = hlist.numel (); - if (obj) - draw (obj, toplevel); - } - } + for (int i = len-1; i >= 0; i--) + { + graphics_object obj = gh_manager::get_object (hlist(i)); - virtual void set_viewport (int w, int h); - virtual graphics_xform get_transform (void) const { return xform; } + if (obj) + draw (obj, toplevel); + } + } - virtual void finish (void); + virtual void set_viewport (int w, int h); + virtual graphics_xform get_transform (void) const { return xform; } + + virtual void finish (void); -protected: - virtual void draw_figure (const figure::properties& props); - virtual void draw_axes (const axes::properties& props); - virtual void draw_line (const line::properties& props); - virtual void draw_surface (const surface::properties& props); - virtual void draw_patch (const patch::properties& props); - virtual void draw_light (const light::properties& props); - virtual void draw_hggroup (const hggroup::properties& props); - virtual void draw_text (const text::properties& props); - virtual void draw_image (const image::properties& props); - virtual void draw_uipanel (const uipanel::properties& props, - const graphics_object& go); - virtual void draw_uibuttongroup (const uibuttongroup::properties& props, - const graphics_object& go); - virtual void init_gl_context (bool enhanced, const Matrix& backgroundColor); - virtual void setup_opengl_transformation (const axes::properties& props); + protected: + virtual void draw_figure (const figure::properties& props); + virtual void draw_axes (const axes::properties& props); + virtual void draw_line (const line::properties& props); + virtual void draw_surface (const surface::properties& props); + virtual void draw_patch (const patch::properties& props); + virtual void draw_light (const light::properties& props); + virtual void draw_hggroup (const hggroup::properties& props); + virtual void draw_text (const text::properties& props); + virtual void draw_image (const image::properties& props); + virtual void draw_uipanel (const uipanel::properties& props, + const graphics_object& go); + virtual void draw_uibuttongroup (const uibuttongroup::properties& props, + const graphics_object& go); + virtual void init_gl_context (bool enhanced, const Matrix& backgroundColor); + virtual void setup_opengl_transformation (const axes::properties& props); - virtual void set_color (const Matrix& c); - virtual void set_polygon_offset (bool on, float offset = 0.0f); - virtual void set_linewidth (float w); - virtual void set_linestyle (const std::string& s, bool stipple = false); - virtual void set_clipbox (double x1, double x2, double y1, double y2, - double z1, double z2); - virtual void set_clipping (bool on); - virtual void set_font (const base_properties& props); - virtual void set_interpreter (const caseless_str& interp) - { - interpreter = interp; - } + virtual void set_color (const Matrix& c); + virtual void set_polygon_offset (bool on, float offset = 0.0f); + virtual void set_linewidth (float w); + virtual void set_linestyle (const std::string& s, bool stipple = false); + virtual void set_clipbox (double x1, double x2, double y1, double y2, + double z1, double z2); + virtual void set_clipping (bool on); + virtual void set_font (const base_properties& props); + virtual void set_interpreter (const caseless_str& interp) + { + interpreter = interp; + } - virtual void init_marker (const std::string& m, double size, float width); - virtual void end_marker (void); - virtual void draw_marker (double x, double y, double z, - const Matrix& lc, const Matrix& fc); + virtual void init_marker (const std::string& m, double size, float width); + virtual void end_marker (void); + virtual void draw_marker (double x, double y, double z, + const Matrix& lc, const Matrix& fc); - virtual void text_to_pixels (const std::string& txt, - uint8NDArray& pixels, - Matrix& bbox, - int halign = 0, int valign = 0, - double rotation = 0.0); + virtual void text_to_pixels (const std::string& txt, + uint8NDArray& pixels, + Matrix& bbox, + int halign = 0, int valign = 0, + double rotation = 0.0); - virtual void text_to_strlist (const std::string& txt, - std::list<text_renderer::string>& lst, - Matrix& bbox, - int halign = 0, int valign = 0, - double rotation = 0.0); + virtual void text_to_strlist (const std::string& txt, + std::list<text_renderer::string>& lst, + Matrix& bbox, + int halign = 0, int valign = 0, + double rotation = 0.0); - virtual Matrix render_text (const std::string& txt, - double x, double y, double z, - int halign, int valign, double rotation = 0.0); + virtual Matrix render_text (const std::string& txt, + double x, double y, double z, + int halign, int valign, double rotation = 0.0); - virtual void draw_pixels (int w, int h, const float *data); - virtual void draw_pixels (int w, int h, const uint8_t *data); - virtual void draw_pixels (int w, int h, const uint16_t *data); + virtual void draw_pixels (int w, int h, const float *data); + virtual void draw_pixels (int w, int h, const uint8_t *data); + virtual void draw_pixels (int w, int h, const uint16_t *data); - virtual void render_grid (const std::string& gridstyle, const Matrix& ticks, - double lim1, double lim2, - double p1, double p1N, double p2, double p2N, - int xyz, bool is_3D); + virtual void render_grid (const std::string& gridstyle, const Matrix& ticks, + double lim1, double lim2, + double p1, double p1N, double p2, double p2N, + int xyz, bool is_3D); - virtual void render_tickmarks (const Matrix& ticks, double lim1, double lim2, - double p1, double p1N, double p2, double p2N, - double dx, double dy, double dz, - int xyz, bool doubleside); + virtual void render_tickmarks (const Matrix& ticks, double lim1, double lim2, + double p1, double p1N, double p2, double p2N, + double dx, double dy, double dz, + int xyz, bool doubleside); - virtual void render_ticktexts (const Matrix& ticks, - const string_vector& ticklabels, - double lim1, double lim2, - double p1, double p2, - int xyz, int ha, int va, - int& wmax, int& hmax); + virtual void render_ticktexts (const Matrix& ticks, + const string_vector& ticklabels, + double lim1, double lim2, + double p1, double p2, + int xyz, int ha, int va, + int& wmax, int& hmax); -private: + private: - // No copying! - - opengl_renderer (const opengl_renderer&); + // No copying! - opengl_renderer& operator = (const opengl_renderer&); + opengl_renderer (const opengl_renderer&); + + opengl_renderer& operator = (const opengl_renderer&); - bool is_nan_or_inf (double x, double y, double z) const - { - return (octave::math::isnan (x) || octave::math::isnan (y) || octave::math::isnan (z) - || octave::math::isinf (x) || octave::math::isinf (y) || octave::math::isinf (z)); - } + bool is_nan_or_inf (double x, double y, double z) const + { + return (octave::math::isnan (x) || octave::math::isnan (y) || octave::math::isnan (z) + || octave::math::isinf (x) || octave::math::isinf (y) || octave::math::isinf (z)); + } - octave_uint8 clip_code (double x, double y, double z) const - { - return ((x < xmin ? 1 : 0) - | (x > xmax ? 1 : 0) << 1 - | (y < ymin ? 1 : 0) << 2 - | (y > ymax ? 1 : 0) << 3 - | (z < zmin ? 1 : 0) << 4 - | (z > zmax ? 1 : 0) << 5 - | (is_nan_or_inf (x, y, z) ? 0 : 1) << 6); - } + octave_uint8 clip_code (double x, double y, double z) const + { + return ((x < xmin ? 1 : 0) + | (x > xmax ? 1 : 0) << 1 + | (y < ymin ? 1 : 0) << 2 + | (y > ymax ? 1 : 0) << 3 + | (z < zmin ? 1 : 0) << 4 + | (z > zmax ? 1 : 0) << 5 + | (is_nan_or_inf (x, y, z) ? 0 : 1) << 6); + } - void set_normal (int bfl_mode, const NDArray& n, int j, int i); - - unsigned int make_marker_list (const std::string& m, double size, - bool filled) const; + void set_normal (int bfl_mode, const NDArray& n, int j, int i); - void draw_axes_planes (const axes::properties& props); - void draw_axes_boxes (const axes::properties& props); + unsigned int make_marker_list (const std::string& m, double size, + bool filled) const; - void draw_axes_x_grid (const axes::properties& props); - void draw_axes_y_grid (const axes::properties& props); - void draw_axes_z_grid (const axes::properties& props); + void draw_axes_planes (const axes::properties& props); + void draw_axes_boxes (const axes::properties& props); - void draw_axes_children (const axes::properties& props); - - void draw_all_lights (const base_properties& props, std::list<graphics_object>& obj_list); + void draw_axes_x_grid (const axes::properties& props); + void draw_axes_y_grid (const axes::properties& props); + void draw_axes_z_grid (const axes::properties& props); -private: - // The graphics toolkit associated with the figure being rendered. - graphics_toolkit toolkit; + void draw_axes_children (const axes::properties& props); + + void draw_all_lights (const base_properties& props, std::list<graphics_object>& obj_list); - // axes transformation data - graphics_xform xform; + private: + // The graphics toolkit associated with the figure being rendered. + graphics_toolkit toolkit; - // axis limits in model scaled coordinate - double xmin, xmax; - double ymin, ymax; - double zmin, zmax; + // axes transformation data + graphics_xform xform; - // Z projection limits in windows coordinate - double xZ1, xZ2; - - // call lists identifiers for markers - unsigned int marker_id, filled_marker_id; + // axis limits in model scaled coordinate + double xmin, xmax; + double ymin, ymax; + double zmin, zmax; - // camera information for primitive sorting and lighting - ColumnVector camera_pos, camera_dir, view_vector; + // Z projection limits in windows coordinate + double xZ1, xZ2; - // interpreter to be used by text_to_pixels - caseless_str interpreter; + // call lists identifiers for markers + unsigned int marker_id, filled_marker_id; + + // camera information for primitive sorting and lighting + ColumnVector camera_pos, camera_dir, view_vector; - text_renderer txt_renderer; + // interpreter to be used by text_to_pixels + caseless_str interpreter; + + text_renderer txt_renderer; - // light object present and visible - int num_lights; - unsigned int current_light; - int max_lights; + // light object present and visible + int num_lights; + unsigned int current_light; + int max_lights; -private: - class patch_tesselator; -}; - + private: + class patch_tesselator; + }; } #endif
--- a/libinterp/corefcn/gl2ps-print.cc Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/gl2ps-print.cc Wed Aug 17 11:43:27 2016 -0400 @@ -50,314 +50,312 @@ namespace octave { + class + OCTINTERP_API + gl2ps_renderer : public opengl_renderer + { + public: -class -OCTINTERP_API -gl2ps_renderer : public opengl_renderer -{ -public: + gl2ps_renderer (FILE *_fp, const std::string& _term) + : octave::opengl_renderer () , fp (_fp), term (_term), fontsize (), + fontname (), buffer_overflow (false) + { } + + ~gl2ps_renderer (void) { } + + // FIXME: should we import the functions from the base class and + // overload them here, or should we use a different name so we don't + // have to do this? Without the using declaration or a name change, + // the base class functions will be hidden. That may be OK, but it + // can also cause some confusion. + using octave::opengl_renderer::draw; + + void draw (const graphics_object& go, const std::string& print_cmd); + + protected: + + Matrix render_text (const std::string& txt, + double x, double y, double z, + int halign, int valign, double rotation = 0.0); - gl2ps_renderer (FILE *_fp, const std::string& _term) - : octave::opengl_renderer () , fp (_fp), term (_term), fontsize (), - fontname (), buffer_overflow (false) - { } + void set_font (const base_properties& props); + + void draw_axes (const axes::properties& props) + { + // Initialize a sorting tree (viewport) in gl2ps for each axes + GLint vp[4]; + glGetIntegerv (GL_VIEWPORT, vp); + gl2psBeginViewport (vp); + + // Draw and finish () or there may primitives missing in the gl2ps output. + octave::opengl_renderer::draw_axes (props); + finish (); - ~gl2ps_renderer (void) { } + // Finalize viewport + GLint state = gl2psEndViewport (); + if (state == GL2PS_NO_FEEDBACK) + warning ("gl2ps_renderer::draw_axes: empty feedback buffer and/or nothing else to print"); + else if (state == GL2PS_ERROR) + error ("gl2ps_renderer::draw_axes: gl2psEndPage returned GL2PS_ERROR"); + + buffer_overflow |= (state == GL2PS_OVERFLOW); + + // Don't draw background for subsequent viewports (legends, subplots, etc.) + GLint opts; + gl2psGetOptions (&opts); + opts &= ~GL2PS_DRAW_BACKGROUND; + gl2psSetOptions (opts); + } + + void draw_text (const text::properties& props); - // FIXME: should we import the functions from the base class and - // overload them here, or should we use a different name so we don't - // have to do this? Without the using declaration or a name change, - // the base class functions will be hidden. That may be OK, but it - // can also cause some confusion. - using octave::opengl_renderer::draw; + void draw_pixels (int w, int h, const float *data); + void draw_pixels (int w, int h, const uint8_t *data); + void draw_pixels (int w, int h, const uint16_t *data); + + void set_linestyle (const std::string& s, bool use_stipple = false) + { + octave::opengl_renderer::set_linestyle (s, use_stipple); + + if (s == "-" && ! use_stipple) + gl2psDisable (GL2PS_LINE_STIPPLE); + else + gl2psEnable (GL2PS_LINE_STIPPLE); + } - void draw (const graphics_object& go, const std::string& print_cmd); - -protected: + void set_polygon_offset (bool on, float offset = 0.0f) + { + if (on) + { + octave::opengl_renderer::set_polygon_offset (on, offset); + gl2psEnable (GL2PS_POLYGON_OFFSET_FILL); + } + else + { + gl2psDisable (GL2PS_POLYGON_OFFSET_FILL); + octave::opengl_renderer::set_polygon_offset (on, offset); + } + } - Matrix render_text (const std::string& txt, - double x, double y, double z, - int halign, int valign, double rotation = 0.0); + void set_linewidth (float w) + { + gl2psLineWidth (w); + } - void set_font (const base_properties& props); + private: + + // Use xform to compute the coordinates of the string list + // that have been parsed by freetype + void fix_strlist_position (double x, double y, double z, + Matrix box, double rotation, + std::list<octave::text_renderer::string>& lst); - void draw_axes (const axes::properties& props) + int alignment_to_mode (int ha, int va) const; + FILE *fp; + caseless_str term; + double fontsize; + std::string fontname; + bool buffer_overflow; + }; + + void + gl2ps_renderer::draw (const graphics_object& go, const std::string& print_cmd) { - // Initialize a sorting tree (viewport) in gl2ps for each axes - GLint vp[4]; - glGetIntegerv (GL_VIEWPORT, vp); - gl2psBeginViewport (vp); + static bool in_draw = false; + static std::string old_print_cmd; + + if (! in_draw) + { + octave::unwind_protect frame; - // Draw and finish () or there may primitives missing in the gl2ps output. - octave::opengl_renderer::draw_axes (props); - finish (); + frame.protect_var (in_draw); + + in_draw = true; + + GLint gl2ps_term; + if (term.find ("eps") != std::string::npos) + gl2ps_term = GL2PS_EPS; + else if (term.find ("pdf") != std::string::npos) + gl2ps_term = GL2PS_PDF; + else if (term.find ("ps") != std::string::npos) + gl2ps_term = GL2PS_PS; + else if (term.find ("svg") != std::string::npos) + gl2ps_term = GL2PS_SVG; + else if (term.find ("pgf") != std::string::npos) + gl2ps_term = GL2PS_PGF; + else if (term.find ("tex") != std::string::npos) + gl2ps_term = GL2PS_TEX; + else + error ("gl2ps_renderer::draw: Unknown terminal %s", term.c_str ()); + + GLint gl2ps_text = 0; + if (term.find ("notxt") != std::string::npos) + gl2ps_text = GL2PS_NO_TEXT; + + // Default sort order optimizes for 3D plots + GLint gl2ps_sort = GL2PS_BSP_SORT; - // Finalize viewport - GLint state = gl2psEndViewport (); - if (state == GL2PS_NO_FEEDBACK) - warning ("gl2ps_renderer::draw_axes: empty feedback buffer and/or nothing else to print"); - else if (state == GL2PS_ERROR) - error ("gl2ps_renderer::draw_axes: gl2psEndPage returned GL2PS_ERROR"); + // For 2D plots we can use a simpler Z-depth sorting algorithm + if (term.find ("is2D") != std::string::npos) + gl2ps_sort = GL2PS_SIMPLE_SORT; + + // Use a temporary file in case an overflow happens + FILE* tmpf = octave_tmpfile_wrapper (); + + if (! tmpf) + error ("gl2ps_renderer::draw: couldn't open temporary file for printing"); + + GLint buffsize = 2*1024*1024; + buffer_overflow = true; - buffer_overflow |= (state == GL2PS_OVERFLOW); + while (buffer_overflow) + { + buffer_overflow = false; + buffsize *= 2; + std::fseek (tmpf, 0, SEEK_SET); + octave_ftruncate_wrapper (fileno (tmpf), 0); - // Don't draw background for subsequent viewports (legends, subplots, etc.) - GLint opts; - gl2psGetOptions (&opts); - opts &= ~GL2PS_DRAW_BACKGROUND; - gl2psSetOptions (opts); - } + // For LaTeX output the fltk print process uses 2 drawnow() commands. + // The first one is for the pdf/ps/eps graph to be included. The + // print_cmd is saved as old_print_cmd. Then the second drawnow() + // outputs the tex-file and the graphic filename to be included is + // extracted from old_print_cmd. + + std::string include_graph; + + size_t found_redirect = old_print_cmd.find (">"); - void draw_text (const text::properties& props); + if (found_redirect != std::string::npos) + include_graph = old_print_cmd.substr (found_redirect + 1); + else + include_graph = old_print_cmd; + + size_t n_begin = include_graph.find_first_not_of (" "); - void draw_pixels (int w, int h, const float *data); - void draw_pixels (int w, int h, const uint8_t *data); - void draw_pixels (int w, int h, const uint16_t *data); - - void set_linestyle (const std::string& s, bool use_stipple = false) - { - octave::opengl_renderer::set_linestyle (s, use_stipple); + if (n_begin != std::string::npos) + { + size_t n_end = include_graph.find_last_not_of (" "); + include_graph = include_graph.substr (n_begin, + n_end - n_begin + 1); + } + else + include_graph = "foobar-inc"; - if (s == "-" && ! use_stipple) - gl2psDisable (GL2PS_LINE_STIPPLE); - else - gl2psEnable (GL2PS_LINE_STIPPLE); - } + // GL2PS_SILENT was removed to allow gl2ps to print errors on stderr + GLint ret = gl2psBeginPage ("gl2ps_renderer figure", "Octave", 0, + gl2ps_term, gl2ps_sort, + (GL2PS_NO_BLENDING + | GL2PS_OCCLUSION_CULL + | GL2PS_BEST_ROOT + | gl2ps_text + | GL2PS_DRAW_BACKGROUND + | GL2PS_NO_PS3_SHADING + | GL2PS_USE_CURRENT_VIEWPORT), + GL_RGBA, 0, 0, 0, 0, 0, + buffsize, tmpf, include_graph.c_str ()); + if (ret == GL2PS_ERROR) + { + old_print_cmd.clear (); + error ("gl2ps_renderer::draw: gl2psBeginPage returned GL2PS_ERROR"); + } + + octave::opengl_renderer::draw (go); + + if (! buffer_overflow) + old_print_cmd = print_cmd; - void set_polygon_offset (bool on, float offset = 0.0f) - { - if (on) - { - octave::opengl_renderer::set_polygon_offset (on, offset); - gl2psEnable (GL2PS_POLYGON_OFFSET_FILL); + // Don't check return value of gl2psEndPage, it is not meaningful. + // Errors and warnings are checked after gl2psEndViewport in + // gl2ps_renderer::draw_axes instead. + gl2psEndPage (); + } + + // Copy temporary file to pipe + std::fseek (tmpf, 0, SEEK_SET); + char str[256]; + size_t nread, nwrite; + nread = 1; + while (! feof (tmpf) && nread) + { + nread = std::fread (str, 1, 256, tmpf); + if (nread) + { + nwrite = std::fwrite (str, 1, nread, fp); + if (nwrite != nread) + { + octave::signal_handler (); // Clear SIGPIPE signal + error ("gl2ps_renderer::draw: internal pipe error"); + } + } + } } else - { - gl2psDisable (GL2PS_POLYGON_OFFSET_FILL); - octave::opengl_renderer::set_polygon_offset (on, offset); - } - } - - void set_linewidth (float w) - { - gl2psLineWidth (w); + octave::opengl_renderer::draw (go); } -private: - - // Use xform to compute the coordinates of the string list - // that have been parsed by freetype - void fix_strlist_position (double x, double y, double z, - Matrix box, double rotation, - std::list<octave::text_renderer::string>& lst); - - int alignment_to_mode (int ha, int va) const; - FILE *fp; - caseless_str term; - double fontsize; - std::string fontname; - bool buffer_overflow; -}; - -void -gl2ps_renderer::draw (const graphics_object& go, const std::string& print_cmd) -{ - static bool in_draw = false; - static std::string old_print_cmd; - - if (! in_draw) - { - octave::unwind_protect frame; - - frame.protect_var (in_draw); - - in_draw = true; - - GLint gl2ps_term; - if (term.find ("eps") != std::string::npos) - gl2ps_term = GL2PS_EPS; - else if (term.find ("pdf") != std::string::npos) - gl2ps_term = GL2PS_PDF; - else if (term.find ("ps") != std::string::npos) - gl2ps_term = GL2PS_PS; - else if (term.find ("svg") != std::string::npos) - gl2ps_term = GL2PS_SVG; - else if (term.find ("pgf") != std::string::npos) - gl2ps_term = GL2PS_PGF; - else if (term.find ("tex") != std::string::npos) - gl2ps_term = GL2PS_TEX; - else - error ("gl2ps_renderer::draw: Unknown terminal %s", term.c_str ()); - - GLint gl2ps_text = 0; - if (term.find ("notxt") != std::string::npos) - gl2ps_text = GL2PS_NO_TEXT; - - // Default sort order optimizes for 3D plots - GLint gl2ps_sort = GL2PS_BSP_SORT; - - // For 2D plots we can use a simpler Z-depth sorting algorithm - if (term.find ("is2D") != std::string::npos) - gl2ps_sort = GL2PS_SIMPLE_SORT; - - // Use a temporary file in case an overflow happens - FILE* tmpf = octave_tmpfile_wrapper (); - - if (! tmpf) - error ("gl2ps_renderer::draw: couldn't open temporary file for printing"); - - GLint buffsize = 2*1024*1024; - buffer_overflow = true; - - while (buffer_overflow) - { - buffer_overflow = false; - buffsize *= 2; - std::fseek (tmpf, 0, SEEK_SET); - octave_ftruncate_wrapper (fileno (tmpf), 0); - - // For LaTeX output the fltk print process uses 2 drawnow() commands. - // The first one is for the pdf/ps/eps graph to be included. The - // print_cmd is saved as old_print_cmd. Then the second drawnow() - // outputs the tex-file and the graphic filename to be included is - // extracted from old_print_cmd. - - std::string include_graph; - - size_t found_redirect = old_print_cmd.find (">"); - - if (found_redirect != std::string::npos) - include_graph = old_print_cmd.substr (found_redirect + 1); - else - include_graph = old_print_cmd; - - size_t n_begin = include_graph.find_first_not_of (" "); - - if (n_begin != std::string::npos) - { - size_t n_end = include_graph.find_last_not_of (" "); - include_graph = include_graph.substr (n_begin, - n_end - n_begin + 1); - } - else - include_graph = "foobar-inc"; + int + gl2ps_renderer::alignment_to_mode (int ha, int va) const + { + int gl2psa = GL2PS_TEXT_BL; - // GL2PS_SILENT was removed to allow gl2ps to print errors on stderr - GLint ret = gl2psBeginPage ("gl2ps_renderer figure", "Octave", 0, - gl2ps_term, gl2ps_sort, - (GL2PS_NO_BLENDING - | GL2PS_OCCLUSION_CULL - | GL2PS_BEST_ROOT - | gl2ps_text - | GL2PS_DRAW_BACKGROUND - | GL2PS_NO_PS3_SHADING - | GL2PS_USE_CURRENT_VIEWPORT), - GL_RGBA, 0, 0, 0, 0, 0, - buffsize, tmpf, include_graph.c_str ()); - if (ret == GL2PS_ERROR) - { - old_print_cmd.clear (); - error ("gl2ps_renderer::draw: gl2psBeginPage returned GL2PS_ERROR"); - } - - octave::opengl_renderer::draw (go); - - if (! buffer_overflow) - old_print_cmd = print_cmd; - - // Don't check return value of gl2psEndPage, it is not meaningful. - // Errors and warnings are checked after gl2psEndViewport in - // gl2ps_renderer::draw_axes instead. - gl2psEndPage (); - } - - // Copy temporary file to pipe - std::fseek (tmpf, 0, SEEK_SET); - char str[256]; - size_t nread, nwrite; - nread = 1; - while (! feof (tmpf) && nread) - { - nread = std::fread (str, 1, 256, tmpf); - if (nread) - { - nwrite = std::fwrite (str, 1, nread, fp); - if (nwrite != nread) - { - octave::signal_handler (); // Clear SIGPIPE signal - error ("gl2ps_renderer::draw: internal pipe error"); - } - } - } - } - else - octave::opengl_renderer::draw (go); -} - -int -gl2ps_renderer::alignment_to_mode (int ha, int va) const -{ - int gl2psa = GL2PS_TEXT_BL; + if (ha == 0) + { + if (va == 0 || va == 3) + gl2psa=GL2PS_TEXT_BL; + else if (va == 2) + gl2psa=GL2PS_TEXT_TL; + else if (va == 1) + gl2psa=GL2PS_TEXT_CL; + } + else if (ha == 2) + { + if (va == 0 || va == 3) + gl2psa=GL2PS_TEXT_BR; + else if (va == 2) + gl2psa=GL2PS_TEXT_TR; + else if (va == 1) + gl2psa=GL2PS_TEXT_CR; + } + else if (ha == 1) + { + if (va == 0 || va == 3) + gl2psa=GL2PS_TEXT_B; + else if (va == 2) + gl2psa=GL2PS_TEXT_T; + else if (va == 1) + gl2psa=GL2PS_TEXT_C; + } - if (ha == 0) - { - if (va == 0 || va == 3) - gl2psa=GL2PS_TEXT_BL; - else if (va == 2) - gl2psa=GL2PS_TEXT_TL; - else if (va == 1) - gl2psa=GL2PS_TEXT_CL; - } - else if (ha == 2) - { - if (va == 0 || va == 3) - gl2psa=GL2PS_TEXT_BR; - else if (va == 2) - gl2psa=GL2PS_TEXT_TR; - else if (va == 1) - gl2psa=GL2PS_TEXT_CR; - } - else if (ha == 1) - { - if (va == 0 || va == 3) - gl2psa=GL2PS_TEXT_B; - else if (va == 2) - gl2psa=GL2PS_TEXT_T; - else if (va == 1) - gl2psa=GL2PS_TEXT_C; - } + return gl2psa; + } + + void + gl2ps_renderer::fix_strlist_position (double x, double y, double z, + Matrix box, double rotation, + std::list<octave::text_renderer::string>& lst) + { + for (std::list<octave::text_renderer::string>::iterator p = lst.begin (); + p != lst.end (); p++) + { + // Get pixel coordinates + ColumnVector coord_pix = get_transform ().transform (x, y, z, false); - return gl2psa; -} - -void -gl2ps_renderer::fix_strlist_position (double x, double y, double z, - Matrix box, double rotation, - std::list<octave::text_renderer::string>& lst) -{ - for (std::list<octave::text_renderer::string>::iterator p = lst.begin (); - p != lst.end (); p++) - { - // Get pixel coordinates - ColumnVector coord_pix = get_transform ().transform (x, y, z, false); + // Translate and rotate + double rot = rotation * 4.0 * atan (1.0) / 180; + coord_pix(0) += ((*p).get_x () + box(0))*cos (rot) + - ((*p).get_y () + box(1))*sin (rot); + coord_pix(1) -= ((*p).get_y () + box(1))*cos (rot) + + ((*p).get_x () + box(0))*sin (rot);; - // Translate and rotate - double rot = rotation * 4.0 * atan (1.0) / 180; - coord_pix(0) += ((*p).get_x () + box(0))*cos (rot) - - ((*p).get_y () + box(1))*sin (rot); - coord_pix(1) -= ((*p).get_y () + box(1))*cos (rot) - + ((*p).get_x () + box(0))*sin (rot);; - - // Turn coordinates back into current gl coordinates - ColumnVector coord = - get_transform ().untransform (coord_pix(0), coord_pix(1), - coord_pix(2), false); - (*p).set_x (coord(0)); - (*p).set_y (coord(1)); - (*p).set_z (coord(2)); - } -} - + // Turn coordinates back into current gl coordinates + ColumnVector coord = + get_transform ().untransform (coord_pix(0), coord_pix(1), + coord_pix(2), false); + (*p).set_x (coord(0)); + (*p).set_y (coord(1)); + (*p).set_z (coord(2)); + } + } } static std::string @@ -414,7 +412,7 @@ else if (code == 8730) retval = std::string ("\xd6"); else if (code == 8707) - retval = std::string ("\x24"); + retval = std::string ("\x24"); else if (code == 9830) retval = std::string ("\xa8"); else if (code == 8747) @@ -572,199 +570,198 @@ namespace octave { - -Matrix -gl2ps_renderer::render_text (const std::string& txt, - double x, double y, double z, - int ha, int va, double rotation) -{ - std::string saved_font = fontname; + Matrix + gl2ps_renderer::render_text (const std::string& txt, + double x, double y, double z, + int ha, int va, double rotation) + { + std::string saved_font = fontname; - if (txt.empty ()) - return Matrix (1, 4, 0.0); + if (txt.empty ()) + return Matrix (1, 4, 0.0); - // We have no way to get a bounding box from gl2ps, so we parse the raw - // string using freetype - Matrix bbox; - std::string str = txt; - std::list<octave::text_renderer::string> lst; + // We have no way to get a bounding box from gl2ps, so we parse the raw + // string using freetype + Matrix bbox; + std::string str = txt; + std::list<octave::text_renderer::string> lst; - text_to_strlist (str, lst, bbox, ha, va, rotation); + text_to_strlist (str, lst, bbox, ha, va, rotation); - // When using "tex" or when the string has only one line and no - // special characters, use gl2ps for alignment - if (lst.empty () || term.find ("tex") != std::string::npos - || (lst.size () == 1 && ! lst.front ().get_code ())) - { - std::string name = fontname; - int sz = fontsize; - if (! lst.empty () && term.find ("tex") == std::string::npos) - { - octave::text_renderer::string s = lst.front (); - name = select_font (s.get_name (), s.get_weight () == "bold", - s.get_angle () == "italic"); - set_color (s.get_color ()); - str = s.get_string (); - sz = s.get_size (); - } + // When using "tex" or when the string has only one line and no + // special characters, use gl2ps for alignment + if (lst.empty () || term.find ("tex") != std::string::npos + || (lst.size () == 1 && ! lst.front ().get_code ())) + { + std::string name = fontname; + int sz = fontsize; + if (! lst.empty () && term.find ("tex") == std::string::npos) + { + octave::text_renderer::string s = lst.front (); + name = select_font (s.get_name (), s.get_weight () == "bold", + s.get_angle () == "italic"); + set_color (s.get_color ()); + str = s.get_string (); + sz = s.get_size (); + } - glRasterPos3d (x, y, z); + glRasterPos3d (x, y, z); - // Escape parenthesis until gl2ps does it (see bug ##45301). - if (term.find ("svg") == std::string::npos - && term.find ("tex") == std::string::npos) - { - escape_character ("(", str); - escape_character (")", str); - } + // Escape parenthesis until gl2ps does it (see bug ##45301). + if (term.find ("svg") == std::string::npos + && term.find ("tex") == std::string::npos) + { + escape_character ("(", str); + escape_character (")", str); + } - gl2psTextOpt (str.c_str (), name.c_str (), sz, - alignment_to_mode (ha, va), rotation); - return bbox; - } + gl2psTextOpt (str.c_str (), name.c_str (), sz, + alignment_to_mode (ha, va), rotation); + return bbox; + } - // Translate and rotate coordinates in order to use bottom-left alignment - fix_strlist_position (x, y, z, bbox, rotation, lst); + // Translate and rotate coordinates in order to use bottom-left alignment + fix_strlist_position (x, y, z, bbox, rotation, lst); - for (std::list<octave::text_renderer::string>::iterator p = lst.begin (); - p != lst.end (); p++) - { - fontname = select_font ((*p).get_name (), - (*p).get_weight () == "bold", - (*p).get_angle () == "italic"); - if ((*p).get_code ()) - { - // This is only one character represented by a uint32 (utf8) code. - // We replace it by the corresponding character in the - // "Symbol" font except for svg which has built-in utf8 support. - if (term.find ("svg") == std::string::npos) - { - fontname = "Symbol"; - str = code_to_symbol ((*p).get_code ()); - } - else - { - std::stringstream ss; - ss << (*p).get_code (); - str = "&#" + ss.str () + ";"; - } - } - else - { - str = (*p).get_string (); - // Escape parenthesis until gl2ps does it (see bug ##45301). - if (term.find ("svg") == std::string::npos) - { - escape_character ("(", str); - escape_character (")", str); - } - } + for (std::list<octave::text_renderer::string>::iterator p = lst.begin (); + p != lst.end (); p++) + { + fontname = select_font ((*p).get_name (), + (*p).get_weight () == "bold", + (*p).get_angle () == "italic"); + if ((*p).get_code ()) + { + // This is only one character represented by a uint32 (utf8) code. + // We replace it by the corresponding character in the + // "Symbol" font except for svg which has built-in utf8 support. + if (term.find ("svg") == std::string::npos) + { + fontname = "Symbol"; + str = code_to_symbol ((*p).get_code ()); + } + else + { + std::stringstream ss; + ss << (*p).get_code (); + str = "&#" + ss.str () + ";"; + } + } + else + { + str = (*p).get_string (); + // Escape parenthesis until gl2ps does it (see bug ##45301). + if (term.find ("svg") == std::string::npos) + { + escape_character ("(", str); + escape_character (")", str); + } + } - set_color ((*p).get_color ()); - glRasterPos3d ((*p).get_x (), (*p).get_y (), (*p).get_z ()); - gl2psTextOpt (str.c_str (), fontname.c_str (), (*p).get_size (), - GL2PS_TEXT_BL, rotation); - } + set_color ((*p).get_color ()); + glRasterPos3d ((*p).get_x (), (*p).get_y (), (*p).get_z ()); + gl2psTextOpt (str.c_str (), fontname.c_str (), (*p).get_size (), + GL2PS_TEXT_BL, rotation); + } - fontname = saved_font; - return bbox; -} + fontname = saved_font; + return bbox; + } -void -gl2ps_renderer::set_font (const base_properties& props) -{ - octave::opengl_renderer::set_font (props); + void + gl2ps_renderer::set_font (const base_properties& props) + { + octave::opengl_renderer::set_font (props); - // Set the interpreter so that text_to_pixels can parse strings properly - if (props.has_property ("interpreter")) - set_interpreter (props.get ("interpreter").string_value ()); + // Set the interpreter so that text_to_pixels can parse strings properly + if (props.has_property ("interpreter")) + set_interpreter (props.get ("interpreter").string_value ()); - fontsize = props.get ("fontsize_points").double_value (); + fontsize = props.get ("fontsize_points").double_value (); - caseless_str fn = props.get ("fontname").xtolower ().string_value (); - bool isbold = - (props.get ("fontweight").xtolower ().string_value () == "bold"); - bool isitalic = - (props.get ("fontangle").xtolower ().string_value () == "italic"); + caseless_str fn = props.get ("fontname").xtolower ().string_value (); + bool isbold = + (props.get ("fontweight").xtolower ().string_value () == "bold"); + bool isitalic = + (props.get ("fontangle").xtolower ().string_value () == "italic"); - fontname = select_font (fn, isbold, isitalic); -} + fontname = select_font (fn, isbold, isitalic); + } -void -gl2ps_renderer::draw_pixels (int w, int h, const float *data) -{ - gl2psDrawPixels (w, h, 0, 0, GL_RGB, GL_FLOAT, data); -} + void + gl2ps_renderer::draw_pixels (int w, int h, const float *data) + { + gl2psDrawPixels (w, h, 0, 0, GL_RGB, GL_FLOAT, data); + } -void -gl2ps_renderer::draw_pixels (int w, int h, const uint8_t *data) -{ - // gl2psDrawPixels only supports the GL_FLOAT type. + void + gl2ps_renderer::draw_pixels (int w, int h, const uint8_t *data) + { + // gl2psDrawPixels only supports the GL_FLOAT type. - OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h); + OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h); - static const float maxval = std::numeric_limits<float>::max (); + static const float maxval = std::numeric_limits<float>::max (); - for (int i = 0; i < 3*w*h; i++) - tmp_data[i] = data[i] / maxval; + for (int i = 0; i < 3*w*h; i++) + tmp_data[i] = data[i] / maxval; - draw_pixels (w, h, tmp_data); -} + draw_pixels (w, h, tmp_data); + } -void -gl2ps_renderer::draw_pixels (int w, int h, const uint16_t *data) -{ - // gl2psDrawPixels only supports the GL_FLOAT type. + void + gl2ps_renderer::draw_pixels (int w, int h, const uint16_t *data) + { + // gl2psDrawPixels only supports the GL_FLOAT type. - OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h); + OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h); - static const float maxval = std::numeric_limits<float>::max (); + static const float maxval = std::numeric_limits<float>::max (); - for (int i = 0; i < 3*w*h; i++) - tmp_data[i] = data[i] / maxval; + for (int i = 0; i < 3*w*h; i++) + tmp_data[i] = data[i] / maxval; - draw_pixels (w, h, tmp_data); -} + draw_pixels (w, h, tmp_data); + } -void -gl2ps_renderer::draw_text (const text::properties& props) -{ - if (props.get_string ().is_empty ()) - return; + void + gl2ps_renderer::draw_text (const text::properties& props) + { + if (props.get_string ().is_empty ()) + return; - // First set font properties: freetype will use them to compute - // coordinates and gl2ps will retrieve the color directly from the - // feedback buffer - set_font (props); - set_color (props.get_color_rgb ()); + // First set font properties: freetype will use them to compute + // coordinates and gl2ps will retrieve the color directly from the + // feedback buffer + set_font (props); + set_color (props.get_color_rgb ()); - std::string saved_font = fontname; + std::string saved_font = fontname; - // Alignment - int halign = 0; - int valign = 0; + // Alignment + int halign = 0; + int valign = 0; - if (props.horizontalalignment_is ("center")) - halign = 1; - else if (props.horizontalalignment_is ("right")) - halign = 2; + if (props.horizontalalignment_is ("center")) + halign = 1; + else if (props.horizontalalignment_is ("right")) + halign = 2; - if (props.verticalalignment_is ("top")) - valign = 2; - else if (props.verticalalignment_is ("baseline")) - valign = 3; - else if (props.verticalalignment_is ("middle")) - valign = 1; + if (props.verticalalignment_is ("top")) + valign = 2; + else if (props.verticalalignment_is ("baseline")) + valign = 3; + else if (props.verticalalignment_is ("middle")) + valign = 1; - // FIXME: handle margin and surrounding box - // Matrix bbox; + // FIXME: handle margin and surrounding box + // Matrix bbox; - const Matrix pos = get_transform ().scale (props.get_data_position ()); - std::string str = props.get_string ().string_vector_value ().join ("\n"); + const Matrix pos = get_transform ().scale (props.get_data_position ()); + std::string str = props.get_string ().string_vector_value ().join ("\n"); - render_text (str, pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0, - halign, valign, props.get_rotation ()); -} + render_text (str, pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0, + halign, valign, props.get_rotation ()); + } } @@ -787,59 +784,58 @@ namespace octave { -// If the name of the stream begins with '|', open a pipe to the command -// named by the rest of the string. Otherwise, write to the named file. + // If the name of the stream begins with '|', open a pipe to the command + // named by the rest of the string. Otherwise, write to the named file. -void -gl2ps_print (const graphics_object& fig, const std::string& stream, - const std::string& term) -{ + void + gl2ps_print (const graphics_object& fig, const std::string& stream, + const std::string& term) + { #if defined (HAVE_GL2PS_H) - // FIXME: should we have a way to create a file that begins with the - // character '|'? + // FIXME: should we have a way to create a file that begins with the + // character '|'? - bool have_cmd = stream.length () > 1 && stream[0] == '|'; + bool have_cmd = stream.length () > 1 && stream[0] == '|'; - FILE *fp = 0; + FILE *fp = 0; - octave::unwind_protect frame; + octave::unwind_protect frame; - if (have_cmd) - { - // Create process and pipe gl2ps output to it. + if (have_cmd) + { + // Create process and pipe gl2ps output to it. - std::string cmd = stream.substr (1); + std::string cmd = stream.substr (1); - fp = octave_popen (cmd.c_str (), "w"); + fp = octave_popen (cmd.c_str (), "w"); - if (! fp) - error ("print: failed to open pipe \"%s\"", stream.c_str ()); + if (! fp) + error ("print: failed to open pipe \"%s\"", stream.c_str ()); - frame.add_fcn (safe_pclose, fp); - } - else - { - // Write gl2ps output directly to file. + frame.add_fcn (safe_pclose, fp); + } + else + { + // Write gl2ps output directly to file. - fp = std::fopen (stream.c_str (), "w"); + fp = std::fopen (stream.c_str (), "w"); - if (! fp) - error ("gl2ps_print: failed to create file \"%s\"", stream.c_str ()); + if (! fp) + error ("gl2ps_print: failed to create file \"%s\"", stream.c_str ()); - frame.add_fcn (safe_fclose, fp); - } + frame.add_fcn (safe_fclose, fp); + } - gl2ps_renderer rend (fp, term); + gl2ps_renderer rend (fp, term); - rend.draw (fig, stream); + rend.draw (fig, stream); - // Make sure buffered commands are finished!!! - rend.finish (); + // Make sure buffered commands are finished!!! + rend.finish (); #else - err_disabled_feature ("gl2ps_print", "gl2ps"); + err_disabled_feature ("gl2ps_print", "gl2ps"); #endif + } } - -}
--- a/libinterp/corefcn/text-renderer.cc Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/text-renderer.cc Wed Aug 17 11:43:27 2016 -0400 @@ -41,105 +41,103 @@ namespace octave { - -text_renderer::text_renderer (void) - : rep (make_text_renderer ()) -{ } + text_renderer::text_renderer (void) + : rep (make_text_renderer ()) + { } -text_renderer::~text_renderer (void) -{ - delete rep; -} + text_renderer::~text_renderer (void) + { + delete rep; + } -bool -text_renderer::ok (void) const -{ - static bool warned = false; + bool + text_renderer::ok (void) const + { + static bool warned = false; - if (! rep) - { - if (! warned) - { - warn_disabled_feature ("opengl_renderer::render_text", - "rendering text (FreeType)"); + if (! rep) + { + if (! warned) + { + warn_disabled_feature ("opengl_renderer::render_text", + "rendering text (FreeType)"); - warned = true; - } - } + warned = true; + } + } - return rep != 0; -} + return rep != 0; + } -Matrix -text_renderer::get_extent (text_element *elt, double rotation) -{ - static Matrix empty_extent (1, 4, 0.0); + Matrix + text_renderer::get_extent (text_element *elt, double rotation) + { + static Matrix empty_extent (1, 4, 0.0); - return ok () ? rep->get_extent (elt, rotation) : empty_extent; -} + return ok () ? rep->get_extent (elt, rotation) : empty_extent; + } -Matrix -text_renderer::get_extent (const std::string& txt, double rotation, - const caseless_str& interpreter) -{ - static Matrix empty_extent (1, 4, 0.0); + Matrix + text_renderer::get_extent (const std::string& txt, double rotation, + const caseless_str& interpreter) + { + static Matrix empty_extent (1, 4, 0.0); - return ok () ? rep->get_extent (txt, rotation, interpreter) : empty_extent; -} + return ok () ? rep->get_extent (txt, rotation, interpreter) : empty_extent; + } -void -text_renderer::set_font (const std::string& name, const std::string& weight, - const std::string& angle, double size) -{ - if (ok ()) - rep->set_font (name, weight, angle, size); -} + void + text_renderer::set_font (const std::string& name, const std::string& weight, + const std::string& angle, double size) + { + if (ok ()) + rep->set_font (name, weight, angle, size); + } -void -text_renderer::set_color (const Matrix& c) -{ - if (ok ()) - rep->set_color (c); -} + void + text_renderer::set_color (const Matrix& c) + { + if (ok ()) + rep->set_color (c); + } -void -text_renderer::text_to_pixels (const std::string& txt, - uint8NDArray& pxls, Matrix& bbox, - int halign, int valign, double rotation, - const caseless_str& interpreter, - bool handle_rotation) -{ - static Matrix empty_bbox (1, 4, 0.0); - static uint8NDArray empty_pxls; + void + text_renderer::text_to_pixels (const std::string& txt, + uint8NDArray& pxls, Matrix& bbox, + int halign, int valign, double rotation, + const caseless_str& interpreter, + bool handle_rotation) + { + static Matrix empty_bbox (1, 4, 0.0); + static uint8NDArray empty_pxls; - if (ok ()) - rep->text_to_pixels (txt, pxls, bbox, halign, valign, rotation, - interpreter, handle_rotation); - else - { - bbox = empty_bbox; - pxls = empty_pxls; - } -} + if (ok ()) + rep->text_to_pixels (txt, pxls, bbox, halign, valign, rotation, + interpreter, handle_rotation); + else + { + bbox = empty_bbox; + pxls = empty_pxls; + } + } -void -text_renderer::text_to_strlist (const std::string& txt, - std::list<text_renderer::string>& lst, - Matrix& bbox, int halign, int valign, - double rotation, - const caseless_str& interpreter) -{ - static Matrix empty_bbox (1, 4, 0.0); - static std::list<text_renderer::string> empty_lst; + void + text_renderer::text_to_strlist (const std::string& txt, + std::list<text_renderer::string>& lst, + Matrix& bbox, int halign, int valign, + double rotation, + const caseless_str& interpreter) + { + static Matrix empty_bbox (1, 4, 0.0); + static std::list<text_renderer::string> empty_lst; - if (ok ()) - rep->text_to_strlist (txt, lst, bbox, halign, valign, rotation, - interpreter); - else - { - bbox = empty_bbox; - lst = empty_lst; - } + if (ok ()) + rep->text_to_strlist (txt, lst, bbox, halign, valign, rotation, + interpreter); + else + { + bbox = empty_bbox; + lst = empty_lst; + } + } } - -}
--- a/libinterp/corefcn/text-renderer.h Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/corefcn/text-renderer.h Wed Aug 17 11:43:27 2016 -0400 @@ -37,181 +37,179 @@ namespace octave { - -class base_text_renderer; - -class -OCTINTERP_API -text_renderer -{ -public: - - text_renderer (void); - - ~text_renderer (void); - - bool ok (void) const; - - Matrix get_extent (text_element *elt, double rotation = 0.0); - - Matrix get_extent (const std::string& txt, double rotation = 0.0, - const caseless_str& interpreter = "tex"); - - void set_font (const std::string& name, const std::string& weight, - const std::string& angle, double size); - - void set_color (const Matrix& c); - - void text_to_pixels (const std::string& txt, - uint8NDArray& pxls, Matrix& bbox, - int halign, int valign, double rotation = 0.0, - const caseless_str& interpreter = "tex", - bool handle_rotation = true); - - class font - { - public: - - font (void) - : name (), weight (), angle (), size (0) - { } + class base_text_renderer; - font (const std::string& nm, const std::string& wt, - const std::string& ang, double sz) - : name (nm), weight (wt), angle (ang), size (sz) - { } - - font (const font& ft) - : name (ft.name), weight (ft.weight), angle (ft.angle), - size (ft.size) - { } - - ~font (void) { } - - font& operator = (const font& ft) - { - if (&ft != this) - { - name = ft.name; - weight = ft.weight; - angle = ft.angle; - size = ft.size; - } - - return *this; - } - - std::string get_name (void) const { return name; } - - std::string get_weight (void) const { return weight; } - - std::string get_angle (void) const { return angle; } - - double get_size (void) const { return size; } - - protected: - - std::string name; - std::string weight; - std::string angle; - double size; - }; - - // Container for substrings after parsing. - - class string + class + OCTINTERP_API + text_renderer { public: - string (const std::string& s, font& f, const double x0, const double y0) - : str (s), fnt (f), x (x0), y (y0), z (0.0), code (0), - color (Matrix (1,3,0.0)) - { } + text_renderer (void); + + ~text_renderer (void); + + bool ok (void) const; + + Matrix get_extent (text_element *elt, double rotation = 0.0); + + Matrix get_extent (const std::string& txt, double rotation = 0.0, + const caseless_str& interpreter = "tex"); - string (const string& s) - : str (s.str), fnt (s.fnt), x (s.x), y (s.y), code (s.code), - color (s.color) - { } + void set_font (const std::string& name, const std::string& weight, + const std::string& angle, double size); + + void set_color (const Matrix& c); - ~string (void) { } + void text_to_pixels (const std::string& txt, + uint8NDArray& pxls, Matrix& bbox, + int halign, int valign, double rotation = 0.0, + const caseless_str& interpreter = "tex", + bool handle_rotation = true); - string& operator = (const string& s) + class font { - if (&s != this) - { - str = s.str; - fnt = s.fnt; - x = s.x; - y = s.y; - code = s.code; - color = s.color; - } + public: + + font (void) + : name (), weight (), angle (), size (0) + { } + + font (const std::string& nm, const std::string& wt, + const std::string& ang, double sz) + : name (nm), weight (wt), angle (ang), size (sz) + { } + + font (const font& ft) + : name (ft.name), weight (ft.weight), angle (ft.angle), + size (ft.size) + { } + + ~font (void) { } - return *this; - } + font& operator = (const font& ft) + { + if (&ft != this) + { + name = ft.name; + weight = ft.weight; + angle = ft.angle; + size = ft.size; + } + + return *this; + } + + std::string get_name (void) const { return name; } + + std::string get_weight (void) const { return weight; } - void set_string (const std::string& s) { str = s; } + std::string get_angle (void) const { return angle; } + + double get_size (void) const { return size; } + + protected: - std::string get_string (void) const { return str; } + std::string name; + std::string weight; + std::string angle; + double size; + }; + + // Container for substrings after parsing. + + class string + { + public: - std::string get_name (void) const { return fnt.get_name (); } - - std::string get_weight (void) const { return fnt.get_weight (); } + string (const std::string& s, font& f, const double x0, const double y0) + : str (s), fnt (f), x (x0), y (y0), z (0.0), code (0), + color (Matrix (1,3,0.0)) + { } - std::string get_angle (void) const { return fnt.get_angle (); } + string (const string& s) + : str (s.str), fnt (s.fnt), x (s.x), y (s.y), code (s.code), + color (s.color) + { } - double get_size (void) const { return fnt.get_size (); } + ~string (void) { } - void set_x (const double x0) { x = x0; } - - double get_x (void) const { return x; } + string& operator = (const string& s) + { + if (&s != this) + { + str = s.str; + fnt = s.fnt; + x = s.x; + y = s.y; + code = s.code; + color = s.color; + } - void set_y (const double y0) { y = y0; } + return *this; + } + + void set_string (const std::string& s) { str = s; } + + std::string get_string (void) const { return str; } - double get_y (void) const { return y; } + std::string get_name (void) const { return fnt.get_name (); } + + std::string get_weight (void) const { return fnt.get_weight (); } + + std::string get_angle (void) const { return fnt.get_angle (); } + + double get_size (void) const { return fnt.get_size (); } - void set_z (const double z0) { z = z0; } + void set_x (const double x0) { x = x0; } + + double get_x (void) const { return x; } - double get_z (void) const { return z; } + void set_y (const double y0) { y = y0; } + + double get_y (void) const { return y; } - void set_code (const uint32_t c) { code = c; } + void set_z (const double z0) { z = z0; } + + double get_z (void) const { return z; } - uint32_t get_code (void) const { return code; } + void set_code (const uint32_t c) { code = c; } + + uint32_t get_code (void) const { return code; } - void set_color (const uint8NDArray& c) - { - color(0) = static_cast<double> (c(0)) / 255; - color(1) = static_cast<double> (c(1)) / 255; - color(2) = static_cast<double> (c(2)) / 255; - } + void set_color (const uint8NDArray& c) + { + color(0) = static_cast<double> (c(0)) / 255; + color(1) = static_cast<double> (c(1)) / 255; + color(2) = static_cast<double> (c(2)) / 255; + } + + Matrix get_color (void) const { return color; } + + private: - Matrix get_color (void) const { return color; } + std::string str; + font fnt; + double x, y, z; + uint32_t code; + Matrix color; + }; + + void text_to_strlist (const std::string& txt, + std::list<string>& lst, Matrix& box, + int halign, int valign, double rotation = 0.0, + const caseless_str& interpreter = "tex"); private: - std::string str; - font fnt; - double x, y, z; - uint32_t code; - Matrix color; - }; + base_text_renderer *rep; - void text_to_strlist (const std::string& txt, - std::list<string>& lst, Matrix& box, - int halign, int valign, double rotation = 0.0, - const caseless_str& interpreter = "tex"); + // No copying! -private: - - base_text_renderer *rep; - - // No copying! + text_renderer (const text_renderer&); - text_renderer (const text_renderer&); - - text_renderer& operator = (const text_renderer&); -}; - + text_renderer& operator = (const text_renderer&); + }; } #endif
--- a/libinterp/octave-value/ov-java.cc Wed Aug 17 08:20:26 2016 -0700 +++ b/libinterp/octave-value/ov-java.cc Wed Aug 17 11:43:27 2016 -0400 @@ -184,100 +184,99 @@ namespace octave { -class JVMArgs -{ -public: - - JVMArgs (void) - { - vm_args.version = JNI_VERSION_1_2; - vm_args.nOptions = 0; - vm_args.options = 0; - vm_args.ignoreUnrecognized = false; - } - - ~JVMArgs (void) - { - clean (); - } - - JavaVMInitArgs *to_args () - { - update (); - return &vm_args; - } - - void add (const std::string& opt) - { - java_opts.push_back (opt); - } - - void read_java_opts (const std::string& filename) + class JVMArgs { - std::ifstream js (filename.c_str ()); - - if (! js.bad () && ! js.fail ()) - { - std::string line; - - while (! js.eof () && ! js.fail ()) - { - std::getline (js, line); - - if (line.find ("-") == 0) - java_opts.push_back (line); - else if (line.length () > 0 && Vdebug_java) - std::cerr << "invalid JVM option, skipping: " << line << std::endl; - } - } - } - -private: - - void clean (void) - { - if (vm_args.options != 0) - { - for (int i = 0; i < vm_args.nOptions; i++) - delete [] vm_args.options[i].optionString; - - delete [] vm_args.options; - - vm_args.options = 0; - vm_args.nOptions = 0; - } - } - - void update (void) - { - clean (); - - if (java_opts.size () > 0) - { - int index = 0; - - vm_args.nOptions = java_opts.size (); - vm_args.options = new JavaVMOption [vm_args.nOptions]; - - for (std::list<std::string>::const_iterator it = java_opts.begin (); - it != java_opts.end (); ++it) - { - if (Vdebug_java) - std::cout << *it << std::endl; - vm_args.options[index++].optionString = strsave ((*it).c_str ()); - } - - java_opts.clear (); - } - } - -private: - - JavaVMInitArgs vm_args; - - std::list<std::string> java_opts; -}; - + public: + + JVMArgs (void) + { + vm_args.version = JNI_VERSION_1_2; + vm_args.nOptions = 0; + vm_args.options = 0; + vm_args.ignoreUnrecognized = false; + } + + ~JVMArgs (void) + { + clean (); + } + + JavaVMInitArgs *to_args () + { + update (); + return &vm_args; + } + + void add (const std::string& opt) + { + java_opts.push_back (opt); + } + + void read_java_opts (const std::string& filename) + { + std::ifstream js (filename.c_str ()); + + if (! js.bad () && ! js.fail ()) + { + std::string line; + + while (! js.eof () && ! js.fail ()) + { + std::getline (js, line); + + if (line.find ("-") == 0) + java_opts.push_back (line); + else if (line.length () > 0 && Vdebug_java) + std::cerr << "invalid JVM option, skipping: " << line << std::endl; + } + } + } + + private: + + void clean (void) + { + if (vm_args.options != 0) + { + for (int i = 0; i < vm_args.nOptions; i++) + delete [] vm_args.options[i].optionString; + + delete [] vm_args.options; + + vm_args.options = 0; + vm_args.nOptions = 0; + } + } + + void update (void) + { + clean (); + + if (java_opts.size () > 0) + { + int index = 0; + + vm_args.nOptions = java_opts.size (); + vm_args.options = new JavaVMOption [vm_args.nOptions]; + + for (std::list<std::string>::const_iterator it = java_opts.begin (); + it != java_opts.end (); ++it) + { + if (Vdebug_java) + std::cout << *it << std::endl; + vm_args.options[index++].optionString = strsave ((*it).c_str ()); + } + + java_opts.clear (); + } + } + + private: + + JavaVMInitArgs vm_args; + + std::list<std::string> java_opts; + }; } #if defined (OCTAVE_USE_WINDOWS_API)