/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "dumpToSVG.h"

/**
 * SECTION:dumpToSVG
 * @short_description: add an export capability into SVG files.
 * @include: extensions/box.h, extensions/axes.h, visu_pairs.h and visu_data.h
 *
 * This provides a write routine to export V_Sim views into SVG
 * files. Currently, this is an experimental feature. Not all V_Sim
 * elements are rendered, only the nodes, the box, the pairs and the
 * axes. All the characteristics are not used (no line stipple for
 * instance). In spin mode, nodes are only atomic.
 *
 * Since: 3.4
 */

#ifdef HAVE_CAIRO

#include <math.h>
#include <string.h>
#include <glib.h>
#include <cairo.h>
#include <GL/gl.h>

#include <visu_tools.h>
#include <visu_pairset.h>
#include <opengl.h>
#include <OSOpenGL/visu_openGL.h>
#include <openGLFunctions/light.h>
#include <openGLFunctions/view.h>
#include <visu_extset.h>
#include <extensions/box.h>
#include <extensions/fogAndBGColor.h>
#include <extensions/axes.h>
#include <extensions/legend.h>
#include <extensions/pairs.h>
#include <pairsModeling/iface_wire.h>
#include <pairsModeling/wire_renderer.h>
#include <renderingMethods/elementAtomic.h>

#if CAIRO_VERSION_MINOR > 1
#include <cairo-svg.h>
#include <cairo-pdf.h>

#define FONT_SIZE 16.
#define FONT_SMALL 12.

#define NVERT 2
#define NVALS 8
#define NPASS 6
#define NBUFF (NVERT * NVALS + NPASS * 2)

#define PAIRS_NVERT 2
#define PAIRS_NVALS 4
#define PAIRS_XBUFF (PAIRS_NVERT * PAIRS_NVALS)
#define PAIRS_NPASS 7
#define PAIRS_NBUFF (PAIRS_XBUFF + PAIRS_NPASS * 2)
#define PAIRS_NCMPT (4 + 1 + PAIRS_NPASS)

static gboolean writeViewInSvgFormat(ToolFileFormat *format, const char* filename,
                                     VisuGlNodeScene *scene, guint width, guint height,
                                     GError **error, ToolVoidDataFunc functionWait, gpointer data);
static gboolean writeViewInPdfFormat(ToolFileFormat *format, const char* filename,
                                     VisuGlNodeScene *scene, guint width, guint height,
                                     GError **error, ToolVoidDataFunc functionWait, gpointer data);

static cairo_pattern_t** svgSetup_patterns(VisuNodeArrayRenderer *array, gboolean flat);
static GList* svgSetup_pairs(VisuData *dataObj);
static void svgSetup_scale(cairo_t *cr, VisuData *dataObj);
static GLfloat* svgCompute_pairs(VisuData *dataObj, VisuGlExtPairs *pairs, int *nPairs_out);
static GLfloat* svgCompute_coordinates(VisuData *dataObj, VisuNodeArrayRenderer *renderer, int *nNodes_out);
static GLfloat* svgCompute_box(int *nBox_out, VisuGlExtBox *extBox);
static gboolean svgDraw_line(cairo_t *cr, float x0, float y0, float x1, float y1,
                             float z0, float z1, float *rgb,
                             gboolean useFog, float *fog, float se[2]);
static void svgDraw_boxBack(cairo_t *cr, GLfloat *box, int nValuesBox, GLfloat val,
                            double width, float *rgb,
                            gboolean useFog, float fog[4], float se[2]);
static void svgDraw_boxFront(cairo_t *cr, GLfloat *box, int nValuesBox, GLfloat val,
                             double width, float *rgb,
                             gboolean useFog, float fog[4], float se[2]);
static void svgDraw_nodesAndPairs(cairo_t *cr, GLfloat *coordinates, int nNodes,
				  GLfloat *pairs, int nPairs, cairo_pattern_t **pat,
				  gboolean flat, gboolean useFog,
                                  float rgbaFog[4], float se[2]);
static void svgDraw_legend(cairo_t *cr, VisuGlExtLegend *extLegend, cairo_pattern_t **pat);
static void svgDraw_axes(cairo_t *cr, VisuGlExtAxes *extAxes);
static void svgDraw_pairs(cairo_t *cr, GLfloat *pairs, int nPairs,
			  int *iPairs, GLfloat val,
                          gboolean useFog, float fog[4], float se[2]);
static void svgDraw_node(cairo_t *cr, VisuElementAtomicShapeId shape, float radius);

static VisuDump *svg;
static double g_width, g_height;
static double zoomLevel = 1.;
static VisuDumpCairoAdd postFunc = NULL;

/**
 * visu_dump_cairo_setPostFunc:
 * @func: (allow-none) (scope call): a #VisuDumpCairoAdd function or %NULL.
 *
 * Allow to add a function that will be called on every Cairo
 * exportation after V_Sim rendering to allow post-processing.
 *
 * Since: 3.7
 **/
void visu_dump_cairo_setPostFunc(VisuDumpCairoAdd func)
{
  DBG_fprintf(stderr, "Visu DumpCairo: set a new post-processing method.\n");
  postFunc = func;
}

const VisuDump* visu_dump_cairo_svg_getStatic()
{
  const gchar *typeSVG[] = {"*.svg", (char*)0};
#define descrSVG _("Scalar Vector Graphic (SVG) file")

  if (svg)
    return svg;

  svg = VISU_DUMP(visu_dump_scene_new(descrSVG, typeSVG, writeViewInSvgFormat, TRUE));

  tool_file_format_addPropertyBoolean(TOOL_FILE_FORMAT(svg), "use_flat_rendering",
                                      _("Use flat colours for scheme rendering"),
                                      FALSE);
  
  return svg;
}
static VisuDump *pdf;
const VisuDump* visu_dump_cairo_pdf_getStatic()
{
  const gchar *typePDF[] = {"*.pdf", (char*)0};
#define descrPDF _("Portable Document Format (PDF) file")

  if (pdf)
    return pdf;

  pdf = VISU_DUMP(visu_dump_scene_new(descrPDF, typePDF, writeViewInPdfFormat, TRUE));

  tool_file_format_addPropertyBoolean(TOOL_FILE_FORMAT(pdf), "use_flat_rendering",
                                      _("Use flat colours for scheme rendering"),
                                      FALSE);
  
  return pdf;
}

static void sort_by_z(float *coordinates, float *buffer,
		      int n, int z, int begin, int end)
{
  int i;
  int middle;

  if( begin >= end ) return;
  
  memcpy(buffer, coordinates + begin * n, sizeof(float) * n);
  memcpy(coordinates + begin * n, coordinates + (end + begin) / 2 * n, sizeof(float) * n);
  memcpy(coordinates + (end + begin) / 2 * n, buffer, sizeof(float) * n);

  middle = begin;
  for(i = begin + 1; i <= end; i++)
    {
      if ( coordinates[i * n + z] > coordinates[begin * n + z] )
	{
	  middle += 1;
	  memcpy(buffer, coordinates + i * n, sizeof(float) * n);
	  memcpy(coordinates + i * n, coordinates + middle * n, sizeof(float) * n);
	  memcpy(coordinates + middle * n, buffer, sizeof(float) * n);
	}
    }
  memcpy(buffer, coordinates + begin * n, sizeof(float) * n);
  memcpy(coordinates + begin * n, coordinates + middle * n, sizeof(float) * n);
  memcpy(coordinates + middle * n, buffer, sizeof(float) * n);
  sort_by_z(coordinates, buffer, n, z, begin, middle - 1);
  sort_by_z(coordinates, buffer, n, z, middle + 1, end);
}

gboolean writeDataToCairoSurface(cairo_surface_t *cairo_surf, guint width, guint height,
                                 ToolFileFormat *format, VisuGlNodeScene *scene, GError **error,
				 ToolVoidDataFunc functionWait, gpointer user_data)
{
  cairo_t *cr;
  cairo_status_t status;
  cairo_pattern_t **pat;
  guint i;
  int nNodes, nPairs;
  float rgbaBg[4], centre[3], fog[4], se[2];
  GLfloat *coordinates, *box, *pairs;
  int nValuesBox;
  gboolean flat;
  VisuNodeArrayIter iter;
  cairo_matrix_t scale = {1., 0., 0., 1., 0., 0.};
  VisuData *dataObj;
  VisuGlView *view;
  double gross;
  guint oldW, oldH;
  VisuGlExtBox *extBox;
  VisuDataColorizer *colorizer;
  VisuPixmapContext *dumpData;

  DBG_fprintf(stderr, "Dump SVG: begin OpenGL buffer writing.\n");

  /* We get the properties related to SVG output. */
  flat = tool_file_format_getPropertyBoolean(format, "use_flat_rendering");
  /* We setup the cairo output. */
  cr = cairo_create(cairo_surf);
  status = cairo_status(cr);
  if (status != CAIRO_STATUS_SUCCESS)
    {
      *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_FILE,
			   "%s", cairo_status_to_string(status));
      cairo_destroy(cr);
      return FALSE;
    }

  view = visu_gl_node_scene_getGlView(scene);

  /* We need a fake pixmap context for OpenGL rendering. */
  dumpData = visu_pixmap_context_new((width > 0) ? width : view->window.width,
                                     (height > 0) ? height : view->window.height);
  if (!dumpData)
    {
      *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_FILE,
			   "can't create off-screen pixmap.");
      cairo_destroy(cr);
      return FALSE;
    }

  /* We set the glViewport of this new context. */
  visu_gl_initContext(VISU_GL(scene));

  /* We must change the viewport and zoom... */
  gross = view->camera.gross;
  oldW = view->window.width;
  oldH = view->window.height;
  /* We unzoom to allow GL to render out-of-the-box points. */
  zoomLevel = MAX(view->camera.gross, 1.f);
  visu_gl_view_setGross(view, MIN(view->camera.gross, 1.f));

  visu_gl_view_setViewport(view,
                           (width > 0) ? width : view->window.width,
                           (height > 0) ? height : view->window.height);

  dataObj = visu_gl_node_scene_getData(scene);

  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
  DBG_fprintf(stderr, "Dump SVG: setup scale and patterns.\n");
  svgSetup_scale(cr, dataObj);
  pat = svgSetup_patterns(VISU_NODE_ARRAY_RENDERER(visu_gl_node_scene_getNodes(scene)), flat);
  DBG_fprintf(stderr, " | OK.\n");

  glPushMatrix();
  visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(dataObj)), centre);
  glTranslated(-centre[0], -centre[1], -centre[2]);

  /* We calculate the node positions. */
  DBG_fprintf(stderr, "Dump SVG: compute coordinates.\n");
  coordinates = svgCompute_coordinates(dataObj, VISU_NODE_ARRAY_RENDERER(visu_gl_node_scene_getNodes(scene)), &nNodes);
  if (functionWait)
    functionWait(user_data);

  /* Calculate the pair positions. */
  nPairs = -1;
  pairs = (GLfloat*)0;
  if (visu_gl_ext_getActive(VISU_GL_EXT(visu_gl_node_scene_getPairs(scene))))
    pairs = svgCompute_pairs(dataObj, visu_gl_node_scene_getPairs(scene), &nPairs);
  if (functionWait)
    functionWait(user_data);

  /* We draw the background colour. */
  visu_gl_ext_set_getBgColor(VISU_GL_EXT_SET(scene), rgbaBg);
  cairo_set_source_rgba(cr, rgbaBg[0], rgbaBg[1], rgbaBg[2], rgbaBg[3]);
  cairo_paint(cr);

  /* Experimental. */
  g_width = width;
  g_height = height;
  cairo_save(cr);
  cairo_scale(cr, zoomLevel, zoomLevel);
  cairo_translate(cr, g_width * (1. / zoomLevel - 1.) * 0.5,
                  g_height * (1. / zoomLevel - 1.) * 0.5);
  cairo_rectangle(cr, -g_width * (1. / zoomLevel - 1.) * 0.5,
                  -g_height * (1. / zoomLevel - 1.) * 0.5,
                  (g_width - 0.5) / zoomLevel, (g_height - 0.5) / zoomLevel);
  cairo_clip(cr);

  /* Draw the back box. */
  nValuesBox = -1;
  box = (GLfloat*)0;
  extBox = visu_gl_node_scene_getBox(scene);
  visu_gl_ext_set_getFogColor(VISU_GL_EXT_SET(scene), fog);
  visu_gl_ext_set_getFogStartFull(VISU_GL_EXT_SET(scene), se);
  if (visu_gl_ext_getActive(VISU_GL_EXT(extBox)))
    {
      box = svgCompute_box(&nValuesBox, visu_gl_node_scene_getBox(scene));
      svgDraw_boxBack(cr, box, nValuesBox, coordinates[nNodes - NBUFF + 3],
                      (double)visu_gl_ext_lined_getWidth(VISU_GL_EXT_LINED(extBox)) / zoomLevel,
                      visu_gl_ext_lined_getRGBA(VISU_GL_EXT_LINED(extBox)),
                      visu_gl_ext_set_getFogActive(VISU_GL_EXT_SET(scene)), fog, se);
    }
  if (functionWait)
    functionWait(user_data);

  DBG_fprintf(stderr, "Dump SVG: begin main SVG exportation.\n");

  /* We draw nodes and pairs. */
  colorizer = visu_node_array_renderer_getColorizer(VISU_NODE_ARRAY_RENDERER(visu_gl_node_scene_getNodes(scene)));
  svgDraw_nodesAndPairs(cr, coordinates, nNodes, pairs, nPairs,
			!(colorizer && visu_data_colorizer_getActive(colorizer)) ?
			pat:(cairo_pattern_t**)0, flat,
                        visu_gl_ext_set_getFogActive(VISU_GL_EXT_SET(scene)), fog, se);
  if (pairs)
    g_free(pairs);
  if (functionWait)
    functionWait(user_data);

  /* Draw the front box. */
  if (box)
    svgDraw_boxFront(cr, box, nValuesBox, coordinates[nNodes - NBUFF + 3],
                     (double)visu_gl_ext_lined_getWidth(VISU_GL_EXT_LINED(extBox)) / zoomLevel,
                     visu_gl_ext_lined_getRGBA(VISU_GL_EXT_LINED(extBox)),
                     visu_gl_ext_set_getFogActive(VISU_GL_EXT_SET(scene)), fog, se);
  if (box)
    g_free(box);
  if (functionWait)
    functionWait(user_data);

  cairo_restore(cr);

  g_free(coordinates);

  /* We draw the axes. */
  if (visu_gl_ext_getActive(VISU_GL_EXT(visu_gl_node_scene_getAxes(scene))))
    svgDraw_axes(cr, visu_gl_node_scene_getAxes(scene));
  if (functionWait)
    functionWait(user_data);

  glPopMatrix();

  /* We draw a legend, if required. */
  if (visu_gl_ext_getActive(VISU_GL_EXT(visu_gl_node_scene_getLegend(scene))))
    svgDraw_legend(cr, visu_gl_node_scene_getLegend(scene), pat);
  if (functionWait)
    functionWait(user_data);

  visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
  for (i = 0; i < iter.nElements; i++)
    cairo_pattern_destroy(pat[i]);
  g_free(pat);

  if (postFunc)
    {
      DBG_fprintf(stderr, "Visu DumpCairo: call the post-processing method.\n");
      cairo_set_matrix(cr, &scale);
      postFunc(cr, width, height);
    }

  cairo_show_page(cr);
  cairo_destroy(cr);

  /* We free the pixmap context. */
  visu_pixmap_context_free(dumpData);

  /* We must put back the viewport... */
  visu_gl_view_setViewport(view, oldW, oldH);
  /* We must put back the gross also. */
  visu_gl_view_setGross(view, gross);

  return TRUE;
}

static gboolean writeViewInSvgFormat(ToolFileFormat *format, const char* filename,
                                     VisuGlNodeScene *scene, guint width, guint height,
                                     GError **error, ToolVoidDataFunc functionWait, gpointer data)
{
  cairo_surface_t *svg_surface;
  cairo_status_t status;

  g_return_val_if_fail(error && !*error, FALSE);

  DBG_fprintf(stderr, "Dump Cairo: begin export to SVG.\n");

  svg_surface = cairo_svg_surface_create(filename, (double)width, (double)height);
  status = cairo_surface_status(svg_surface);
  if (status != CAIRO_STATUS_SUCCESS)
    {
      *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_FILE,
			   "%s", cairo_status_to_string(status));
      cairo_surface_destroy(svg_surface);
      return FALSE;
    }

  writeDataToCairoSurface(svg_surface, width, height, format, scene,
                          error, functionWait, data);
  cairo_surface_destroy(svg_surface);

  return TRUE;
}

static gboolean writeViewInPdfFormat(ToolFileFormat *format, const char* filename,
				      VisuGlNodeScene *scene, guint width, guint height,
				      GError **error, ToolVoidDataFunc functionWait, gpointer data)
{
  cairo_surface_t *pdf_surface;
  cairo_status_t status;

  g_return_val_if_fail(error && !*error, FALSE);

  DBG_fprintf(stderr, "Dump Cairo: begin export to PDF.\n");

  pdf_surface = cairo_pdf_surface_create(filename, (double)width, (double)height);
  status = cairo_surface_status(pdf_surface);
  if (status != CAIRO_STATUS_SUCCESS)
    {
      *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_FILE,
			   "%s", cairo_status_to_string(status));
      cairo_surface_destroy(pdf_surface);
      return FALSE;
    }

  writeDataToCairoSurface(pdf_surface, width, height, format, scene,
                          error, functionWait, data);
  cairo_surface_destroy(pdf_surface);

  return TRUE;
}

static void svgDraw_legend(cairo_t *cr, VisuGlExtLegend *extLegend, cairo_pattern_t **pat)
{
  cairo_matrix_t scaleFont = {FONT_SIZE, 0., 0., -FONT_SIZE, 0., 0.};
  GLfloat *legend;
  GLint nValuesLegend;
  float lgWidth, lgHeight, max;
  int i, iEle;
  gchar *lbl;
  float radius;
  VisuNodeArrayRenderer *nodes;
  VisuNodeArrayRendererIter iter;

  legend = g_malloc(sizeof(GLfloat) * 70000);
  glFeedbackBuffer(70000, GL_3D, legend);
  glRenderMode(GL_FEEDBACK);
  visu_gl_ext_call(VISU_GL_EXT(extLegend), TRUE);
  nValuesLegend = glRenderMode(GL_RENDER);

  cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL,
			 CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size(cr, 12.0);
  /* The two first polygons are the white frame. */
  if (nValuesLegend >= 22)
    {
      cairo_set_source_rgba(cr, 1.f, 1.f, 1.f, 0.4f);
      lgWidth  = legend[5] - legend[2];
      lgHeight = legend[9] - legend[3];
      cairo_rectangle(cr, legend[2], legend[3], lgWidth, lgHeight);
      cairo_fill(cr);
      /* Run to the names and the elements... */
      iEle = 0;
      nodes = visu_gl_ext_legend_getNodes(extLegend);
      visu_node_array_renderer_iter_new(nodes, &iter, TRUE);
      max = visu_node_array_renderer_getMaxElementSize(nodes, NULL);
      for (i = 0; i < nValuesLegend; i++)
	if (legend[i] == GL_BITMAP_TOKEN)
	  {
	    /* Write the name. */
	    cairo_move_to(cr, legend[i + 1], legend[i + 2]);
	    lbl = g_strdup_printf("%s (%d)", iter.element->name, iter.nStoredNodes);
	    cairo_set_source_rgb(cr, 0.f, 0.f, 0.f);
	    cairo_set_font_matrix(cr, &scaleFont);
	    cairo_show_text(cr, lbl);
	    g_free(lbl);

	    /* Draw the element. */
	    radius = 0.4f * lgHeight * visu_element_atomic_getRadius(VISU_ELEMENT_ATOMIC(iter.renderer)) / max;
	    cairo_new_path(cr);
	    cairo_save(cr);
	    cairo_translate(cr, legend[i + 1] - 0.6f * lgHeight,
			    legend[3] + 0.5f * lgHeight);
            svgDraw_node(cr, visu_element_atomic_getShape(VISU_ELEMENT_ATOMIC(iter.renderer)), radius);
	    cairo_save(cr);
	    cairo_scale(cr, radius, radius);
	    cairo_set_source(cr, pat[iEle]);
	    cairo_fill_preserve(cr);
	    cairo_restore(cr);
	    cairo_set_source_rgb(cr, 0.f, 0.f, 0.f);
	    cairo_stroke(cr);
	    cairo_restore(cr);

	    iEle += 1;
            visu_node_array_renderer_iter_next(&iter);
	    do
              i += 4;
	    while (legend[i] == GL_BITMAP_TOKEN && i < nValuesLegend);
	  }
    }
  g_free(legend);
}

static void svgDraw_axes(cairo_t *cr, VisuGlExtAxes *extAxes)
{
  cairo_matrix_t scaleFont = {FONT_SIZE, 0., 0., -FONT_SIZE, 0., 0.};
  GLfloat *axes;
  GLint nValuesAxes;
  float *rgb;

  /* We create the feedback for the axes. */
  axes = g_malloc(sizeof(GLfloat) * 7000);
  glFeedbackBuffer(7000, GL_3D, axes);
  glRenderMode(GL_FEEDBACK);
  visu_gl_ext_call(VISU_GL_EXT(extAxes), TRUE);
  nValuesAxes = glRenderMode(GL_RENDER);
  DBG_fprintf(stderr, "Dump Cairo: found %d axes output.\n", nValuesAxes);

  cairo_set_line_width(cr, (double)visu_gl_ext_lined_getWidth(VISU_GL_EXT_LINED(extAxes)));
  rgb = visu_gl_ext_lined_getRGBA(VISU_GL_EXT_LINED(extAxes));
  cairo_set_source_rgb(cr, rgb[0], rgb[1], rgb[2]);
  cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL,
			 CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size(cr, 12.0);
  /* We draw the 3 lines of the axes. */
  if (nValuesAxes >= 21 && axes[0] == GL_LINE_RESET_TOKEN &&
      axes[7] == GL_LINE_RESET_TOKEN && axes[14] == GL_LINE_RESET_TOKEN)
    {
      DBG_fprintf(stderr, "Dump Cairo: output axes.\n");
      cairo_move_to(cr, axes[0 + 1], axes[0 + 2]);
      cairo_line_to(cr, axes[0 + 4], axes[0 + 5]);
      cairo_stroke(cr);
      cairo_move_to(cr, axes[7 + 1], axes[7 + 2]);
      cairo_line_to(cr, axes[7 + 4], axes[7 + 5]);
      cairo_stroke(cr);
      cairo_move_to(cr, axes[14 + 1], axes[14 + 2]);
      cairo_line_to(cr, axes[14 + 4], axes[14 + 5]);
      cairo_stroke(cr);
    }
  cairo_set_source_rgb(cr, 1.f - rgb[0], 1.f - rgb[1], 1.f - rgb[2]);
  cairo_set_font_matrix(cr, &scaleFont);
  if (nValuesAxes >= 33 && axes[21] == GL_BITMAP_TOKEN &&
      axes[25] == GL_BITMAP_TOKEN && axes[29] == GL_BITMAP_TOKEN)
    {
      DBG_fprintf(stderr, "Dump Cairo: output axes names.\n");
      cairo_move_to(cr, axes[21 + 1], axes[21 + 2]);
      cairo_show_text(cr, "x");
      cairo_move_to(cr, axes[25 + 1], axes[25 + 2]);
      cairo_show_text(cr, "y");
      cairo_move_to(cr, axes[29 + 1], axes[29 + 2]);
      cairo_show_text(cr, "z");
    }
  g_free(axes);
}

static GList* svgSetup_pairs(VisuData *dataObj)
{
  GList *pairsLst;
  VisuPairLinkIter iter, *pairData;
  VisuPairSet *pairs;
  VisuPairSetIter it;
  gboolean valid;

  pairsLst = (GList*)0;

  pairs = visu_pair_set_new();
  visu_pair_set_setModel(pairs, dataObj);
  for (visu_pair_set_iter_new(pairs, &it, FALSE); it.link; visu_pair_set_iter_next(&it))
    {
      for (valid = visu_pair_link_iter_new(it.link, dataObj, &iter, FALSE); valid;
           valid = visu_pair_link_iter_next(&iter))
        {
#if GLIB_MINOR_VERSION > 9
          pairData = g_slice_alloc(sizeof(VisuPairLinkIter));
#else
          pairData = g_malloc(sizeof(VisuPairLinkIter));
#endif
          *pairData = iter;
          pairsLst = g_list_prepend(pairsLst, pairData);
        }
    }
  g_object_unref(pairs);
  return pairsLst;
}

static GLfloat* svgCompute_pairs(VisuData *dataObj, VisuGlExtPairs *pairs, int *nPairs_out)
{
  int nPairs, i;
  guint width;
  GLfloat *glPairs;
  GLint nValues;
  GList *tmpLst;
  VisuPairLinkIter *pairData;
  float u[3], radius, norm;
  ToolColor *color;
  float tmpPairs[6];
  GList *pairsLst;
  VisuElementRenderer *eleRenderer;
  VisuNodeArrayRenderer *nodeRenderer;
  VisuPairLinkRenderer *renderer;
  VisuDataColorizer *colorizer;

  pairsLst = svgSetup_pairs(dataObj);
  *nPairs_out = nPairs = g_list_length(pairsLst);
  if (nPairs <= 0)
    return (GLfloat*)0;

  DBG_fprintf(stderr, "Dump Cairo: found %d pairs to draw.\n", nPairs);
  glPairs = g_malloc(sizeof(GLfloat) * nPairs * PAIRS_NBUFF);
  glFeedbackBuffer(nPairs * PAIRS_NBUFF, GL_3D, glPairs);
  glRenderMode(GL_FEEDBACK);
  glPushMatrix();

  /* Render the list of pairs and free them each time. */
  nodeRenderer = visu_gl_ext_pairs_getDataRenderer(pairs);
  colorizer = visu_node_array_renderer_getColorizer(nodeRenderer);
  for (tmpLst = pairsLst; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      pairData = (VisuPairLinkIter*)tmpLst->data;

      u[0] = pairData->xyz2[0] - pairData->xyz1[0];
      u[1] = pairData->xyz2[1] - pairData->xyz1[1];
      u[2] = pairData->xyz2[2] - pairData->xyz1[2];
      norm = sqrt(pairData->d2);
      u[0] /= norm;
      u[1] /= norm;
      u[2] /= norm;
      eleRenderer = visu_node_array_renderer_get(nodeRenderer, pairData->iter1.element);
      radius = visu_element_renderer_getExtent(eleRenderer);
      radius *= (colorizer) ? visu_data_colorizer_getScalingFactor(colorizer, dataObj, pairData->iter1.node) : 1.f;
      pairData->xyz1[0] += radius * u[0];
      pairData->xyz1[1] += radius * u[1];
      pairData->xyz1[2] += radius * u[2];
      eleRenderer = visu_node_array_renderer_get(nodeRenderer, pairData->iter2.element);
      radius = visu_element_renderer_getExtent(eleRenderer);
      radius *= (colorizer) ? visu_data_colorizer_getScalingFactor(colorizer, dataObj, pairData->iter2.node) : 1.f;
      pairData->xyz2[0] -= radius * u[0];
      pairData->xyz2[1] -= radius * u[1];
      pairData->xyz2[2] -= radius * u[2];
      glBegin(GL_POINTS);
      glVertex3fv(pairData->xyz1);
      glVertex3fv(pairData->xyz2);
      glEnd();
      /* We save colour channel as passthrough. */
      color = visu_pair_link_getColor(pairData->parent);
      glPassThrough(color->rgba[0]);
      glPassThrough(color->rgba[1]);
      glPassThrough(color->rgba[2]);
      glPassThrough(pairData->coeff);
      /* We save the width and the printLength in a passthrough. */
      renderer = visu_gl_ext_pairs_getLinkRenderer(pairs, pairData->parent);
      if (VISU_IS_PAIR_WIRE_RENDERER(renderer))
        width = visu_pair_wire_getWidth(VISU_PAIR_WIRE(pairData->parent));
      else
        width = 10;
      glPassThrough((float)width);
      if (visu_pair_link_getPrintLength(pairData->parent))
	glPassThrough(norm);
      else
	glPassThrough(-1.f);
      glPassThrough((float)visu_pair_wire_getStipple(VISU_PAIR_WIRE(pairData->parent)));
	  
      /* Free the data. */
#if GLIB_MINOR_VERSION > 9
      g_slice_free1(sizeof(VisuPairLinkIter), tmpLst->data);
#else
      g_free(tmpLst->data);
#endif
    }
  glPopMatrix();

  /* Free the list itself. */
  g_list_free(pairsLst);

  /* Analyse the OpenGL results. */
  nValues = glRenderMode(GL_RENDER);
  DBG_fprintf(stderr, " | OpenGL returns %d.\n", nValues);
  i = 0;
  nPairs = 0;
  while (i < nValues)
    {
	  
      if (glPairs[i] == GL_POINT_TOKEN && 
	  glPairs[i + PAIRS_NVALS] == GL_POINT_TOKEN &&
	  glPairs[i + 2 * PAIRS_NVALS] == GL_PASS_THROUGH_TOKEN)
	{
	  /* Copy all these values into the beginning of the pairs
	     array. */
	  norm = (glPairs[i + 3] + glPairs[i + 7]) / 2.f;
	  glPairs[nPairs * PAIRS_NCMPT +  0] = glPairs[i + 1]; /* x1 */
	  glPairs[nPairs * PAIRS_NCMPT +  1] = glPairs[i + 2]; /* y1 */
	  glPairs[nPairs * PAIRS_NCMPT +  2] = glPairs[i + 5]; /* x2 */
	  glPairs[nPairs * PAIRS_NCMPT +  3] = glPairs[i + 6]; /* y2 */
	  glPairs[nPairs * PAIRS_NCMPT +  4] = norm;         /* altitude */
	  glPairs[nPairs * PAIRS_NCMPT +  5] = glPairs[i + PAIRS_XBUFF +  7]; /* alpha */
	  glPairs[nPairs * PAIRS_NCMPT +  6] = glPairs[i + PAIRS_XBUFF +  1]; /* red   */
	  glPairs[nPairs * PAIRS_NCMPT +  7] = glPairs[i + PAIRS_XBUFF +  3]; /* green */
	  glPairs[nPairs * PAIRS_NCMPT +  8] = glPairs[i + PAIRS_XBUFF +  5]; /* blue  */
	  glPairs[nPairs * PAIRS_NCMPT +  9] = glPairs[i + PAIRS_XBUFF +  9]; /* width */
	  glPairs[nPairs * PAIRS_NCMPT + 10] = glPairs[i + PAIRS_XBUFF + 11]; /* prtLg */
	  glPairs[nPairs * PAIRS_NCMPT + 11] = glPairs[i + PAIRS_XBUFF + 13]; /* Stipp */
	  i += PAIRS_NBUFF;
	  nPairs += 1;
	}
      else if (glPairs[i] == GL_POINT_TOKEN &&
	       glPairs[i + PAIRS_NVALS] == GL_PASS_THROUGH_TOKEN)
	{
	  DBG_fprintf(stderr, "| uncomplete pair for i=%d\n", i);
	  i += PAIRS_NVALS + 2 * PAIRS_NPASS;
	}
      else if (glPairs[i] == GL_PASS_THROUGH_TOKEN)
	{
	  DBG_fprintf(stderr, "| no pair for i=%d\n", i);
	  i += 2 * PAIRS_NPASS;
	}
    }
  sort_by_z(glPairs, tmpPairs, PAIRS_NCMPT, 4, 0, nPairs - 1);
  DBG_fprintf(stderr, " | will draw %d pairs.\n", nPairs);
  *nPairs_out = nPairs;

  return glPairs;
}

static GLfloat* svgCompute_coordinates(VisuData *dataObj, VisuNodeArrayRenderer *nodeRenderer, int *nNodes_out)
{
  VisuNodeArrayIter iter;
  VisuElementRenderer *renderer;
  const ToolColor *color;
  const gfloat *material;
  GLfloat *coordinates;
  GLint nValues;
  float tmpFloat[NBUFF];
  float modelView[16];
  float xyz[3], radius, rgba[4];
  int i, nNodes;
  VisuDataColorizer *colorizer;

  colorizer = visu_node_array_renderer_getColorizer(nodeRenderer);
  visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);

  /* We create a feedback mode to get node coordinates. */
  coordinates = g_malloc(sizeof(GLfloat) * (iter.nAllStoredNodes * NBUFF));
  glFeedbackBuffer((iter.nAllStoredNodes * NBUFF), GL_3D_COLOR, coordinates);
  glRenderMode(GL_FEEDBACK);
  glGetFloatv(GL_MODELVIEW_MATRIX, modelView);

  /* First thing is to order nodes along z axes. */
  for (visu_node_array_iterStartVisible(VISU_NODE_ARRAY(dataObj), &iter); iter.node;
       visu_node_array_iterNextVisible(VISU_NODE_ARRAY(dataObj), &iter))
    {
      renderer = visu_node_array_renderer_get(nodeRenderer, iter.element);
      color = visu_element_renderer_getColor(renderer);
      material = visu_element_renderer_getMaterial(renderer);
      glBegin(GL_POINTS);
      visu_data_getNodePosition(dataObj, iter.node, xyz);
      if (!colorizer || !visu_data_colorizer_getColor(colorizer, rgba, dataObj, iter.node))
        {
          rgba[0] = color->rgba[0];
          rgba[1] = color->rgba[1];
          rgba[2] = color->rgba[2];
          rgba[3] = color->rgba[3];
        }
      visu_gl_setColor((VisuGl*)0, material, rgba);
      /* We compute the node position in the eyes coordinates. */
      glVertex3fv(xyz);
      /* We compute the node apparent radius using the real radius in
	 X direction in ModelView. */
      radius = visu_element_atomic_getRadius(VISU_ELEMENT_ATOMIC(renderer));
      radius *= (colorizer) ? visu_data_colorizer_getScalingFactor(colorizer, dataObj, iter.node) : 1.f;
      glVertex3f(xyz[0] + modelView[0] * radius,
		 xyz[1] + modelView[4] * radius,
		 xyz[2] + modelView[8] * radius);
      glEnd();

      /* We store the number of the VisuElement. */
      glPassThrough(iter.iElement);
      /* We store the element shape. */
      glPassThrough(visu_element_atomic_getShape(VISU_ELEMENT_ATOMIC(renderer)));
      /* We store the node colour. */
      glPassThrough(rgba[0]);
      glPassThrough(rgba[1]);
      glPassThrough(rgba[2]);
      glPassThrough(rgba[3]);
    }

  /* We sort the coordinates along z. */
  nValues = glRenderMode(GL_RENDER);
  /* We compact coordinates to keep complete vertex list. */
  i = 0;
  nNodes = 0;
  while (i < nValues)
    {
      if (coordinates[i] == GL_POINT_TOKEN &&
	  coordinates[i + NVALS] == GL_POINT_TOKEN)
	{
	  /* 	  fprintf(stderr, "Found a complete node %d, at %gx%g.\n", */
	  /* 		  i, coordinates[i + 1], coordinates[i + 2]); */
	  /* 	  fprintf(stderr, " | move it from %d to %d.\n", i, nNodes); */
	  /* A complete set, copying it to nNodes location. */
	  if (nNodes != i)
	    memcpy(coordinates + nNodes, coordinates + i, sizeof(GLfloat) * NBUFF);
	  i += NBUFF;
	  nNodes += NBUFF;
	}
      else
	{
	  /* 	  fprintf(stderr, "Found a uncomplete node at %d.\n", i); */
	  /* Incomplete set, go on till the GL_PASS_THROUGH_TOKEN. */
	  while (coordinates[i] != GL_PASS_THROUGH_TOKEN)
	    i += 1;
	  /* Remove the GL_POINT_TOKEN. */
	  i += 2;
	  /* 	  fprintf(stderr, " | jump to %d.\n", i); */
	}
    }
  sort_by_z(coordinates, tmpFloat, NBUFF, 3, 0, nNodes / NBUFF - 1);

  *nNodes_out = nNodes;
  return coordinates;
}

static cairo_pattern_t* svgGet_pattern(gboolean flat, const float rgba[4], float alpha)
{
  cairo_pattern_t *pat;
  float hsl[3], rgb[3], lum;
  if (flat)
    pat = cairo_pattern_create_rgba(rgba[0], rgba[1], rgba[2], rgba[3] * alpha);
  else
    {
      pat = cairo_pattern_create_radial(.4f, .4f, .1f, 0.f, 0.f, 1.f);
      /* We get the Element colour in HSL. */
      tool_color_convertRGBtoHSL(hsl, rgba);
      lum = hsl[2];
      hsl[2] = CLAMP(lum + 0.2f, 0.f, 1.f);
      tool_color_convertHSLtoRGB(rgb, hsl);
      cairo_pattern_add_color_stop_rgba(pat, 0, rgb[0], rgb[1], rgb[2], alpha);
      hsl[2] = CLAMP(lum + 0.05f, 0.f, 1.f);
      tool_color_convertHSLtoRGB(rgb, hsl);
      cairo_pattern_add_color_stop_rgba(pat, 0.3, rgb[0], rgb[1], rgb[2], alpha);
      hsl[2] = CLAMP(lum - 0.05f, 0.f, 1.f);
      tool_color_convertHSLtoRGB(rgb, hsl);
      cairo_pattern_add_color_stop_rgba(pat, 0.7, rgb[0], rgb[1], rgb[2], alpha);
      hsl[2] = CLAMP(lum - 0.2f, 0.f, 1.f);
      tool_color_convertHSLtoRGB(rgb, hsl);
      cairo_pattern_add_color_stop_rgba(pat, 1, rgb[0], rgb[1], rgb[2], alpha);
    }
  return pat;
}
static cairo_pattern_t** svgSetup_patterns(VisuNodeArrayRenderer *array, gboolean flat)
{
  cairo_pattern_t **pat;
  VisuNodeArrayRendererIter iter;
  guint i;

  pat = g_malloc(sizeof(cairo_pattern_t*) * visu_node_array_getNElements(visu_node_array_renderer_getNodeArray(array), FALSE));
  for (visu_node_array_renderer_iter_new(array, &iter, FALSE), i = 0; iter.element;
       visu_node_array_renderer_iter_next(&iter), i++)
    pat[i] = svgGet_pattern(flat, visu_element_renderer_getColor(iter.renderer)->rgba, 1.f);
  return pat;
}

static void svgSetup_scale(cairo_t *cr, VisuData *dataObj _U_)
{
  int viewport[4];
  cairo_matrix_t scale = {1., 0., 0., -1., 0., 0.};

  glGetIntegerv(GL_VIEWPORT, viewport);
  scale.y0 = (double)viewport[3];
  cairo_set_matrix(cr, &scale);
}

static GLfloat* svgCompute_box(int *nBox_out, VisuGlExtBox *extBox)
{
  GLfloat *box;
  GLint nValuesBox;

  box = g_malloc(sizeof(GLfloat) * 7000);
  glFeedbackBuffer(7000, GL_3D, box);
  glRenderMode(GL_FEEDBACK);
  visu_gl_ext_call(VISU_GL_EXT(extBox), FALSE);
  nValuesBox = glRenderMode(GL_RENDER);

  *nBox_out = (int)nValuesBox;
  return box;
}
static gboolean svgDraw_line(cairo_t *cr, float x0, float y0, float x1, float y1,
                             float z0, float z1, float *rgb,
                             gboolean useFog, float fog[4], float se[2])
{
  float d[4], a;
  cairo_pattern_t *pat;
  double xm, ym; /* projection of surface centre on AB. */
  double xo, yo /* surface centre. */;
  double alpha, beta, lambda;
  double xmin, xmax, ymin, ymax;

  alpha = x1 - x0;
  beta  = y1 - y0;
  xo = g_width * 0.5;
  yo = g_height * 0.5;
  xm = alpha * alpha * xo - alpha * beta * (y0 - yo) + beta * beta * x0;
  ym = alpha * alpha * y0 - alpha * beta * (x0 - xo) + beta * beta * yo;
  xm /= alpha * alpha + beta * beta;
  ym /= alpha * alpha + beta * beta;
  lambda = (alpha * (xm - x0) + beta * (ym - y0)) / (alpha * alpha + beta * beta);

  xmin = g_width * (1. - 1. / zoomLevel) * 0.5;
  xmax = g_width * (1. + 1. / zoomLevel) * 0.5;
  ymin = g_height * (1. - 1. / zoomLevel) * 0.5;
  ymax = g_height * (1. + 1. / zoomLevel) * 0.5;

  /* The projection of AB is outside the box. */
  if (xm < xmin || xm > xmax || ym < ymin || ym > ymax)
    return FALSE;
  /* The projection is outside AB and a and B are outside the box. */
  if ((lambda < 0. || lambda > 1.) &&
      (x0 < xmin || x0 > xmax || y0 < ymin || y0 > ymax) &&
      (x1 < xmin || x1 > xmax || y1 < ymin || y1 > ymax))
    return FALSE;

  pat = (cairo_pattern_t*)0;
  if (useFog)
    {
      DBG_fprintf(stderr, "Dump SVG: draw a line between %g - %g with fog at %g - %g.\n",
                  z0, z1, se[0], se[1]);
      if (z0 != z1)
	{
	  pat = cairo_pattern_create_linear(x0, y0, x1, y1);
	  a = CLAMP(se[0] + (se[1] - z0) / (se[1] - se[0]), 0.f, 1.f);
	  d[0] = a * rgb[0] + (1.f - a) * fog[0];
	  d[1] = a * rgb[1] + (1.f - a) * fog[1];
	  d[2] = a * rgb[2] + (1.f - a) * fog[2];
	  d[3] = a          + (1.f - a) * fog[3];
	  cairo_pattern_add_color_stop_rgba(pat, 0, d[0], d[1], d[2], d[3]);
	  a = CLAMP(se[0] + (se[1] - z1) / (se[1] - se[0]), 0.f, 1.f);
	  d[0] = a * rgb[0] + (1.f - a) * fog[0];
	  d[1] = a * rgb[1] + (1.f - a) * fog[1];
	  d[2] = a * rgb[2] + (1.f - a) * fog[2];
	  d[3] = a          + (1.f - a) * fog[3];
	  cairo_pattern_add_color_stop_rgba(pat, 1, d[0], d[1], d[2], d[3]);
	  cairo_set_source(cr, pat);
	}
      else
	{
	  a = CLAMP(se[0] + (se[1] - z1) / (se[1] - se[0]), 0.f, 1.f);
	  d[0] = a * rgb[0] + (1.f - a) * fog[0];
	  d[1] = a * rgb[1] + (1.f - a) * fog[1];
	  d[2] = a * rgb[2] + (1.f - a) * fog[2];
	  d[3] = a          + (1.f - a) * fog[3];
	  cairo_set_source_rgba(cr, d[0], d[1], d[2], d[3]);
	}	
    }
  else
    cairo_set_source_rgb(cr, rgb[0], rgb[1], rgb[2]);
  cairo_move_to(cr, x0, y0);
  cairo_line_to(cr, x1, y1);
  cairo_stroke(cr);
  if (pat)
    cairo_pattern_destroy(pat);

  return TRUE;
}
static void svgDraw_boxBack(cairo_t *cr, GLfloat *box, int nValuesBox, GLfloat val,
                            double width, float *rgb,
                            gboolean useFog, float fog[4], float se[2])
{
  int i;

  cairo_set_line_width(cr, width);
  /* We draw the lines that have a boundary hidden by elements. */
  for (i = 0; i < nValuesBox; i += 7)
    if (box[i + 3] >= val && box[i + 6] >= val)
      svgDraw_line(cr, box[i + 1], box[i + 2], box[i + 4], box[i + 5],
		   box[i + 3], box[i + 6], rgb, useFog, fog, se);
}
static void svgDraw_boxFront(cairo_t *cr, GLfloat *box, int nValuesBox, GLfloat val,
                             double width, float *rgb,
                             gboolean useFog, float fog[4], float se[2])
{
  int i;

  cairo_set_line_width(cr, width);
  /* We draw the lines that have a boundary hidden by elements. */
  for (i = 0; i < nValuesBox; i += 7)
    if (box[i + 3] < val || box[i + 6] < val)
      svgDraw_line(cr, box[i + 1], box[i + 2], box[i + 4], box[i + 5],
		   box[i + 3], box[i + 6], rgb, useFog, fog, se);
}

static void svgDraw_node(cairo_t *cr, VisuElementAtomicShapeId shape, float radius)
{
  if (shape == VISU_ELEMENT_ATOMIC_POINT)
    cairo_rectangle(cr, -radius / 4, -radius / 4, radius / 2, radius / 2);
  else
    cairo_arc(cr, 0.f, 0.f, radius, 0., 2 * G_PI);
}

static void svgDraw_nodesAndPairs(cairo_t *cr, GLfloat *coordinates, int nNodes,
				  GLfloat *pairs, int nPairs, cairo_pattern_t **pat,
				  gboolean flat, gboolean useFog,
                                  float rgbaFog[4], float se[2])
{
  int iPairs, i;
  float alpha, radius, rgba[4];
  cairo_matrix_t scaleFont = {FONT_SMALL / zoomLevel, 0., 0.,
                              -FONT_SMALL / zoomLevel, 0., 0.};
  cairo_pattern_t *pat_;
  VisuElementAtomicShapeId shape;
  double xmin, xmax, ymin, ymax;

  cairo_set_line_width(cr, 1. / zoomLevel);
  cairo_set_font_matrix(cr, &scaleFont);
  iPairs = 0;
  alpha = 1.f;
  xmin = g_width * (1. - 1. / zoomLevel) * 0.5;
  xmax = g_width * (1. + 1. / zoomLevel) * 0.5;
  ymin = g_height * (1. - 1. / zoomLevel) * 0.5;
  ymax = g_height * (1. + 1. / zoomLevel) * 0.5;
  for (i = 0; i < nNodes; i+= NBUFF)
    {
      /* We draw the pairs in between. */
      svgDraw_pairs(cr, pairs, nPairs, &iPairs, coordinates[i + 3], useFog, rgbaFog, se);

      /* Compute the alpha for the fog. */
      alpha = (useFog) ?
        CLAMP(se[0] + (se[1] - coordinates[i + 3]) / (se[1] - se[0]), 0.f, 1.f) : 1.f;
      
      radius = (coordinates[i + NVALS + 1] - coordinates[i + 1]) *
	(coordinates[i + NVALS + 1] - coordinates[i + 1]) +
	(coordinates[i + NVALS + 2] - coordinates[i + 2]) *
	(coordinates[i + NVALS + 2] - coordinates[i + 2]);
      radius = sqrt(radius);

/*       fprintf(stderr, "%gx%g %g %g %g\n", coordinates[i + 1], coordinates[i + 2], */
/*  	      radius, coordinates[i + 3], coordinates[i + NBUFF - 1]); */
      
      if (alpha > 0.f && radius > 0.f &&
          (coordinates[i + 1] + radius) >= xmin &&
          (coordinates[i + 1] - radius) <= xmax &&
          (coordinates[i + 2] + radius) >= ymin &&
          (coordinates[i + 2] - radius) <= ymax )
	{
          /* cairo_push_group(cr); */
          cairo_new_path(cr);
          cairo_save(cr);

          shape = (VisuElementAtomicShapeId)coordinates[i + NVERT * NVALS + 3];
	  cairo_translate(cr, coordinates[i + 1], coordinates[i + 2]);
          svgDraw_node(cr, shape, radius);
	  cairo_save(cr);
	  cairo_scale(cr, radius, radius);
      
	  if (pat)
	    cairo_set_source(cr, pat[(int)coordinates[i + NVERT * NVALS + 1]]);
	  else
	    {
              if (flat)
                {
                  rgba[0] = coordinates[i + NVERT * NVALS + 5];
                  rgba[1] = coordinates[i + NVERT * NVALS + 7];
                  rgba[2] = coordinates[i + NVERT * NVALS + 9];
                  rgba[3] = coordinates[i + NVERT * NVALS + 11];
                }
              else
                {
                  rgba[0] = coordinates[i + 4];
                  rgba[1] = coordinates[i + 5];
                  rgba[2] = coordinates[i + 6];
                  rgba[3] = 1.f;
                }
	      pat_ = svgGet_pattern(flat, rgba, 1.f);
	      cairo_set_source(cr, pat_);
	      cairo_pattern_destroy(pat_);
	    }
	  cairo_fill_preserve(cr);
	  if (alpha < 1.f)
	    {
	      cairo_set_source_rgba(cr, rgbaFog[0], rgbaFog[1], rgbaFog[2],
				    1.f - alpha);
	      cairo_fill_preserve(cr);
            }
	  cairo_restore(cr);
	  cairo_set_source_rgb(cr, (1.f - alpha) * rgbaFog[0],
			       (1.f - alpha) * rgbaFog[1],
			       (1.f - alpha) * rgbaFog[2]);
          cairo_set_line_width(cr, 1. / zoomLevel);
	  cairo_stroke(cr);
          cairo_restore(cr);
          /* cairo_pop_group_to_source(cr); */
          /* cairo_paint_with_alpha (cr, alpha); */
	}
    }
  /* We draw the remaining pairs. */
  svgDraw_pairs(cr, pairs, nPairs, &iPairs, -1, useFog, rgbaFog, se);
}
static int svgGet_stipple(guint16 stipple, double dashes[16])
{
  int n, i;
  gboolean status;

  n = 0;
  memset(dashes, '\0', sizeof(double) * 16);
  for (i = 0, status = (stipple & 1); i < 16; status = (stipple & (1 << i++)))
    {
      if (((stipple & (1 << i)) && !status) || (!(stipple & (1 << i)) && status))
        n += 1;
      dashes[n] += 1;
    }
  DBG_fprintf(stderr, " | %d -> ", stipple);
  for (i = 0; i <= n; i++)
    DBG_fprintf(stderr, " %f", dashes[i]);
  DBG_fprintf(stderr, "\n");
  return n + 1;
}
static void svgDraw_pairs(cairo_t *cr, GLfloat *pairs, int nPairs,
			  int *iPairs, GLfloat val,
                          gboolean useFog, float fog[4], float se[2])
{
  char distStr[8];
  double dashes[16];
  int n;

  if (pairs && *iPairs < PAIRS_NCMPT * nPairs)
    {
      while (pairs[*iPairs + 4] > val && *iPairs < PAIRS_NCMPT * nPairs)
	{
          n = svgGet_stipple((guint16)pairs[*iPairs + 11], dashes);
	  DBG_fprintf(stderr, " | pair %d at (%f;%f) - (%f;%f) %g,%g %d -> %d\n",
		      *iPairs / PAIRS_NCMPT,
		      pairs[*iPairs + 0], pairs[*iPairs + 1],
		      pairs[*iPairs + 2], pairs[*iPairs + 3],
		      pairs[*iPairs + 4], pairs[*iPairs + 5],
                      (guint16)pairs[*iPairs + 11], n);
          if (n > 1)
            cairo_set_dash(cr, dashes, n, 0);
	  cairo_set_line_width(cr, pairs[*iPairs + 9] / zoomLevel);
	  if (svgDraw_line(cr, pairs[*iPairs + 0], pairs[*iPairs + 1],
                           pairs[*iPairs + 2], pairs[*iPairs + 3],
                           pairs[*iPairs + 4], pairs[*iPairs + 4],
                           pairs + *iPairs + 6, useFog, fog, se) &&
              pairs[*iPairs + 10] > 0)
	    {
	      cairo_move_to(cr, (pairs[*iPairs + 0] + pairs[*iPairs + 2]) * 0.5,
			    (pairs[*iPairs + 1] + pairs[*iPairs + 3]) * 0.5);
	      sprintf(distStr, "%7.3f", pairs[*iPairs + 10]);
	      cairo_show_text(cr, distStr);
	    }
          cairo_set_dash(cr, dashes, 0, 0);
	  *iPairs += PAIRS_NCMPT;
	}
      cairo_set_line_width(cr, 1. / zoomLevel);
    }
}


#endif
#endif
