libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
size2d.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <cstdlib>
14#include <cerrno>
15#include <type_traits>
16#include <promeki/namespace.h>
17#include <promeki/string.h>
18#include <promeki/point.h>
19#include <promeki/error.h>
20#include <promeki/result.h>
21
22PROMEKI_NAMESPACE_BEGIN
23
47template <typename T> class Size2DTemplate {
48 public:
50 Size2DTemplate(const T &width = 0, const T &height = 0) : _width(width), _height(height) {}
51
53 ~Size2DTemplate() {}
54
56 bool isValid() const { return (_width > 0) && (_height > 0); }
57
59 void set(const T &w, const T &h) {
60 _width = w;
61 _height = h;
62 return;
63 }
64
66 void setWidth(const T &val) {
67 _width = val;
68 return;
69 }
70
72 const T &width() const { return _width; }
73
75 void setHeight(const T &val) {
76 _height = val;
77 return;
78 }
79
81 const T &height() const { return _height; }
82
84 T area() const { return _width * _height; }
85
87 String toString() const { return String::number(_width) + "x" + String::number(_height); }
88
90 operator String() const { return toString(); }
91
104 static Result<Size2DTemplate<T>> fromString(const String &str) {
105 const char *s = str.cstr();
106 if (s == nullptr || *s == '\0') {
107 return makeError<Size2DTemplate<T>>(Error::Invalid);
108 }
109 T w{};
110 const char *end = nullptr;
111 if (!parseDim(s, w, end)) {
112 return makeError<Size2DTemplate<T>>(Error::Invalid);
113 }
114 if (*end != 'x' && *end != 'X') {
115 return makeError<Size2DTemplate<T>>(Error::Invalid);
116 }
117 const char *rest = end + 1;
118 if (*rest == '\0') {
119 return makeError<Size2DTemplate<T>>(Error::Invalid);
120 }
121 T h{};
122 if (!parseDim(rest, h, end) || *end != '\0') {
123 return makeError<Size2DTemplate<T>>(Error::Invalid);
124 }
125 return makeResult(Size2DTemplate<T>(w, h));
126 }
127
129 bool operator==(const Size2DTemplate &other) const {
130 return _width == other._width && _height == other._height;
131 }
132
134 bool operator!=(const Size2DTemplate &other) const { return !(*this == other); }
135
137 template <typename N> bool pointIsInside(const Point<N, 2> &p) const {
138 // First gate the >=0 check so a signed point coordinate paired with
139 // an unsigned size dimension doesn't trip -Wsign-compare on the
140 // upper bound: after p.x() >= 0 the cast to T is always safe.
141 if (p.x() < N{0} || p.y() < N{0}) return false;
142 return static_cast<T>(p.x()) < _width && static_cast<T>(p.y()) < _height;
143 }
144
145 private:
146 T _width = 0;
147 T _height = 0;
148
149 // Parses one width or height token from @p s, dispatching to
150 // strtoll for signed T and strtoull for unsigned T so that
151 // negative inputs are accepted by signed templates and
152 // rejected by unsigned templates. On success returns true,
153 // sets @p out, and points @p end at the first unparsed
154 // character. On parse failure returns false.
155 static bool parseDim(const char *s, T &out, const char *&end) {
156 char *parseEnd = nullptr;
157 errno = 0;
158 if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
159 long long v = std::strtoll(s, &parseEnd, 10);
160 end = parseEnd;
161 if (parseEnd == s || errno != 0) return false;
162 out = static_cast<T>(v);
163 return true;
164 } else if constexpr (std::is_integral_v<T>) {
165 // strtoull silently accepts a leading '-' and
166 // returns the negation as unsigned. Reject up
167 // front so an unsigned size cannot be parsed
168 // from a negative-looking input.
169 const char *p = s;
170 while (*p == ' ' || *p == '\t') ++p;
171 if (*p == '-') return false;
172 unsigned long long v = std::strtoull(s, &parseEnd, 10);
173 end = parseEnd;
174 if (parseEnd == s || errno != 0) return false;
175 out = static_cast<T>(v);
176 return true;
177 } else {
178 double v = std::strtod(s, &parseEnd);
179 end = parseEnd;
180 if (parseEnd == s || errno != 0) return false;
181 out = static_cast<T>(v);
182 return true;
183 }
184 }
185};
186
188using Size2Di32 = Size2DTemplate<int32_t>;
190using Size2Du32 = Size2DTemplate<uint32_t>;
192using Size2Df = Size2DTemplate<float>;
194using Size2Dd = Size2DTemplate<double>;
195
196PROMEKI_NAMESPACE_END
197
206template <typename T>
207struct std::formatter<promeki::Size2DTemplate<T>> : promeki::ToStringFormatter<promeki::Size2DTemplate<T>> {};
208
209#endif // PROMEKI_ENABLE_CORE