Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
texts_grid.C
Go to the documentation of this file.
1/// \file
2/// \ingroup tutorial_eve_7
3/// SDF text on a fixed grid, for judging REveText rendering quality.
4///
5/// Unlike texts.C, which places random words at random sizes, this macro is
6/// deterministic, so two runs can be compared pixel by pixel. Each panel varies
7/// one parameter:
8///
9/// A screen mode, font-size ladder
10/// B screen mode, one line per font at the same size
11/// C world mode, size ladder in world units
12/// D world mode, text rotated about y and about x
13/// E mixed mode, text at increasing depth; all lines should be the same size
14///
15/// REveText::SetMode() takes 0 for world, 1 for screen and 2 for mixed. Two
16/// viewers, one perspective and one orthographic, show the same scenes.
17///
18/// Panels can be isolated from the ROOT prompt while the macro runs:
19/// tg_only("C") // show panel C alone
20/// tg_only("") // show all panels again
21///
22/// \macro_code
23///
24/// \author Matevz Tadel
25
26#include <ROOT/REveElement.hxx>
27#include <ROOT/REveManager.hxx>
28#include <ROOT/REveBox.hxx>
29#include <ROOT/REveScene.hxx>
30#include <ROOT/REveText.hxx>
31#include <ROOT/REveViewer.hxx>
32
33#include "TROOT.h"
34#include "TMath.h"
35
36#include <map>
37#include <string>
38
39namespace REX = ROOT::Experimental;
40using namespace ROOT::Experimental;
41
42// Short on purpose: at the top of the size ladder a long string runs off the
43// viewport. Chosen to expose the usual SDF failure modes --
44// Ill1 vertical stems a pixel apart OQ08 round counters
45// //\\ diagonals, the worst case for AA .. dots that vanish when the
46// smoothstep is too wide
47const char *kRuler = "Ill1 OQ08 //\\\\ ..";
48
49// Every one of these ships in TROOT::GetDataDir()/fonts. ROOT has Liberation Mono
50// and Serif but no Liberation Sans.
51// clang-format off
52const char *kFonts[] = {"LiberationSerif-Regular", "LiberationMono-Regular", "verdana",
53 "georgia", "comic", "comicbd",
54 "BlackChancery"};
55// clang-format on
56const int kNFonts = sizeof(kFonts) / sizeof(char *);
57
58// Screen-mode font sizes, as fractions of viewport height.
59const float kSizes[] = {0.005f, 0.008f, 0.012f, 0.018f, 0.024f, 0.033f, 0.045f, 0.060f};
60const int kNSizes = sizeof(kSizes) / sizeof(float);
61
62const double kWorldLim = 100.0; // half-extent of the world-mode panels
63
64std::map<std::string, REveElement *> gPanels;
65
66//------------------------------------------------------------------------------
67
68static REveText *MakeText(REveElement *holder, const char *name, const std::string &txt, const char *font, int mode,
69 float size, Color_t col)
70{
71 auto t = new REveText(name);
72 t->SetText(txt);
73 t->SetFont(font);
74 t->SetMode(mode);
75 t->SetFontSize(size);
76 t->SetTextColor(col);
77 t->SetDrawFrame(false); // frames would dominate the picture at small sizes
78 holder->AddElement(t);
79 return t;
80}
81
82// REveText contributes nothing to the scene bounding box, so a scene of world text
83// gives the camera nothing to frame. Add a faint box of known extent.
84static void AddBBoxAnchor(REveElement *holder, double lim)
85{
86 auto b = new REveBox("bbox_anchor");
87 const double v[8][3] = {{-1, -1, -1}, {-1, 1, -1}, {1, 1, -1}, {1, -1, -1},
88 {-1, -1, 1}, {-1, 1, 1}, {1, 1, 1}, {1, -1, 1}};
89 for (int i = 0; i < 8; ++i)
90 b->SetVertex(i, lim * v[i][0], lim * v[i][1], lim * v[i][2]);
91 b->SetMainColor(kGray);
92 b->SetMainTransparency(90);
93 holder->AddElement(b);
94}
95
96// A -- SCREEN mode, font-size ladder, upper part of the left column.
97static void PanelA(REveElement *h)
98{
99 float y = 0.97;
100 for (int i = 0; i < kNSizes; ++i) {
101 float s = kSizes[i];
102 auto t = MakeText(h, Form("A_%02d_size_%.3f", i, s), Form("%.3f %s", s, kRuler), "LiberationSerif-Regular", 1, s,
103 kBlack);
104 t->SetPosition(REveVector(0.01, y, 0.0));
105 y -= 2.2 * s + 0.012;
106 }
107}
108
109// B -- SCREEN mode, one line per font, same size, below panel A.
110static void PanelB(REveElement *h)
111{
112 float y = 0.40;
113 for (int i = 0; i < kNFonts; ++i) {
114 auto t = MakeText(h, Form("B_%02d_%s", i, kFonts[i]), Form("%s %s", kRuler, kFonts[i]), kFonts[i], 1, 0.018f,
115 kBlue + 2);
116 t->SetPosition(REveVector(0.01, y, 0.0));
117 y -= 0.048;
118 }
119}
120
121// C -- WORLD mode, size ladder, stacked in y, all facing +z.
122static void PanelC(REveElement *h)
123{
124 AddBBoxAnchor(h, kWorldLim);
125 float y = -kWorldLim + 10.0;
126 for (int i = 0; i < kNSizes; ++i) {
127 float s = 200.0 * kSizes[i]; // world units: 1.0 .. 12.0
128 auto t = MakeText(h, Form("C_%02d_size_%.1f", i, s), Form("%.1f %s", s, kRuler), "LiberationSerif-Regular", 0, s,
129 kRed + 2);
130 t->RefMainTrans().SetPos(-kWorldLim, y, 0.0);
131 y += 2.4 * s + 3.0;
132 }
133}
134
135// D -- WORLD mode, constant size, rotated about y (top row) then x (bottom row).
136static void PanelD(REveElement *h)
137{
138 AddBBoxAnchor(h, kWorldLim);
139 const double d2r = TMath::Pi() / 180.0;
140 const int angles[] = {0, 20, 40, 60, 75};
141 for (int i = 0; i < 5; ++i) {
142 auto t = MakeText(h, Form("D_rotY_%02d", angles[i]), Form("Y%02d %s", angles[i], kRuler),
143 "LiberationSerif-Regular", 0, 8.0f, kGreen + 3);
144 auto &tr = t->RefMainTrans();
145 tr.SetPos(-kWorldLim, 20.0 * i + 10.0, 0.0);
146 tr.SetRotByAngles(0.0, angles[i] * d2r, 0.0);
147 }
148 for (int i = 0; i < 5; ++i) {
149 auto t = MakeText(h, Form("D_rotX_%02d", angles[i]), Form("X%02d %s", angles[i], kRuler),
150 "LiberationSerif-Regular", 0, 8.0f, kMagenta + 2);
151 auto &tr = t->RefMainTrans();
152 tr.SetPos(-kWorldLim, -20.0 * i - 10.0, 0.0);
153 tr.SetRotByAngles(angles[i] * d2r, 0.0, 0.0);
154 }
155}
156
157// E -- MIXED mode: world anchor, screen size, front facing. All five lines must
158// come out the same size on screen; if they shrink with depth the mode has
159// fallen back to world-space scaling.
160static void PanelE(REveElement *h)
161{
162 AddBBoxAnchor(h, kWorldLim);
163 for (int i = 0; i < 5; ++i) {
164 double z = -kWorldLim + i * (2.0 * kWorldLim / 4.0);
165 auto t = MakeText(h, Form("E_%02d_z_%+.0f", i, z), Form("z%+.0f %s", z, kRuler), "LiberationSerif-Regular", 2,
166 0.022f, kOrange + 7);
167 t->RefMainTrans().SetPos(-kWorldLim, -kWorldLim + i * 45.0, z);
168 }
169}
170
171//------------------------------------------------------------------------------
172/// Show only the named panel ("A".."E"); empty string shows all of them.
173/// Callable from the ROOT prompt while the macro is running.
174
175void tg_only(const char *panel = "")
176{
178 std::string want(panel);
179 for (auto &[name, el] : gPanels) {
180 bool on = want.empty() || name == want;
181 el->SetRnrSelf(on);
182 el->SetRnrChildren(on);
183 }
184}
185
186//------------------------------------------------------------------------------
187
188/// \param panels which panels to build, e.g. "ABCDE" (all) or "CDE" (world and
189/// mixed only). Leave out A and B to fit the camera to the world-mode
190/// panels, since screen-mode text in [0,1] is counted in the camera fit.
191void texts_grid(const char *panels = "ABCDE")
192{
193 std::string want(panels);
194 auto eveMng = REveManager::Create();
195 eveMng->AllowMultipleRemoteConnections(false, false);
196
197 // Generate the SDF atlases on first use. This needs a display. Once the
198 // .png and .js.gz files exist it does nothing.
199 std::string rf = std::string(TROOT::GetDataDir().Data()) + "/fonts/";
200 for (int i = 0; i < kNFonts; ++i)
201 REveText::AssertSdfFont(kFonts[i], rf + kFonts[i] + ".ttf");
202
203 // One scene per mode, so a mode can be switched off in the browser tree; one
204 // holder per panel, so tg_only() can isolate a panel from the prompt.
205 auto *scScreen = eveMng->SpawnNewScene("Screen text", "Panels A, B -- screen mode");
206 auto *scWorld = eveMng->SpawnNewScene("World text", "Panels C, D -- world mode");
207 auto *scMixed = eveMng->SpawnNewScene("Mixed text", "Panel E -- mixed mode");
208
209 // clang-format off
210 struct { const char *key; const char *name; REveScene *scene; void (*fill)(REveElement *); } defs[] = {
211 {"A", "A_screen_size_ladder", scScreen, PanelA},
212 {"B", "B_screen_fonts", scScreen, PanelB},
213 {"C", "C_world_size_ladder", scWorld, PanelC},
214 {"D", "D_world_rotation_fan", scWorld, PanelD},
215 {"E", "E_mixed_depth_ladder", scMixed, PanelE},
216 };
217 // clang-format on
218 for (auto &d : defs) {
219 if (want.find(d.key) == std::string::npos)
220 continue;
221 auto *h = new REveElement(d.name);
222 d.fill(h);
223 d.scene->AddElement(h);
224 gPanels[d.key] = h;
225 }
226
227 // The same scenes in a perspective and an orthographic viewer, to check that
228 // text renders the same under both cameras.
229 auto *v0 = (REveViewer *)eveMng->GetViewers()->FirstChild();
230 v0->SetName("Perspective");
231 v0->SetCameraType(REveViewer::kCameraPerspXOY); // XY plane face-on: panels C/D live there
232 auto *v1 = eveMng->SpawnNewViewer("Orthographic", "Same scenes, ortho camera");
233 v1->SetCameraType(REveViewer::kCameraOrthoXOY);
234
235 for (auto *v : {v0, v1}) {
236 v->AddScene(scScreen);
237 v->AddScene(scWorld);
238 v->AddScene(scMixed);
239 v->SetAxesType(REveViewer::kAxesEdge);
240 }
241
242 eveMng->Show();
243}
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
short Color_t
Color number (short)
Definition RtypesCore.h:100
@ kGray
Definition Rtypes.h:65
@ kRed
Definition Rtypes.h:66
@ kOrange
Definition Rtypes.h:67
@ kBlack
Definition Rtypes.h:65
@ kGreen
Definition Rtypes.h:66
@ kMagenta
Definition Rtypes.h:66
@ kBlue
Definition Rtypes.h:66
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void on
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx:142
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2571
3D box with arbitrary vertices (cuboid).
Base class for REveUtil visualization elements, providing hierarchy management, rendering control and...
RAII guard for locking Eve manager (ctor) and processing changes (dtor).
Eve representation of TGLScene.
Eve representation of a GL view.
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:3406
Double_t y[n]
Definition legend1.C:17
Namespace for ROOT features in testing.
Definition TROOT.h:100
constexpr Double_t Pi()
Definition TMath.h:40