/* MetaContour, version 0.1 -------------------------------------- */
/* Copyright(C) 2004, Brooks Moses                                 */
/*                                                                 */
/* This version of MetaContour is made available under the Gnu     */
/* Public License; see metacontour_main.cc for details.            */
/*                                                                 */
/* This is a very pre-release version of MetaContour, distributed  */
/* primarily as an example of a use of MetaPlot.  It can be        */
/* compiled with gcc using the line:                               */
/*                                                                 */
/*   g++ metacontour.cc cpoint.cc metacontour_main.cc              */
/*                                                                 */
/*-----------------------------------------------------------------*/

#include"metacontour.h"
using namespace std;

MetapostContours::MetapostContours(cparray& PlotData,
                                  vector<double>& ContourListIn,
                                  string& PlotNameIn)
{
 ContourList = ContourListIn;
 PlotName = PlotNameIn;

 GridCommand = "addto " + PlotName + ".Grid doublepath ";
 DrawCommand = "addto " + PlotName + ".LinePlot doublepath ";
 FillCommand = "addto " + PlotName + ".FillPlot contour ";

 GridStream << "picture " << PlotName << ".Grid; "
            << PlotName << ".Grid := nullpicture;\n";
 LinePlotStream << "picture " << PlotName << ".LinePlot; "
                << PlotName << ".LinePlot := nullpicture;\n";
 FillPlotStream << "picture " << PlotName << ".FillPlot; "
                << PlotName << ".FillPlot := nullpicture;\n";


 /* Construct contours */

 cpoint x00; cpoint x01; cpoint x10; cpoint x11;
 for (int j=1; j<PlotData.j_dim(); j++) {
   x00 = PlotData(0,j-1);
   x01 = PlotData(0,j);
   DrawGrid(x00, x01);
 }
 for (int i=1; i<PlotData.i_dim(); i++) {
   x00 = PlotData(i-1,0);
   x10 = PlotData(i,0);
   DrawGrid(x00, x10);
   for (int j=1; j<PlotData.j_dim(); j++) {
     x00 = PlotData(i-1, j-1);
     x01 = PlotData(i-1,  j );
     x10 = PlotData( i , j-1);
     x11 = PlotData( i ,  j );
     DrawGrid(x10, x11);
     DrawGrid(x01, x11);
     PlotSquare(x00, x10, x11, x01);
   }
 }
}


/* Start plotting a given grid square */
//
// Search Region: x3--------x2
//                |oooooooooo|
//                |oooooooooo|
//                |oooooooooo|
//                |oooooooooo|
//                x4--------x1
//
void MetapostContours::PlotSquare(cpoint x1, cpoint x2,
                            cpoint x3, cpoint x4)
{
 /* Organize things so x4 is smallest */

 if (x1.z < x4.z) {   // 1 is lowest of (1,4)
   if (x2.z < x1.z) {   // 2 is lowest of (1,2,3,4)
     if (x3.z < x2.z)  {   // 3 is lowest of all
       cpoint xtemp = x4; x4 = x3; x3 = x2; x2 = x1; x1 = xtemp;
     } else {             // 2 is lowest of all
       cpoint xtemp = x4; x4 = x2; x2 = xtemp; xtemp = x3; x3 = x1; x1 = xtemp;
     }
   } else {             // 1 is lowest of (1,2,4)
     if (x3.z < x1.z)  {   // 3 is lowest of all
       cpoint xtemp = x4; x4 = x3; x3 = x2; x2 = x1; x1 = xtemp;
     } else {             // 1 is lowest of all
       cpoint xtemp = x4; x4 = x1; x1 = x2; x2 = x3; x3 = xtemp;
     }
   }
 } else {             // 4 is lowest of (1,4)
   if (x2.z < x4.z) {   // 2 is lowest of (1,2,4)
     if (x3.z < x2.z)  {   // 3 is lowest of all
       cpoint xtemp = x4; x4 = x3; x3 = x2; x2 = x1; x1 = xtemp;
     } else {             // 2 is lowest of all
       cpoint xtemp = x4; x4 = x2; x2 = xtemp; xtemp = x3; x3 = x1; x1 = xtemp;
     }
   } else {             // 4 is lowest of (1,2,4)
     if (x3.z < x4.z)  {   // 3 is lowest of all
       cpoint xtemp = x4; x4 = x3; x3 = x2; x2 = x1; x1 = xtemp;
     }                    // 4 is lowest of all, do nothing
   }
 }

 /* Find initial level, draw square, start rest if appropriate */
 if (ContourList[ContourList.size()-1] < x4.z) {
   DrawFilled(ContourList.size()-1, x1, x4, x3, x2);
 } else {
   int contourLevel = 0;
   while (ContourList[contourLevel] < x4.z &&
          contourLevel < ContourList.size()-1) contourLevel++;
   DrawFilled(contourLevel-1, x1, x4, x3, x2);
   PlotPent(x1, x2, x3, x4, contourLevel);
 }
}


/* Continue plotting for a corner of a grid square */
//
// Search Region: x3--------x2
//                |..........|
//                |........oo|
//                |......oooo|
//                |....oooooo|
//                x4--------x1
//
void MetapostContours::PlotCorner(cpoint x1, cpoint x2,
                            cpoint x3, cpoint x4, int contourLevel)
{
 if (contourLevel >= ContourList.size()) return;
 double zc2 = ContourList[contourLevel];
 if (x1.z > zc2) {
   /* another corner */
   cpoint y1 = x4.interpolateto(x1, zc2);
   cpoint y2 = x2.interpolateto(x1, zc2);
   DrawFilled(contourLevel, y1, y2, x1);
   DrawLine(contourLevel, y1, y2);
   PlotCorner(x1, x2, x3, x4, contourLevel+1);
 }
}


/* Continue plotting for a two-corner side of a grid square */
//
// Search Region: x3--------x2
//                |.....ooooo|
//                |.....ooooo|
//                |.....ooooo|
//                |.....ooooo|
//                x4--------x1
//
void MetapostContours::PlotRect(cpoint x1, cpoint x2,
                          cpoint x3, cpoint x4, int contourLevel)
{
 if (contourLevel >= ContourList.size()) return;
 double zc2 = ContourList[contourLevel];
 if (x1.z > zc2) {
   if (x2.z > zc2) {
     /* another rectangle */
     cpoint y1 = x4.interpolateto(x1, zc2);
     cpoint y2 = x3.interpolateto(x2, zc2);
     DrawFilled(contourLevel, y1, y2, x2, x1);
     DrawLine(contourLevel, y1, y2);
     PlotRect(x1, x2, x3, x4, contourLevel+1);
   } else {
     /* corner around x1 */
     cpoint y1 = x4.interpolateto(x1, zc2);
     cpoint y2 = x2.interpolateto(x1, zc2);
     DrawFilled(contourLevel, y1, y2, x1);
     DrawLine(contourLevel, y1, y2);
     PlotCorner(x1, x2, x3, x4, contourLevel+1);
   }
 } else {
   if (x2.z > zc2) {
     /* corner around x2 */
     cpoint y1 = x1.interpolateto(x2, zc2);
     cpoint y2 = x3.interpolateto(x2, zc2);
     DrawFilled(contourLevel, y1, y2, x2);
     DrawLine(contourLevel, y1, y2);
     PlotCorner(x2, x3, x4, x1, contourLevel+1);
   }
 }
}


/* Continue plotting for a three-corner pentagon of a grid square */
//
// Search Region: x3--------x2
//                |oooooooooo|
//                |..oooooooo|
//                |....oooooo|
//                |......oooo|
//                x4--------x1
//
void MetapostContours::PlotPent(cpoint x1, cpoint x2,
                          cpoint x3, cpoint x4, int contourLevel)
{
 if (contourLevel >= ContourList.size()) return;
 double zc2 = ContourList[contourLevel];
 if (x1.z > zc2) {
   if (x3.z > zc2) {
     if (x2.z < zc2) {
       /* a saddle-point hexagon, or two corners */
       cpoint y1 = x4.interpolateto(x1, zc2);
       cpoint y2 = x2.interpolateto(x1, zc2);
       cpoint y3 = x2.interpolateto(x3, zc2);
       cpoint y4 = x4.interpolateto(x3, zc2);
       double zcenter = cpoint::saddlepoint(x1, x2, x3, x4);
       if (zcenter < zc2) {
         /* two corners around x1 and x3 */
         DrawFilled(contourLevel, y1, y2, x1);
         DrawLine(contourLevel, y1, y2);
         PlotCorner(x1, x2, x3, x4, contourLevel+1);
         DrawFilled(contourLevel, y3, y4, x3);
         DrawLine(contourLevel, y3, y4);
         PlotCorner(x3, x4, x1, x2, contourLevel+1);
       } else {
         /* a saddle-point hexagon */
         DrawFilled(contourLevel, y1, y4, x3, y3, y2, x1);
         DrawLine(contourLevel, y1, y4);
         DrawLine(contourLevel, y3, y2);
         PlotHex(x1, x2, x3, x4, contourLevel+1, zcenter);
       }
     } else {
       /* another pentagon */
       cpoint y1 = x4.interpolateto(x1, zc2);
       cpoint y2 = x4.interpolateto(x3, zc2);
       DrawFilled(contourLevel, y1, y2, x3, x2, x1);
       DrawLine(contourLevel, y1, y2);
       PlotPent(x1, x2, x3, x4, contourLevel+1);
     }
   } else {                // (x3.z < zc2)
     if (x2.z > zc2) {
       /* rectangle around x1, x2 */
       cpoint y1 = x4.interpolateto(x1, zc2);
       cpoint y2 = x3.interpolateto(x2, zc2);
       DrawFilled(contourLevel, y1, y2, x2, x1);
       DrawLine(contourLevel, y1, y2);
       PlotRect(x1, x2, x3, x4, contourLevel+1);
     } else {
       /* corner around x1 */
       cpoint y1 = x4.interpolateto(x1, zc2);
       cpoint y2 = x2.interpolateto(x1, zc2);
       DrawFilled(contourLevel, y1, y2, x1);
       DrawLine(contourLevel, y1, y2);
       PlotCorner(x1, x2, x3, x4, contourLevel+1);
     }
   }
 } else {                  // (x1.z < zc2)
   if (x3.z > zc2) {
     if (x2.z > zc2) {
       /* rectangle around x2, x3 */
       cpoint y1 = x1.interpolateto(x2, zc2);
       cpoint y2 = x4.interpolateto(x3, zc2);
       DrawFilled(contourLevel, y1, y2, x3, x2);
       DrawLine(contourLevel, y1, y2);
       PlotRect(x2, x3, x4, y1, contourLevel+1);
     } else {
       /* corner around x3 */
       cpoint y1 = x2.interpolateto(x3, zc2);
       cpoint y2 = x4.interpolateto(x3, zc2);
       DrawFilled(contourLevel, y1, y2, x3);
       DrawLine(contourLevel, y1, y2);
       PlotCorner(x3, x4, x1, x2, contourLevel+1);
     }
   } else {
     if (x2.z > zc2) {
       /* corner around x2 */
       cpoint y1 = x1.interpolateto(x2, zc2);
       cpoint y2 = x3.interpolateto(x2, zc2);
       DrawFilled(contourLevel, y1, y2, x2);
       DrawLine(contourLevel, y1, y2);
       PlotCorner(x2, x3, x4, x1, contourLevel+1);
     }
   }
 }
}


/* Continue plotting for a two-corner hex in a grid square */
//
// Search Region: x3--------x2
//                |oooooo....|
//                |oooooooo..|
//                |..oooooooo|
//                |....oooooo|
//                x4--------x1
//
void MetapostContours::PlotHex(cpoint x1, cpoint x2,
                         cpoint x3, cpoint x4,
                         int contourLevel, double zcenter)
{
 if (contourLevel >= ContourList.size()) return;
 double zc2 = ContourList[contourLevel];
 if (x1.z > zc2) {
   if (x3.z > zc2) {
     /* another saddle-point hexagon, or two corners */
     cpoint y1 = x4.interpolateto(x1, zc2);
     cpoint y2 = x2.interpolateto(x1, zc2);
     cpoint y3 = x2.interpolateto(x3, zc2);
     cpoint y4 = x4.interpolateto(x3, zc2);
     if (zcenter < zc2) {
       /* two corners around x1 and x3 */
       DrawFilled(contourLevel, y1, y2, x1);
       DrawLine(contourLevel, y1, y2);
       PlotCorner(x1, x2, x3, x4, contourLevel+1);
       DrawFilled(contourLevel, y3, y4, x3);
       DrawLine(contourLevel, y3, y4);
       PlotCorner(x3, x4, x1, x2, contourLevel+1);
     } else {
       /* another saddle-point hexagon */
       DrawFilled(contourLevel, y1, y4, x3, y3, y2, x1);
       DrawLine(contourLevel, y1, y4);
       DrawLine(contourLevel, y3, y2);
       PlotHex(x1, x2, x3, x4, contourLevel+1, zcenter);
     }
   } else {
     /* corner around x1 */
     cpoint y1 = x4.interpolateto(x1, zc2);
     cpoint y2 = x2.interpolateto(x1, zc2);
     DrawFilled(contourLevel, y1, y2, x1);
     DrawLine(contourLevel, y1, y2);
     PlotCorner(x1, x2, x3, x4, contourLevel+1);
   }
 } else {
   if (x3.z > zc2) {
     /* corner around x3 */
     cpoint y1 = x2.interpolateto(x3, zc2);
     cpoint y2 = x4.interpolateto(x3, zc2);
     DrawFilled(contourLevel, y1, y2, x2);
     DrawLine(contourLevel, y1, y2);
     PlotCorner(x3, x4, x1, x2, contourLevel+1);
   }
 }
}

void MetapostContours::DrawGrid (cpoint x1, cpoint x2) {
 GridStream << GridCommand << x1.metapoint() << "--" << x2.metapoint()
            << ";\n";
}

void MetapostContours::DrawLine   (int contourLevel, cpoint x1, cpoint x2) {
 LinePlotStream << DrawCommand << x1.metapoint() << "--" << x2.metapoint()
                << " withcolor contourcolor"
                << contourLevel+1 << ";\n";
}

void MetapostContours::DrawFilled (int contourLevel, cpoint x1, cpoint x2, cpoint x3) {
 FillPlotStream << FillCommand << x1.metapoint() << "--" << x2.metapoint()
                << "--" << x3.metapoint()
                << "--cycle withcolor contourcolor" << contourLevel+1 << ";\n";
}

void MetapostContours::DrawFilled (int contourLevel, cpoint x1, cpoint x2, cpoint x3,
                                  cpoint x4) {
 FillPlotStream << FillCommand << x1.metapoint() << "--" << x2.metapoint()
                << "--" << x3.metapoint() << "--" << x4.metapoint()
                << "--cycle withcolor contourcolor" << contourLevel+1 << ";\n";
}

void MetapostContours::DrawFilled (int contourLevel, cpoint x1, cpoint x2, cpoint x3,
                                  cpoint x4, cpoint x5) {
 FillPlotStream << FillCommand << x1.metapoint() << "--" << x2.metapoint()
                << "--" << x3.metapoint() << "--" << x4.metapoint()
                << "--" << x5.metapoint()
                << "--cycle withcolor contourcolor" << contourLevel+1 << ";\n";
}

void MetapostContours::DrawFilled (int contourLevel, cpoint x1, cpoint x2, cpoint x3,
                                  cpoint x4, cpoint x5, cpoint x6) {
 FillPlotStream << FillCommand << x1.metapoint() << "--" << x2.metapoint()
                << "--" << x3.metapoint() << "--" << x4.metapoint()
                << "--" << x5.metapoint() << "--" << x6.metapoint()
                << "--cycle withcolor contourcolor" << contourLevel+1 << ";\n";
}