Logo ROOT   6.18/05
Reference Guide
RPadLength.cxx
Go to the documentation of this file.
1/// \file RPadLength.cxx
2/// \ingroup Gpad ROOT7
3/// \author Axel Naumann <axel@cern.ch>
4/// \date 2018-07-22
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#include "ROOT/RPadLength.hxx"
17
18#include <ROOT/RLogger.hxx>
19
20#include <algorithm> // std::transform
21#include <cctype> // std::tolower
22
23namespace {
24 enum ELengthKind {
25 kNormal,
26 kPixel,
27 kUser
28 };
29
30 struct RLengthElement {
31 ELengthKind fKind;
32 double fVal = 0.;
33 };
34
35 struct RLengthParseElements {
36 char fSignOrOp = '+';
37 std::string fNumber;
38 std::string fUnit;
39 int fIndex = 0;
40
41 bool Done() const { return fIndex == 3; }
42
43 std::string SetMaybeSignOrOpAndGetError(std::string_view tok)
44 {
45 fIndex = 1;
46 if (tok.front() == '-' || tok.front() == '+')
47 fSignOrOp = tok.front();
48
49 if (tok.length() > 1)
50 return SetNumberAndGetError(tok.substr(1));
51
52 return {};
53 }
54
55 std::string SetNumberAndGetError(std::string_view tok)
56 {
57 // must be all numbers, up to possible remaining unit.
58 fIndex = 2;
59
60 if (tok.front() == '-') {
61 if (fSignOrOp == '-')
62 fSignOrOp = '+';
63 else
64 fSignOrOp = '-';
65 tok = tok.substr(1);
66 } else if (tok.front() == '+')
67 tok = tok.substr(1);
68
69 bool hadDot = false;
70 for (const char &c: tok) {
71 if (std::isdigit(c) || c == '.') {
72 if (c == '.') {
73 if (hadDot) {
74 std::string err = "syntax error: multiple '.' in ";
75 err += std::string(tok);
76 return err;
77 }
78 hadDot = true;
79 }
80
81 fNumber += c;
82 } else {
83 return SetUnitAndGetError(tok.substr(&c - tok.data()));
84 }
85 }
86 return {};
87 }
88
89 std::string SetUnitAndGetError(std::string_view tok)
90 {
91 fIndex = 3;
92 fUnit = std::string(tok);
93 std::transform(fUnit.begin(), fUnit.end(), fUnit.begin(), [](char c) { return std::tolower(c); });
94 return "";
95 }
96
97 std::string SetNextAndGetError(std::string_view tok)
98 {
99 switch (fIndex) {
100 case 0: return SetMaybeSignOrOpAndGetError(tok);
101 case 1: return SetNumberAndGetError(tok);
102 case 2: return SetUnitAndGetError(tok);
103 default: return "invalid syntax: too many / duplicate elements";
104 }
105 return {};
106 }
107
108 std::pair<RLengthElement, std::string> ToLengthElement() const {
109 RLengthElement ret;
110 std::size_t posStrToD;
111 if (fNumber.empty()) {
112 return {{}, "empty floating point number"};
113 }
114 ret.fVal = std::stod(fNumber, &posStrToD);
115 if (posStrToD != fNumber.length())
116 return {{}, std::string("invalid floating point number ") + fNumber};
117
118 if (fSignOrOp == '-')
119 ret.fVal *= -1;
120
121 if (fUnit == "normal")
122 ret.fKind = kNormal;
123 else if (fUnit == "px" || fUnit == "pixel")
124 ret.fKind = kPixel;
125 else if (fUnit == "user")
126 ret.fKind = kUser;
127 else
128 return {{}, std::string("invalid unit, expect normal, px, or user but got ") + fUnit};
129 return {ret, ""};
130 }
131 };
132
133 static std::string HandleToken(std::string &tok, RLengthParseElements &parse, ROOT::Experimental::RPadLength &obj) {
134 std::string err = parse.SetNextAndGetError(tok);
135 tok.clear();
136 if (!err.empty())
137 return err;
138
139 if (parse.Done()) {
140 std::pair<RLengthElement, std::string> parseRes = parse.ToLengthElement();
141 if (!parseRes.second.empty())
142 return parseRes.second;
143 switch (parseRes.first.fKind) {
144 case kNormal: obj += ROOT::Experimental::RPadLength::Normal(parseRes.first.fVal); break;
145 case kPixel: obj += ROOT::Experimental::RPadLength::Pixel(parseRes.first.fVal); break;
146 case kUser: obj += ROOT::Experimental::RPadLength::User(parseRes.first.fVal); break;
147 }
148
149 // Restart.
150 parse = {};
151 }
152 return {};
153 }
154}
155
156////////////////////////////////////////////////////////////////////////////////
157/// Initialize a RPadLength from a style string.
158/// Syntax: a series of numbers separated by "+", where each number is
159/// followed by one of `px`, `user`, `normal` to specify an extent in pixel,
160/// user or normal coordinates. Spaces between any part is allowed.
161/// Example: `100 px + 0.1 user, 0.5 normal` is a `RPadExtent{100_px + 0.1_user, 0.5_normal}`.
162
163void ROOT::Experimental::RPadLength::SetFromAttrString(const std::string &val, const std::string &name)
164{
165 *this = {}; // Value-initialize this.
166 std::string tok;
167 RLengthParseElements parse;
168
169 for (const char c: val) {
170 if (c == ' ') {
171 if (!tok.empty()) {
172 std::string err = HandleToken(tok, parse, *this);
173 if (!err.empty()) {
174 R__ERROR_HERE("Gpad") << "Invalid syntax in '" << val
175 << "' while parsing pad length for " << name << ": " << err;
176 return;
177 }
178 }
179 } else
180 tok += c;
181 }
182
183 // Handle last token:
184 if (!tok.empty()) {
185 std::string err = HandleToken(tok, parse, *this);
186 if (!err.empty()) {
187 R__ERROR_HERE("Gpad") << "Invalid syntax in '" << val
188 << "' while parsing pad length for " << name << ": " << err;
189 return;
190 }
191 }
192 if (parse.fIndex != 0) {
193 R__ERROR_HERE("Gpad") << "Invalid syntax in '" << val
194 << "' while parsing pad length for " << name
195 << ": missing elements, expect [+-] number (normal|px|user)";
196 return;
197 }
198}
199
201{
202 RPadLength ret;
203 ret.SetFromAttrString(val, name);
204 return ret;
205}
206
208{
209 std::stringstream strm;
210 strm << len.fNormal.fVal << " normal + " << len.fPixel.fVal << " px + " << len.fUser.fVal << " user";
211 return strm.str();
212}
213
214
#define R__ERROR_HERE(GROUP)
Definition: RLogger.hxx:183
#define c(i)
Definition: RSha256.hxx:101
char name[80]
Definition: TGX11.cxx:109
A coordinate in a RPad.
Definition: RPadLength.hxx:28
Pixel fPixel
The pixel coordinate summand.
Definition: RPadLength.hxx:101
User fUser
The user coordinate summand.
Definition: RPadLength.hxx:104
void SetFromAttrString(const std::string &val, const std::string &name)
Initialize a RPadLength from a style string.
Definition: RPadLength.cxx:163
Normal fNormal
The normalized coordinate summand.
Definition: RPadLength.hxx:98
basic_string_view< char > string_view
std::string ToAttributeString(const RColor &val)
Return a std::string representation of a RColor, suitable as input to ColorFromString().
Definition: RColor.cxx:144
RColor FromAttributeString(const std::string &str, const std::string &name, RColor *)
Initialize a RColor from a string value.
Definition: RColor.cxx:132
A normalized coordinate: 0 in the left, bottom corner, 1 in the top, right corner of the RPad.
Definition: RPadLength.hxx:77
A pixel coordinate: 0 in the left, bottom corner, 1 in the top, right corner of the RPad.
Definition: RPadLength.hxx:85
A user coordinate, as defined by the EUserCoordSystem parameter of the RPad.
Definition: RPadLength.hxx:92