// Copyright (c) 1999-2004 // Utrecht University (The Netherlands), // ETH Zurich (Switzerland), // INRIA Sophia-Antipolis (France), // Max-Planck-Institute Saarbruecken (Germany), // and Tel-Aviv University (Israel). All rights reserved. // // This file is part of CGAL (www.cgal.org); you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 3 of the License, // or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // SPDX-License-Identifier: LGPL-3.0+ // // // Author(s) : Andreas Fabri, Herve Bronnimann, Sylvain Pion #ifdef CGAL_USE_GEOMVIEW #ifdef CGAL_HEADER_ONLY #define CGAL_INLINE_FUNCTION inline #else #define CGAL_INLINE_FUNCTION #endif #include #include #include #include #include #include #include // kill() on SunPro requires these 2 #includes. #include #include namespace CGAL { CGAL_INLINE_FUNCTION Geomview_stream::Geomview_stream(const Bbox_3 &bbox, const char *machine, const char *login) : bb(bbox), vertex_color(BLACK), edge_color(BLACK), face_color(BLACK), wired_flag(false), echo_flag(true), raw_flag(false), trace_flag(false), binary_flag(false), line_width(1) { setup_geomview(machine, login); frame(bbox); pickplane(bbox); set_vertex_radius((bbox.xmax() - bbox.xmin())/100.0); } CGAL_INLINE_FUNCTION Geomview_stream::~Geomview_stream() { kill(pid, SIGKILL); // kills geomview } CGAL_INLINE_FUNCTION void Geomview_stream::setup_geomview(const char *machine, const char *login) { int pipe_out[2], pipe_in[2]; // Communication between CGAL and geomview should be possible // in two directions. To achieve this we open two pipes std::cout << "Starting Geomview..." << std::flush; if (pipe(pipe_out) < 0) { CGAL_error_msg( "out pipe failed" ); } if (pipe(pipe_in) < 0) { CGAL_error_msg( "in pipe failed" ); } switch (pid = fork()){ case -1: CGAL_error_msg( "fork failed" ); case 0: // The child process close(pipe_out[1]); // does not write to the out pipe, close(pipe_in[0]); // does not read from the in pipe. if (dup2(pipe_out[0], 0) != 0) std::cerr << "Connect pipe to stdin failed." << std::endl; if (dup2(pipe_in[1], 1) != 1) std::cerr << "Connect pipe to stdout failed." << std::endl; if (machine && (std::strlen(machine)>0)) { std::string s (" rgeomview "); s += machine; s += ":0.0"; execlp("rsh", "rsh", machine, "-l", login, s.data(), static_cast(NULL)); // cast to stop gcc warning } else { execlp("geomview", "geomview", "-c", "-", static_cast(NULL)); // cast to stop gcc warning } // if we get to this point something went wrong. std::cerr << "execl geomview failed" << std::endl; switch(errno) { case EACCES: std::cerr << "please check your environment variable PATH" << std::endl; std::cerr << "make sure the file `geomview' is contained in it" << std::endl; std::cerr << "and is executable" << std::endl; break; case ELOOP: std::cerr << "too many links for filename `geomview'" << std::endl; break; default: std::cerr << "error number " << errno << " (check `man execlp')" << std::endl; }; CGAL_error(); default: // The parent process close(pipe_out[0]); // does not read from the out pipe, close(pipe_in[1]); // does not write to the in pipe. in = pipe_in[0]; out = pipe_out[1]; // Necessary to wait a little bit for Geomview, // otherwise you won't be able to ask for points... sleep(1); #if 1 // We want to get rid of the requirement in the CGAL doc about // (echo "started"). But we want to be backward compatible, that is, // people who have this echo in their .geomview must still have CGAL // working, at least for a few public releases. // So the plan is to send, from CGAL, the command : (echo "CGAL-3D") // It's the same length as "started", 7. // Then we read 7 chars from Geomview, and test which string it is. // If it's "CGAL-3D", then fine, the user doesn't have .geomview with // the back-compatible echo command. // In the very long run, we'll be able to get rid of all this code as // well. // Maybe we should simply read the pipe, till we find "CGAL-3D" ? *this << "(echo \"CGAL-3D\")"; char inbuf[10]; std::size_t retread=::read(in, inbuf, 7); (void)retread; if (std::strncmp(inbuf, "started", 7) == 0) { // std::cerr << "You still have a .geomview file with the\n" // << "(echo \"started\") command. Note that this is not\n" // << "compulsory anymore, since CGAL 2.3" << std::endl; // Then the next one is supposed to be CGAL-3D. retread=::read(in, inbuf, 7); (void)retread; if (std::strncmp(inbuf, "CGAL-3D", 7) != 0) std::cerr << "Unexpected string from Geomview !" << std::endl; } else if (std::strncmp(inbuf, "CGAL-3D", 7) == 0) { // std::cerr << "Good, you don't have a .geomview file with the\n" // << "(echo \"started\") command" << std::endl; } else { std::cerr << "Unexcepted string from Geomview at initialization!\n" << "Going on nevertheless !" << std::endl; } #else // Old original version char inbuf[10]; // Waits for "started" from the .geomview file. retread=::read(in, inbuf, 7); (void)retread; #endif std::cout << "done." << std::endl; (*this) << "(normalization g* none)(bbox-draw g* no)"; } } CGAL_INLINE_FUNCTION void Geomview_stream::pickplane(const Bbox_3 &bbox) { bool bin_bak = set_binary_mode(); (*this) << "(geometry pickplane {QUAD BINARY\n" << 1 // here are the four corners << bbox.xmin() << bbox.ymin() << bbox.zmin() << bbox.xmin() << bbox.ymax() << bbox.zmin() << bbox.xmax() << bbox.ymax() << bbox.zmin() << bbox.xmax() << bbox.ymin() << bbox.zmin() // close the text bracket << "}) (pickable pickplane no)"; set_ascii_mode(bin_bak); } CGAL_INLINE_FUNCTION void Geomview_stream::clear() { (*this) << "(delete World)"; id.clear(); } CGAL_INLINE_FUNCTION void Geomview_stream::look_recenter() { (*this) << "(look-recenter World)"; } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(const std::string & s) { if ((int)s.length() != ::write(out, s.data(), s.length())) { CGAL_error_msg( "write problem in the pipe while sending data to geomview" ); } trace(s); return *this; } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(int i) { // Depending on the mode chosen if (get_binary_mode()) { // we write raw binary data to the stream. int num = i; I_swap_to_big_endian(num); std::size_t retwrite=::write(out, (char*)&num, sizeof(num)); (void)retwrite; trace(i); } else { // transform the int in a character sequence and put whitespace around std::ostringstream str; str << i << ' ' << std::ends; *this << str.str().c_str(); } return *this; } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(unsigned int i) { // Depending on the mode chosen if (get_binary_mode()) { // we write raw binary data to the stream. unsigned int num = i; I_swap_to_big_endian(num); std::size_t retwrite=::write(out, (char*)&num, sizeof(num)); (void)retwrite; trace(i); } else { // transform the int in a character sequence and put whitespace around std::ostringstream str; str << i << ' ' << std::ends; *this << str.str().c_str(); } return *this; } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(long i) { return operator<<((int) i); } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(unsigned long i) { return operator<<((unsigned int) i); } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(double d) { float f = float(d); if (get_binary_mode()) { float num = float(d); I_swap_to_big_endian(num); std::size_t retwrite= ::write(out, (char*)&num, sizeof(num)); (void)retwrite; trace(f); } else { // 'copy' the float in a string and append a blank std::ostringstream str; str << f << ' ' << std::ends; *this << str.str().c_str(); } return *this; } CGAL_INLINE_FUNCTION Geomview_stream& operator<<(Geomview_stream &gv, const Bbox_2 &bbox) { bool ascii_bak = gv.set_ascii_mode(); gv << "(geometry " << gv.get_new_id("Bbox") << " {VECT 1 5 0 5 0 "; // here are the four corners gv << bbox.xmin() << bbox.ymin() << 0.0 << bbox.xmin() << bbox.ymax() << 0.0 << bbox.xmax() << bbox.ymax() << 0.0 << bbox.xmax() << bbox.ymin() << 0.0 << bbox.xmin() << bbox.ymin() << 0.0; // close the text bracket gv << "})"; gv.set_ascii_mode(ascii_bak); return gv; } CGAL_INLINE_FUNCTION Geomview_stream& operator<<(Geomview_stream &gv, const Bbox_3 &bbox) { bool ascii_bak = gv.set_ascii_mode(); gv << "(geometry " << gv.get_new_id("Bbox") << " {appearance {material {edgecolor " << gv.ecr() << gv.ecg() << gv.ecb() << "}}{SKEL 8 4 " // here are the corners << bbox.xmin() << bbox.ymin() << bbox.zmin() << bbox.xmin() << bbox.ymax() << bbox.zmin() << bbox.xmax() << bbox.ymax() << bbox.zmin() << bbox.xmax() << bbox.ymin() << bbox.zmin() << bbox.xmax() << bbox.ymin() << bbox.zmax() << bbox.xmax() << bbox.ymax() << bbox.zmax() << bbox.xmin() << bbox.ymax() << bbox.zmax() << bbox.xmin() << bbox.ymin() << bbox.zmax() << "10 0 1 2 3 4 5 6 7 0 3\n" << "2 1 6\n" << "2 2 5\n" << "2 4 7\n" // close the text bracket << "}})"; gv.set_ascii_mode(ascii_bak); return gv; } CGAL_INLINE_FUNCTION void Geomview_stream::set_bg_color(const Color &c) { bool ascii_bak = set_ascii_mode(); *this << "(backcolor \"Camera\" " << double(c.r())/255.0 << double(c.g())/255.0 << double(c.b())/255.0 << ")"; set_ascii_mode(ascii_bak); } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator<<(const Color &c) { vertex_color = edge_color = face_color = c; return (*this); } CGAL_INLINE_FUNCTION Color Geomview_stream::get_vertex_color() const { return vertex_color; } CGAL_INLINE_FUNCTION Color Geomview_stream::get_edge_color() const { return edge_color; } CGAL_INLINE_FUNCTION Color Geomview_stream::get_face_color() const { return face_color; } CGAL_INLINE_FUNCTION Color Geomview_stream::set_vertex_color(const Color &c) { Color old = vertex_color; vertex_color = c; return old; } CGAL_INLINE_FUNCTION Color Geomview_stream::set_edge_color(const Color &c) { Color old = edge_color; edge_color = c; return old; } CGAL_INLINE_FUNCTION Color Geomview_stream::set_face_color(const Color &c) { Color old = face_color; face_color = c; return old; } CGAL_INLINE_FUNCTION double Geomview_stream::vcr() const { return double(vertex_color.r())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::vcg() const { return double(vertex_color.g())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::vcb() const { return double(vertex_color.b())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::ecr() const { return double(edge_color.r())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::ecg() const { return double(edge_color.g())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::ecb() const { return double(edge_color.b())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::fcr() const { return double(face_color.r())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::fcg() const { return double(face_color.g())/255.0; } CGAL_INLINE_FUNCTION double Geomview_stream::fcb() const { return double(face_color.b())/255.0; } CGAL_INLINE_FUNCTION void Geomview_stream::frame(const Bbox_3 &bbox) { (*this) << bbox << "(look-recenter g0 c0)"; } CGAL_INLINE_FUNCTION Geomview_stream& Geomview_stream::operator>>(char *expr) { // Skip whitespaces do { std::size_t retread=::read(in, expr, 1); (void)retread; } while (expr[0] != '('); int pcount = 1; int i = 1; while (1) { std::size_t retread=::read(in, &expr[i], 1); (void)retread; if (expr[i] == ')'){ pcount--; } else if (expr[i] == '('){ pcount++; } if (pcount == 0){ expr[i+1]='\0'; break; // we encountered a balanced number of parantheses } i++; } return *this; } // Parse a Lisp expression, return a pointer to the beginning of the // nth subexpression, and terminate it by '\0'. // It's either a word terminated by ' ' or ')', or a well parenthesed // expression, or a quoted "string". CGAL_INLINE_FUNCTION char* Geomview_stream::nth(char* s, int count) { s++; // skip first character (always a parenthesis) // Skip "count" words. for(; count != 0; count--) { while (*s == ' ') // skip whitespaces s++; s++; while (*s != ' ') // skip a word s++; } while (*s == ' ') // skip whitespaces s++; // Now we have the beginning of the searched sub-expression. int j = 1; if (*s == '(') // Case of a well-parenthesed expression. for (int pcount = 1; pcount != 0;) { if (s[j] == ')') pcount--; if (s[j] == '(') pcount++; j++; } else if (*s == '"') { // Case of a quoted "string". while (s[j] != '"') j++; j++; } else // Case of a word terminated by ' ' or ')'. while (s[j] != ' ' && s[j] != ')') j++; s[j] = '\0'; return s; } CGAL_INLINE_FUNCTION void Geomview_stream::parse_point(const char* pickpoint, double &x, double &y, double &z, double &w) { std::stringstream ss; ss << pickpoint << std::ends; char parenthesis; ss >> parenthesis >> x >> y >> z >> w; } CGAL_INLINE_FUNCTION std::string Geomview_stream::get_new_id(const std::string & s) { std::ostringstream str; str << s << id[s]++ << std::ends; return str.str(); } } //namespace CGAL #else #ifndef CGAL_HEADER_ONLY // Add a dummy symbol to prevent warnings of empty translation unit. namespace CGAL { namespace { int dummy; } } //namespace CGAL #endif #endif // CGAL_USE_GEOMVIEW