From f4d46fa70667af1a049cce1fda144c072f9a22ab Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 28 Aug 1997 09:48:08 +0000 Subject: [PATCH] Initial revision --- Packages/Geomview/web/geomview.fw | 1715 +++++++++++++++++++++++++++++ 1 file changed, 1715 insertions(+) create mode 100644 Packages/Geomview/web/geomview.fw diff --git a/Packages/Geomview/web/geomview.fw b/Packages/Geomview/web/geomview.fw new file mode 100644 index 00000000000..f502ee940b3 --- /dev/null +++ b/Packages/Geomview/web/geomview.fw @@ -0,0 +1,1715 @@ +@! $RCSfile$ + +\documentclass[]{article} +\usepackage{amssymb} +\usepackage{fw-latex} +\usepackage{cc_manual} +\usepackage{cgal} + +\textwidth=6in +\textheight=8.9in +\oddsidemargin=0.25in +\evensidemargin=0.25in +\topmargin=-0.5in +\begin{document} + +\RCSdef{\geomviewRevision}{$Revision$} +\RCSdefDate{\geomviewDate}{$Date$} + +@p typesetter = tex +@p no_doc_header +@p maximum_output_line_length = 110 + +\title{\bf How to Implement Geomview Stream IO Operators} +\author{Andreas Fabri} +\date{\geomviewRevision, \geomviewDate} +\maketitle + +\tableofcontents + +\cleardoublepage + +\section{Introduction} + +{\it geomview} is a viewer for three-dimensional objects, developed at +the Geometry Center in Minneapolis\footnote{\tt +http://www.geom.umn.edu/}. We provide bidirectional commmunication +between \cgal\ applications and {\it geomview}. + + +This document explains the implementation in order to enable you +to write further input and output operators. This document should be +read together with the {\it Geomview Manual} \cite{GVManual} and the manual +pages for the \ccc{CGAL_Geomview_stream}. This document further +explains how the interprocess communication between the application +and geomview works, but you do not have to know about these details +in order to write output operators. + + +\section{Output Operators for CGAL Kernel Classes} + +{\it geomview} can be controlled with {\it gcl}, the {\it geomview +command language}. Geomview stream output operators for geometric +objects build {\it gcl} expressions that describe features of the geometric +object. This expression is sent to geomview, interpreted and the +object appears in the camera view of geomview. + + +\subsection{2D Point (SKEL)} + +Two dimensional objects are dumped in the $xy$-plane with $z$-coordinate 0. + +@$@+=@{@- +#ifdef CGAL_POINT_2_H +#ifndef CGAL_GV_OUT_POINT_2_H +#define CGAL_GV_OUT_POINT_2_H + +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Point_2 &p) +{ + ostrstream os; + os << "p" << gv.point_count++ << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id + << " {appearance {linewidth 5 material {edgecolor " + << gv.vcr() << gv.vcg() << gv.vcb() + << "}}{SKEL 1 1 " ; + + gv << CGAL_to_double(p.x()) + << CGAL_to_double(p.y()) + << 0.0 + << "1 0\n" + << "}})" ; + + return gv; +} +#endif // CGAL_GV_OUT_POINT_2_H +#endif // CGAL_POINT_2_H +@} + +\subsection{3D Point (SKEL)} + +@$@+=@{@- +#ifdef CGAL_POINT_3_H +#ifndef CGAL_GV_OUT_POINT_3_H +#define CGAL_GV_OUT_POINT_3_H + +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Point_3 &p) +{ + ostrstream os; + os << "p" << gv.point_count++ << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id + << " {appearance {linewidth 5 material {edgecolor " + << gv.vcr() << gv.vcg() << gv.vcb() + << "}}{SKEL 1 1 " + + << CGAL_to_double(p.x()) + << CGAL_to_double(p.y()) + << CGAL_to_double(p.z()) + << "1 0\n" + << "}})" ; + + return gv; +} +#endif // CGAL_GV_OUT_POINT_3_H +#endif // CGAL_POINT_3_H +@} + + +\subsection{2D Segment (VECT)} + + +@$@+=@{@- +#ifdef CGAL_SEGMENT_2_H +#ifndef CGAL_GV_OUT_SEGMENT_2_H +#define CGAL_GV_OUT_SEGMENT_2_H +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Segment_2 &segment) +{ + ostrstream os; + os << "seg" << gv.segment_count++ << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id << " {{appearance {linewidth " + << gv.get_line_width() << "}{VECT " + << 1 << 2 << 1 // 1 polyline, two vertices, 1 color + << 2 // the first polyline contains 2 vertices + << 1 // and it has 1 color + + // here are start and end points + << CGAL_to_double(segment.source().x()) + << CGAL_to_double(segment.source().y()) + << 0.0 + << CGAL_to_double(segment.target().x()) + << CGAL_to_double(segment.target().y()) + << 0.0 + + // and the color of the segment and its opaqueness + << gv.ecr() << gv.ecg() << gv.ecb() << 1.0 + + // close the text bracket + << "}})" ; + + return gv; +} +#endif // CGAL_GV_OUT_SEGMENT_2_H +#endif //CGAL_SEGMENT_2_H +@} + + +\subsection{3D Segment (VECT)} + +@$@+=@{@- +#ifdef CGAL_SEGMENT_3_H +#ifndef CGAL_GV_OUT_SEGMENT_3_H +#define CGAL_GV_OUT_SEGMENT_3_H +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Segment_3 &segment) +{ + ostrstream os; + os << "seg" << gv.segment_count++ << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id + << " {appearance {linewidth " + << gv.get_line_width() + << " }{VECT " + << 1 << 2 << 1 // 1 polyline, two vertices, 1 color + << 2 // the first polyline contains 2 vertices + << 1 // and it has 1 color + + // here are start and end points + << CGAL_to_double(segment.source().x()) + << CGAL_to_double(segment.source().y()) + << CGAL_to_double(segment.source().z()) + << CGAL_to_double(segment.target().x()) + << CGAL_to_double(segment.target().y()) + << CGAL_to_double(segment.target().z()) + + // and the color of the segment and its opaqueness + << gv.ecr() << gv.ecg() << gv.ecb() << 1.0 + + // close the text bracket + << "}})"; + + return gv; +} +#endif // CGAL_GV_OUT_SEGMENT_3_H +#endif // CGAL_SEGMENT_3_H +@} + +\subsection{2D Triangle (OFF)} + +@$@+=@{@- +#ifdef CGAL_TRIANGLE_2_H +#ifndef CGAL_GV_OUT_TRIANGLE_2_H +#define CGAL_GV_OUT_TRIANGLE_2_H + +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Triangle_2 &t) +{ + ostrstream os; + os << "tr" << gv.triangle_count++ << ends; + char *id = os.str(); + + gv << ascii + << "(geometry " << id << " {appearance {+edge material {edgecolor " + << gv.ecr() << gv.ecg() << gv.ecb() << " } shading constant}{ " + << binary + // it's a planar polygon + << "OFF BINARY\n" + + // it has 3 vertices, 1 face and 3 edges + << 3 << 1 << 3; + + for(int i=0; i<3; i++){ + gv << CCAL_to_double(t[i].x()) + << CGAL_to_double(t[i].y()) + << 0.0; + } + + // the face + gv << 3 << 0 << 1 << 2 << 4 << gv.fcr() << gv.fcg() << gv.fcb() << 1.0 + << "}})" + << ascii; + + return gv; +} +#endif // CGAL_GV_OUT_TRIANGLE_2_H +#endif // CGAL_TRIANGLE_2_H +@} + + +\subsection{3D Triangle (OFF)} + +@$@+=@{@- +#ifdef CGAL_TRIANGLE_3_H +#ifndef CGAL_GV_OUT_TRIANGLE_3_H +#define CGAL_GV_OUT_TRIANGLE_3_H + +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Triangle_3 &t) +{ + ostrstream os; + os << "tr" << gv.triangle_count++ << ends; + char *id = os.str(); + + gv << ascii + << "(geometry " << id << " {appearance {+edge material {edgecolor " + << gv.ecr() << gv.ecg() << gv.ecb() << "} shading constant}{ " + << binary + // it's a planar polygon + << "OFF BINARY\n" + + // it has 3 vertices, 1 face and 3 edges + << 3 << 1 << 3; + + for(int i=0; i<3; i++){ + gv << CGAL_to_double(t[i].x()) + << CGAL_to_double(t[i].y()) + << CGAL_to_double(t[i].z()); + } + + // the face + gv << 3 << 0 << 1 << 2 << 4 << gv.fcr() << gv.fcg() << gv.fcb() << 1.0 + << "}})" + << ascii; + + return gv; +} +#endif // CGAL_GV_OUT_TRIANGLE_3_H +#endif // CGAL_TRIANGLE_3_H +@} + + +\subsection{Tetrahedron (OFF)} + +@$@+=@{@- +#ifdef CGAL_TETRAHEDRON_3_H +#ifndef CGAL_GV_OUT_TETRAHEDRON_3_H +#define CGAL_GV_OUT_TETRAHEDRON_3_H + +template < class R > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Tetrahedron_3 &t) +{ + ostrstream os; + os << "tetra" << gv.tetrahedron_count++ << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id << " {appearance {}{ " + << binary + << "OFF BINARY\n" + + // it has 4 vertices, 4 face and 6 edges + << 4 << 4 << 6 ; + + // the vertices + for(int i=0; i<4; i++){ + gv << CGAL_to_double(t[i].x()) + << CGAL_to_double(t[i].y()) + << CGAL_to_double(t[i].z()); + } + + // the faces + double r = gv.fcr(), + g = gv.fcg(), + b = gv.fcb(); + gv << 3 << 0 << 1 << 2 << 4 << r << g << b << 1.0 + << 3 << 3 << 0 << 1 << 4 << r << g << b << 1.0 + << 3 << 3 << 1 << 2 << 4 << r << g << b << 1.0 + << 3 << 3 << 0 << 2 << 4 << r << g << b << 1.0 + << "}})" << ascii; + return gv; +} +#endif // CGAL_GV_OUT_TETRAHEDRON_3_H +#endif // CGAL_TETRAHEDRON_3_H +@} + +\subsection{2D Bounding Box (VECT)} + +@$@+=@{@- +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Bbox_2 &bbox) +{ + ostrstream os; + os << "bbox" << gv.bbox_count++ << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id << " {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 << "})" ; + + return gv; +} +@} + +And this will go in the header file: + +@$@+=@{@- +#ifdef CGAL_BBOX_2_H +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Bbox_2 &bbox); +#endif // CGAL_BBOX_2_H +@} + +\subsection{3D Bounding Box (SKEL)} + +Here you can see how to specifiy the color of edges. It is put in an +{\em appearance} description. + +@$@+=@{@- +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Bbox_3 &bbox) +{ + ostrstream os; + os << "bbox" << gv.bbox_count++ << ends ; + char *id = os.str(); + + + gv << ascii + << "(geometry " << id << " {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 + << "}})" ; + + return gv; +} +@} + + +And this will go in the header file: + + +@$@+=@{@- +#ifdef CGAL_BBOX_3_H +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Bbox_3 &bbox); +#endif // CGAL_BBOX_3_H +@} + +\section{Input Operators for CGAL Kernel Classes} + +Geomview stream input operators build {\it gcl} expressions that +notify geomview that one is interested in certain events (typically a +mouse click on an object). If the event happens geomview sends a {\it +gcl} expressions back to the application that has to interpret the +event and to react appropriately. + +Input here means that when you click with the right mouse button on the +{pickplane} in the Camera of {\it geomview} you enter a new geometric +object. + +\subsection{3D Point} +@$@+=@{@- +#ifdef CGAL_POINT_3_H +#ifndef CGAL_GV_IN_POINT_3_H +#define CGAL_GV_IN_POINT_3_H + +template < class R > +CGAL_Geomview_stream& +operator>>(CGAL_Geomview_stream &gv, + CGAL_Point_3 &point) +{ + char gclpick[100]; + strcpy(gclpick, "(pick world pickplane * nil nil nil nil nil nil nil)"); + + gv << ascii + << "(pickable pickplane yes) (ui-target pickplane yes)" + << "(interest " << gclpick <<")"; + + gv >> gv.sexpr; // this reads a gcl expression + + char* pickpoint = CGAL_nth(gv.sexpr, 3); + // this gives something as: (0.0607123 0.0607125 4.76837e-07 0.529628) + parse_point(pickpoint,point); + + // we echo the input + gv << point; + + // we are done and tell geomview to stop sending pick events + gv << "(uninterest " << gclpick << ") (pickable pickplane no)" ; + + return gv ; +} +#endif // CGAL_GV_IN_POINT_3_H +#endif // CGAL_POINT_3_H +@} + + + +\section{IO Operators for CGAL Basic Library Classes} + +\subsection{Tetrahedalization} + +At the moment we perform a loop over the simplices. That results in +many geomview objects. Instead we should construct a single object. + +@$@+=@{@- +#ifdef CGAL_TETRAHEDRALIZATION_3_H +#ifndef CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_3_H +#define CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_3_H + +template < class Tr > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream& os, CGAL_Tetrahedralization_3& T) +{ + CGAL_Tetrahedralization_3::Simplex_iterator + it = T.simplices_begin(), + end = T.simplices_end(); + + while(it != end){ + if(! T.is_infinite(*it)){ + os << *it; + } + ++it; + } + return os; +} +#endif // CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_3_H +#endif // CGAL_TETRAHEDRALIZATION_3_H +@} + +\subsection{Tetrahedralization Simplex (OFF)} + +This takes a pointer to a simplex and displays the corresponding +tetrahedron. The address is put in the name of the geomview object +so that the object can be selected with the mouse and the pointer +can be passed back to the application. + +@$@+=@{@- +#ifdef CGAL_TETRAHEDRALIZATION_SIMPLEX_H +#ifndef CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_SIMPLEX_H +#define CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_SIMPLEX_H + +template < class V > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Tetrahedralization_simplex* s) +{ + ostrstream os; + os << "Simplex_" << (unsigned long int)s << ends ; + char *id = os.str(); + + gv << ascii + << "(geometry " << id << " {appearance {+edge material {edgecolor " + << gv.ecr() << gv.ecg() << gv.ecb() << "} shading constant}{ " + << binary + << "OFF BINARY\n"; + // it has 4 vertices, 4 face and 6 edges + gv << 4 << 4 << 6 ; + + // the vertices + for(int i=0; i<4; i++){ + gv << CGAL_to_double(s->vertex(i)->point().x()) + << CGAL_to_double(s->vertex(i)->point().y()) + << CGAL_to_double(s->vertex(i)->point().z()) ; + } + + // the faces + double r = gv.fcr(), + g = gv.fcg(), + b = gv.fcb(); + + gv << 3 << 3 << 1 << 2 << 4 << r << g << b << 1.0 + << 3 << 3 << 0 << 2 << 4 << r << g << b << 1.0 + << 3 << 3 << 0 << 1 << 4 << r << g << b << 1.0 + << 3 << 0 << 1 << 2 << 4 << r << g << b << 1.0 + << "}})" + << ascii; + + return gv; +} +#endif // CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_SIMPLEX_H +#endif // CGAL_TETRAHEDRALIZATION_SIMPLEX_H +@} + +\subsection{Tetrahedralization Simplex Input} + +Input here means that when you click with the right mouse button on a +simplex in the Camera of {\it geomview} you get a pointer to the +simplex in your application. Note that it is in the responsibility of +the user to guarantee that the pointer still points to memory allocated +for a simplex. + +The extract operators notify \ccc{interest} for a certain kind of +events. When such an event happens geomview sends a description of the +event in \ccc{gcl} and the extract operator has to parse this +expression. + +@$@+=@{@- +#ifdef CGAL_TETRAHEDRALIZATION_SIMPLEX_H +#ifndef CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_SIMPLEX_H +#define CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_SIMPLEX_H + +#include + +template < class V > +CGAL_Geomview_stream& +operator>>(CGAL_Geomview_stream &gv, + CGAL_Tetrahedralization_simplex*& s) +{ + char* id; + char gclpick[100]; + strcpy(gclpick, "(pick world * nil nil nil nil nil nil nil nil)"); + + gv << ascii ; + gv << "(interest " << gclpick << ")" ; + + while(true) { + gv >> gv.sexpr; // this reads a gcl expression + id = CGAL_nth(gv.sexpr, 2); + + if(! CGAL_is_prefix("Simplex_", id)){ + cerr << "You did not click on a simplex" << endl; + continue; + } else { + break; + } + } + gv << "(ui-target " << id << " yes)"; + gv << "(uninterest " << gclpick << ")"; + id+=8; // remove first 7 chars + unsigned long int ui = strtoul(id, (char **)NULL, 10); + s = (CGAL_Tetrahedralization_simplex*)ui; + + return gv; +} +#endif // CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_SIMPLEX_H +#endif // CGAL_TETRAHEDRALIZATION_SIMPLEX_H +@} + +\subsection{Tetrahedralization Vertex (OFF)} + +We cannot use the code of the point in order to draw a vertex +as we want it to be pickable, and a line even with elevated +\ccc{line_width} is not pickable. We could draw a sphere, but +that slows geomview tremendously down, so we draw a cube. The +size of the cube is controlled by the geomview stream. + + +@$@+=@{@- +#ifdef CGAL_TETRAHEDRALIZATION_VERTEX_H +#ifndef CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_VERTEX_H +#define CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_VERTEX_H + +template < class P > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream &gv, + const CGAL_Tetrahedralization_vertex

* v) +{ + ostrstream os; + os << "Vertex_" << (unsigned long int)v << ends ; + char *id = os.str(); + double x = CGAL_to_double(v->point().x()); + double y = CGAL_to_double(v->point().y()); + double z = CGAL_to_double(v->point().z()); + double radius = gv.get_vertex_radius(); + gv << ascii + << "(geometry " << id << " {appearance {}{ " + << binary + << "OFF BINARY\n" + + // it has 4 vertices, 6 face and 12 edges + << 8 << 6 << 12; + + // the vertices + gv << x-radius << y-radius << z-radius + << x+radius << y-radius << z-radius + << x+radius << y+radius << z-radius + << x-radius << y+radius << z-radius + << x-radius << y+radius << z+radius + << x+radius << y+radius << z+radius + << x+radius << y-radius << z+radius + << x-radius << y-radius << z+radius; + + // the faces + double r = gv.vcr(), + g = gv.vcg(), + b = gv.vcb(); + gv << 4 << 0 << 1 << 6 << 7 << 4 << r << g << b << 1.0 + << 4 << 1 << 2 << 5 << 6 << 4 << r << g << b << 1.0 + << 4 << 2 << 3 << 4 << 5 << 4 << r << g << b << 1.0 + << 4 << 3 << 0 << 7 << 4 << 4 << r << g << b << 1.0 + << 4 << 0 << 1 << 2 << 3 << 4 << r << g << b << 1.0 + << 4 << 4 << 5 << 6 << 7 << 4 << r << g << b << 1.0 + << "}})" << ascii; + + return gv; +} +#endif // CGAL_GV_OUT_CGAL_TETRAHEDRALIZATION_VERTEX_H +#endif // CGAL_TETRAHEDRALIZATION_VERTEX_H +@} + +\subsection{Tetrahedralization Vertex Input} + +@$@+=@{@- +#ifdef CGAL_TETRAHEDRALIZATION_VERTEX_H +#ifndef CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_VERTEX_H +#define CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_VERTEX_H + +#include + +template < class P > +CGAL_Geomview_stream& +operator>>(CGAL_Geomview_stream &gv, + CGAL_Tetrahedralization_vertex

*& v) +{ + char* id; + char gclpick[100]; + strcpy(gclpick, "(pick world * nil nil nil nil nil nil nil nil)"); + + gv << ascii + << "(interest " << gclpick << ")" ; + + while(true) { + gv >> gv.sexpr; // this reads a gcl expression + id = CGAL_nth(gv.sexpr, 2); + + if(! CGAL_is_prefix("Vertex_", id)){ + cerr << "You did not click on a vertex" << endl; + continue; + } else { + break; + } + } + gv << "(ui-target " << id << " yes)"; + gv << "(uninterest " << gclpick << ")"; + + id+=7; // cut first 6 chars + unsigned long int ui = strtoul(id, (char **)NULL, 10); + v = (CGAL_Tetrahedralization_vertex

*)ui; + + return gv; +} +#endif // CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_VERTEX_H +#endif // CGAL_TETRAHEDRALIZATION_VERTEX_H +@} + +\subsection{Tetrahedralization Output} + +@$@+=@{@- +#ifdef CGAL_DELAUNAY_TETRAHEDRALIZATION_H +#ifndef CGAL_GV_OUT_CGAL_DELAUNAY_TETRAHEDRALIZATION_H +#define CGAL_GV_OUT_CGAL_DELAUNAY_TETRAHEDRALIZATION_H +template < class I > +CGAL_Geomview_stream& +operator<<(CGAL_Geomview_stream& gv, + const CGAL_Delaunay_tetrahedralization_3 &DT) +{ + // return gv << (const CGAL_Tetrahedralization_3&)DT; + CGAL_Tetrahedralization_3::Simplex_iterator + it = DT.simplices_begin(), + end = DT.simplices_end(); + + while(it != end){ + if(! DT.is_infinite(*it)){ + gv << *it; + } + ++it; + } + return gv; +} +#endif // CGAL_GV_OUT_CGAL_DELAUNAY_TETRAHEDRALIZATION_H +#endif // CGAL_DELAUNAY_TETRAHEDRALIZATION_H +@} + +\section{Auxiliary Output Operators} + +\subsection{Colors} + +At the moment we do not distinguish between colors for vertices, edges +and faces. Inserting a color in the stream means that from that moment +on everything is painted in the given color. An object of the type +{\tt CGAL\_Color} is constructed with three integers between 0 and 255 + as arguments which determine the fraction of red green and blue. + +@$@+=@{@- +void +CGAL_Geomview_stream::set_bg_color(const CGAL_Color &c) +{ + *this << ascii + << "(backcolor \"Camera\" " + << double(c.r())/255.0 + << double(c.g())/255.0 + << double(c.b())/255.0 + << ")"; +} + +CGAL_Geomview_stream& +CGAL_Geomview_stream::operator<<(const CGAL_Color &c) +{ + col = c; + vertex_color = c; + edge_color = c; + face_color = c; + return (*this); +} + + +CGAL_Color +CGAL_Geomview_stream::get_vertex_color() const +{ + return vertex_color; +} + + +CGAL_Color +CGAL_Geomview_stream::get_edge_color() const +{ + return edge_color; +} + + +CGAL_Color +CGAL_Geomview_stream::get_face_color() const +{ + return face_color; +} + + +CGAL_Color +CGAL_Geomview_stream::set_vertex_color(const CGAL_Color &c) +{ + CGAL_Color old = vertex_color; + vertex_color = c; + return old; +} + + +CGAL_Color +CGAL_Geomview_stream::set_edge_color(const CGAL_Color &c) +{ + CGAL_Color old = edge_color; + edge_color = c; + return old; +} + + +CGAL_Color +CGAL_Geomview_stream::set_face_color(const CGAL_Color &c) +{ + CGAL_Color old = face_color; + face_color = c; + return old; +} + + +double +CGAL_Geomview_stream::vcr() const +{ + return double(vertex_color.r())/255.0; +} + +double +CGAL_Geomview_stream::vcg() const +{ + return double(vertex_color.g())/255.0; +} + +double +CGAL_Geomview_stream::vcb() const +{ + return double(vertex_color.b())/255.0; +} + + +double +CGAL_Geomview_stream::ecr() const +{ + return double(edge_color.r())/255.0; +} + +double +CGAL_Geomview_stream::ecg() const +{ + return double(edge_color.g())/255.0; +} + +double +CGAL_Geomview_stream::ecb() const +{ + return double(edge_color.b())/255.0; +} + +double +CGAL_Geomview_stream::fcr() const +{ + return double(face_color.r())/255.0; +} + +double +CGAL_Geomview_stream::fcg() const +{ + return double(face_color.g())/255.0; +} + +double +CGAL_Geomview_stream::fcb() const +{ + return double(face_color.b())/255.0; +} +@} + + + +\subsection{Ascii and binary Mode} + +@$@+=@{@- +void +CGAL_Geomview_stream::set_binary_mode() +{ + bflag = 1; +} + +void +CGAL_Geomview_stream::set_ascii_mode() +{ + bflag = 0; +} + +bool +CGAL_Geomview_stream::in_binary_mode() const +{ + return bflag; +} + +bool +CGAL_Geomview_stream::in_ascii_mode() const +{ + return ! bflag; +} +@} + + + +{\em Manipulators} are functions that manipulate the state of a +stream (See a book on \CC\ streams). These two are there for switching +between binary and {\sc Ascii} representation of numbers. + + +@$@+=@{@- +inline +CGAL_Geomview_stream& +binary(CGAL_Geomview_stream &os) +{ + os.set_binary_mode(); + return os; +} + +inline +CGAL_Geomview_stream& +ascii(CGAL_Geomview_stream &os) +{ + os.set_ascii_mode(); + return os; +} +@} + +The statement \ccc{os << binary;} inserts a pointer to the +function \ccc{binary(CGAL_Geomview_stream&)} in the stream. The +following function just calls the dereferenced function. + +@$@+=@{@- +CGAL_Geomview_stream& +CGAL_Geomview_stream::operator<< +(CGAL_Geomview_stream&(*fct)(CGAL_Geomview_stream&)) +{ + (*fct)(*this); + return *this; +} +@} + +\subsection{Tracing} + +This allows to see what {\it gcl} expressions are sent to {\it geomview}. +You only need it for debugging. + +@$@+=@{@- +bool +CGAL_Geomview_stream::get_trace() const +{ + return _trace; +} + +bool +CGAL_Geomview_stream::set_trace(bool b) +{ + bool old = _trace; + _trace = b; + return old; +} + + +void +CGAL_Geomview_stream::trace(const char *cptr) const +{ + if(_trace){ + cerr << cptr; + } +} + +void +CGAL_Geomview_stream::trace(double d) const +{ + if(_trace){ + cerr << d << ' '; + } +} + +void +CGAL_Geomview_stream::trace(int i) const +{ + if(_trace){ + cerr << i << ' '; + } +} +@} + +\subsection{Vertex Radius} + +As it is impossible to pick an edge, even with hight \ccc{linewidth} +we have to display a vertex as sphere or cube. The +An ugly solution, but look at the alternatives: +@$@+=@{@- +double +CGAL_Geomview_stream::get_vertex_radius() const +{ + return _radius; +} + + +double +CGAL_Geomview_stream::set_vertex_radius(double r) +{ + double old = _radius; + _radius = r; + return old; +} +@} + +\subsection{Line width} + + +@$@+=@{@- +int +CGAL_Geomview_stream::get_line_width() const +{ + return _line_width; +} + + +int +CGAL_Geomview_stream::set_line_width(int w) +{ + int old = _line_width; + _line_width = w; + return old; +} +@} + +\subsection{Miscellaneous} + +@$@+=@{@- +void +CGAL_Geomview_stream::clear() +{ + (*this) << "(delete World)"; +} + +void +CGAL_Geomview_stream::look_recenter() const +{ + CGAL_Geomview_stream* ncthis = (CGAL_Geomview_stream*)this; + (*ncthis) << "(look-recenter World)"; +} +@} + + +\subsection{Builtin Types} + +Chains of characters are just passed through to geomview. The other +output operators create such strings, but this is also the way for a user +to send a {\it gcl} expression to {\it geomview}. + +@$@+=@{@- +CGAL_Geomview_stream& +CGAL_Geomview_stream::operator<<(const char *cptr) +{ + int length = strlen(cptr); + if (length != write(out, cptr, length)) { + cerr << "write problem in the pipe while sending data to geomview" + << endl; + exit(-1); + } + trace(cptr); + + return *this; +} +@} + +{\it geomview} can read numbers in their {\sc Ascii} representation +as well as in their binary representation. The binary representation +is much more compact (4 byte) and no conversion from binary to {\sc +Ascii} to binary happens. + +@$@+=@{@- +CGAL_Geomview_stream& +CGAL_Geomview_stream::operator<<(int i) +{ + // Depending on the mode chosen + if (in_binary_mode()) { + // we write raw binary data to the stream. + write(out, (char*)&i, sizeof(i)); + } else { + // transform the int in a character sequence and put whitespace around + ostrstream str; + str << i << ' ' << ends; + char *bptr = str.str(); + write(out, bptr, int(strlen(bptr))); + } + trace(i); + + return *this; +} +@} + +{\it geomview} only reads floats. {\bf Attention:} the {\tt double}s used +must fit in a {\tt float}. This is not checked yet. + +@$@+=@{@- +CGAL_Geomview_stream& +CGAL_Geomview_stream::operator<<(double d) +{ + float f = d; + + if (in_binary_mode()) { + write(out, (char*)&f, sizeof(f)); + } else { + // 'copy' the float in a string and append a blank + ostrstream str; + str << f << " " << ends ; + char *bptr = str.str(); + + write(out, bptr, int(strlen(bptr))); + } + trace(f); + return *this; +} +@} + + + + +\subsection{Parsing Lisp} + +The {\it geomview command language} is {\sc Lisp}-like. That means when +{\it geomview} sends us an expression we have to parse it. + + +The following operator reads a Lisp S-expression from the input pipe. +The S-expression is not interpreted though. +It is not clear yet if we can keep it that simple, or if +we have to parse complicated structures, which would make it +reasonable to write a real parser using {\it lex} and {\it yacc}. + +{\bf Attention:} This function does not check if the string is long enough. + +@$@+=@{@- +CGAL_Geomview_stream& +CGAL_Geomview_stream::operator>>(char *expr) +{ + // skip whitespace + read(in, expr, 1); + while(expr[0] != '('){ + read(in, expr, 1); + } + int pcount = 1; + int i = 1; + while (1) { + read(in, &expr[i], 1); + 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 ; +} +@} + + +Here come some utility functions. {\bf Attention:} Function \ccc{CGAL_nth()} +starts at position 0. + +@$@==@{@- +char* +CGAL_nth(char* s, int count); + +bool +CGAL_is_prefix(const char* p, const char* w); +@} + +@$@+=@{@- +char* +CGAL_nth(char* s, int count) +{ + int length = strlen(s); + + // the first character is always a parenthesis + int i = 1, // char count + wc = 0; // word count + + while (1) { + // skip whitespace + while(s[i]==' '){ + i++; + } + int j = 0; + int pcount = 1; + switch (s[i]){ + case '(': + j=i+1; + while (1) { + if ( s[j]==')' ) { + pcount--; + } else if (s[j]=='(' ) { + pcount++; + } + if ((pcount == 0 )&& (wc == count)) { + s[j+1] = '\0'; + return s + i; + } + j++; + } + i = j+1; + break; + case '"': + j = i+1; + while (1) { + if ( (s[j]=='"') && (wc == count) ) { + s[j+1] = '\0'; + return s + i; + } + j++; + } + i = j+1; + break; + default: + j=i+1; + while( ( s[j]!=' ' ) && ( s[j]!=')' ) ){ + j++; + } + if (wc == count){ + s[j] = '\0'; + return s + i; + } + i = j; + break; + } + wc++; + } + + return s; +} +@} + + +@$@+=@{@- +bool +CGAL_is_prefix(const char* p, const char* w) +{ + while((*p != '\0') && (*w != '\0')){ + if(*p != *w){ + return false; + } + ++p; + ++w; + } + if((*w == '\0') && (*p != '\0')){ + return false; + } + return true; +} +@} + +@$@+=@{@- +#ifdef CGAL_POINT_3_H +template < class R > +void +parse_point(char* pickpoint, + CGAL_Point_3 &point) +{ + strstream ss; + ss << pickpoint << ends ; + + double x, y, z, w; + char parenthesis; + ss >> parenthesis >> x >> y >> z >> w; + point = CGAL_Point_3(x, y, z, w); +} +#endif // CGAL_POINT_3_H +@} + + + + + + + +\section{Constructors and Destructors} + +The parameters for the constructor are a bounding box, the machine +where to run {\it geomview}, e.g., {\it sophia.inria.fr}, and a login name +(as you might have another login name on another machine). The +camera is positioned such that the bounding box is in the scene. You +can draw outside the bounding box and you can reposition the camera +later. + +If you do not supply a machine name and a login, it will run on your +local machine. The remote facility is useful if you have a graphics +workstation at hand: run {\it geomview} there while your application +runs on your machine. + + +The constructor of the class \ccc{CGAL_Geomview_stream} forks a +process and establishes two pipes between the processes. The forked +process is then overlayed with geomview. The file descriptors +\ccc{stdin} and \ccc{stdout} of geomview are hooked on the two pipes. + +@$@+=@{@- +CGAL_Geomview_stream::CGAL_Geomview_stream(const CGAL_Bbox_3 &bbox, + const char *machine, + const char *login) + : _line_width(1) +{ + setup_geomview(machine,login); + frame(bbox); + pickplane(bbox); + set_vertex_radius((bbox.xmax() - bbox.xmin())/100.0); +} +@} + +We keep the following constructor for backward compatibility, and +warn the user. + +@$@+=@{@- +CGAL_Geomview_stream::CGAL_Geomview_stream(const char *machine, + const char *login, + const CGAL_Bbox_3 &bbox) + : _line_width(1) +{ + cerr << "Warning: This constructor is going to disappear" << endl + << "The bounding box should come as first argument" << endl + << "machine and login default to NULL" << endl; + setup_geomview(machine,login); + frame(bbox); + pickplane(bbox); + set_vertex_radius((bbox.xmax() - bbox.xmin())/100.0); +} +@} + +@$@+=@{@- +CGAL_Geomview_stream::~CGAL_Geomview_stream() +{ + kill(pid, SIGKILL); // kills geomview +} +@} + +\subsection{The setup of geomview} + +The idea is as follows: We open a pipe. We fork the process, +that is we create a child process. As they both keep all file +descriptors the father process can write into the pipe and the +child process can read from it. We overlay the child process +by {\it geomview}. As we want bidirectional communication we +open not only one pipe but two, but have a look at the details: + +@$@+=@{@- +void CGAL_Geomview_stream::setup_geomview(const char *machine, + const char *login) +{ + bflag = 0; + _trace = false; + col = CGAL_BLACK; + vertex_color = CGAL_BLACK; + edge_color = CGAL_BLACK; + face_color = CGAL_BLACK; + 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 + + cout << "Starting Geomview..." << flush ; + if (pipe(pipe_out) < 0) { + cerr << "out pipe failed" << endl ; + exit(-1); + } + + if (pipe(pipe_in) < 0) { + cerr << "in pipe failed" << endl ; + exit(-1); + } + + switch (pid = fork()){ + case -1: + cerr << "fork failed" << endl ; + exit(-1); +@} + +We forked a child process and connect parent and child to the two ends +of the pipes. We redirect stdin and stdout because these are the +only streams geomview can communicate with. + +@$@+=@{@- + 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. + + + close (0); // this is the file descriptor of cin + dup(pipe_out[0]); // we connect it to the pipe + close (1); // this is the file descriptor of cout + dup(pipe_in[1]); // we connect it to the pipe +@} + +Finally, we overlay the child process with geomview. The function execlp +expects the absolute path for the commands rsh and geomview. These come from +the makefile. + +@$@+=@{@- + if (machine && (strlen(machine)>0)) { + ostrstream os; + os << " rgeomview " << machine << ":0.0" << ends ; + ostrstream logos; + execlp(CGAL_RSH, + "rsh", + machine, + "-l", + login, + os.str(), + (char *)0); + } else { + execlp(CGAL_GEOMVIEW, + "geomview", + "-c", + "-", + (char *)0); + } + + // if we get to this point something went wrong. + cerr << "execl geomview failed" << endl ; + exit(-1); +@} + + +The parent process continues to be the application. The child process, +that is {\it geomview} is ready as soon as we find the string "started" +on the input pipe. Therefore, the following {\it gcl} command must be in the +".geomview file": (echo "started"). + +@$@+=@{@- + 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]; + + char inbuf[10]; + read(in, inbuf, 7); + + cout << "done." << endl; + + bbox_count = 0; + triangle_count = 0; + segment_count = 0; + point_count = 0; + tetrahedron_count = 0; + (*this) << "(normalization g* none)(bbox-draw g* no)" ; + + break; + } +} +@} + + + +\subsection{The Frame} + +The constructor of the geomview stream gets a bounding box as +an argument. This bounding box is drawn and and the camera is +positioned so that you can see the entire bounding box. + +@$@+=@{@- +void +CGAL_Geomview_stream::frame(const CGAL_Bbox_3 &bbox) +{ + (*this) << bbox + << ascii + << "(look-recenter g0 c0)(delete bbox0)" ; +} +@} +\subsection{Pickplane} + + As the input is a point in three-dimensional space, but the screen +is flat, we input points on an object called {\em pickplane}. The +pickplane can be moved around, as it is a normal {\it geomview} object. + +@$@+=@{@- +void +CGAL_Geomview_stream::pickplane(const CGAL_Bbox_3 &bbox) +{ + (*this) << binary + << "(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)" + << ascii ; +} +@} + +\section{The Class Definition} + + +The following two macros contain the public and the private part +respectively. + +\subsection{The public part} + +@$@+=@{@- +class CGAL_Geomview_stream { +public: + CGAL_Geomview_stream(const CGAL_Bbox_3 &bbox = CGAL_Bbox_3(0,0,0, 1,1,1), + const char *machine = (char*)NULL, + const char *login = (char*)NULL); + + // kept for backward compatibility + CGAL_Geomview_stream(const char *machine, + const char *login, + const CGAL_Bbox_3 &bbox = CGAL_Bbox_3(0,0,0, 1,1,1)); + + ~CGAL_Geomview_stream(); + + void clear(); + void look_recenter() const; + + void set_bg_color(const CGAL_Color &c); + + CGAL_Geomview_stream &operator<<(const CGAL_Color &c); + CGAL_Color get_vertex_color() const; + CGAL_Color get_edge_color() const; + CGAL_Color get_face_color() const; + + CGAL_Color set_vertex_color(const CGAL_Color&); + CGAL_Color set_edge_color(const CGAL_Color&); + CGAL_Color set_face_color(const CGAL_Color&); + + double vcr() const; + double vcg() const; + double vcb() const; + + double ecr() const; + double ecg() const; + double ecb() const; + + double fcr() const; + double fcg() const; + double fcb() const; + + double get_vertex_radius() const; + double set_vertex_radius(double r); + + int get_line_width() const; + int set_line_width(int w); + + CGAL_Geomview_stream &operator<<(const char *cptr); + + CGAL_Geomview_stream &operator<<(int i); + CGAL_Geomview_stream &operator<<(double d); + + bool get_trace() const; + bool set_trace(bool b); + + void trace(const char *cptr) const; + void trace(double d) const; + void trace(int i) const; + + void set_binary_mode(); + void set_ascii_mode(); + bool in_binary_mode() const; + bool in_ascii_mode() const; + + CGAL_Geomview_stream &operator<< ( + CGAL_Geomview_stream& (*fct)(CGAL_Geomview_stream&)); + + CGAL_Geomview_stream &operator>>(char *expr); + + int bbox_count; + int triangle_count; + int segment_count; + int point_count; + int tetrahedron_count; + + char sexpr[1024]; +@} + +\subsection{The private part} + +@$@+=@{@- +private: + void setup_geomview(const char *machine, const char *login); + void frame(const CGAL_Bbox_3 &bbox); + void pickplane(const CGAL_Bbox_3 &bbox); + + CGAL_Color col, vertex_color, edge_color, face_color; + bool _trace; + int in; // file descriptor for input pipe + int out; // file descriptor for output pipe + int pid; // the geomview process identification + int bflag ; // bool that makes operator<< write binary format + double _radius; + int _line_width; +}; +@} + +\section{Files} + +\subsection{The Header File} + + +@O@<../include/CGAL/IO/Geomview_stream.h@>==@{@- +#ifndef CGAL_GEOMVIEW_STREAM_H +#define CGAL_GEOMVIEW_STREAM_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +@ + +@ + +@ + +@ + +@ + +@ + +#endif // CGAL_GEOMVIEW_STREAM_H +@} + + +\subsection{The Source File} + +@O@<../src/Geomview_stream.C@>==@{@- + +#include + + +@ + +@ + +@ + +@ + +@ + +@ + +@ + +@} +\begin{thebibliography}{10} + +\bibitem{GVManual} +Mark Phillips et al. +\newblock {\em Geomview Manual}. +\newblock Version 1.6.1 for Unix Workstations. +\newblock December 10, 1996. +\end{thebibliography} + +\end{document} + + + + + +