#include <algorithm>
#include <cassert>
#include <limits>
#include <memory>
#include <vector>
#include "TPadPainter.h"
#include "TVirtualX.h"
#include "TCanvas.h"
#include "TPoint.h"
#include "TError.h"
#include "TImage.h"
#include "TROOT.h"
#include "TMath.h"
#include "TPad.h"
namespace {
typedef std::vector<TPoint>::size_type size_type;
template<typename T>
void ConvertPoints(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys,
std::vector<TPoint> &dst);
inline
void MergePointsX(std::vector<TPoint> &points, unsigned nMerged, SCoord_t yMin,
SCoord_t yMax, SCoord_t yLast);
inline
size_type MergePointsInplaceY(std::vector<TPoint> &dst, size_type nMerged, SCoord_t xMin,
SCoord_t xMax, SCoord_t xLast, size_type first);
template<typename T>
void ConvertPointsAndMergePassX(TVirtualPad *pad, unsigned nPoints, const T *x, const T *y,
std::vector<TPoint> &dst);
void ConvertPointsAndMergeInplacePassY(std::vector<TPoint> &dst);
template<class T>
void DrawFillAreaAux(TVirtualPad *pad, Int_t nPoints, const T *xs, const T *ys);
template<typename T>
void DrawPolyLineAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys);
template<class T>
void DrawPolyMarkerAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys);
}
ClassImp(TPadPainter)
TPadPainter::TPadPainter()
{
}
Color_t TPadPainter::GetLineColor() const
{
return gVirtualX->GetLineColor();
}
Style_t TPadPainter::GetLineStyle() const
{
return gVirtualX->GetLineStyle();
}
Width_t TPadPainter::GetLineWidth() const
{
return gVirtualX->GetLineWidth();
}
void TPadPainter::SetLineColor(Color_t lcolor)
{
gVirtualX->SetLineColor(lcolor);
}
void TPadPainter::SetLineStyle(Style_t lstyle)
{
gVirtualX->SetLineStyle(lstyle);
}
void TPadPainter::SetLineWidth(Width_t lwidth)
{
gVirtualX->SetLineWidth(lwidth);
}
Color_t TPadPainter::GetFillColor() const
{
return gVirtualX->GetFillColor();
}
Style_t TPadPainter::GetFillStyle() const
{
return gVirtualX->GetFillStyle();
}
Bool_t TPadPainter::IsTransparent() const
{
return gVirtualX->IsTransparent();
}
void TPadPainter::SetFillColor(Color_t fcolor)
{
gVirtualX->SetFillColor(fcolor);
}
void TPadPainter::SetFillStyle(Style_t fstyle)
{
gVirtualX->SetFillStyle(fstyle);
}
void TPadPainter::SetOpacity(Int_t percent)
{
gVirtualX->SetOpacity(percent);
}
Short_t TPadPainter::GetTextAlign() const
{
return gVirtualX->GetTextAlign();
}
Float_t TPadPainter::GetTextAngle() const
{
return gVirtualX->GetTextAngle();
}
Color_t TPadPainter::GetTextColor() const
{
return gVirtualX->GetTextColor();
}
Font_t TPadPainter::GetTextFont() const
{
return gVirtualX->GetTextFont();
}
Float_t TPadPainter::GetTextSize() const
{
return gVirtualX->GetTextSize();
}
Float_t TPadPainter::GetTextMagnitude() const
{
return gVirtualX->GetTextMagnitude();
}
void TPadPainter::SetTextAlign(Short_t align)
{
gVirtualX->SetTextAlign(align);
}
void TPadPainter::SetTextAngle(Float_t tangle)
{
gVirtualX->SetTextAngle(tangle);
}
void TPadPainter::SetTextColor(Color_t tcolor)
{
gVirtualX->SetTextColor(tcolor);
}
void TPadPainter::SetTextFont(Font_t tfont)
{
gVirtualX->SetTextFont(tfont);
}
void TPadPainter::SetTextSize(Float_t tsize)
{
gVirtualX->SetTextSize(tsize);
}
void TPadPainter::SetTextSizePixels(Int_t npixels)
{
gVirtualX->SetTextSizePixels(npixels);
}
Int_t TPadPainter::CreateDrawable(UInt_t w, UInt_t h)
{
return gVirtualX->OpenPixmap(Int_t(w), Int_t(h));
}
void TPadPainter::ClearDrawable()
{
gVirtualX->ClearWindow();
}
void TPadPainter::CopyDrawable(Int_t id, Int_t px, Int_t py)
{
gVirtualX->CopyPixmap(id, px, py);
}
void TPadPainter::DestroyDrawable()
{
gVirtualX->ClosePixmap();
}
void TPadPainter::SelectDrawable(Int_t device)
{
gVirtualX->SelectWindow(device);
}
void TPadPainter::DrawPixels(const unsigned char * , UInt_t , UInt_t ,
Int_t , Int_t , Bool_t )
{
}
void TPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2)
{
const Int_t px1 = gPad->XtoPixel(x1);
const Int_t px2 = gPad->XtoPixel(x2);
const Int_t py1 = gPad->YtoPixel(y1);
const Int_t py2 = gPad->YtoPixel(y2);
gVirtualX->DrawLine(px1, py1, px2, py2);
}
void TPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2)
{
const Int_t px1 = gPad->UtoPixel(u1);
const Int_t py1 = gPad->VtoPixel(v1);
const Int_t px2 = gPad->UtoPixel(u2);
const Int_t py2 = gPad->VtoPixel(v2);
gVirtualX->DrawLine(px1, py1, px2, py2);
}
void TPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EBoxMode mode)
{
Int_t px1 = gPad->XtoPixel(x1);
Int_t px2 = gPad->XtoPixel(x2);
Int_t py1 = gPad->YtoPixel(y1);
Int_t py2 = gPad->YtoPixel(y2);
if (TMath::Abs(px2 - px1) < 1)
px2 = px1 + 1;
if (TMath::Abs(py1 - py2) < 1)
py1 = py2 + 1;
gVirtualX->DrawBox(px1, py1, px2, py2, (TVirtualX::EBoxMode)mode);
}
void TPadPainter::DrawFillArea(Int_t nPoints, const Double_t *xs, const Double_t *ys)
{
if (nPoints < 3) {
::Error("TPadPainter::DrawFillArea", "invalid number of points %d", nPoints);
return;
}
DrawFillAreaAux(gPad, nPoints, xs, ys);
}
void TPadPainter::DrawFillArea(Int_t nPoints, const Float_t *xs, const Float_t *ys)
{
if (nPoints < 3) {
::Error("TPadPainter::DrawFillArea", "invalid number of points %d", nPoints);
return;
}
DrawFillAreaAux(gPad, nPoints, xs, ys);
}
void TPadPainter::DrawPolyLine(Int_t n, const Double_t *xs, const Double_t *ys)
{
if (n < 2) {
::Error("TPadPainter::DrawPolyLine", "invalid number of points");
return;
}
DrawPolyLineAux(gPad, n, xs, ys);
}
void TPadPainter::DrawPolyLine(Int_t n, const Float_t *xs, const Float_t *ys)
{
if (n < 2) {
::Error("TPadPainter::DrawPolyLine", "invalid number of points");
return;
}
DrawPolyLineAux(gPad, n, xs, ys);
}
void TPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t *v)
{
if (n < 2) {
::Error("TPadPainter::DrawPolyLineNDC", "invalid number of points %d", n);
return;
}
std::vector<TPoint> xy(n);
for (Int_t i = 0; i < n; ++i) {
xy[i].fX = (SCoord_t)gPad->UtoPixel(u[i]);
xy[i].fY = (SCoord_t)gPad->VtoPixel(v[i]);
}
gVirtualX->DrawPolyLine(n, &xy[0]);
}
void TPadPainter::DrawPolyMarker(Int_t n, const Double_t *x, const Double_t *y)
{
if (n < 1) {
::Error("TPadPainter::DrawPolyMarker", "invalid number of points %d", n);
return;
}
DrawPolyMarkerAux(gPad, n, x, y);
}
void TPadPainter::DrawPolyMarker(Int_t n, const Float_t *x, const Float_t *y)
{
if (n < 1) {
::Error("TPadPainter::DrawPolyMarker", "invalid number of points %d", n);
return;
}
DrawPolyMarkerAux(gPad, n, x, y);
}
void TPadPainter::DrawText(Double_t x, Double_t y, const char *text, ETextMode mode)
{
const Int_t px = gPad->XtoPixel(x);
const Int_t py = gPad->YtoPixel(y);
const Double_t angle = GetTextAngle();
const Double_t mgn = GetTextMagnitude();
gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode);
}
void TPadPainter::DrawText(Double_t x, Double_t y, const wchar_t *text, ETextMode mode)
{
const Int_t px = gPad->XtoPixel(x);
const Int_t py = gPad->YtoPixel(y);
const Double_t angle = GetTextAngle();
const Double_t mgn = GetTextMagnitude();
gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode);
}
void TPadPainter::DrawTextNDC(Double_t u, Double_t v, const char *text, ETextMode mode)
{
const Int_t px = gPad->UtoPixel(u);
const Int_t py = gPad->VtoPixel(v);
const Double_t angle = GetTextAngle();
const Double_t mgn = GetTextMagnitude();
gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode);
}
void TPadPainter::SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const
{
if (gVirtualX->InheritsFrom("TGCocoa") && !gROOT->IsBatch() &&
pad->GetCanvas() && pad->GetCanvas()->GetCanvasID() != -1) {
TCanvas * const canvas = pad->GetCanvas();
canvas->Flush();
const UInt_t w = canvas->GetWw();
const UInt_t h = canvas->GetWh();
const unsigned char * const pixelData =
gVirtualX->GetColorBits(canvas->GetCanvasID(), 0, 0, w, h);
if (pixelData) {
const std::auto_ptr<TImage> image(TImage::Create());
if (image.get()) {
image->DrawRectangle(0, 0, w, h);
if (unsigned char *argb = (unsigned char *)image->GetArgbArray()) {
if (sizeof(UInt_t) == 4) {
std::copy(pixelData, pixelData + 4 * w * h, argb);
} else {
const unsigned shift = std::numeric_limits<unsigned char>::digits;
unsigned *dstPixel = (unsigned *)argb, *end = dstPixel + w * h;
const unsigned char *srcPixel = pixelData;
for (;dstPixel != end; ++dstPixel, srcPixel += 4) {
*dstPixel = srcPixel[0] & (srcPixel[1] << shift) &
(srcPixel[2] << 2 * shift) &
(srcPixel[3] << 3 * shift);
}
}
image->WriteImage(fileName, (TImage::EImageFileTypes)type);
delete [] pixelData;
return;
}
}
delete [] pixelData;
}
}
if (type == TImage::kGif) {
gVirtualX->WriteGIF((char*)fileName);
} else {
const std::auto_ptr<TImage> img(TImage::Create());
if (img.get()) {
img->FromPad(pad);
img->WriteImage(fileName, (TImage::EImageFileTypes)type);
}
}
}
void TPadPainter::DrawTextNDC(Double_t u, Double_t v, const wchar_t *text, ETextMode mode)
{
const Int_t px = gPad->UtoPixel(u);
const Int_t py = gPad->VtoPixel(v);
const Double_t angle = GetTextAngle();
const Double_t mgn = GetTextMagnitude();
gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode);
}
namespace {
template<typename T>
void ConvertPoints(TVirtualPad *pad, unsigned nPoints, const T *x, const T *y,
std::vector<TPoint> &dst)
{
if (!nPoints)
return;
dst.resize(nPoints);
for (unsigned i = 0; i < nPoints; ++i) {
dst[i].fX = (SCoord_t)pad->XtoPixel(x[i]);
dst[i].fY = (SCoord_t)pad->YtoPixel(y[i]);
}
}
inline void MergePointsX(std::vector<TPoint> &points, unsigned nMerged, SCoord_t yMin,
SCoord_t yMax, SCoord_t yLast)
{
const SCoord_t firstPointX = points.back().fX;
const SCoord_t firstPointY = points.back().fY;
if (nMerged == 2) {
points.push_back(TPoint(firstPointX, yLast));
} else if (nMerged == 3) {
yMin == firstPointY ? points.push_back(TPoint(firstPointX, yMax)) :
points.push_back(TPoint(firstPointX, yMin));
points.push_back(TPoint(firstPointX, yLast));
} else {
points.push_back(TPoint(firstPointX, yMin));
points.push_back(TPoint(firstPointX, yMax));
points.push_back(TPoint(firstPointX, yLast));
}
}
inline size_type MergePointsInplaceY(std::vector<TPoint> &dst, size_type nMerged, SCoord_t xMin,
SCoord_t xMax, SCoord_t xLast, size_type first)
{
const TPoint &firstPoint = dst[first];
if (nMerged == 2) {
dst[first + 1].fX = xLast;
dst[first + 1].fY = firstPoint.fY;
} else if (nMerged == 3) {
dst[first + 1].fX = xMin == firstPoint.fX ? xMax : xMin;
dst[first + 1].fY = firstPoint.fY;
dst[first + 2].fX = xLast;
dst[first + 2].fY = firstPoint.fY;
} else {
dst[first + 1].fX = xMin;
dst[first + 1].fY = firstPoint.fY;
dst[first + 2].fX = xMax;
dst[first + 2].fY = firstPoint.fY;
dst[first + 3].fX = xLast;
dst[first + 3].fY = firstPoint.fY;
nMerged = 4;
}
return nMerged;
}
template<typename T>
void ConvertPointsAndMergePassX(TVirtualPad *pad, unsigned nPoints, const T *x, const T *y,
std::vector<TPoint> &dst)
{
TPoint currentPoint;
SCoord_t yMin = 0, yMax = 0, yLast = 0;
unsigned nMerged = 0;
for (unsigned i = 0; i < nPoints;) {
currentPoint.fX = (SCoord_t)pad->XtoPixel(x[i]);
currentPoint.fY = (SCoord_t)pad->YtoPixel(y[i]);
yMin = currentPoint.fY;
yMax = yMin;
dst.push_back(currentPoint);
bool merged = false;
nMerged = 1;
for (unsigned j = i + 1; j < nPoints; ++j) {
const SCoord_t newX = pad->XtoPixel(x[j]);
if (newX == currentPoint.fX) {
yLast = pad->YtoPixel(y[j]);
yMin = TMath::Min(yMin, yLast);
yMax = TMath::Max(yMax, yLast);
++nMerged;
} else {
if (nMerged > 1)
MergePointsX(dst, nMerged, yMin, yMax, yLast);
merged = true;
break;
}
}
if (!merged && nMerged > 1)
MergePointsX(dst, nMerged, yMin, yMax, yLast);
i += nMerged;
}
}
void ConvertPointsAndMergeInplacePassY(std::vector<TPoint> &dst)
{
size_type i = 0;
for (size_type j = 1, nPoints = dst.size(); i < nPoints;) {
const TPoint ¤tPoint = dst[i];
SCoord_t xMin = currentPoint.fX;
SCoord_t xMax = xMin;
SCoord_t xLast = 0;
bool merged = false;
size_type nMerged = 1;
for (; j < nPoints; ++j) {
const TPoint &nextPoint = dst[j];
if (nextPoint.fY == currentPoint.fY) {
xLast = nextPoint.fX;
xMin = TMath::Min(xMin, xLast);
xMax = TMath::Max(xMax, xLast);
++nMerged;
} else {
if (nMerged > 1)
nMerged = MergePointsInplaceY(dst, nMerged, xMin, xMax, xLast, i);
merged = true;
break;
}
}
if (!merged && nMerged > 1)
nMerged = MergePointsInplaceY(dst, nMerged, xMin, xMax, xLast, i);
i += nMerged;
if (j < nPoints) {
dst[i] = dst[j];
++j;
} else
break;
}
dst.resize(i);
}
template<typename T>
void ConvertPointsAndMerge(TVirtualPad *pad, unsigned threshold, unsigned nPoints, const T *x,
const T *y, std::vector<TPoint> &dst)
{
if (!nPoints)
return;
dst.clear();
dst.reserve(threshold);
ConvertPointsAndMergePassX(pad, nPoints, x, y, dst);
if (dst.size() < threshold)
return;
ConvertPointsAndMergeInplacePassY(dst);
}
template<class T>
void DrawFillAreaAux(TVirtualPad *pad, Int_t nPoints, const T *xs, const T *ys)
{
std::vector<TPoint> xy;
const Int_t threshold = Int_t(TMath::Min(pad->GetWw() * pad->GetAbsWNDC(),
pad->GetWh() * pad->GetAbsHNDC())) * 2;
if (threshold <= 0) {
::Error("DrawFillAreaAux", "invalid pad's geometry");
return;
}
if (nPoints < threshold)
ConvertPoints(gPad, nPoints, xs, ys, xy);
else
ConvertPointsAndMerge(gPad, threshold, nPoints, xs, ys, xy);
if (!gVirtualX->GetFillStyle())
xy.push_back(xy.front());
if (xy.size() > 2)
gVirtualX->DrawFillArea(xy.size(), &xy[0]);
}
template<typename T>
void DrawPolyLineAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys)
{
std::vector<TPoint> xy;
const Int_t threshold = Int_t(TMath::Min(pad->GetWw() * pad->GetAbsWNDC(),
pad->GetWh() * pad->GetAbsHNDC())) * 2;
if (threshold <= 0) {
::Error("DrawPolyLineAux", "invalid pad's geometry");
return;
}
if (nPoints < (unsigned)threshold)
ConvertPoints(pad, nPoints, xs, ys, xy);
else
ConvertPointsAndMerge(pad, threshold, nPoints, xs, ys, xy);
if (xy.size() > 1)
gVirtualX->DrawPolyLine(xy.size(), &xy[0]);
}
template<class T>
void DrawPolyMarkerAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys)
{
std::vector<TPoint> xy(nPoints);
for (unsigned i = 0; i < nPoints; ++i) {
xy[i].fX = (SCoord_t)pad->XtoPixel(xs[i]);
xy[i].fY = (SCoord_t)pad->YtoPixel(ys[i]);
}
gVirtualX->DrawPolyMarker(nPoints, &xy[0]);
}
}