mirror of https://github.com/CGAL/cgal
1716 lines
42 KiB
Plaintext
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}
|
|
|
|
|
|
|
|
|
|
|
|
|