#include <iostream>
#include <cctype>
#ifdef WIN32
#define NOMINMAX
#endif
#include "TVirtualX.h"
#include "TString.h"
#include "TROOT.h"
#include "TGLOrthoCamera.h"
#include "TGLParametric.h"
#include "TGLIncludes.h"
#include "TVirtualPad.h"
#include "KeySymbols.h"
#include "Buttons.h"
#include "TString.h"
#include "TColor.h"
#include "TMath.h"
namespace
{
   
   void ReplaceUVNames(TString &equation)
   {
      
      
      
      
      using namespace std;
      const Ssiz_t len = equation.Length();
      
      
      Int_t vFound = 0;
      for (Ssiz_t i = 0; i < len;) {
         const char c = equation[i];
         if (!isalpha(c)) {
            ++i;
            continue;
         } else{
            ++i;
            if (c == 'u' || c == 'v') {
               
               
               if (i == len || !isalpha(equation[i]) && !isdigit(equation[i]) && equation[i] != '_') {
                  
                  equation[i - 1] = c == 'u' ? 'x' : (++vFound, 'y');
               } else {
                  
                  
                  while (i < len && (isalpha(equation[i]) || isdigit(equation[i]) || equation[i] == '_'))
                     ++i;
               }
            } else {
               while (i < len && (isalpha(equation[i]) || isdigit(equation[i]) || equation[i] == '_'))
                  ++i;
            }
         }
      }
      if (!vFound)
         equation += "+0*y";
   }
}
ClassImp(TGLParametricEquation)
TGLParametricEquation::TGLParametricEquation(const TString &name, const TString &xFun, const TString &yFun,
                             const TString &zFun, Double_t uMin, Double_t uMax,
                             Double_t vMin, Double_t vMax)
                  : TNamed(name, name),
                    fEquation(0),
                    fURange(uMin, uMax),
                    fVRange(vMin, vMax),
                    fConstrained(kFALSE),
                    fModified(kFALSE)
{
   
   
   
   
   if (!xFun.Length() || !yFun.Length() || !zFun.Length()) {
      Error("TGLParametricEquation", "One of string expressions iz empty");
      MakeZombie();
      return;
   }
   TString equation(xFun);
   equation.ToLower();
   ReplaceUVNames(equation);
   fXEquation.reset(new TF2(name + "xEquation", equation.Data(), uMin, uMax, vMin, vMax));
   
   if (fXEquation->IsZombie()) {
      MakeZombie();
      return;
   }
   equation = yFun;
   equation.ToLower();
   ReplaceUVNames(equation);
   fYEquation.reset(new TF2(name + "yEquation", equation.Data(), uMin, uMax, vMin, vMax));
   
   if (fYEquation->IsZombie()) {
      MakeZombie();
      return;
   }
   equation = zFun;
   equation.ToLower();
   ReplaceUVNames(equation);
   fZEquation.reset(new TF2(name + "zEquation", equation.Data(), uMin, uMax, vMin, vMax));
   
   if (fZEquation->IsZombie())
      MakeZombie();
}
TGLParametricEquation::TGLParametricEquation(const TString &name, ParametricEquation_t equation,
                             Double_t uMin, Double_t uMax, Double_t vMin, Double_t vMax)
                  : TNamed(name, name),
                    fEquation(equation),
                    fURange(uMin, uMax),
                    fVRange(vMin, vMax),
                    fConstrained(kFALSE),
                    fModified(kFALSE)
{
   
   if (!fEquation) {
      Error("TGLParametricEquation", "Function ptr is null");
      MakeZombie();
   }
}
Rgl::Range_t TGLParametricEquation::GetURange()const
{
   
   return fURange;
}
Rgl::Range_t TGLParametricEquation::GetVRange()const
{
   
   return fVRange;
}
Bool_t TGLParametricEquation::IsConstrained()const
{
   
   return fConstrained;
}
void TGLParametricEquation::SetConstrained(Bool_t c)
{
   
   fConstrained = c;
}
Bool_t TGLParametricEquation::IsModified()const
{
   
   return fModified;
}
void TGLParametricEquation::SetModified(Bool_t m)
{
   
   fModified = m;
}
void TGLParametricEquation::EvalVertex(TGLVertex3 &newVertex, Double_t u, Double_t v)const
{
   
   if (fEquation)
      return fEquation(newVertex, u, v);
   if (IsZombie())
      return;
   newVertex.X() = fXEquation->Eval(u, v);
   newVertex.Y() = fYEquation->Eval(u, v);
   newVertex.Z() = fZEquation->Eval(u, v);
}
Int_t TGLParametricEquation::DistancetoPrimitive(Int_t px, Int_t py)
{
   
   if (fPainter.get())
      return fPainter->DistancetoPrimitive(px, py);
   return 9999;
}
void TGLParametricEquation::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   
   if (fPainter.get())
      return fPainter->ExecuteEvent(event, px, py);
}
char *TGLParametricEquation::GetObjectInfo(Int_t , Int_t ) const
{
   
   return "parametric surface";
}
void TGLParametricEquation::Paint(Option_t * )
{
   
   if (!fPainter.get())
      fPainter.reset(new TGLHistPainter(this));
   fPainter->Paint("dummyoption");
}
ClassImp(TGLParametricPlot)
TGLParametricPlot::TGLParametricPlot(TGLParametricEquation *eq,
                                     TGLOrthoCamera *camera, TGLPaintDevice *dev)
                      : TGLPlotPainter(camera, dev),
                        fMeshSize(90),
                        fShowMesh(kFALSE),
                        fColorScheme(4),
                        fEquation(eq)
{
   
   InitGeometry();
   InitColors();
}
Bool_t TGLParametricPlot::InitGeometry()
{
   
   
   
   
   Bool_t constrained = fEquation->IsConstrained();
   if (fMeshSize * fMeshSize != (Int_t)fMesh.size() || fEquation->IsModified()) {
      if (fEquation->IsZombie())
         return kFALSE;
      fEquation->SetModified(kFALSE);
      fMesh.resize(fMeshSize * fMeshSize);
      fMesh.SetRowLen(fMeshSize);
      const Rgl::Range_t uRange(fEquation->GetURange());
      const Rgl::Range_t vRange(fEquation->GetVRange());
      const Double_t dU = (uRange.second - uRange.first) / (fMeshSize - 1);
      const Double_t dV = (vRange.second - vRange.first) / (fMeshSize - 1);
      const Double_t dd = 0.001;
      Double_t u = uRange.first;
      TGLVertex3 min;
      fEquation->EvalVertex(min, uRange.first, vRange.first);
      TGLVertex3 max(min), newVert, v1, v2;
      using namespace TMath;
      for (Int_t i = 0; i < fMeshSize; ++i) {
         Double_t v = vRange.first;
         for (Int_t j = 0; j < fMeshSize; ++j) {
            fEquation->EvalVertex(newVert, u, v);
            min.X() = Min(min.X(), newVert.X());
            max.X() = Max(max.X(), newVert.X());
            min.Y() = Min(min.Y(), newVert.Y());
            max.Y() = Max(max.Y(), newVert.Y());
            min.Z() = Min(min.Z(), newVert.Z());
            max.Z() = Max(max.Z(), newVert.Z());
            fMesh[i][j].fPos = newVert;
            if (!constrained) {
               fEquation->EvalVertex(v1, u + dd, v);
               fEquation->EvalVertex(v2, u, v + dd);
               Normal2Plane(newVert.CArr(), v1.CArr(), v2.CArr(), fMesh[i][j].fNormal.Arr());
            }
            v += dV;
         }
         u += dU;
      }
      const Double_t xRange = max.X() - min.X(), yRange = max.Y() - min.Y(), zRange = max.Z() - min.Z();
      if (!xRange || !yRange || !zRange) {
         Error("InitGeometry", "Zero axis range");
         return kFALSE;
      }
      Double_t maxRange = Max(Max(xRange, yRange), zRange);
      const Double_t xZoom = maxRange / xRange, yZoom = maxRange / yRange, zZoom = maxRange / zRange;
      if (constrained) {
         u = uRange.first;
         for (Int_t i = 0; i < fMeshSize; ++i) {
            Double_t v = vRange.first;
            for (Int_t j = 0; j < fMeshSize; ++j) {
               TGLVertex3 &ver = fMesh[i][j].fPos;
               ver.X() *= xZoom, ver.Y() *= yZoom, ver.Z() *= zZoom;
               fEquation->EvalVertex(v1, u + dd, v);
               fEquation->EvalVertex(v2, u, v + dd);
               v1.X() *= xZoom, v1.Y() *= yZoom, v1.Z() *= zZoom;
               v2.X() *= xZoom, v2.Y() *= yZoom, v2.Z() *= zZoom;
               Normal2Plane(ver.CArr(), v1.CArr(), v2.CArr(), fMesh[i][j].fNormal.Arr());
               v += dV;
            }
            u += dU;
         }
      }
      using Rgl::Range_t;
      constrained ? fBackBox.SetPlotBox(Range_t(min.X(), max.X()), Range_t(min.Y(), max.Y()), Range_t(min.Z(), max.Z())):
                    fBackBox.SetPlotBox(Range_t(min.X() * xZoom, max.X() * xZoom),
                                        Range_t(min.Y() * yZoom, max.Y() * yZoom),
                                        Range_t(min.Z() * zZoom, max.Z() * zZoom));
      if (fCamera) fCamera->SetViewVolume(fBackBox.Get3DBox());
   }
   return kTRUE;
}
void TGLParametricPlot::StartPan(Int_t px, Int_t py)
{
   
   fMousePosition.fX = px;
   fMousePosition.fY = fCamera->GetHeight() - py;
   fCamera->StartPan(px, py);
   fBoxCut.StartMovement(px, fCamera->GetHeight() - py);
}
void TGLParametricPlot::Pan(Int_t px, Int_t py)
{
   
   
   if (!MakeGLContextCurrent())
      return;
   if (fSelectedPart) {
      if (fBoxCut.IsActive() && (fSelectedPart >= kXAxis && fSelectedPart <= kZAxis))
         fBoxCut.MoveBox(px, fCamera->GetHeight() - py, fSelectedPart);
      else
         fCamera->Pan(px, py);
   }
   fUpdateSelection = kTRUE;
}
char *TGLParametricPlot::GetPlotInfo(Int_t , Int_t )
{
   
   return "parametric surface";
}
void TGLParametricPlot::AddOption(const TString &)
{
   
}
void TGLParametricPlot::ProcessEvent(Int_t event, Int_t , Int_t py)
{
   
   
   if (event == kButton1Double && fBoxCut.IsActive()) {
      fBoxCut.TurnOnOff();
      if (!gVirtualX->IsCmdThread())
         gROOT->ProcessLineFast(Form("((TGLPlotPainter *)0x%x)->Paint()", this));
      else
         Paint();
   } else if (event == kKeyPress) {
      if (py == kKey_c || py == kKey_C) {
         if (fHighColor)
            Info("ProcessEvent", "Switch to true color to use box cut");
         else {
            fBoxCut.TurnOnOff();
            fUpdateSelection = kTRUE;
         }
      } else if (py == kKey_s || py == kKey_S) {
         fColorScheme == 20 ? fColorScheme = -1 : ++fColorScheme;
         InitColors();
      } else if (py == kKey_w || py == kKey_W) {
         fShowMesh = !fShowMesh;
      } else if (py == kKey_l || py == kKey_L) {
         fMeshSize == kHigh ? fMeshSize = kLow : fMeshSize += 15;
         InitGeometry();
         InitColors();
      }
   }
}
void TGLParametricPlot::InitGL()const
{
   
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glDisable(GL_CULL_FACE);
   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
}
void TGLParametricPlot::DrawPlot()const
{
   
   if (!fSelectionPass) {
      SetSurfaceColor();
      if (fShowMesh) {
         glEnable(GL_POLYGON_OFFSET_FILL);
         glPolygonOffset(1.f, 1.f);
      }
   } else {
      Rgl::ObjectIDToColor(fSelectionBase, fHighColor);
   }
   glBegin(GL_TRIANGLES);
   for (Int_t i = 0; i < fMeshSize - 1; ++i) {
      for (Int_t j = 0; j < fMeshSize - 1; ++j) {
         if (fBoxCut.IsActive()) {
            using TMath::Min;
            using TMath::Max;
            const Double_t xMin = Min(Min(fMesh[i][j].fPos.X(), fMesh[i + 1][j].fPos.X()), Min(fMesh[i][j + 1].fPos.X(), fMesh[i + 1][j + 1].fPos.X()));
            const Double_t xMax = Max(Max(fMesh[i][j].fPos.X(), fMesh[i + 1][j].fPos.X()), Max(fMesh[i][j + 1].fPos.X(), fMesh[i + 1][j + 1].fPos.X()));
            const Double_t yMin = Min(Min(fMesh[i][j].fPos.Y(), fMesh[i + 1][j].fPos.Y()), Min(fMesh[i][j + 1].fPos.Y(), fMesh[i + 1][j + 1].fPos.Y()));
            const Double_t yMax = Max(Max(fMesh[i][j].fPos.Y(), fMesh[i + 1][j].fPos.Y()), Max(fMesh[i][j + 1].fPos.Y(), fMesh[i + 1][j + 1].fPos.Y()));
            const Double_t zMin = Min(Min(fMesh[i][j].fPos.Z(), fMesh[i + 1][j].fPos.Z()), Min(fMesh[i][j + 1].fPos.Z(), fMesh[i + 1][j + 1].fPos.Z()));
            const Double_t zMax = Max(Max(fMesh[i][j].fPos.Z(), fMesh[i + 1][j].fPos.Z()), Max(fMesh[i][j + 1].fPos.Z(), fMesh[i + 1][j + 1].fPos.Z()));
            if (fBoxCut.IsInCut(xMin, xMax, yMin, yMax, zMin, zMax))
               continue;
         }
         glNormal3dv(fMesh[i + 1][j + 1].fNormal.CArr());
         if(fColorScheme != -1)
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fMesh[i + 1][j + 1].fRGBA);
         glVertex3dv(fMesh[i + 1][j + 1].fPos.CArr());
         glNormal3dv(fMesh[i][j + 1].fNormal.CArr());
         if(fColorScheme != -1)
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fMesh[i][j + 1].fRGBA);
         glVertex3dv(fMesh[i][j + 1].fPos.CArr());
         glNormal3dv(fMesh[i][j].fNormal.CArr());
         if(fColorScheme != -1)
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fMesh[i][j].fRGBA);
         glVertex3dv(fMesh[i][j].fPos.CArr());
         glNormal3dv(fMesh[i + 1][j].fNormal.CArr());
         if(fColorScheme != -1)
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fMesh[i + 1][j].fRGBA);
         glVertex3dv(fMesh[i + 1][j].fPos.CArr());
         glNormal3dv(fMesh[i + 1][j + 1].fNormal.CArr());
         if(fColorScheme != -1)
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fMesh[i + 1][j + 1].fRGBA);
         glVertex3dv(fMesh[i + 1][j + 1].fPos.CArr());
         glNormal3dv(fMesh[i][j].fNormal.CArr());
         if(fColorScheme != -1)
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fMesh[i][j].fRGBA);
         glVertex3dv(fMesh[i][j].fPos.CArr());
      }
   }
   glEnd();
   if (!fSelectionPass && fShowMesh) {
      glDisable(GL_POLYGON_OFFSET_FILL);
      const TGLDisableGuard lightGuard(GL_LIGHTING);
      const TGLEnableGuard blendGuard(GL_BLEND);
      const TGLEnableGuard smoothGuard(GL_LINE_SMOOTH);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glColor4d(0., 0., 0., 0.5);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      for (Int_t i = 0; i < fMeshSize - 1; ++i) {
         for (Int_t j = 0; j < fMeshSize - 1; ++j) {
            if (fBoxCut.IsActive()) {
               using TMath::Min;
               using TMath::Max;
               const Double_t xMin = Min(Min(fMesh[i][j].fPos.X(), fMesh[i + 1][j].fPos.X()), Min(fMesh[i][j + 1].fPos.X(), fMesh[i + 1][j + 1].fPos.X()));
               const Double_t xMax = Max(Max(fMesh[i][j].fPos.X(), fMesh[i + 1][j].fPos.X()), Max(fMesh[i][j + 1].fPos.X(), fMesh[i + 1][j + 1].fPos.X()));
               const Double_t yMin = Min(Min(fMesh[i][j].fPos.Y(), fMesh[i + 1][j].fPos.Y()), Min(fMesh[i][j + 1].fPos.Y(), fMesh[i + 1][j + 1].fPos.Y()));
               const Double_t yMax = Max(Max(fMesh[i][j].fPos.Y(), fMesh[i + 1][j].fPos.Y()), Max(fMesh[i][j + 1].fPos.Y(), fMesh[i + 1][j + 1].fPos.Y()));
               const Double_t zMin = Min(Min(fMesh[i][j].fPos.Z(), fMesh[i + 1][j].fPos.Z()), Min(fMesh[i][j + 1].fPos.Z(), fMesh[i + 1][j + 1].fPos.Z()));
               const Double_t zMax = Max(Max(fMesh[i][j].fPos.Z(), fMesh[i + 1][j].fPos.Z()), Max(fMesh[i][j + 1].fPos.Z(), fMesh[i + 1][j + 1].fPos.Z()));
               if (fBoxCut.IsInCut(xMin, xMax, yMin, yMax, zMin, zMax))
                  continue;
            }
            glBegin(GL_POLYGON);
            glVertex3dv(fMesh[i][j].fPos.CArr());
            glVertex3dv(fMesh[i][j + 1].fPos.CArr());
            glVertex3dv(fMesh[i + 1][j + 1].fPos.CArr());
            glVertex3dv(fMesh[i + 1][j].fPos.CArr());
            glEnd();
         }
      }
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   }
   if (fBoxCut.IsActive())
      fBoxCut.DrawBox(fSelectionPass, fSelectedPart);
}
void TGLParametricPlot::InitColors()
{
   
   
   
   if (fColorScheme == -1)
      return;
   const Rgl::Range_t uRange(fEquation->GetURange());
   const Float_t dU = Float_t((uRange.second - uRange.first) / (fMeshSize - 1));
   Float_t u = Float_t(uRange.first);
   for (Int_t i = 0; i < fMeshSize; ++i) {
      for (Int_t j = 0; j < fMeshSize; ++j)
         Rgl::GetColor(u, uRange.first, uRange.second, fColorScheme, fMesh[i][j].fRGBA);
      u += dU;
   }
}
void TGLParametricPlot::DrawSectionXOZ()const
{
   
}
void TGLParametricPlot::DrawSectionYOZ()const
{
   
}
void TGLParametricPlot::DrawSectionXOY()const
{
   
}
void TGLParametricPlot::SetSurfaceColor()const
{
   
   const Float_t specular[] = {1.f, 1.f, 1.f, 1.f};
   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.f);
   if (fColorScheme == -1) {
      const Float_t outerDiff[] = {0.5f, 0.42f, 0.f, 1.f};
      glMaterialfv(GL_FRONT, GL_DIFFUSE, outerDiff);
      const Float_t innerDiff[] = {0.5f, 0.2f,  0.f, 1.f};
      glMaterialfv(GL_BACK,  GL_DIFFUSE, innerDiff);
   }
}
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.