/* ui_debug.c: debug submenu */

#include <stdio.h>

#include "ui.h"
#include "uit.h"
#include "scene.h"
#include "geom.h"
#include "select.h"
#include "canvas.h"
#include "patch.h"
#include "error.h"
#include "monitor.h"
#include "time.h"
#include "render.h"
#include "file.h"

#include "ui_pathdebug.h"

#include "radiance.h"
#include "GALERKIN/galerkin.h"

/*************************** Debug menu ******************************************/
static void DumpMemoryBreakdownCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  PrintPooledMemoryBreakdown(stderr);
}

static int more = FALSE;
static Widget moreButton;

static void MoreCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  more = TRUE;
}

/* This routine manages the "More..." button in the main menu and
 * polls for events until the user pushes the button. */
void WaitForMore(void)
{
  XtManageChild(moreButton);
  while (!more) ProcessWaitingEvents();
  XtUnmanageChild(moreButton);
  ProcessWaitingEvents();
  more = FALSE;
}

static void CreateMoreButton(Widget menuBar)
{
#ifdef MORE_BUTTON
  /* push buttons do not belong in the main menu ... some versions of Motif even
   * refuse to realize them. */
  moreButton = CreatePushButton(menuBar, "moreButton", MoreCallback, (XtPointer)NULL);
  XtUnmanageChild(moreButton);
#endif
  more = FALSE;
}

static void DoDumpPatchBounds(PATCH *P, VECTOR *hitp)
{
  fprintf(stderr, "%f %f %f  %f %f %f\n",
	  P->bounds[MIN_X], P->bounds[MIN_Y], P->bounds[MIN_Z], 
	  P->bounds[MAX_X], P->bounds[MAX_Y], P->bounds[MAX_Z]);
}

void DumpPatchBounds(void)
{
  SelectPatchSetCallback(DoDumpPatchBounds);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void DoDumpPatch(PATCH *P, VECTOR *hitp)
{
  PatchPrint(stderr, P);
}

static void DumpPatchCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  SelectPatchSetCallback(DoDumpPatch);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void LocatePatchCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int patchid = *(int *)client_data;
  int found = FALSE;
  ForAllPatches(P, Patches) {
    if (P->id == patchid) {
      RGBSET(P->color, 1., 0., 0.);
      found = TRUE;
      break;
    }
  } EndForAll;

  if (!found) {
    Error(NULL, "No patch with id %d found", patchid);
  } else {
    RenderNewDisplayList();
    RenderScene();
  }
}

static Widget CreateLocatePatchDialog(Widget parent, char *name)
{
  static int patchid;
  Widget dialog = CreatePromptDialog(parent, name, FET_INTEGER, (XtPointer)&patchid, NULL, 0);
  XtAddCallback(dialog, XmNokCallback, LocatePatchCallback, (XtPointer)&patchid);
  return dialog;
}

static void DumpWorldCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  GeomListIterate1B(World, GeomPrint, stdout);  
}

static void DumpClustersCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  GeomListIterate1B(ClusteredWorld, GeomPrint, stdout);
}

#ifdef TEST_SUBDIVISION
#include "galerkinP.h"
#include "render.h"

static PATCH *rcv, *src;

static INTERACTION *the_link;

static void SelectInteraction(INTERACTION *link)
{
  if (link->rcv->patch == rcv && link->src->patch == src)
    the_link = link;
}

static INTERACTION *FindInteraction(void)
{
  fprintf(stderr, "Finding interactoin between rcv=patch id %d and src=patch id %d ...\n",
	  rcv->id, src->id);

  the_link = (INTERACTION *)NULL;
  if (galstate.iteration_method == SOUTHWELL) {
    InteractionListIterate(TOPLEVEL_ELEMENT(src)->interactions, SelectInteraction);
  } else {
    InteractionListIterate(TOPLEVEL_ELEMENT(rcv)->interactions, SelectInteraction);
  }
  return the_link;
}

extern int RefineRecursive(INTERACTION *link);

static void TestRegularElementSubdivision(PATCH *patch2, VECTOR *hitp)
{
  ELEMENT *el = (ELEMENT *)patch2->radiance_data;
  INTERACTION *link;

  src = patch2;
  fprintf(stderr, "patch ID %d.\n", src->id);

  if (el && el->patch != patch2) {
    Error(NULL, "No Galerkin element for these patches!!\n");
    return;
  }

  if (!(link=FindInteraction()))
    fprintf(stderr, "The patches don't interact\n");
  else {
    fprintf(stderr, "The interaction:\n");
    InteractionPrint(stderr, link);
    fprintf(stderr, "Now refining:\n");
    if (RefineRecursive(link))
      fprintf(stderr, "====> REPLACED\n");
    else
      fprintf(stderr, "====> ACCURATE ENOUGH\n");
    fprintf(stderr, "Now doing push-pull on the receiver:\n");
    PushPullRadiance(link->rcv);
    RenderScene();
  }
  /*
  fprintf(stderr, "Subdividing element ... "); fflush(stderr);
  RegularSubdivideElement(el);
  fprintf(stderr, "done. It now looks like:\n");
  PrintElement(stderr, el);
  */
}

static void TestRegularElementSubdivision1(PATCH *patch, VECTOR *hitp)
{
  rcv = patch;
  fprintf(stderr, "patch ID %d\nNow select the source patch ... ",
	  rcv->id); 
  fflush(stderr);

  SelectPatchSetCallback(TestRegularElementSubdivision); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void TestRegularElementSubdivisionCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select receiver patch ... "); fflush(stderr);
  SelectPatchSetCallback(TestRegularElementSubdivision1); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif 

#ifdef TEST_UNIFORM_MAPPING
#include "patch.h"

static void TestUniformMapping(PATCH *patch, POINT *p)
{
  double uniformu, uniformv, u, v;

  fprintf(stderr, "patch %d, point (%g,%g,%g)", patch->id, p->x, p->y, p->z);
  PatchUV(patch, p, &u, &v);
  fprintf(stderr, "bilinear or barycentric (u,v) = (%g,%g)\n", u, v);
  PatchUniformUV(patch, p, &uniformu, &uniformv);
  fprintf(stderr, "uniform (u,v) = (%g,%g)\n", uniformu, uniformv);
  PatchUniformPoint(patch, uniformu, uniformv, p);
  fprintf(stderr, "PatchUniformPoint -> (%g,%g,%g)\n", p->x, p->y, p->z);
}

static void TestUniformMappingCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point on a patch ... "); fflush(stderr);
  SelectPatchSetCallback(TestUniformMapping); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /*TEST_UNIFORM_MAPPING*/

#define TEST_ID_RENDERING
#ifdef TEST_ID_RENDERING
#include "pools.h"
#include "patch.h"
#include "render.h"
#include "potential.h"

static void TestIdRenderingCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  unsigned long *ids, *id;
  long x, y, i, nrpixels, lostpixels, maxpatchid;
  int *patchpixels;

  ids = RenderIds(&x, &y);
  nrpixels = x*y; lostpixels=0;
  maxpatchid = PatchGetNextID()-1;

  patchpixels = (int *)Alloc((maxpatchid+1) * sizeof(int));
  for (i=0; i<=maxpatchid; i++)
    patchpixels[i] = 0;

  for (i=0, id=ids; i<nrpixels; i++, id++) {
    unsigned long the_id = (*id)&0xffffff;
    if (the_id <= maxpatchid) 
      patchpixels[the_id] ++;
    else
      lostpixels++;
  }
  
  Free((char *)ids, nrpixels * sizeof(unsigned long));

  for (i=0; i<=maxpatchid; i++) {
    if (patchpixels[i] > 0)
      fprintf(stderr, "patch %ld: %d pixels.\n", i, patchpixels[i]);
  }
  fprintf(stderr, "%ld lost pixels.\n", lostpixels);

  Free((char *)patchpixels, (maxpatchid+1)*sizeof(int));

  UpdateDirectPotential();
  RenderDirectPotential();
}
#endif /* TEST_ID_RENDERING */

#ifdef TEST_POTENTIAL
#include "potential.h"

static void TestPotentialCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  UpdateDirectPotential();
  RenderDirectPotential();
}
#endif /*TEST_POTENTIAL*/

/* #define TEST_SHAFTCULLING  */
#ifdef TEST_SHAFTCULLING
#include "shaftculling.h"
#include "scene.h"
#include "render.h"
#include "camera.h"
#include "geometry3d.h"

extern int ShaftCullTestPatch(PATCH *, SHAFT *);
extern void RenderShaft(SHAFT *shaft);

static PATCH *rcv, *src;
static SHAFT shaft;
static POLYGON rcvpoly, srcpoly;
static GEOMLIST *candlist = (GEOMLIST *)NULL;
static enum {POLY_TO_POLY, BOX_TO_BOX} shaft_type;
static int nrinside, nroverlap, nroutside;

static POLYGON *PatchPolygon(PATCH *patch, POLYGON *poly)
{
  poly->normal = patch->normal;
  poly->index = patch->index;
  BoundsCopy(patch->bounds, poly->bounds);
  poly->nrvertices = patch->nrvertices;
  poly->plane_constant = patch->plane_constant;
  poly->vertex[0] = *patch->vertex[0]->point;
  poly->vertex[1] = *patch->vertex[1]->point;
  poly->vertex[2] = *patch->vertex[2]->point;
  if (poly->nrvertices > 3)
  poly->vertex[3] = *patch->vertex[3]->point;

  return poly;
}

static void MakeShaftStats(PATCH *patch)
{
  switch (ShaftCullTestPatch(patch, &shaft)) {
  case -1:
    nrinside++;
    break;
  case 0:
    nroverlap++;
    break;
  case 1:
    nroutside++;
    break;    
  }
}

static void TestShaftCulling2(PATCH *patch, VECTOR *hitp)
{
  PATCHLIST *patches = (PATCHLIST *)NULL;

  src = patch;
  fprintf(stderr, "patch ID %d\n", src->id); 
  fflush(stderr);

  if (shaft_type == POLY_TO_POLY) {
    fprintf(stderr, "Constructing polygon-to-polygon shaft.\n");
    if (!ConstructPolygonToPolygonShaft(PatchPolygon(rcv, &rcvpoly),
					PatchPolygon(src, &srcpoly),
					&shaft)) 
      fprintf(stderr, "Couldn't construct shaft.\n");
  } else {
    BOUNDINGBOX bounds;
    fprintf(stderr, "Constructing box-to-box shaft.\n");
    if (!rcv->bounds) PatchBounds(rcv, bounds);
    if (!src->bounds) PatchBounds(src, bounds);
    if (!ConstructShaft(rcv->bounds, src->bounds, &shaft))
      fprintf(stderr, "Couldn't construct shaft.\n");	
  }

  ShaftOmit(&shaft, (GEOM *)&rcv);
  ShaftOmit(&shaft, (GEOM *)&src);

  candlist = DoShaftCulling(ClusteredWorld, &shaft, GeomListCreate());
  patches = BuildPatchList(candlist, PatchListCreate());
  fprintf(stderr, "%d items in shaft, %s%d patches: ", 
	  GeomListCount(candlist), 
	  shaft.cut ? "shaft cut, " : "",
	  PatchListCount(patches));
  PatchListIterate1A(patches, PatchPrintID, stderr);

  nrinside = nroverlap = nroutside = 0;
  PatchListIterate(patches, MakeShaftStats);
  fprintf(stderr, "%d inside, %d overlapping, %d outside.\n",
	  nrinside, nroverlap, nroutside);

  PatchListDestroy(patches);
}

static void TestShaftCulling1(PATCH *patch, VECTOR *hitp)
{
  rcv = patch;
  fprintf(stderr, "patch ID %d\nNow select the source patch ... ",
	  rcv->id); 
  fflush(stderr);

  SelectPatchSetCallback(TestShaftCulling2); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void SelectPtPShaftCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  shaft_type = POLY_TO_POLY;
  fprintf(stderr, "Select receiver patch ... "); fflush(stderr);
  SelectPatchSetCallback(TestShaftCulling1); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void SelectBtBShaftCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  shaft_type = BOX_TO_BOX;
  fprintf(stderr, "Select receiver patch ... "); fflush(stderr);
  SelectPatchSetCallback(TestShaftCulling1); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void ShowShaftCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  RenderShaft(&shaft);
}

static void ShowShaftPatch(PATCH *patch)
{
  RenderSetColor(&Blue);
  RenderPatchOutline(patch);
  
  /* find intersection of patch with the shaft */
}

static void ShowShaftContentsCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  PATCHLIST *patches = (PATCHLIST *)NULL;
  patches = BuildPatchList(candlist, PatchListCreate());
  PatchListIterate(patches, ShowShaftPatch);
  PatchListDestroy(patches);
}

static void TestShaftCullTestPatch(PATCH *patch, VECTOR *hitp)
{
  int code = ShaftCullTestPatch(patch, &shaft);
  fprintf(stderr, "Patch %d is %s the shaft\n",
	  patch->id,
	  code == -1 ? "INSIDE" :
	 (code == 0 ? "OVERLAPPING with" :
	 (code == 1 ? "OUTSIDE" : "UNKNOWN w.r.t.")));
}

static void TestShaftCullTestPatchCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a patch ... "); fflush(stderr);
  SelectPatchSetCallback(TestShaftCullTestPatch); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

/* #define TESTSHAFTFORMFACTOR */
#ifdef TESTSHAFTFORMFACTOR
#include "GALERKIN/galerkinP.h"
#include "GALERKIN/basis.h"
extern unsigned TestAreaToAreaFormFactor(INTERACTION *link, GEOMLIST *shadowlist);

static void TestShaftFormFactorCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  INTERACTION link;
  float ff[MAXBASISSIZE*MAXBASISSIZE];
  link.K.p = ff;
  link.src = TOPLEVEL_ELEMENT(src);
  link.rcv = TOPLEVEL_ELEMENT(rcv);
  link.nsrc = link.src->basis_size;
  link.nrcv = link.rcv->basis_size;
  link.vis = TestAreaToAreaFormFactor(&link, candlist);
  InteractionPrint(stderr, &link);
}
#endif /*TESTSHAFTFORMFACTOR*/
#endif /*TEST_SHAFTCULLING*/

/*#define TESTFORMFACTOR*/
#ifdef TESTFORMFACTOR
#include "radiance.h"
#include "GALERKIN/galerkin.h"
#include "GALERKIN/galerkinP.h"
#include "GALERKIN/basis.h"
extern unsigned TestAreaToAreaFormFactor(INTERACTION *link, GEOMLIST *shadowlist);

PATCH *rcv, *src;

static void TestFormFactor2(PATCH *p, POINT *hit)
{
  INTERACTION link;
  float ff[MAXBASISSIZE*MAXBASISSIZE];

  rcv = p;

  link.K.p = ff;
  link.src = TOPLEVEL_ELEMENT(src);
  link.rcv = TOPLEVEL_ELEMENT(rcv);
  link.nsrc = link.src->basis_size;
  link.nrcv = link.rcv->basis_size;
  link.vis = TestAreaToAreaFormFactor(&link, World);
  InteractionPrint(stderr, &link);
}

static void TestFormFactor1(PATCH *p, POINT *hit)
{
  src = p;
  fprintf(stderr, "Select receiver patch ...\n");
  SelectPatchSetCallback(TestFormFactor2); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void TestFormFactorCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (!Radiance || Radiance != &GalerkinRadiosity) {
    Error(NULL, "First enable Galerkin radiosity ...");
    return;
  }

  fprintf(stderr, "Select source patch ...\n");
  SelectPatchSetCallback(TestFormFactor1); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /*TESTFORMFACTOR*/

static void StartMonitoringPatch(PATCH *patch, POINT *hitp)
{
  fprintf(stderr, "patch id %d\n", patch->id);
  MonitorAdd(patch);
}

static void StartMonitoringPatchCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select the patch to be monitored ... "); fflush(stderr);
  SelectPatchSetCallback(StartMonitoringPatch); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);  
}

static void StartMonitorPatchIDCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int patchid = *(int *)client_data;

  ForAllPatches(P, Patches) {
    if (P->id == patchid) {
      MonitorAdd(P);
      return;
    }
  } EndForAll;

  Error(NULL, "No patch with id %d found", patchid);
}

static Widget CreateMonitorPatchDialog(Widget parent, char *name)
{
  static int patchid;
  Widget dialog = CreatePromptDialog(parent, name, FET_INTEGER, (XtPointer)&patchid, NULL, 0);
  XtAddCallback(dialog, XmNokCallback, StartMonitorPatchIDCallback, (XtPointer)&patchid);
  return dialog;
}

static void StopMonitoringPatch(PATCH *patch, POINT *hitp)
{
  fprintf(stderr, "patch id %d\n", patch->id);
  MonitorRemove(patch);
}

static void StopMonitoringPatchCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select the patch not to be monitored anymore ... "); fflush(stderr);
  SelectPatchSetCallback(StopMonitoringPatch); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);  
}

#ifdef RECORD_MONITORED /* patch.h */
static void ClosePatchMonitorFilesCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr,"Closing all monitor-files\n");
  PatchListIterate(Patches, PatchCloseAllRecordFiles);
}
#endif

#ifdef TEST_BREP
static void PrintNeighbourPatch(BREP_WING *wing)
{
  BREP_WING *owing = BrepEdgeOtherWing(wing);
  if (!owing->contour) 
    fprintf(stderr, "No neighbour.\n");
  else
    fprintf(stderr, "Patch %d.\n", ((PATCH *)(owing->contour->face->client_data))->id);
}

static void TestPatchNeighbours(PATCH *patch, POINT *hitp)
{
  fprintf(stderr, "patch id %d.\n", patch->id);
  
  if (!patch->brep_data)
    fprintf(stderr, "No BREP data for this patch!\n");
  else if (!patch->brep_data->outer_contour)
    fprintf(stderr, "No contour defined for the BREP_FACE corresponding to the patch!\n");
  else {
    fprintf(stderr, "iterating over the wings of the contour of the face corresponding to the patch ...\n");
    BrepContourIterateWings(patch->brep_data->outer_contour, PrintNeighbourPatch);
    fprintf(stderr, "So ... that's it!\n");
  }
}

static void TestNeighboursCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a patch ... "); fflush(stderr);
  SelectPatchSetCallback(TestPatchNeighbours); 
  CanvasPushMode(CANVASMODE_SELECT_PATCH);    
}
#endif /*TEST_BREP*/

/* #define DUMP_GALERKIN_MATRIX */
#ifdef DUMP_GALERKIN_MATRIX
#include "GALERKIN/galerkinP.h"

static void PatchDumpFormFactors(PATCH *patch, FILE *fp)
{
  ELEMENT *top = TOPLEVEL_ELEMENT(patch);
  INTERACTIONLIST *ilist;

  for (ilist = top->interactions; ilist; ilist = ilist->next) {
    INTERACTION *link = ilist->interaction;
    double d;
    VECTORDIST(link->rcv->pog.patch->midpoint, link->src->pog.patch->midpoint, d);
    fprintf(fp, "%d %d %10f %f %f\n", link->rcv->id, link->src->id,
	    REFLECTIVITY(link->rcv->pog.patch).r * ((link->nrcv == 1 && link->nsrc == 1) ? link->K.f : link->K.p[0]) / link->rcv->area,
	    SELFEMITTED_RADIANCE(link->rcv->pog.patch).r,
	    d);
  }
}

static void DumpMatrixCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  FILE *fp;
  char *fname = "formfactor.matrix";

  if (!Radiance || Radiance != &GalerkinRadiosity) {
    Error(NULL, "Can only dump the form factor matrix after doing Galerkin radiosity computations");
    return;
  }

  if ((fp = fopen(fname, "w")) == (FILE *)NULL) {
    Error(NULL, "Can't open file '%s' for writing", fname);
    return;
  }
  fprintf(stderr, "Dumping form factor matrix to file '%s' ... \n", fname);
  
  PatchListIterate1A(Patches, (void (*)(PATCH *, void *))PatchDumpFormFactors, (void *)fp);

  fprintf(stderr, "Done.\n");
  fclose(fp);
}
#endif /*DUMP_GALERKIN_MATRIX*/

/* #define EXAMINE_ELEMENT_HIERARCHY */
#ifdef EXAMINE_ELEMENT_HIERARCHY
#include "radiance.h"
#include "GALERKIN/galerkin.h"
#include "GALERKIN/galerkinP.h"

static ELEMENT *current_element = (ELEMENT *)NULL;
static PATCH *current_patch = (PATCH *)NULL;
static VECTOR current_hitp;

static void DrawLink(INTERACTION *link)
{
  POINT x, y;
  ELEMENT *drawel = link->src == current_element? link->rcv : link->src;
  RGB linkcol;

  linkcol.r = linkcol.g = linkcol.b = (float)link->vis / 255.;
  RenderSetColor(&linkcol);

  x = ElementMidpoint(link->rcv);
  y = ElementMidpoint(link->src);
  RenderLine(&x, &y);
  DrawElementOutline(drawel);
}

static void DrawLinks(ELEMENT *elem)
{
  InteractionListIterate(elem->interactions, DrawLink);
}

static void DoStartExamineElementHierarchy(PATCH *P, VECTOR *hitp)
{
  current_hitp = *hitp;
  fprintf(stderr, "at point ");
  VectorPrint(stderr, current_hitp);
  fprintf(stderr, "\n");

  current_patch = P;
  current_element = TOPLEVEL_ELEMENT(P);

  RenderSetColor(&Green);
  DrawElementOutline(current_element);
  DrawLinks(current_element);
  PrintElement(stderr, current_element);
}

static void ExamineElementHierarchyUp(Widget w, XtPointer client_data, XtPointer call_darta)
{
  if (!current_element) {
    Error(NULL, "First select a point at which to examine the element hierarchy");
    return;
  }

  if (!current_element->parent) {
    fprintf(stderr, "At top of element hierarchy.\n");
    return;
  }

  current_element = current_element->parent;

  RenderSetColor(&Green);
  DrawElementOutline(current_element);
  DrawLinks(current_element);
  PrintElement(stderr, current_element);
}

static int ClusterContainsPatch(ELEMENT *clus, PATCH *patch)
{
  if (!IsCluster(clus))
    return clus->pog.patch == patch;
  else {
    /* cluster contains the patch if any of it's subclusters contains the patch */
    ELEMENTLIST *ellist;
    for (ellist=clus->irregular_subelements; ellist; ellist=ellist->next) {
      ELEMENT *subclus = ellist->element;
      if (ClusterContainsPatch(subclus, patch))
	return TRUE;
    }
  }

  return FALSE;
}

static ELEMENT *SubclusterContainingPatch(ELEMENT *elem, PATCH *patch)
{
  ELEMENTLIST *ellist;
  for (ellist=elem->irregular_subelements; ellist; ellist=ellist->next) {
    ELEMENT *subclus = ellist->element;
    if (ClusterContainsPatch(subclus, patch))
      return subclus;
  }
  return (ELEMENT *)NULL;
}

static ELEMENT *RegularSubelementContainingPoint(ELEMENT *elem, POINT *point)
{
  ELEMENT *subelem;
  double u, v;

  PatchUniformUV(current_patch, point, &u, &v);
  subelem = RegularLeafElementAtPoint(TOPLEVEL_ELEMENT(current_patch), &u, &v);
  while (subelem->parent) {
    if (subelem->parent == elem)
      return subelem;
    else
      subelem = subelem->parent;
  }
  return (ELEMENT *)NULL;
}

static void ExamineElementHierarchyDown(Widget w, XtPointer client_data, XtPointer call_darta)
{
  if (!current_element) {
    Error(NULL, "First select a point at which to examine the element hierarchy");
    return;
  }

  if (!current_element->regular_subelements && !current_element->irregular_subelements) {
    fprintf(stderr, "At bottom of element hierarchy.\n");
    return;
  }

  if (IsCluster(current_element)) 
    current_element = SubclusterContainingPatch(current_element, current_patch);
  else
    current_element = RegularSubelementContainingPoint(current_element, &current_hitp);
  if (!current_element) {
    Error(NULL, "Coulnd't find subelement at current location");
    return;
  }

  RenderSetColor(&Green);
  DrawElementOutline(current_element);
  DrawLinks(current_element);
  PrintElement(stderr, current_element);
}

static void StartExamineElementHierarchy(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (Radiance != &GalerkinRadiosity) {
    Error(NULL, "This function only works for Galerkin radiosity.");
    return;
  }

  current_element = (ELEMENT *)NULL;

  fprintf(stderr, "Select a patch ... ");
  SelectPatchSetCallback(DoStartExamineElementHierarchy);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /*EXAMINE_ELEMENT_HIERARCHY*/

#define TEST_SGL
#ifdef TEST_SGL
#include "camera.h"
#include "SGL/sgl.h"

#include <stdio.h>

static void TestSGLRenderPatch(PATCH *patch)
{
  VECTOR verts[4];
  int i;

  /* back face culling */
  if (VECTORDOTPRODUCT(patch->normal, Camera.eyep) + patch->plane_constant < EPSILON)
    return;

  sglSetColor((SGL_PIXEL)
	      ((unsigned)(patch->color.b * 255.) << 16 |
	       (unsigned)(patch->color.g * 255.) << 8 |
	       (unsigned)(patch->color.r * 255.)));
  
  for (i=0; i<patch->nrvertices; i++) 
    verts[i] = *patch->vertex[i]->point;

  sglPolygon(patch->nrvertices, verts);
}

static void TestSGLDumpFrameBuffer(SGL_CONTEXT *sgl, char *fname)
{
  SGL_PIXEL *pix;
  int i;

  FILE *fp = fopen(fname, "w");
  if (!fp) {
    Error("TestSGLDumpFrameBuffer", "Can't open file '%s' for writing", fname);
    return;
  }

  fprintf(fp, "P6\n%d %d\n255\n", sgl->width, sgl->height);
  for (pix = sgl->fbuf, i=0; i<sgl->width * sgl->height; pix++, i++) {
    fputc((*pix) & 0xff, fp);
    fputc((*pix >> 8) & 0xff, fp);
    fputc((*pix >> 16) & 0xff, fp);
  }

  fclose(fp);
}

static void TestSGLCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  clock_t t;
  SGL_CONTEXT *sgl;

  sgl = sglOpen(Camera.hres, Camera.vres);
  sglDepthTesting(TRUE);
  sglClipping(TRUE);
  /* sglViewport(0, 0, Camera.hres-20, Camera.vres-20); */
  sglClear(0, SGL_ZMAX);

  sglLoadMatrix(Perspective(Camera.fov*2.*M_PI/180., (float)Camera.hres/(float)Camera.vres, Camera.near, Camera.far)); 
  sglMultMatrix(LookAt(Camera.eyep, Camera.lookp, Camera.updir)); 
  /* {
  BOUNDINGBOX bbx;
  TRANSFORM lookat = LookAt(Camera.eyep, Camera.lookp, Camera.updir);
  BoundsTransform(ClusteredWorldGeom->bounds, &lookat, bbx);
  sglLoadMatrix(Ortho(bbx[MIN_X], bbx[MAX_X], bbx[MIN_Y], bbx[MAX_Y], -bbx[MAX_Z], -bbx[MIN_Z]));
  sglMultMatrix(lookat);
  } */

  t = clock();
  PatchListIterate(Patches, TestSGLRenderPatch);
  t = clock() - t;
fprintf(stderr, "1x rendering took %g sec.\n", (double)t / (double)CLOCKS_PER_SEC);

fprintf(stderr, "Dumping frame buffer to 'sgl.ppm'.\n");
  TestSGLDumpFrameBuffer(sgl, "sgl.ppm");

  fprintf(stderr, "Done.\n");
  sglClose(sgl);
}
#endif /*TEST_SGL*/

#include "cie.h"

static void MakeMgfColor(COLOR col, float *x, float *y, float *intensity)
{
  float s, rgb[3], xyz[3];

#ifdef RGBCOLORS
  rgb[0] = col.spec[0];
  rgb[1] = col.spec[1];
  rgb[2] = col.spec[2];
  rgb_xyz(rgb, xyz);
#else
  xyz[0] = col.spec[0];
  xyz[1] = col.spec[1];
  xyz[2] = col.spec[2];
#endif

  *intensity = xyz[1];
  s = xyz[0] + xyz[1] + xyz[2];
  if (s > EPSILON) {
    *x = xyz[0] / s;
    *y = xyz[1] / s;
  } else {
    *x = *y = 0.3333333333;
  }
}

#ifdef DUMP_REFL
/* doesn't work anymore!!!! */

static void DumpMaterial(MATERIAL *mat)
{
  HITREC hit;
  VECTOR p = {0.,0.,0.};
  VECTOR n = {0.,0.,1.};
  InitHit(&hit, (PATCH*)1 /*cheating*/, (GEOM *)NULL, &p, &n, mat, 0);
  COLOR rho = BsdfDiffuseReflectance(mat->bsdf, &hit, &n);
  float x, y, rd;

  MakeMgfColor(rho, &x, &y, &rd);
  printf("m %s =\n\tc\n\t\tcxy %g %g\n\trd %g\n\n",
	 mat->name, x, y, rd);
}

static void DumpReflectivities(Widget w, XtPointer client_data, XtPointer call_data)
{
  ForAllInList(MATERIAL, mat, MaterialLib) {
    DumpMaterial(mat);
  } EndForAll;
}
#endif /*DUMP_REFL*/

/* #define TEST_RAYTRACING */
#ifdef TEST_RAYTRACING
static void DrawVirtualScreen(Widget w, XtPointer client_data, 
			      XtPointer call_data)
{
  VECTOR upperleft, s,e;
  double x,y,h,v;
  int i, num;

  RenderSetColor(&Red);

  h = 2. * tan(Camera.hfov * M_PI/180.) / (float)(Camera.hres-1);
  v = 2. * tan(Camera.vfov * M_PI/180.) / (float)(Camera.vres-1);

  x = -h * (float)(Camera.hres)/2.0 ;
  y = -v * (float)(Camera.vres)/2.0;

  VECTORSCALE(1.0, Camera.Z, upperleft);
  VECTORCOMB3(upperleft, x, Camera.X, y, Camera.Y, upperleft);
  VECTORADD(Camera.eyep, upperleft, upperleft);

  num = 10;

  for(i=0; i <= num; i++)
  {
    /* Horizontal */

    VECTORCOMB2(1.0, upperleft, i*(Camera.vres*v)/(float)num, Camera.Y, s);
    VECTORCOMB2(1.0, s, (Camera.hres*h), Camera.X, e);

    RenderLine(&s,&e);
    RenderLine(&Camera.lookp,&s);
    RenderLine(&Camera.lookp,&e);

    VECTORCOMB2(1.0, upperleft, i*(Camera.hres*h)/(float)num, Camera.X, s);
    VECTORCOMB2(1.0, s, (Camera.vres*v), Camera.Y, e);
    RenderLine(&s,&e);
    RenderLine(&Camera.lookp,&s);
    RenderLine(&Camera.lookp,&e);

  }
}
#endif

/* #define TEST_RAYINTERSECT */
#ifdef  TEST_RAYINTERSECT
static void DoTestRayIntersect(PATCH *patch, POINT *p)
{
  fprintf(stderr, "hit patch %d ('%s' material)\n", patch->id, patch->surface->material->name);
}

static void TestRayIntersect(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a pixel ... "); fflush(stderr);
  SelectPatchSetCallback(DoTestRayIntersect);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /* TEST_RAYINTERSECT*/

#define TEST_LIGHTMETERING
#ifdef TEST_LIGHTMETERING
#include "lightmetering.h"
#include "statistics.h"

static void TestLightMetering(Widget w, XtPointer client_data, XtPointer call_data)
{
  float avlum = DetermineAverageIncidentLuminance();
  fprintf(stderr, "Average incident luminance = %g\n", avlum);
  reference_luminance = 5.42 *avlum;
  RenderNewDisplayList();
  RenderScene();
}
#endif /*TEST_LIGHTMETERING*/

#define TEST_GLOBAL_LINE
#ifdef TEST_GLOBAL_LINE
#include "globallines.h"

static void ShowHit(HITREC *hit)
{
  RenderSetColor(&Yellow);
  RenderPoint(&hit->point);
  /*
  RenderSetColor(&Blue);
  RenderPatchOutline(hit->patch);
  */
}

static void PrintRenderSpan(HITREC *start, HITREC *end)
{
  PrintSpan(start, end);

  /* show the line */
  ShowHit(start); ShowHit(end);
  RenderSetColor(&Yellow);
  RenderLine(&start->point, &end->point);  
}

static void PrintRenderSpans(HITLIST *hits)
{
  if (ProcessSpans(hits, PrintRenderSpan) == 0)
    fprintf(stderr, "No spans.\n");
}

/* generates, traces and processes a global line */
void TestGlobalLine(void)
{
  int i;
  InitGlobalLines(NULL, DefCompareHits, PrintRenderSpans);
  for (i=0; i<100; i++) {
    fprintf(stderr, "Line %d\n", i);
    if (!DoGlobalLine(i))
      fprintf(stderr, "No hits.\n");
  }
  RenderFinish();
}

static void TestGlobalLineCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  TestGlobalLine();
}

#endif /*TEST_GLOBAL_LINE*/

#define RAYTRACING_TEST
#ifdef RAYTRACING_TEST
#include "scene.h"
#include "hitlist.h"

/* Ray Tracing Confidence Test */
static int rtct_global_lines_per_patch = 10;

static double *Random4D(int n)
{
  static double xi[4];
  srand48(n);
  xi[0] = drand48();
  xi[1] = drand48();
  xi[2] = drand48();
  xi[3] = drand48();
  return xi;
}

static void AccumulateHitCount(HITLIST *hits)
{
  ForAllHits(hit, hits) {
    hit->patch->radiance_data = (void*)((int)(hit->patch->radiance_data) + 1);
  } EndForAll;
}

static void TestRayTracingCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i, numpatches, nr_global_lines;
  float avlpp, avlpp2, lines_unit_area;

  if (Radiance) {
    Error("TestRayTracingCallback", "Switch off radiance computations before doing the ray tracing confidence test ...");
    return;
  }

  /* count nr of patches and set nr of hits for each patch to zero. */
  numpatches = 0;
  ForAllPatches(P, Patches) {
    P->radiance_data = (void*)0; /* store hit count in 'radiance_data' */
    numpatches++;
  } EndForAll;

  /* initialise global line tracing */
  InitGlobalLines(Random4D, NULL /* no sorting */, AccumulateHitCount);

  /* glin_Radius and glin_Center (unused here) are the radius and center of
   * a bounding sphere for the scene. They are computed as a side result
   * of InitGLobalLines */
  nr_global_lines = rtct_global_lines_per_patch * numpatches;
  lines_unit_area = (2.0 * nr_global_lines) / (4. * M_PI * glin_Radius * glin_Radius);

  /* trace global lines anc count hits */
  fprintf(stderr, "Tracing %d global lines ...\n", nr_global_lines);
  CanvasPushMode(CANVASMODE_WORKING);
  for (i=0; i<nr_global_lines; i++) {
    DoGlobalLine(i);
    if (i%1000 == 0 && i != 0)	/* progress indicator */
      fprintf(stderr, ".");
  }
  fprintf(stderr, "\n");
  CanvasPullMode();

  /* do some statistics */
  avlpp = avlpp2 = 0.;
  ForAllPatches(P, Patches) {
    int have = (int)(P->radiance_data);		/* observed nr of lines */
    float expect = P->area * lines_unit_area;	/* expected nr of lines */
    float dev = (have - expect) / expect;  	/* relative deviation */

    if      (dev < -0.5) P->color = Red;
    else if (dev < -0.1) P->color = Yellow;
    else if (dev <  0.1) P->color = White;
    else if (dev <  0.5) P->color = Turquoise;
    else                 P->color = Blue;

    avlpp += have/expect;
    avlpp2 += have * have / (expect * expect);
  } EndForAll;

  avlpp /= numpatches;
  avlpp2 /= numpatches;
  fprintf(stderr, "Effective/predicted lines per patch ratio: %g (+/-%g)\n", avlpp, sqrt(avlpp2 - avlpp*avlpp));

  if (renderopts.smooth_shading) {
    Warning(NULL, "Switch off smooth shading in order to see the result.");
  }
  RenderScene();
}

static Widget CreateLinesPerPatchDialog(Widget parent, char *name)
{
  return CreatePromptDialog(parent, name, FET_INTEGER, (XtPointer)&rtct_global_lines_per_patch, NULL, 0);
}

static void CreateRayTracingTestMenu(Widget parent)
{
  Widget menu = CreateSubMenu(parent, "Ray Tracing Confidence Test Settings", "rtctMenu");
  CreateCascadeDialog(menu, "Lines per Patch", CreateLinesPerPatchDialog, "linesPerPatchDialog", NULL, NULL);
}

#endif /* RAYTRACING_TEST */

#define SHOW_NORMALS
#ifdef SHOW_NORMALS
extern int do_render_normals;

static void ShowNormalsCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  do_render_normals = 1 - do_render_normals;
  RenderScene();
}
#endif /*SHOW_NORMALS*/

#define TEST_BSDF_SAMPLING 
#ifdef  TEST_BSDF_SAMPLING
#include "camera.h"
#include "ray.h"

static POINT refpoint;
int debug_mat = FALSE;

static void DoTestBsdfSampling(PATCH *patch, POINT *p)
{
  RAY ray;
  HITREC *hit, hitstore;
  VECTOR inDir, outDir;
  double pdf, pdfRR;
  float dist;
  COLOR bsdfval;
  int doRR = FALSE;
  HITREC phit;

  debug_mat = TRUE;

  fprintf(stderr, "hit patch %d ('%s' material)\n", patch->id, patch->surface->material->name);
  InitHit(&phit, patch, NULL, p, &patch->normal, patch->surface->material, 0.);

  VECTORSUBTRACT(Camera.eyep, *p, inDir);
  VECTORNORMALIZE(inDir);
  fprintf(stderr, "Camera.eyep = ");
  VectorPrint(stderr, Camera.eyep);
  fprintf(stderr, " - point = ");
  VectorPrint(stderr, *p);
  fprintf(stderr, " ===> inDir = ");
  VectorPrint(stderr, inDir);
  fprintf(stderr, "\n");

  ray.pos = *p;
  ray.dir = BsdfSample(patch->surface->material->bsdf,
		       &phit,
		       (BSDF *)NULL, (BSDF *)NULL,
		       &inDir,
		       doRR, BSDF_ALL_COMPONENTS,
		       drand48(), drand48(),
		       &pdf);

  fprintf(stderr, "pdf = %f, ray.dir = ", pdf);
  VectorPrint(stderr, ray.dir);
  fprintf(stderr, "\n");

  bsdfval = BsdfEval(patch->surface->material->bsdf,
		     &phit,
		     (BSDF *)NULL, (BSDF *)NULL,
		     &inDir, &ray.dir,
		     BSDF_ALL_COMPONENTS);
  fprintf(stderr, "bsdfval = ");
  ColorPrint(stderr, bsdfval);
  if (pdf > 0.) {
    COLORSCALE((VECTORDOTPRODUCT(ray.dir, patch->normal)/pdf), bsdfval, bsdfval);
    fprintf(stderr, ", times cos / pdf   = ");
    ColorPrint(stderr, bsdfval);
  }
  fprintf(stderr, "\n");

  BsdfEvalPdf(patch->surface->material->bsdf,
	      &phit,
	      (BSDF *)NULL, (BSDF *)NULL,
	      &inDir, &ray.dir,
	      BSDF_ALL_COMPONENTS,
	      &pdf, &pdfRR);

  fprintf(stderr, "pdf = %f, pdfRR = %f\n",
	  pdf, pdfRR);

  dist = HUGE;
  if (pdf > 0. && (hit = GridIntersect(WorldGrid, &ray, EPSILON, &dist, HIT_FRONT, &hitstore))) {
    fprintf(stderr, "ray.pos = ");
    VectorPrint(stderr, ray.pos);
    fprintf(stderr, ", hit->point = ");
    VectorPrint(stderr, hit->point);
    fprintf(stderr, "\n");
    RenderSetColor(&Yellow);
    RenderLine(&ray.pos, &hit->point);
    RenderFinish();
  }

  fprintf(stderr, "BSDF and PDF evaluation w.r.t refpoint:\n");

  VECTORSUBTRACT(refpoint, *p, outDir);
  VECTORNORMALIZE(outDir);
  fprintf(stderr, "refpoint = ");
  VectorPrint(stderr, refpoint);
  fprintf(stderr, " - point = ");
  VectorPrint(stderr, *p);
  fprintf(stderr, " = outDir = ");
  VectorPrint(stderr, outDir);
  fprintf(stderr, "\n");

  fprintf(stderr, "cos_in = %g, cos_out = %g\n",
	  VECTORDOTPRODUCT(inDir, patch->normal),
	  VECTORDOTPRODUCT(outDir, patch->normal));

  bsdfval = BsdfEval(patch->surface->material->bsdf,
		     &phit,
		     (BSDF *)NULL, (BSDF *)NULL,
		     &inDir, &outDir,
		     BSDF_ALL_COMPONENTS);
  fprintf(stderr, "bsdfval = ");
  ColorPrint(stderr, bsdfval);
  fprintf(stderr, "\n");

  BsdfEvalPdf(patch->surface->material->bsdf,
	      &phit,
	      (BSDF *)NULL, (BSDF *)NULL,
	      &inDir, &outDir,
	      BSDF_ALL_COMPONENTS,
	      &pdf, &pdfRR);

  fprintf(stderr, "pdf = %f, pdfRR = %f\n", pdf, pdfRR);
  if (pdf > 0.) {
    COLORSCALE((VECTORDOTPRODUCT(inDir, patch->normal)/pdf), bsdfval, bsdfval);
    fprintf(stderr, "bsdf * times cos_in / pdf = ");
    ColorPrint(stderr, bsdfval);
    fprintf(stderr, "\n");
  }

  fprintf(stderr, "And now if we switch inDir and outDir:\n");
  bsdfval = BsdfEval(patch->surface->material->bsdf,
		     &phit,
		     (BSDF *)NULL, (BSDF *)NULL,
		     &outDir, &inDir,
		     BSDF_ALL_COMPONENTS);
  fprintf(stderr, "bsdfval = ");
  ColorPrint(stderr, bsdfval);
  fprintf(stderr, "\n");

  BsdfEvalPdf(patch->surface->material->bsdf,
	      &phit,
	      (BSDF *)NULL, (BSDF *)NULL,
	      &outDir, &inDir,
	      BSDF_ALL_COMPONENTS,
	      &pdf, &pdfRR);

  fprintf(stderr, "pdf = %f, pdfRR = %f\n",
	  pdf, pdfRR);
  if (pdf > 0.) {
    COLORSCALE((VECTORDOTPRODUCT(outDir, patch->normal)/pdf), bsdfval, bsdfval);
    fprintf(stderr, "bsdf x times cos_out / pdf   = ");
    ColorPrint(stderr, bsdfval);
  }
  fprintf(stderr, "\n");

  debug_mat = FALSE;
}

static void DoTestBsdfSampling1(PATCH *patch, POINT *p)
{
  refpoint = *p;

  fprintf(stderr, "\n");
  fprintf(stderr, "Select a scattering point ... "); fflush(stderr);
  SelectPatchSetCallback(DoTestBsdfSampling);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void TestBsdfSampling(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a reference point ... "); fflush(stderr);
  SelectPatchSetCallback(DoTestBsdfSampling1);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void ShowShadingFrame(PATCH *patch, POINT *P)
{
  HITREC hit; VECTOR X, Y, Z;

  fprintf(stderr, "hit patch %d ('%s' material)\n", patch->id, patch->surface->material->name);
  InitHit(&hit, patch, (GEOM*)NULL, P, &patch->normal, patch->surface->material, 0.);
  HitShadingFrame(&hit, &X, &Y, &Z);
  RenderCoordinateFrame(&hit.point, &X, &Y, &Z);
  fprintf(stderr, "point = "); VectorPrint(stderr, hit.point); fprintf(stderr, "\n");
  fprintf(stderr, "normal= "); VectorPrint(stderr, hit.gnormal); fprintf(stderr, "\n");
  fprintf(stderr, "X     = "); VectorPrint(stderr, X); fprintf(stderr, ", norm = %f\n", VECTORNORM(X));
  fprintf(stderr, "Y     = "); VectorPrint(stderr, Y); fprintf(stderr, ", norm = %f\n", VECTORNORM(Y));
  fprintf(stderr, "Z     = "); VectorPrint(stderr, Z); fprintf(stderr, ", norm = %f\n", VECTORNORM(Z));
}

static void ShowShadingFrameCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point ... "); fflush(stderr);
  SelectPatchSetCallback(ShowShadingFrame);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);  
}

static void PrintAlbedoAtHit(HITREC *hit, VECTOR *eyedir, BSDFFLAGS mode, char *name)
{
  COLOR albedo = BsdfScatteredPower(hit->material->bsdf, hit, eyedir, mode);
  fprintf(stderr, "%s = ", name); ColorPrint(stderr, albedo); fprintf(stderr, "\n");
}

static void PrintReflectanceAtHit(HITREC *hit, VECTOR *eyedir, XXDFFLAGS mode, char *name)
{
  COLOR albedo = BsdfReflectance(hit->material->bsdf, hit, eyedir, mode);
  fprintf(stderr, "%s = ", name); ColorPrint(stderr, albedo); fprintf(stderr, "\n");
}

static void PrintTransmittanceAtHit(HITREC *hit, VECTOR *eyedir, XXDFFLAGS mode, char *name)
{
  COLOR albedo = BsdfTransmittance(hit->material->bsdf, hit, eyedir, mode);
  fprintf(stderr, "%s = ", name); ColorPrint(stderr, albedo); fprintf(stderr, "\n");
}

static void PrintAlbedoes(PATCH *patch, POINT *P)
{
  HITREC hit; VECTOR eyedir;

  fprintf(stderr, "hit patch %d ('%s' material)\n", patch->id, patch->surface->material->name);
  InitHit(&hit, patch, (GEOM*)NULL, P, &patch->normal, patch->surface->material, 0.);

  VECTORSUBTRACT(Camera.eyep, *P, eyedir);
  VECTORNORMALISE(eyedir);

  if (hit.material->bsdf) {
    fprintf(stderr, "Testing BsdfScatetredPower():\n");
    PrintAlbedoAtHit(&hit, &eyedir, BRDF_DIFFUSE_COMPONENT, "diffuse reflection");
    PrintAlbedoAtHit(&hit, &eyedir, BRDF_GLOSSY_COMPONENT, "glossy reflection");
    PrintAlbedoAtHit(&hit, &eyedir, BRDF_SPECULAR_COMPONENT, "specular reflection");
    PrintAlbedoAtHit(&hit, &eyedir, BTDF_DIFFUSE_COMPONENT, "diffuse refraction");
    PrintAlbedoAtHit(&hit, &eyedir, BTDF_GLOSSY_COMPONENT, "glossy refraction");
    PrintAlbedoAtHit(&hit, &eyedir, BTDF_SPECULAR_COMPONENT, "specular refraction");
    fprintf(stderr, "Testing BsdfReflectance():\n");
    PrintReflectanceAtHit(&hit, &eyedir, DIFFUSE_COMPONENT, "diffuse reflection");
    PrintReflectanceAtHit(&hit, &eyedir, GLOSSY_COMPONENT, "glossy reflection");
    PrintReflectanceAtHit(&hit, &eyedir, SPECULAR_COMPONENT, "specular reflection");
    fprintf(stderr, "Testing BsdfTransmittance():\n");
    PrintTransmittanceAtHit(&hit, &eyedir, DIFFUSE_COMPONENT, "diffuse refraction");
    PrintTransmittanceAtHit(&hit, &eyedir, GLOSSY_COMPONENT, "glossy refraction");
    PrintTransmittanceAtHit(&hit, &eyedir, SPECULAR_COMPONENT, "specular refraction");
  } else
    fprintf(stderr, "not a scattering material\n");
}

static void PrintAlbedoesCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point ... "); fflush(stderr);
  SelectPatchSetCallback(PrintAlbedoes);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);  
}

static void TestHackPath(PATCH *patch, POINT *P)
{
  HITREC hit; VECTOR eyedir;
  RAY ray;

  

}

static void TestHackPathCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point ... "); fflush(stderr);
  SelectPatchSetCallback(TestHackPath);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);  
}

#endif /* TEST_BSDF_SAMPLING */



/* #define TEST_PHOTONMAP */
#ifdef TEST_PHOTONMAP

#include "PHOTONMAP/pmap.h"
static POINT rc_point;
static PATCH *rc_patch;

static void DoTestPmapReconstruction(PATCH *patch, POINT *p)
{
  VECTOR dir;

  VECTORSUBTRACT(*p, rc_point, dir);
  VECTORNORMALIZE(dir);

  PmapTestReconstruction(rc_point, rc_patch, dir);
}

static void DoTestPmapReconstruction1(PATCH *patch, POINT *p)
{
  rc_point = *p;
  rc_patch = patch;

  fprintf(stderr, "\n");
  fprintf(stderr, "Select a destination point ... \n"); fflush(stderr);
  SelectPatchSetCallback(DoTestPmapReconstruction);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

void TestPhotonmapReconstruction(Widget w, XtPointer client_data, 
				 XtPointer call_data)
{
  fprintf(stderr, "Photonmap reconstruction\n");
  fprintf(stderr, "Select a reconstruction point ... "); fflush(stderr);
  SelectPatchSetCallback(DoTestPmapReconstruction1);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /* TEST_PHOTONMAP */


static void BuildIdToPatchTable(PATCH *patch, PATCH **id2patch)
{
  id2patch[patch->id] = patch;
}

static void Raycast(FILE *fp, COLOR (*GetRadiance)(PATCH *P, double u, double v, VECTOR dir))
{
  PATCH **id2patch;
  unsigned long *ids, *id;
  long i, j, x, y, nrpixels, maxpatchid, lostpixels;
  RGB *pix;
  RAY ray;
  float v, h, xsample, ysample, dist;
  HITREC *hit, hitstore;

  CanvasPushMode(CANVASMODE_RENDER);

  /* get the patch IDs for each pixel. */
  ids = RenderIds(&x, &y);
  if (!ids) return;
  nrpixels = x*y; lostpixels=0;

  /* build a table to convert a patch ID to the corresponding PATCH * */
  maxpatchid = PatchGetNextID()-1;  
  id2patch = (PATCH **)Alloc((maxpatchid+1) * sizeof(PATCH *));
  for (i=0; i<=maxpatchid; i++) id2patch[i] = (PATCH *)NULL;
  PatchListIterate1A(Patches, BuildIdToPatchTable, (void *)id2patch);

  pix = (RGB *)Alloc(x * sizeof(RGB));

  /* h and v are the horizontal resp. vertical distance between two 
   * neighbooring pixels on the screen. */
  h = 2. * tan(Camera.hfov * M_PI/180.) / (float)x;
  v = 2. * tan(Camera.vfov * M_PI/180.) / (float)y;

  /* the ray origin is the same for all eye-rays */
  ray.pos = Camera.eyep; 

  if (fp) 
    fprintf(fp, "P6\n%ld %ld\n255\n", x, y);

  for (j=y-1, ysample=-v*(float)(y-1)/2.; j>=0; j--, ysample+=v) {
    id = ids + j*x;
    for (i=0, xsample=-h*(float)(x-1)/2.; i<x; i++, id++, xsample+=h) {
      unsigned long the_id = (*id)&0xffffff;
      PATCH *the_patch = the_id <= maxpatchid ? id2patch[the_id] : (PATCH *)NULL;

      if (the_id == 0) {	/* background visible through pixel */
	pix[i] = Black;
	continue;
      }

      /* construct ray direction to center of pixel */
      VECTORCOMB3(Camera.Z, xsample, Camera.X, ysample, Camera.Y, ray.dir);
      VECTORNORMALIZE(ray.dir);

      pix[i] = Black;
      dist = HUGE;
      hit = (HITREC *)NULL;
      if (the_patch)		/* most probably the nearest patch */
      {
	hit = PatchIntersect(the_patch, &ray, 0., &dist, HIT_FRONT, &hitstore);
      }

      if (!hit) {
	dist = HUGE;
	hit = GeomListDiscretisationIntersect(ClusteredWorld, &ray, 0., &dist, HIT_FRONT, &hitstore);
      }

      if (hit) {
	double u, v;
	VECTOR pt, dir;
	COLOR rad;

	VECTORADDSCALED(ray.pos, dist, ray.dir, pt);
	PatchUV(hit->patch, &pt, &u, &v);
	VECTORSCALE(-1, ray.dir, dir);

	rad = GetRadiance(hit->patch, u, v, dir);
	/*	rad = GetDirectRadiance(hit->patch, u, v, dir); */
	RadianceToRGB(rad, &pix[i]);
	/*
	if (hit->patch != the_patch) {
	  pix[i] = Red;
	  lostpixels++;
	}
	*/
      } else {
	pix[i] = Black;
	/*	
	if (the_patch) {
	  pix[i] = Red;
	  lostpixels++;
	}
	*/
      }
    }

    if (fp) {
      for (i=0; i<x; i++) {
	fprintf(fp, "%c%c%c", 
		(char)(pix[i].r * 255.), 
		(char)(pix[i].g * 255.), 
		(char)(pix[i].b * 255.));
      }
    }

    RenderPixels(0, j, x, 1, pix);
  }

  if (lostpixels > 0)
    Warning(NULL, "%d lost pixels", lostpixels);

  Free((char *)pix, x * sizeof(RGB));
  Free((char *)id2patch, (maxpatchid+1) * sizeof(PATCH *));
  Free((char *)ids, nrpixels * sizeof(unsigned long));

  CanvasPullMode();
}

#define TEST_EDF_SAMPLING 
#ifdef  TEST_EDF_SAMPLING
#include "camera.h"
#include "ray.h"

#ifndef TEST_BSDF_SAMPLING
static POINT refpoint;
#endif
static PATCH *refpatch;

static COLOR EvalDirectRadiance(PATCH *P, double u, double v, VECTOR eyedir)
{
  POINT p;
  VECTOR dir;
  HITREC hit;

  PatchPoint(P, u, v, &p);
  InitHit(&hit, refpatch, NULL, &p, &refpatch->normal, refpatch->surface->material, 0.);

  VECTORSUBTRACT(p, refpoint, dir);
  VECTORNORMALIZE(dir);
  return EdfEval(refpatch->surface->material->edf, &hit, &dir, ALL_COMPONENTS, (double *)0);
}

static void DoTestEdfEval(PATCH *patch, POINT *p)
{
  refpoint = *p;
  refpatch = patch;

  if (!patch->surface->material->edf) {
    fprintf(stderr, "Not a light source!! Try again.\n");
    return;
  }

  Raycast((FILE *)NULL, EvalDirectRadiance);
}

static void TestEdfEval(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point on a light source... "); fflush(stderr);
  SelectPatchSetCallback(DoTestEdfEval);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

#include <stdlib.h>
extern double drand48(void);

static void DoTestEdfSampling(PATCH *patch, POINT *p)
{
  VECTOR dir;
  COLOR val; RGB col;
  double pdf;
  EDF *edf = patch->surface->material->edf;
  int i;
  RAY ray;
  HITREC *hit, hitstore;
  float dist;

  if (!edf) {
    fprintf(stderr, "Not a light source!! Try again.\n");
    return;
  }

  ray.pos = *p;
  fprintf(stderr, "%s %d: p = ", __FILE__, __LINE__);
  VectorPrint(stderr, *p);
  fprintf(stderr, "\n");

  fprintf(stderr, "\n");
  for (i=0; i<10; i++) {
    float cosa;
    HITREC phit;
    InitHit(&phit, patch, NULL, p, &patch->normal, patch->surface->material, 0.);

    dir = EdfSample(edf, &phit, ALL_COMPONENTS, 
		    drand48(), drand48(),
		    &val, &pdf);

    fprintf(stderr, "==> dir = ");
    VectorPrint(stderr, dir);
    fprintf(stderr, "\nval = ");
    ColorPrint(stderr, val);
    fprintf(stderr, ", pdf = %f\n", pdf);

    cosa = VECTORDOTPRODUCT(dir, patch->normal);
    COLORSCALE(cosa / pdf, val, val);      
    fprintf(stderr, "radiance = ");
    ColorPrint(stderr, val);
    fprintf(stderr, "\n");

    ray.dir = dir;
    dist = HUGE;
    if ((hit = GridIntersect(WorldGrid, &ray, EPSILON, &dist, HIT_FRONT, &hitstore))) {
      RadianceToRGB(val, &col);
      RenderSetColor(&col);
      RenderLine(p, &hit->point);
    } else
      fprintf(stderr, "No hit.\n");
  }

  RenderFinish();
}

static void TestEdfSampling(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point on a light source  ... "); fflush(stderr);
  SelectPatchSetCallback(DoTestEdfSampling);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /* TEST_EDF_SAMPLING */

#define PRINT_MATERIAL
#ifdef PRINT_MATERIAL
static void DoPrintMaterial(PATCH *P, POINT *p)
{
  MaterialPrint(stderr, P->surface->material);
}

static void PrintMaterialCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a patch ... "); fflush(stderr);
  SelectPatchSetCallback(DoPrintMaterial);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);
}
#endif /*PRINT_MATERIAL*/

/*#define TEST_PATH*/
#ifdef TEST_PATH
extern void TestTracePath(int i);	/* MCRAD/rwrad.c */
static int patcnt = 1;
#include "MCRAD/rwrad.h"

static void TestPathCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (Radiance!=&RandomWalkRadiosity) {
    fprintf(stderr, "This test requires Random Walk Radiosity!\n");
    return;
  }

  TestTracePath(patcnt);
  patcnt++;
}
#endif /*TEST_PATH*/

#define TEST_RTSPIXEL
#ifdef TEST_RTSPIXEL
#include "RAYTRACING/rtstochastic.h"

static void DoTraceDebugPixel(int pixx, int pixy)
{
  RTStochastic_DebugPixel(pixx, pixy);
}

static void TraceDebugPixelCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a pixel ... "); fflush(stderr);
  SelectPixelSetCallback(DoTraceDebugPixel);
  CanvasPushMode(CANVASMODE_SELECT_PIXEL);
}

#endif

#define PIXEL_RADIANCE
#ifdef PIXEL_RADIANCE

#include "color.h"
#include "raytracing.h"
#include "RAYTRACING/rtstochastic.h"
#include "RAYTRACING/bidirpath.h"

static void DoGetPixelRadiance(int pixx, int pixy)
{
  COLOR rad;
  
  if(RayTracing)
    {
    if(RayTracing == &RT_StochasticMethod) rad = RTStochastic_GetPixel(pixx, pixy);
    if(RayTracing == &RT_BidirPathMethod) rad = Bidir_GetPixel(pixx, pixy);
    
    fprintf(stderr, "Radiance at (%d, %d) is ", pixx, pixy);
    ColorPrint(stderr, rad);
    fprintf(stderr, ".\n");
    }
}

static void GetPixelRadianceCallback(Widget w, XtPointer clien_data, XtPointer call_data)
{
  fprintf(stderr, "Select a pixel ..."); fflush(stderr);
  SelectPixelSetCallback(DoGetPixelRadiance);
  CanvasPushMode(CANVASMODE_SELECT_PIXEL);
}
#endif /* PIXEL_RADIANCE */


#define SAVEDEPTH
#ifdef SAVEDEPTH
extern void SaveDepth(char *fname, FILE *fp, int ispipe);

static void SaveDepthCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  FILE *fp = fopen("depth.ppm", "w");
  fprintf(stderr, "Saving depth image in 'depth.ppm' ..."); fflush(stderr);
  SaveDepth("depth.ppm", fp, FALSE);
  fprintf(stderr, "done.\n");
  fclose(fp);
}
#endif /*SAVEDEPTH*/

#define DUMPS
#ifdef DUMPS
#include "ui_dump.h"
#endif /*DUMPS*/

#define MCR_DUMP_LEAF
#ifdef MCR_DUMP_LEAF
#include "MCRAD/mcrad.h"

extern void McrDumpLeaf(PATCH *P, double u, double v);

static void McrDumpLeafAtPoint(PATCH *P, POINT *p)
{
  double u, v;
  PatchUV(P, p, &u, &v);
  McrDumpLeaf(P, u, v);
}

static void McrDumpLeafCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (Radiance != &StochasticRelaxationRadiosity) {
    Error(NULL, "This test requires Stochastic Relaxation Radiosity");
    return;
  }

  fprintf(stderr, "Select a point ... "); fflush(stderr);
  SelectPatchSetCallback(McrDumpLeafAtPoint);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);    
}
#endif /*MCR_DUMP_LEAF*/

#define DUMP_MGF
#ifdef DUMP_MGF
#include "patch_flags.h"

static MATERIAL *lastmat;
static int vcount;

static void GeomListDumpMGF(FILE *out, GEOMLIST *gl);

static void DiffuseColorDumpMGF(FILE *out, COLOR col, char *id)
{
  float x, y, intensity;
  MakeMgfColor(col, &x, &y, &intensity);
  fprintf(out, "c\ncxy %g %g\n%s %g\n", x, y, id, intensity);
}

#include "vertex.h"
static PATCH *FakePatch(void)
{
  VECTOR p[3] = { {0,0,0}, {1,0,0}, {0,1,0} };
  VERTEX *v[3];
  v[0] = VertexCreate(&p[0], NULL, NULL, NULL); 
  v[1] = VertexCreate(&p[1], NULL, NULL, NULL); 
  v[2] = VertexCreate(&p[2], NULL, NULL, NULL); 
  return PatchCreate(3, v[0], v[1], v[2], NULL); 
}

static void MaterialDumpMGF(FILE *out, MATERIAL *mat)
{
  static PATCH *fakepatch = NULL;
  VECTOR p = {0.5, 0.5, 0.};
  HITREC hit;
  if (!fakepatch) fakepatch = FakePatch();
  InitHit(&hit, fakepatch, NULL, &p, &fakepatch->normal, mat, 0.);

  fprintf(out, "m %s =\n", mat->name);
  fprintf(out, "sides %d\n", mat->sided ? 1 : 2);
  if (mat->edf)
  DiffuseColorDumpMGF(out, EdfDiffuseEmittance(mat->edf, &hit), "ed");
  if (mat->bsdf)
  DiffuseColorDumpMGF(out, BsdfDiffuseReflectance(mat->bsdf, &hit, &fakepatch->normal), "rd");
}

static void VertexDumpMGF(FILE *out, VERTEX *v)
{
  fprintf(out, "v v%d =\np %g %g %g\n", ++vcount,
	  v->point->x, v->point->y, v->point->z);
}

static void PatchDumpMGF(FILE *out, PATCH *patch)
{
  int vcbase = vcount;
  VertexDumpMGF(out, patch->vertex[0]);
  VertexDumpMGF(out, patch->vertex[1]);
  VertexDumpMGF(out, patch->vertex[2]);
  if (patch->nrvertices > 3) {
    VertexDumpMGF(out, patch->vertex[3]);
    fprintf(out, "f v%d v%d v%d v%d\n", vcbase+1, vcbase+2, vcbase+3, vcbase+4);
  } else
    fprintf(out, "f v%d v%d v%d\n", vcbase+1, vcbase+2, vcbase+3);  
}

static void PrimitiveDumpMGF(FILE *out, PATCHLIST *patches)
{
  if (patches) {
    MATERIAL *mat = patches->patch->surface->material;
    if (mat != lastmat) {
      /* write out material of first patch (subsequent patches have same material) */
      MaterialDumpMGF(out, patches->patch->surface->material);
      lastmat = mat;
    }
    ForAllPatches(patch, patches) {
      if (!IGNORE_PATCH(patch))
	PatchDumpMGF(out, patch);
    } EndForAll;
  }
}

static void GeomDumpMGF(FILE *out, GEOM *geom)
{
  if (GeomIsAggregate(geom))
    GeomListDumpMGF(out, GeomPrimList(geom));
  else
    PrimitiveDumpMGF(out, GeomPatchList(geom));
}

static void GeomListDumpMGF(FILE *out, GEOMLIST *gl)
{
  ForAllGeoms(geom, gl) {
    GeomDumpMGF(out, geom);
  } EndForAll;
}

static void DumpMGF(char *fname)
{
  int ispipe;
  FILE *fp = OpenFile(fname, "w", &ispipe);
  if (!fp) {
    Error("DumpMGF", "Can't open file %s for writing.", fname);
    return;
  }
  fprintf(stderr, "Dumping MGF model in '%s' ... ", fname);
  vcount=0; lastmat=(MATERIAL *)NULL;
  GeomListDumpMGF(fp, World);
  CloseFile(fp, ispipe);
  fprintf(stderr, "done.\n");
}

static void DumpMGFCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  CanvasPushMode(CANVASMODE_WORKING);
  DumpMGF("dumped.mgf.gz");
  CanvasPullMode();
}
#endif /*DUMP_MGF*/

#define CULL_DUPLICATE_PATCHES
#ifdef CULL_DUPLICATE_PATCHES

#define VECTORCOMPARE(v1, v2) 		\
( (v1.x < v2.x - EPSILON) ? -1 :	\
( (v1.x > v2.x + EPSILON) ? +1 :	\
( (v1.y < v2.y - EPSILON) ? -1 :	\
( (v1.y > v2.y + EPSILON) ? +1 :	\
( (v1.z < v2.z - EPSILON) ? -1 :	\
( (v1.z > v2.z + EPSILON) ? +1 : 0 ))))))

static int SpectrumCompare(SPECTRUM s1, SPECTRUM s2)
{
  InitChannels2(s1, _s1, s2, _s2);
  if (*_s1 < *_s2 - EPSILON) return -1; 
  if (*_s1 > *_s2 + EPSILON) return +1;
  _s1++; _s2++;
  if (*_s1 < *_s2 - EPSILON) return -1; 
  if (*_s1 > *_s2 + EPSILON) return +1;
  _s1++; _s2++;
  if (*_s1 < *_s2 - EPSILON) return -1; 
  if (*_s1 > *_s2 + EPSILON) return +1;
  _s1++; _s2++;
  return 0;
}

#define COLORCOMPARE(c1, c2) SpectrumCompare((c1).spec, (c2).spec)
#define EPS2 1e-4

static int CompareHitsNormals(HITREC *hit1, HITREC *hit2)
{
  int code=0;

  if (hit1->dist < hit2->dist-EPS2)
    code = -1;
  else if (hit1->dist > hit2->dist+EPS2)
    code = +1;
  else {
    /* coincident hitpoints, compare normals of hit patches */
    code = VECTORCOMPARE(hit1->patch->normal, hit2->patch->normal);
  }
  return code;
}

static void PrintCoincidingHits(FILE *out, HITLIST *start, HITLIST *end)
{
  HITLIST *h;
  for (h=start; h!=end; h=h->next) {
    HITREC *hit = h->hit;
    fprintf(stderr, "patch %d (area %g) (u,v) = (%f,%f), ", 
	    hit->patch->id, hit->patch->area, hit->uv.u, hit->uv.v);
  }
}

static void DoCullDuplicates(HITLIST *start, HITLIST *end)
{
  /* find largest patch hit between 'start' and 'hit' in sorted hitlist */
  HITLIST *h, *largest;
  float maxarea;

  if (end == start->next) {
    /* single hit -> no or no complete overlap with other patches */
    PATCH_SET_HAS_NO_OVERLAP(start->hit->patch);
    return;
  }

  /* find patch with largest area at the considered hit point */
  maxarea = 0.; largest = NULL;
  for (h=start; h!=end; h=h->next) {
    HITREC *hit = h->hit;
    if (Monitored(hit->patch)) {
      fprintf(stderr, "\nPatch %d hit: span = ", hit->patch->id);
      PrintCoincidingHits(stderr, start, end);
    }
    if (!(hit->flags & HIT_UV))
      PatchUV(hit->patch, &hit->point, &hit->uv.u, &hit->uv.v);
    if (hit->uv.u < 0.01 || hit->uv.u > 0.99 || hit->uv.v < 0.01 || hit->uv.v > 0.99)
      return;	  /* hit too close to border */
    if (hit->patch->area > maxarea*(1.+EPSILON) ||
	/* also compare ids in order to always get same result
	 * for patches of equal area */
	(largest &&
	 hit->patch->area > maxarea*(1.-EPSILON) &&
	 hit->patch->area < maxarea*(1.+EPSILON) &&
	 hit->patch->id < largest->hit->patch->id)) {
      maxarea = hit->patch->area;
      largest = h;
    }
  }

  PATCH_SET_IS_LARGEST(largest->hit->patch);
  if (PATCH_HAS_OVERLAP(largest->hit->patch)) {
    /* largest has overlap with an even larger patch, so there is no
     * full overlap with a single other patch */
    PATCH_SET_HAS_NO_OVERLAP(largest->hit->patch);
  } else {
    /* mark all except largest as having overlap with the largest */
    for (h=start; h!=end; h=h->next) {
      if (h == largest) continue;
      if (PATCH_HAS_NO_OVERLAP(h->hit->patch)) {
	/* patch contains hits not on 'largest' */
      } else if (PATCH_HAS_OVERLAP(h->hit->patch)) {
	/* check if there was overlap with an other patch than 'largest' 
	 * before. If so, the overlap is not completely with 'largest'
	 * and we will prefer to keep this patch. */
	PATCH *prevovlp = (PATCH *)(h->hit->patch->radiance_data);
	if (prevovlp != largest->hit->patch) {
	  PATCH_SET_HAS_NO_OVERLAP(h->hit->patch);
	  fprintf(stderr, "o");
	  RenderSetColor(&Yellow);
	  RenderPoint(&h->hit->point);
	}
      } else if (PATCH_IS_LARGEST(h->hit->patch)) {
	/* patch was the largest one in a collection of coinciding hits
	 * before, so that must have been without overlap with the
	 * current hit. */
	PATCH_SET_HAS_NO_OVERLAP(h->hit->patch);
	fprintf(stderr, "x");
	RenderSetColor(&Red);
	RenderPoint(&h->hit->point);	
      } else {
	/* new overlap found */
	PATCH_SET_HAS_OVERLAP(h->hit->patch);
	h->hit->patch->radiance_data = largest->hit->patch;
	fprintf(stderr, ".");
	RenderSetColor(&Blue);
	RenderPoint(&h->hit->point);
      }
    }
  }
}

static void CullDuplicatePatches(HITLIST *hits)
{
  HITLIST *start = NULL, *hit;
  for (hit=hits; hit; hit=hit->next) {
    if (hit->hit->patch->omit < 127)
      hit->hit->patch->omit ++;	/* just to indicate we had a hit there */
    if (!start)
      start=hit;
    else if (CompareHitsNormals(start->hit, hit->hit) != 0) {
      DoCullDuplicates(start, hit);
      start = hit;
    }
  }
}

static int interrupt_culling;

static void InterruptCullDuplicatePatchesCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  interrupt_culling = TRUE;
}

#include "globallines.h"

static void CullDuplicatePatchesCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int n = 0, patches_left, nrpatches;

  patches_left = 0;
  ForAllPatches(P, Patches) {
    P->omit = 0;	/* misuse of field!! */
    patches_left ++;
  } EndForAll;
  nrpatches = patches_left;
  interrupt_culling = FALSE;

  InitGlobalLines(NULL, CompareHitsNormals, CullDuplicatePatches);

  CanvasPushMode(CANVASMODE_WORKING);
  while (/*patches_left > 0 &&*/ !interrupt_culling) {
    float area_left, totalarea;
    int i, nrlines = nrpatches * 10;
    for (i=0; i<nrlines; i++) {
      DoGlobalLine(n++);
      CheckForEvents();
    }
    RenderFinish();
    ProcessWaitingEvents();

    patches_left = 0; area_left = 0.; totalarea = 0.;
    ForAllPatches(P, Patches) {
      if (P->omit == 0) {
	patches_left++ ;
	area_left += P->area;
      }
      totalarea += P->area;

      if (PATCH_HAS_OVERLAP(P) &&
	  !PATCH_HAS_NO_OVERLAP(P) &&
	  P->omit > 2) {	/* more than 2 rays decide !! */
	PATCH_SET_IGNORE(P);
      } else {
	PATCH_SET_DONT_IGNORE(P);
      }
    } EndForAll;

    fprintf(stderr, "\n%d patches without hits out of %d (%g%% area)\n", patches_left, nrpatches, area_left/totalarea);

    DumpMGF("culled.mgf.gz");
  }
  CanvasPullMode();
}
#endif /*CULL_DUPLICATE_PATCHES*/

#define DUMP_PATCHES_AT_PIXEL
#ifdef DUMP_PATCHES_AT_PIXEL
#include "hitlist.h"

static void DumpPatchesAtPixel(PATCH *P, POINT *p)
{
  RAY ray;
  HITLIST *hits;
  HITREC *prev;

  ray.pos = Camera.eyep;
  VECTORSUBTRACT(*p, ray.pos, ray.dir);
  VECTORNORMALIZE(ray.dir);

  hits = AllGridIntersections((HITLIST*)NULL, WorldGrid, &ray, -EPSILON, HUGE,  HIT_FRONT|HIT_BACK|HIT_PATCH|HIT_POINT);
  hits = HitListSort(hits, CompareHitsNormals);

  prev = NULL;
  ForAllHits(hit, hits) {
    if (prev && CompareHitsNormals(hit, prev) == 0) {
      fprintf(stderr, "Coinciding: ");
      RenderSetColor(hit->flags&HIT_BACK ? &Turquoise : &Red);
      RenderPatchOutline(prev->patch);
    } else
      RenderSetColor(hit->flags&HIT_BACK ? &Magenta : &Green);
    PrintHit(stderr, hit);
    RenderPatchOutline(hit->patch);
    prev = hit;
  } EndForAll;
  RenderFinish();

  DestroyHitlist(hits);
}

static void DumpPatchesAtPixelCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  fprintf(stderr, "Select a point ... "); fflush(stderr);
  SelectPatchSetCallback(DumpPatchesAtPixel);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);  
}
#endif

#define TEST_GET_RADIANCE
#ifdef TEST_GET_RADIANCE
static void TestGetRadianceAtPoint(PATCH *patch, POINT *point)
{
  double u, v;
  VECTOR dir;
  COLOR rad;
  PatchUV(patch, point, &u, &v);
  VECTORSUBTRACT(Camera.eyep, *point, dir);
  VECTORNORMALIZE(dir);
  fprintf(stderr, "Querying radiance at Point ");
  VectorPrint(stderr, *point);
  fprintf(stderr, " into direction to eye ");
  VectorPrint(stderr, dir);
  fprintf(stderr, "\n");
  rad = Radiance->GetRadiance(patch, u, v, dir);
  fprintf(stderr, "Result = ");
  ColorPrint(stderr, rad);
  fprintf(stderr, ", luminosity = %g\n", 
	  ColorLuminance(rad) * M_PI);
}

static void TestGetRadianceCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (!Radiance || !Radiance->GetRadiance) {
    fprintf(stderr, "No radiance method active, or active radiance method does not have a GetRadiance query routine");
    return;
  }

  fprintf(stderr, "Select a point ... "); fflush(stderr);
  SelectPatchSetCallback(TestGetRadianceAtPoint);
  CanvasPushMode(CANVASMODE_SELECT_PATCH);    
}
#endif

void CreateDebugMenu(Widget menuBar)
{
  Widget debugMenu;

  debugMenu = CreateSubMenu(menuBar, "debugButton", "debugMenu");

  CreateMoreButton(menuBar);

  CreatePushButton(debugMenu, "Path Debug Panel", ShowPathDebugPanel, (XtPointer)NULL);
  
  CreatePushButton(debugMenu, "Dump Pooled Memory Breakdown", DumpMemoryBreakdownCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Dump World", DumpWorldCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Dump Clusters", DumpClustersCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Dump Patch", DumpPatchCallback, (XtPointer)NULL);
  CreateCascadeDialog(debugMenu, "Locate Patch", CreateLocatePatchDialog, "locatePatchDialog", NULL, NULL);
  CreatePushButton(debugMenu, "Start monitoring patch", StartMonitoringPatchCallback, (XtPointer)NULL);
  CreateCascadeDialog(debugMenu, "Start Monitoring Patch w/ ID", CreateMonitorPatchDialog, "locatePatchDialog", NULL, NULL);
  CreatePushButton(debugMenu, "Stop monitoring patch", StopMonitoringPatchCallback, (XtPointer)NULL);
#ifdef RECORD_MONITORED
  CreatePushButton(debugMenu, "Close patch monitor files", ClosePatchMonitorFilesCallback, (XtPointer)NULL);
#endif
#ifdef TEST_SUBDIVISION
  CreatePushButton(debugMenu, "Test Element Subdivision", TestRegularElementSubdivisionCallback, (XtPointer)NULL);
#endif
#ifdef TEST_UNIFORM_MAPPING
  CreatePushButton(debugMenu, "Test Uniform Mapping", TestUniformMappingCallback, (XtPointer)NULL);
#endif /*TEST_UNIFORM_MAPPING*/
#ifdef TEST_ID_RENDERING
  CreatePushButton(debugMenu, "Test ID rendering", TestIdRenderingCallback, (XtPointer)NULL);
#endif
#ifdef TEST_POTENTIAL
  CreatePushButton(debugMenu, "Test Direct Potential", TestPotentialCallback, (XtPointer)NULL);
#endif
#ifdef TEST_SHAFTCULLING
  CreatePushButton(debugMenu, "Select Polygon-To-Polygon Shaft", SelectPtPShaftCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Select Box-To-Box Shaft", SelectBtBShaftCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Show Shaft", ShowShaftCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Show Shaft Contents", ShowShaftContentsCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Test Patch Against Shaft", TestShaftCullTestPatchCallback, (XtPointer)NULL);
#ifdef TESTSHAFTFORMFACTOR
  CreatePushButton(debugMenu, "Test Shaft Form Factor", TestShaftFormFactorCallback, (XtPointer)NULL);
#endif
#endif
#ifdef TESTFORMFACTOR
  CreatePushButton(debugMenu, "Test Form Factor", TestFormFactorCallback, (XtPointer)NULL);
#endif
#ifdef TEST_BREP
  CreatePushButton(debugMenu, "Test Patch Neighbours", TestNeighboursCallback, (XtPointer)NULL);
#endif
#ifdef DUMP_GALERKIN_MATRIX
  CreatePushButton(debugMenu, "Dump Form Factor Matrix", DumpMatrixCallback, (XtPointer)NULL);
#endif /*DUMP_GALERKIN_MATRIX*/
#ifdef EXAMINE_ELEMENT_HIERARCHY
  CreatePushButton(debugMenu, "Start Examine Element Hierarchy", StartExamineElementHierarchy, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Examine Element Hierarchy Up", ExamineElementHierarchyUp, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Examine Element Hierarchy Down", ExamineElementHierarchyDown, (XtPointer)NULL);
#endif /*EXAMINE_ELEMENT_HIERARCHY*/
#ifdef TEST_SGL
  CreatePushButton(debugMenu, "Test SGL", TestSGLCallback, (XtPointer)NULL);
#endif /*TEST_SGL*/
#ifdef DUMP_REFL
  CreatePushButton(debugMenu, "Dump Diffuse Reflectivities", DumpReflectivities, (XtPointer)NULL);
#endif /*DUMP_REFL*/
#ifdef TEST_RAYTRACING
  CreatePushButton(debugMenu, "Draw Virtual Screen", DrawVirtualScreen, (XtPointer)NULL);
#endif
#ifdef TEST_RAYINTERSECT
  CreatePushButton(debugMenu, "Test Ray-World Intersection", TestRayIntersect, (XtPointer)NULL);
#endif /*TEST_RAYINTERSECT*/
#ifdef TEST_LIGHTMETERING
  CreatePushButton(debugMenu, "Test Light Metering", TestLightMetering, (XtPointer)NULL);
#endif /*TEST_LIGHTMETERING*/
#ifdef TEST_GLOBAL_LINE
  CreatePushButton(debugMenu, "Test Global Line Tracing", TestGlobalLineCallback, (XtPointer)NULL);
#endif /*TEST_GLOBAL_LINE*/
#ifdef RAYTRACING_TEST
  CreateRayTracingTestMenu(debugMenu);
  CreatePushButton(debugMenu, "Ray Tracing Confidence Test", TestRayTracingCallback, (XtPointer)NULL);
#endif /*RAYTRACING_TEST*/
#ifdef SHOW_NORMALS
  CreatePushButton(debugMenu, "Show Vertex Normals", ShowNormalsCallback, (XtPointer)NULL);
#endif /*SHOW_NORMALS*/
#ifdef  TEST_BSDF_SAMPLING
  CreatePushButton(debugMenu, "Test BSDF sampling", TestBsdfSampling, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Show Shading Frame At Point", ShowShadingFrameCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Print Albedoes at Point", PrintAlbedoesCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Test Path Tracing Hack at Point", TestHackPathCallback, (XtPointer)NULL);
#endif /*TEST_BSDF_SAMPLING*/
#ifdef TEST_PHOTONMAP
  CreatePushButton(debugMenu, "Test Photonmap", TestPhotonmapReconstruction,
		   (XtPointer)NULL);
#endif
#ifdef  TEST_EDF_SAMPLING
  CreatePushButton(debugMenu, "Test EDF sampling", TestEdfSampling, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Test EDF evaluation", TestEdfEval, (XtPointer)NULL);
#endif /*TEST_EDF_SAMPLING*/
#ifdef PRINT_MATERIAL
  CreatePushButton(debugMenu, "Print Material", PrintMaterialCallback, (XtPointer)NULL);
#endif /*PRINT_MATERIAL*/
#ifdef TEST_PATH
  CreatePushButton(debugMenu, "Test Random Walk", TestPathCallback, (XtPointer)NULL);
#endif /*TEST_PATH*/
#ifdef TEST_RTSPIXEL
  CreatePushButton(debugMenu, "Debug RTS Trace Pixel", TraceDebugPixelCallback, (XtPointer)NULL);
#endif /*TEST_RTSPIXEL*/
#ifdef SAVEDEPTH
  CreatePushButton(debugMenu, "Save Depth", SaveDepthCallback, (XtPointer)NULL);
#endif /*SAVEDEPTH*/
#ifdef DUMPS
  CreateCascadeDialog(debugMenu, "Dump Configuration", CreateDumpConfigureDialog, "configDumpDialog", NULL, NULL);
  CreateCascadeDialog(debugMenu, "Read Radiance/False Color Dump", CreateReadDumpDialog, "readDumpDialog", NULL, NULL);
#endif
#ifdef MCR_DUMP_LEAF
  CreatePushButton(debugMenu, "Dump Leaf Element At Point", McrDumpLeafCallback, (XtPointer)NULL);
#endif
#ifdef DUMP_MGF
  CreatePushButton(debugMenu, "Dump MGF model", DumpMGFCallback, (XtPointer)NULL);
#endif /*DUMP_MGF*/
#ifdef CULL_DUPLICATE_PATCHES
  CreatePushButton(debugMenu, "Cull duplicate patches", CullDuplicatePatchesCallback, (XtPointer)NULL);
  CreatePushButton(debugMenu, "Interrupt Culling", InterruptCullDuplicatePatchesCallback, (XtPointer)NULL);
#endif /*CULL_DUPLICATE_PATCHES*/
#ifdef DUMP_PATCHES_AT_PIXEL
  CreatePushButton(debugMenu, "Dump Patches At Pixel", DumpPatchesAtPixelCallback, (XtPointer)NULL);
#endif
#ifdef TEST_GET_RADIANCE
  CreatePushButton(debugMenu, "Get Radiance At Point", TestGetRadianceCallback, (XtPointer)NULL);
#endif
#ifdef PIXEL_RADIANCE
  CreatePushButton(debugMenu, "Get Radiance At Pixel (RT)", GetPixelRadianceCallback, (XtPointer)NULL);
#endif /* PIXEL_RADIANCE */
}


