cgal/Packages/Geomview/web/geomview.fw

1716 lines
42 KiB
Plaintext

@! $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.
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operator templates for geometric objects@>+=@{@-
#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<R> &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)}
@$@<output operators for geometric objects@>+=@{@-
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:
@$@<output operator templates for geometric objects@>+=@{@-
#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.
@$@<output operators for geometric objects@>+=@{@-
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:
@$@<output operator templates for geometric objects@>+=@{@-
#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}
@$@<input operator templates for geometric objects@>+=@{@-
#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<R> &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.
@$@<output operator templates for geometric objects@>+=@{@-
#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<Tr>& T)
{
CGAL_Tetrahedralization_3<Tr>::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.
@$@<output operator templates for geometric objects@>+=@{@-
#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<V>* 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.
@$@<input operator templates for geometric objects@>+=@{@-
#ifdef CGAL_TETRAHEDRALIZATION_SIMPLEX_H
#ifndef CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_SIMPLEX_H
#define CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_SIMPLEX_H
#include <stdlib.h>
template < class V >
CGAL_Geomview_stream&
operator>>(CGAL_Geomview_stream &gv,
CGAL_Tetrahedralization_simplex<V>*& 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<V>*)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.
@$@<output operator templates for geometric objects@>+=@{@-
#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<P>* 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}
@$@<input operator templates for geometric objects@>+=@{@-
#ifdef CGAL_TETRAHEDRALIZATION_VERTEX_H
#ifndef CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_VERTEX_H
#define CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_VERTEX_H
#include <stdlib.h>
template < class P >
CGAL_Geomview_stream&
operator>>(CGAL_Geomview_stream &gv,
CGAL_Tetrahedralization_vertex<P>*& 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<P>*)ui;
return gv;
}
#endif // CGAL_GV_IN_CGAL_TETRAHEDRALIZATION_VERTEX_H
#endif // CGAL_TETRAHEDRALIZATION_VERTEX_H
@}
\subsection{Tetrahedralization Output}
@$@<output operator templates for geometric objects@>+=@{@-
#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<I> &DT)
{
// return gv << (const CGAL_Tetrahedralization_3<I>&)DT;
CGAL_Tetrahedralization_3<I>::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.
@$@<output operators for geometric objects@>+=@{@-
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}
@$@<auxiliary output operators@>+=@{@-
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.
@$@<manipulators@>+=@{@-
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.
@$@<auxiliary output operators@>+=@{@-
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.
@$@<auxiliary output operators@>+=@{@-
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:
@$@<auxiliary output operators@>+=@{@-
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}
@$@<auxiliary output operators@>+=@{@-
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}
@$@<auxiliary output operators@>+=@{@-
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}.
@$@<auxiliary output operators@>+=@{@-
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.
@$@<auxiliary output operators@>+=@{@-
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.
@$@<auxiliary output operators@>+=@{@-
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.
@$@<Lisp functions@>+=@{@-
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.
@$@<Lisp function declarations@>==@{@-
char*
CGAL_nth(char* s, int count);
bool
CGAL_is_prefix(const char* p, const char* w);
@}
@$@<Lisp functions@>+=@{@-
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;
}
@}
@$@<Lisp functions@>+=@{@-
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;
}
@}
@$@<Lisp template functions@>+=@{@-
#ifdef CGAL_POINT_3_H
template < class R >
void
parse_point(char* pickpoint,
CGAL_Point_3<R> &point)
{
strstream ss;
ss << pickpoint << ends ;
double x, y, z, w;
char parenthesis;
ss >> parenthesis >> x >> y >> z >> w;
point = CGAL_Point_3<R>(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.
@$@<constructors and destructors@>+=@{@-
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.
@$@<constructors and destructors@>+=@{@-
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);
}
@}
@$@<constructors and destructors@>+=@{@-
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:
@$@<setup_geomview@>+=@{@-
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.
@$@<setup_geomview@>+=@{@-
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.
@$@<setup_geomview@>+=@{@-
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").
@$@<setup_geomview@>+=@{@-
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.
@$@<frame@>+=@{@-
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.
@$@<pickplane@>+=@{@-
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@>+=@{@-
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}
@$@<class CGAL_Geomview_stream@>+=@{@-
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 <CGAL/Cartesian.h>
#include <CGAL/Bbox_2.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/IO/Color.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <strstream.h>
#include <iomanip.h>
#include <sys/types.h>
#include <sys/uio.h>
@<class CGAL_Geomview_stream@>
@<manipulators@>
@<output operator templates for geometric objects@>
@<Lisp function declarations@>
@<Lisp template functions@>
@<input operator templates for geometric objects@>
#endif // CGAL_GEOMVIEW_STREAM_H
@}
\subsection{The Source File}
@O@<../src/Geomview_stream.C@>==@{@-
#include <CGAL/IO/Geomview_stream.h>
@<constructors and destructors@>
@<setup_geomview@>
@<pickplane@>
@<auxiliary output operators@>
@<output operators for geometric objects@>
@<frame@>
@<Lisp functions@>
@}
\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}