#include <stdexcept>
#include <cassert>
#include <limits>
#include <memory>
#include <vector>
#include "TAttMarker.h"
#include "TObjArray.h"
#include "TVirtualX.h"
#include "TError.h"
#include "TImage.h"
#include "TROOT.h"
#include "TPad.h"
#include "TColorGradient.h"
#include "TGLPadPainter.h"
#include "TGLIncludes.h"
#include "TGLUtil.h"
#include "TError.h"
#include "TMath.h"
namespace {
bool IsGradientFill(Color_t fillColorIndex)
{
return dynamic_cast<TColorGradient *>(gROOT->GetColor(fillColorIndex));
}
}
ClassImp(TGLPadPainter)
TGLPadPainter::TGLPadPainter()
: fIsHollowArea(kFALSE),
fLocked(kTRUE)
{
fVp[0] = fVp[1] = fVp[2] = fVp[3] = 0;
}
Color_t TGLPadPainter::GetLineColor() const
{
return gVirtualX->GetLineColor();
}
Style_t TGLPadPainter::GetLineStyle() const
{
return gVirtualX->GetLineStyle();
}
Width_t TGLPadPainter::GetLineWidth() const
{
return gVirtualX->GetLineWidth();
}
void TGLPadPainter::SetLineColor(Color_t lcolor)
{
gVirtualX->SetLineColor(lcolor);
}
void TGLPadPainter::SetLineStyle(Style_t lstyle)
{
gVirtualX->SetLineStyle(lstyle);
}
void TGLPadPainter::SetLineWidth(Width_t lwidth)
{
gVirtualX->SetLineWidth(lwidth);
}
Color_t TGLPadPainter::GetFillColor() const
{
return gVirtualX->GetFillColor();
}
Style_t TGLPadPainter::GetFillStyle() const
{
return gVirtualX->GetFillStyle();
}
Bool_t TGLPadPainter::IsTransparent() const
{
return gVirtualX->IsTransparent();
}
void TGLPadPainter::SetFillColor(Color_t fcolor)
{
gVirtualX->SetFillColor(fcolor);
}
void TGLPadPainter::SetFillStyle(Style_t fstyle)
{
gVirtualX->SetFillStyle(fstyle);
}
void TGLPadPainter::SetOpacity(Int_t percent)
{
gVirtualX->SetOpacity(percent);
}
Short_t TGLPadPainter::GetTextAlign() const
{
return gVirtualX->GetTextAlign();
}
Float_t TGLPadPainter::GetTextAngle() const
{
return gVirtualX->GetTextAngle();
}
Color_t TGLPadPainter::GetTextColor() const
{
return gVirtualX->GetTextColor();
}
Font_t TGLPadPainter::GetTextFont() const
{
return gVirtualX->GetTextFont();
}
Float_t TGLPadPainter::GetTextSize() const
{
return gVirtualX->GetTextSize();
}
Float_t TGLPadPainter::GetTextMagnitude() const
{
return gVirtualX->GetTextMagnitude();
}
void TGLPadPainter::SetTextAlign(Short_t align)
{
gVirtualX->SetTextAlign(align);
}
void TGLPadPainter::SetTextAngle(Float_t tangle)
{
gVirtualX->SetTextAngle(tangle);
}
void TGLPadPainter::SetTextColor(Color_t tcolor)
{
gVirtualX->SetTextColor(tcolor);
}
void TGLPadPainter::SetTextFont(Font_t tfont)
{
gVirtualX->SetTextFont(tfont);
}
void TGLPadPainter::SetTextSize(Float_t tsize)
{
gVirtualX->SetTextSize(tsize);
}
void TGLPadPainter::SetTextSizePixels(Int_t npixels)
{
gVirtualX->SetTextSizePixels(npixels);
}
Int_t TGLPadPainter::CreateDrawable(UInt_t, UInt_t)
{
return 0;
}
void TGLPadPainter::ClearDrawable()
{
}
void TGLPadPainter::CopyDrawable(Int_t , Int_t , Int_t )
{
}
void TGLPadPainter::DestroyDrawable()
{
}
void TGLPadPainter::SelectDrawable(Int_t )
{
if (fLocked)
return;
if (TPad *pad = dynamic_cast<TPad *>(gPad)) {
Int_t px = 0, py = 0;
pad->XYtoAbsPixel(pad->GetX1(), pad->GetY1(), px, py);
py = gPad->GetWh() - py;
TGLUtil::InitializeIfNeeded();
const Float_t scale = TGLUtil::GetScreenScalingFactor();
glViewport(GLint(px * scale), GLint(py * scale),
GLsizei(gPad->GetWw() * pad->GetAbsWNDC() * scale),
GLsizei(gPad->GetWh() * pad->GetAbsHNDC() * scale));
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(pad->GetX1(), pad->GetX2(), pad->GetY1(), pad->GetY2(), -10., 10.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(0., 0., -1.);
} else {
::Error("TGLPadPainter::SelectDrawable",
"function was called not from TPad or TCanvas code\n");
throw std::runtime_error("");
}
}
void TGLPadPainter::InitPainter()
{
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
glViewport(0, 0, GLsizei(gPad->GetWw()), GLsizei(gPad->GetWh()));
glDepthMask(GL_TRUE);
glClearColor(1.,1.,1.,1.);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDepthMask(GL_FALSE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(gPad->GetX1(), gPad->GetX2(), gPad->GetY1(), gPad->GetY2(), -10., 10.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(0., 0., -1.);
fLocked = kFALSE;
}
void TGLPadPainter::InvalidateCS()
{
if (fLocked) return;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(gPad->GetX1(), gPad->GetX2(), gPad->GetY1(), gPad->GetY2(), -10., 10.);
glMatrixMode(GL_MODELVIEW);
}
void TGLPadPainter::LockPainter()
{
if (fLocked) return;
glFinish();
fLocked = kTRUE;
}
const Double_t lineWidthTS = 3.;
void TGLPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2)
{
if (fLocked) {
if (gVirtualX->GetDrawMode() == TVirtualX::kInvert) {
gVirtualX->DrawLine(gPad->XtoAbsPixel(x1), gPad->YtoAbsPixel(y1),
gPad->XtoAbsPixel(x2), gPad->YtoAbsPixel(y2));
}
return;
}
const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, gVirtualX->GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE);
glBegin(GL_LINES);
glVertex2d(x1, y1);
glVertex2d(x2, y2);
glEnd();
if (gVirtualX->GetLineWidth() > lineWidthTS) {
Double_t pointSize = gVirtualX->GetLineWidth();
if (pointSize > fLimits.GetMaxPointSize())
pointSize = fLimits.GetMaxPointSize();
glPointSize((GLfloat)pointSize);
const TGLEnableGuard pointSmooth(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glBegin(GL_POINTS);
glVertex2d(x1, y1);
glVertex2d(x2, y2);
glEnd();
glPointSize(1.f);
}
}
void TGLPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2)
{
if (fLocked) return;
const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, gVirtualX->GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE);
const Double_t xRange = gPad->GetX2() - gPad->GetX1();
const Double_t yRange = gPad->GetY2() - gPad->GetY1();
glBegin(GL_LINES);
glVertex2d(gPad->GetX1() + u1 * xRange, gPad->GetY1() + v1 * yRange);
glVertex2d(gPad->GetX1() + u2 * xRange, gPad->GetY1() + v2 * yRange);
glEnd();
}
void TGLPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EBoxMode mode)
{
if (fLocked) return;
if (IsGradientFill(gVirtualX->GetFillColor())) {
Double_t xs[] = {x1, x2, x2, x1};
Double_t ys[] = {y1, y1, y2, y2};
DrawPolygonWithGradient(4, xs, ys);
return;
}
if (mode == kHollow) {
const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, 0, fLimits.GetMaxLineWidth(), kTRUE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glRectd(x1, y1, x2, y2);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glLineWidth(1.f);
} else {
const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE);
glRectd(x1, y1, x2, y2);
}
}
void TGLPadPainter::DrawFillArea(Int_t n, const Double_t *x, const Double_t *y)
{
assert(x != 0 && "DrawFillArea, parameter 'x' is null");
assert(y != 0 && "DrawFillArea, parameter 'y' is null");
if (fLocked)
return;
if (n < 3) {
::Error("TGLPadPainter::DrawFillArea",
"invalid number of points in a polygon");
return;
}
if (IsGradientFill(gVirtualX->GetFillColor()))
return DrawPolygonWithGradient(n, x, y);
if (!gVirtualX->GetFillStyle()) {
fIsHollowArea = kTRUE;
return DrawPolyLine(n, x, y);
}
const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE);
DrawTesselation(n, x, y);
}
void TGLPadPainter::DrawFillArea(Int_t n, const Float_t *x, const Float_t *y)
{
if (fLocked) return;
if (!gVirtualX->GetFillStyle()) {
fIsHollowArea = kTRUE;
return DrawPolyLine(n, x, y);
}
fVs.resize(n * 3);
for (Int_t i = 0; i < n; ++i) {
fVs[i * 3] = x[i];
fVs[i * 3 + 1] = y[i];
}
const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE);
GLUtesselator *t = (GLUtesselator *)fTess.GetTess();
gluBeginPolygon(t);
gluNextContour(t, (GLenum)GLU_UNKNOWN);
for (Int_t i = 0; i < n; ++i)
gluTessVertex(t, &fVs[i * 3], &fVs[i * 3]);
gluEndPolygon(t);
}
void TGLPadPainter::DrawPolyLine(Int_t n, const Double_t *x, const Double_t *y)
{
if (fLocked) return;
const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, gVirtualX->GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE);
glBegin(GL_LINE_STRIP);
for (Int_t i = 0; i < n; ++i)
glVertex2d(x[i], y[i]);
if (fIsHollowArea) {
glVertex2d(x[0], y[0]);
fIsHollowArea = kFALSE;
}
glEnd();
if (gVirtualX->GetLineWidth() > lineWidthTS) {
Double_t pointSize = gVirtualX->GetLineWidth();
if (pointSize > fLimits.GetMaxPointSize())
pointSize = fLimits.GetMaxPointSize();
glPointSize((GLfloat)pointSize);
const TGLEnableGuard pointSmooth(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glBegin(GL_POINTS);
for (Int_t i = 0; i < n; ++i)
glVertex2d(x[i], y[i]);
glEnd();
glPointSize(1.f);
}
}
void TGLPadPainter::DrawPolyLine(Int_t n, const Float_t *x, const Float_t *y)
{
if (fLocked) return;
const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, gVirtualX->GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE);
glBegin(GL_LINE_STRIP);
for (Int_t i = 0; i < n; ++i)
glVertex2f(x[i], y[i]);
if (fIsHollowArea) {
glVertex2f(x[0], y[0]);
fIsHollowArea = kFALSE;
}
glEnd();
}
void TGLPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t *v)
{
if (fLocked) return;
const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, gVirtualX->GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE);
const Double_t xRange = gPad->GetX2() - gPad->GetX1();
const Double_t yRange = gPad->GetY2() - gPad->GetY1();
const Double_t x1 = gPad->GetX1(), y1 = gPad->GetY1();
glBegin(GL_LINE_STRIP);
for (Int_t i = 0; i < n; ++i)
glVertex2d(x1 + u[i] * xRange, y1 + v[i] * yRange);
glEnd();
}
namespace {
template<class ValueType>
void ConvertMarkerPoints(Int_t n, const ValueType *x, const ValueType *y, std::vector<TPoint> & dst);
}
void TGLPadPainter::DrawPolyMarker(Int_t n, const Double_t *x, const Double_t *y)
{
if (fLocked) return;
ConvertMarkerPoints(n, x, y, fPoly);
DrawPolyMarker();
}
void TGLPadPainter::DrawPolyMarker(Int_t n, const Float_t *x, const Float_t *y)
{
if (fLocked) return;
ConvertMarkerPoints(n, x, y, fPoly);
DrawPolyMarker();
}
void TGLPadPainter::DrawPolyMarker()
{
if (fLocked) return;
SaveProjectionMatrix();
glLoadIdentity();
glOrtho(0, gPad->GetAbsWNDC() * gPad->GetWw(), 0, gPad->GetAbsHNDC() * gPad->GetWh(), -10., 10.);
glMatrixMode(GL_MODELVIEW);
const TGLEnableGuard blendGuard(GL_BLEND);
Float_t rgba[4] = {};
Rgl::Pad::ExtractRGBA(gVirtualX->GetMarkerColor(), rgba);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4fv(rgba);
const TPoint *xy = &fPoly[0];
const Style_t markerStyle = gVirtualX->GetMarkerStyle();
const UInt_t n = UInt_t(fPoly.size());
switch (markerStyle) {
case kDot:
fMarker.DrawDot(n, xy);
break;
case kPlus:
fMarker.DrawPlus(n, xy);
break;
case kStar:
fMarker.DrawStar(n, xy);
break;
case kCircle:
case kOpenCircle:
fMarker.DrawCircle(n, xy);
break;
case kMultiply:
fMarker.DrawX(n, xy);
break;
case kFullDotSmall:
fMarker.DrawFullDotSmall(n, xy);
break;
case kFullDotMedium:
fMarker.DrawFullDotMedium(n, xy);
break;
case kFullDotLarge:
case kFullCircle:
fMarker.DrawFullDotLarge(n, xy);
break;
case kFullSquare:
fMarker.DrawFullSquare(n, xy);
break;
case kFullTriangleUp:
fMarker.DrawFullTrianlgeUp(n, xy);
break;
case kFullTriangleDown:
fMarker.DrawFullTrianlgeDown(n, xy);
break;
case kOpenSquare:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
fMarker.DrawFullSquare(n, xy);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case kOpenTriangleUp:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
fMarker.DrawFullTrianlgeUp(n, xy);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case kOpenDiamond:
fMarker.DrawDiamond(n, xy);
break;
case kOpenCross:
fMarker.DrawCross(n, xy);
break;
case kFullStar:
fMarker.DrawFullStar(n, xy);
break;
case kOpenStar:
fMarker.DrawOpenStar(n, xy);
}
RestoreProjectionMatrix();
glMatrixMode(GL_MODELVIEW);
}
template<class Char>
void TGLPadPainter::DrawTextHelper(Double_t x, Double_t y, const Char *text, ETextMode )
{
SaveProjectionMatrix();
glLoadIdentity();
glOrtho(0, gPad->GetAbsWNDC() * gPad->GetWw(), 0, gPad->GetAbsHNDC() * gPad->GetWh(), -10., 10.);
glMatrixMode(GL_MODELVIEW);
Float_t rgba[4] = {};
Rgl::Pad::ExtractRGBA(gVirtualX->GetTextColor(), rgba);
glColor4fv(rgba);
const Int_t shift = TGLFontManager::GetExtendedFontStartIndex();
Int_t fontIndex = TMath::Max(Short_t(10), gVirtualX->GetTextFont());
if (fontIndex / 10 + shift > TGLFontManager::GetFontFileArray()->GetEntries())
fontIndex = 20 + shift * 10;
else
fontIndex += shift * 10;
fFM.RegisterFont(TMath::Max(Int_t(gVirtualX->GetTextSize()) - 1, 10),
TGLFontManager::GetFontNameFromId(fontIndex),
TGLFont::kTexture, fF);
fF.PreRender();
const UInt_t padH = UInt_t(gPad->GetAbsHNDC() * gPad->GetWh());
fF.Render(text, gPad->XtoPixel(x), padH - gPad->YtoPixel(y), GetTextAngle(), GetTextMagnitude());
fF.PostRender();
RestoreProjectionMatrix();
glMatrixMode(GL_MODELVIEW);
}
void TGLPadPainter::DrawText(Double_t x, Double_t y, const char *text, ETextMode mode)
{
if (fLocked) return;
if (!gVirtualX->GetTextSize())
return;
DrawTextHelper(x, y, text, mode);
}
void TGLPadPainter::DrawText(Double_t x, Double_t y, const wchar_t *text, ETextMode mode)
{
if (fLocked) return;
if (!gVirtualX->GetTextSize())
return;
DrawTextHelper(x, y, text, mode);
}
void TGLPadPainter::DrawTextNDC(Double_t u, Double_t v, const char *text, ETextMode mode)
{
if (fLocked) return;
const Double_t xRange = gPad->GetX2() - gPad->GetX1();
const Double_t yRange = gPad->GetY2() - gPad->GetY1();
DrawText(gPad->GetX1() + u * xRange, gPad->GetY1() + v * yRange, text, mode);
}
void TGLPadPainter::DrawTextNDC(Double_t u, Double_t v, const wchar_t *text, ETextMode mode)
{
if (fLocked) return;
const Double_t xRange = gPad->GetX2() - gPad->GetX1();
const Double_t yRange = gPad->GetY2() - gPad->GetY1();
DrawText(gPad->GetX1() + u * xRange, gPad->GetY1() + v * yRange, text, mode);
}
void TGLPadPainter::SaveProjectionMatrix()const
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
}
void TGLPadPainter::RestoreProjectionMatrix()const
{
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
void TGLPadPainter::SaveModelviewMatrix()const
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
}
void TGLPadPainter::RestoreModelviewMatrix()const
{
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void TGLPadPainter::SaveViewport()
{
glGetIntegerv(GL_VIEWPORT, fVp);
}
void TGLPadPainter::RestoreViewport()
{
glViewport(fVp[0], fVp[1], fVp[2], fVp[3]);
}
void TGLPadPainter::SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const
{
TVirtualPad *canvas = (TVirtualPad *)pad->GetCanvas();
if (!canvas)
return;
gROOT->ProcessLine(Form("((TCanvas *)0x%lx)->Flush();", (ULong_t)canvas));
std::vector<unsigned> buff(canvas->GetWw() * canvas->GetWh());
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glReadPixels(0, 0, canvas->GetWw(), canvas->GetWh(), GL_RGBA, GL_UNSIGNED_BYTE, (char *)&buff[0]);
std::auto_ptr<TImage> image(TImage::Create());
if (!image.get()) {
::Error("TGLPadPainter::SaveImage", "TImage creation failed");
return;
}
image->DrawRectangle(0, 0, canvas->GetWw(), canvas->GetWh());
UInt_t *argb = image->GetArgbArray();
if (!argb) {
::Error("TGLPadPainter::SaveImage", "null argb array in TImage object");
return;
}
const Int_t nLines = canvas->GetWh();
const Int_t nPixels = canvas->GetWw();
for (Int_t i = 0; i < nLines; ++i) {
Int_t base = (nLines - 1 - i) * nPixels;
for (Int_t j = 0; j < nPixels; ++j, ++base) {
const UInt_t pix = buff[base];
const UInt_t bgra = ((pix & 0xff) << 16) | (pix & 0xff00) |
((pix & 0xff0000) >> 16) | (pix & 0xff000000);
argb[i * nPixels + j] = bgra;
}
}
image->WriteImage(fileName, (TImage::EImageFileTypes)type);
}
void TGLPadPainter::DrawPixels(const unsigned char *pixelData, UInt_t width, UInt_t height,
Int_t dstX, Int_t dstY, Bool_t enableBlending)
{
if (fLocked)
return;
if (!pixelData) {
::Error("TGLPadPainter::DrawPixels", "pixel data is null");
return;
}
if (std::numeric_limits<UInt_t>::digits >= 32) {
CLRBIT(width, 31);
CLRBIT(height, 31);
}
if (!width) {
::Error("TGLPadPainter::DrawPixels", "invalid width");
return;
}
if (!height) {
::Error("TGLPadPainter::DrawPixels", "invalid height");
return;
}
if (TPad *pad = dynamic_cast<TPad *>(gPad)) {
const Double_t rasterX = Double_t(dstX) / (pad->GetAbsWNDC() * pad->GetWw()) *
(pad->GetX2() - pad->GetX1()) + pad->GetX1();
const Double_t yRange = pad->GetY2() - pad->GetY1();
const Double_t rasterY = yRange - Double_t(dstY + height) / (pad->GetAbsHNDC() * pad->GetWh()) * yRange +
pad->GetY1();
GLdouble oldPos[4] = {};
glGetDoublev(GL_CURRENT_RASTER_POSITION, oldPos);
glRasterPos2d(rasterX, rasterY);
std::vector<unsigned char> upsideDownImage(4 * width * height);
const unsigned char *srcLine = pixelData + 4 * width * (height - 1);
unsigned char *dstLine = &upsideDownImage[0];
for (UInt_t i = 0; i < height; ++i, srcLine -= 4 * width, dstLine += 4 * width)
std::copy(srcLine, srcLine + 4 * width, dstLine);
if (enableBlending) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_BYTE, &upsideDownImage[0]);
if (enableBlending)
glDisable(GL_BLEND);
glRasterPos2d(oldPos[0], oldPos[1]);
} else
::Error("TGLPadPainter::DrawPixels", "no pad found to draw");
}
void TGLPadPainter::DrawPolygonWithGradient(Int_t n, const Double_t *x, const Double_t *y)
{
assert(n > 2 && "DrawPolygonWithGradient, invalid number of points");
assert(x != 0 && "DrawPolygonWithGradient, parameter 'x' is null");
assert(y != 0 && "DrawPolygonWithGradient, parameter 'y' is null");
assert(dynamic_cast<TColorGradient *>(gROOT->GetColor(gVirtualX->GetFillColor())) != 0 &&
"DrawPolygonWithGradient, the current fill color is not a gradient fill");
const TColorGradient * const grad =
dynamic_cast<TColorGradient *>(gROOT->GetColor(gVirtualX->GetFillColor()));
if (fLocked)
return;
const TGLEnableGuard stencilGuard(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
glStencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT);
DrawTesselation(n, x, y);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilMask(0x00);
glStencilFunc(GL_EQUAL, 0, 0xFF);
glStencilFunc(GL_EQUAL, 1, 0xFF);
const TRadialGradient * const rGrad = dynamic_cast<const TRadialGradient *>(grad);
if (rGrad)
DrawGradient(rGrad, n, x, y);
else {
const TLinearGradient * const lGrad = dynamic_cast<const TLinearGradient *>(grad);
assert(lGrad != 0 && "DrawPolygonWithGradient, unknown gradient type");
DrawGradient(lGrad, n, x, y);
}
}
void TGLPadPainter::DrawGradient(const TRadialGradient *grad, Int_t nPoints,
const Double_t *xs, const Double_t *ys)
{
assert(grad != 0 && "DrawGradient, parameter 'grad' is null");
assert(nPoints > 2 && "DrawGradient, invalid number of points");
assert(xs != 0 && "DrawGradient, parameter 'xs' is null");
assert(ys != 0 && "DrawGradient, parameter 'ys' is null");
if (grad->GetGradientType() != TRadialGradient::kSimple) {
::Warning("TGLPadPainter::DrawGradient",
"extended radial gradient is not supported");
return;
}
const auto &bbox = Rgl::Pad::FindBoundingRect(nPoints, xs, ys);
auto center = grad->GetCenter();
auto radius = grad->GetRadius();
if (grad->GetCoordinateMode() == TColorGradient::kObjectBoundingMode) {
radius *= TMath::Max(bbox.fWidth, bbox.fHeight);
center.fX = bbox.fWidth * center.fX + bbox.fXMin;
center.fY = bbox.fHeight * center.fY + bbox.fYMin;
} else {
const auto w = gPad->GetX2() - gPad->GetX1();
const auto h = gPad->GetY2() - gPad->GetY1();
radius *= TMath::Max(w, h);
center.fX *= w;
center.fY *= h;
}
const auto pixelW = gPad->GetAbsWNDC() * gPad->GetWw();
const auto pixelH = gPad->GetAbsHNDC() * gPad->GetWh();
SaveProjectionMatrix();
SaveModelviewMatrix();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0., pixelW, 0., pixelH, -10., 10.);
radius *= TMath::Max(pixelH, pixelW);
center.fX = gPad->XtoPixel(center.fX);
center.fY = pixelH - gPad->YtoPixel(center.fY);
Double_t maxR = 0.;
{
const Double_t xMin = gPad->XtoPixel(bbox.fXMin);
const Double_t xMax = gPad->XtoPixel(bbox.fXMax);
const Double_t yMin = pixelH - gPad->YtoPixel(bbox.fYMin);
const Double_t yMax = pixelH - gPad->YtoPixel(bbox.fYMax);
const Double_t maxDistX = TMath::Max(TMath::Abs(center.fX - xMin),
TMath::Abs(center.fX - xMax));
const Double_t maxDistY = TMath::Max(TMath::Abs(center.fY - yMin),
TMath::Abs(center.fY - yMax));
maxR = TMath::Sqrt(maxDistX * maxDistX + maxDistY * maxDistY);
}
const Bool_t solidFillAfter = maxR > radius;
const UInt_t nSlices = 500;
const auto nColors = grad->GetNumberOfSteps();
const auto nCircles = nColors + 1 + solidFillAfter;
const auto locations = grad->GetColorPositions();
std::vector<Double_t> circles(nSlices * nCircles * 2);
const Double_t angle = TMath::TwoPi() / nSlices;
for (UInt_t i = 0; i < nColors; ++i) {
const auto circle = &circles[i * nSlices * 2];
const auto r = radius * locations[i];
for (UInt_t j = 0, e = nSlices * 2 - 2; j < e; j += 2) {
circle[j] = center.fX + r * TMath::Cos(angle * j);
circle[j + 1] = center.fY + r * TMath::Sin(angle * j);
}
circle[(nSlices - 1) * 2] = circle[0];
circle[(nSlices - 1) * 2 + 1] = circle[1];
}
{
const auto circle = &circles[nColors * nSlices * 2];
for (UInt_t j = 0, e = nSlices * 2 - 2; j < e; j += 2) {
circle[j] = center.fX + radius * TMath::Cos(angle * j);
circle[j + 1] = center.fY + radius * TMath::Sin(angle * j);
}
circle[(nSlices - 1) * 2] = circle[0];
circle[(nSlices - 1) * 2 + 1] = circle[1];
}
if (solidFillAfter) {
const auto circle = &circles[(nCircles - 1) * nSlices * 2];
for (UInt_t j = 0, e = nSlices * 2 - 2; j < e; j += 2) {
circle[j] = center.fX + maxR * TMath::Cos(angle * j);
circle[j + 1] = center.fY + maxR * TMath::Sin(angle * j);
}
circle[(nSlices - 1) * 2] = circle[0];
circle[(nSlices - 1) * 2 + 1] = circle[1];
}
const auto rgba = grad->GetColors();
const TGLEnableGuard alphaGuard(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_TRIANGLE_FAN);
glColor4dv(rgba);
glVertex2d(center.fX, center.fY);
for (UInt_t i = 0, e = nSlices * 2; i < e; i += 2)
glVertex2dv(&circles[i]);
glEnd();
for (UInt_t i = 0; i < nColors - 1; ++i) {
const Double_t * const inner = &circles[i * nSlices * 2];
const auto innerRGBA = rgba + i * 4;
const auto outerRGBA = rgba + (i + 1) * 4;
const Double_t * const outer = &circles[(i + 1) * nSlices * 2];
Rgl::DrawQuadStripWithRadialGradientFill(nSlices, inner, innerRGBA, outer, outerRGBA);
}
{
glBegin(GL_QUAD_STRIP);
const Double_t * const inner = &circles[nSlices * (nColors - 1) * 2];
const auto solidRGBA = rgba + (nColors - 1) * 4;
const Double_t * const outer = &circles[nSlices * nColors * 2];
Rgl::DrawQuadStripWithRadialGradientFill(nSlices, inner, solidRGBA, outer, solidRGBA);
}
if (solidFillAfter) {
glBegin(GL_QUAD_STRIP);
const Double_t * const inner = &circles[nSlices * nColors * 2];
const auto solidRGBA = rgba + (nColors - 1) * 4;
const Double_t * const outer = &circles[nSlices * (nColors + 1) * 2];
Rgl::DrawQuadStripWithRadialGradientFill(nSlices, inner, solidRGBA, outer, solidRGBA);
}
RestoreProjectionMatrix();
RestoreModelviewMatrix();
}
void TGLPadPainter::DrawGradient(const TLinearGradient *grad, Int_t n,
const Double_t *x, const Double_t *y)
{
assert(grad != 0 && "DrawGradient, parameter 'grad' is null");
assert(n > 2 && "DrawGradient, invalid number of points");
assert(x != 0 && "DrawGradient, parameter 'x' is null");
assert(y != 0 && "DrawGradient, parameter 'y' is null");
const auto &bbox = Rgl::Pad::FindBoundingRect(n, x, y);
SaveProjectionMatrix();
SaveModelviewMatrix();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const Double_t pixelW = gPad->GetAbsWNDC() * gPad->GetWw();
const Double_t pixelH = gPad->GetAbsHNDC() * gPad->GetWh();
glOrtho(0., pixelW, 0., pixelH, -10., 10.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
TColorGradient::Point start = grad->GetStart();
TColorGradient::Point end = grad->GetEnd();
if (grad->GetCoordinateMode() == TColorGradient::kPadMode)
{
const Double_t w = gPad->GetX2() - gPad->GetX1();
const Double_t h = gPad->GetY2() - gPad->GetY1();
start.fX = start.fX * w;
start.fY = start.fY * h;
end.fX = end.fX * w;
end.fY = end.fY * h;
} else {
start.fX = start.fX * bbox.fWidth + bbox.fXMin;
start.fY = start.fY * bbox.fHeight + bbox.fYMin;
end.fX = end.fX * bbox.fWidth + bbox.fXMin;
end.fY = end.fY * bbox.fHeight + bbox.fYMin;
}
start.fX = gPad->XtoPixel(start.fX);
start.fY = pixelH - gPad->YtoPixel(start.fY);
end.fX = gPad->XtoPixel(end.fX);
end.fY = pixelH - gPad->YtoPixel(end.fY);
const Double_t xMin = gPad->XtoPixel(bbox.fXMin);
const Double_t xMax = gPad->XtoPixel(bbox.fXMax);
const Double_t yMin = pixelH - gPad->YtoPixel(bbox.fYMin);
const Double_t yMax = pixelH - gPad->YtoPixel(bbox.fYMax);
const Double_t maxDistX = TMath::Max(TMath::Abs(start.fX - xMin), TMath::Abs(start.fX - xMax));
const Double_t maxDistY = TMath::Max(TMath::Abs(start.fY - yMin), TMath::Abs(start.fY - yMax));
const Double_t startEndLength = TMath::Sqrt((end.fX - start.fX) * (end.fX - start.fX) +
(end.fY - start.fY) * (end.fY - start.fY));
const Double_t h = TMath::Max(TMath::Sqrt(maxDistX * maxDistX + maxDistY * maxDistY),
startEndLength);
const Double_t * const colorPositions = grad->GetColorPositions();
std::vector<Double_t> gradBoxes(grad->GetNumberOfSteps() + 2);
gradBoxes[0] = start.fY - h;
for (unsigned i = 1; i <= grad->GetNumberOfSteps(); ++i)
gradBoxes[i] = startEndLength * colorPositions[i - 1] + start.fY;
gradBoxes[grad->GetNumberOfSteps() + 1] = start.fY + h;
Double_t angle = TMath::ACos((startEndLength * (end.fY - start.fY)) /
(startEndLength * startEndLength)) * TMath::RadToDeg();
if (end.fX > start.fX)
angle *= -1;
glTranslated(start.fX, start.fY, 0.);
glRotated(angle, 0., 0., 1.);
glTranslated(-start.fX, -start.fY, 0.);
const Double_t * const rgba = grad->GetColors();
const unsigned nEdges = gradBoxes.size();
const unsigned nColors = grad->GetNumberOfSteps();
const Double_t xLeft = start.fX - h, xRight = start.fX + h;
const TGLEnableGuard alphaGuard(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Rgl::DrawBoxWithGradientFill(gradBoxes[0], gradBoxes[1], xLeft, xRight, rgba, rgba);
Rgl::DrawBoxWithGradientFill(gradBoxes[nEdges - 2], gradBoxes[nEdges - 1], xLeft, xRight,
rgba + (nColors - 1) * 4, rgba + (nColors - 1) * 4);
for (unsigned i = 1; i < nEdges - 2; ++i)
Rgl::DrawBoxWithGradientFill(gradBoxes[i], gradBoxes[i + 1], xLeft,
xRight, rgba + (i - 1) * 4, rgba + i * 4);
RestoreProjectionMatrix();
RestoreModelviewMatrix();
}
void TGLPadPainter::DrawTesselation(Int_t n, const Double_t *x, const Double_t *y)
{
assert(n > 2 && "DrawTesselation, invalid number of points");
assert(x != 0 && "DrawTesselation, parameter 'x' is null");
assert(y != 0 && "DrawTesselation, parameter 'y' is null");
fVs.resize(n * 3);
for (Int_t i = 0; i < n; ++i) {
fVs[i * 3] = x[i];
fVs[i * 3 + 1] = y[i];
fVs[i * 3 + 2] = 0.;
}
GLUtesselator *t = (GLUtesselator *)fTess.GetTess();
gluBeginPolygon(t);
gluNextContour(t, (GLenum)GLU_UNKNOWN);
for (Int_t i = 0; i < n; ++i)
gluTessVertex(t, &fVs[i * 3], &fVs[i * 3]);
gluEndPolygon(t);
}
namespace {
template<class ValueType>
void ConvertMarkerPoints(Int_t n, const ValueType *x, const ValueType *y, std::vector<TPoint> & dst)
{
const UInt_t padH = UInt_t(gPad->GetAbsHNDC() * gPad->GetWh());
dst.resize(n);
for (Int_t i = 0; i < n; ++i) {
dst[i].fX = gPad->XtoPixel(x[i]);
dst[i].fY = padH - gPad->YtoPixel(y[i]);
}
}
}