home
readme
diff
tree
note
docs
0commit d2128331bf02d3d26e8b8770cf6af9712f3e2933
1Author: lakefox <mason@lakefox.net>
2Date: Thu Jan 1 11:43:21 2026 -0700
3
4 Renamed WindowUnitLengthValue to UnitLengthValue now uses Nodes. Added tests and make run_
5
6diff --git a/Makefile b/Makefile
7index 435bf3b..c295710 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -20,12 +20,22 @@ build/parser.o: src/parser.cc include/parser.h | build
11
12 # --- Tests ---
13
14-all_tests: build/html_node_test build/css_selector_test build/html_parser_test build/query_select_test
15- build/html_node_test --skip-benchmarks
16- build/css_selector_test --skip-benchmarks
17- build/html_parser_test --skip-benchmarks
18- build/query_select_test --skip-benchmarks
19- build/css_unit_test --skip-benchmarks
20+TEST_BINARIES = build/html_node_test build/css_selector_test \
21+ build/html_parser_test build/query_select_test \
22+ build/css_unit_test build/css_parser_test \
23+ build/windowunitlength_test
24+
25+all_tests: $(TEST_BINARIES)
26+ @for test in $(TEST_BINARIES); do \
27+ echo "Running $$test..."; \
28+ $$test --skip-benchmarks; \
29+ done
30+
31+# This target clears the screen, builds the specific test/playground, and runs it
32+run_%: build/%
33+ @clear
34+ @echo "--- Running $* ---"
35+ @./build/$* --skip-benchmarks
36
37 build/css_unit_test: tests/css_unit.cc build/grim.o build/catch.o build/parser.o | build
38 ${CC} ${CCFLAGS} -Iinclude/ tests/css_unit.cc build/grim.o build/catch.o build/parser.o -o build/css_unit_test
39@@ -45,6 +55,10 @@ build/css_parser_test: tests/css_parser.cc build/grim.o build/parser.o build/cat
40 build/query_select_test: tests/query_select.cc build/grim.o build/parser.o build/catch.o | build
41 ${CC} ${CCFLAGS} -Iinclude/ tests/query_select.cc build/grim.o build/parser.o build/catch.o -o build/query_select_test
42
43+build/windowunitlength_test: tests/windowunitlength.cc build/grim.o build/parser.o build/catch.o | build
44+ ${CC} ${CCFLAGS} -Iinclude/ tests/windowunitlength.cc build/grim.o build/parser.o build/catch.o -o build/windowunitlength_test
45+
46+
47 build/catch.o: ./include/catch_amalgamated.cpp ./include/catch_amalgamated.hpp | build
48 ${CC} ${CCFLAGS} -Iinclude/ -c include/catch_amalgamated.cpp -o build/catch.o
49
50diff --git a/include/grim.h b/include/grim.h
51index e7ef8c6..5615570 100755
52--- a/include/grim.h
53+++ b/include/grim.h
54@@ -96,6 +96,8 @@ enum class UnitType : short {
55 // Transform Functions
56 MATRIX, MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ, SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX, TRANSLATEY, TRANSLATEZ,
57 SKEW, SKEWX, SKEWY,
58+ // Rounding
59+ NEAREST, UP, DOWN, TO_ZERO,
60
61 // Keyword
62 PRINT, SCREEN, TOKEN, FUNC, GROUP, NO_SUPPORT, IMAGE, STRING
63@@ -465,6 +467,6 @@ For the adapter, it will take a window as a argument to set the adapters capabil
64 Window createWindow();
65
66
67-float WindowUnitLengthValue(Window*, const std::vector<Unit>&, size_t, size_t);
68+float UnitLengthValue(Node*, const std::vector<Unit>&, size_t, size_t);
69
70 #endif // NODE_H
71diff --git a/playground/css_unit.cc b/playground/css_unit.cc
72index 148320a..c064e68 100644
73--- a/playground/css_unit.cc
74+++ b/playground/css_unit.cc
75@@ -4,7 +4,7 @@
76 #include <fstream>
77
78 int main() {
79- std::vector<Unit> test = getUnit("min(100px, 12rem)");
80+ std::vector<Unit> test = getUnit("attr(size px)");
81
82 std::cout << "Done" << std::endl;
83 }
84diff --git a/playground/window.cc b/playground/window.cc
85index a76ef6e..d8a79a5 100644
86--- a/playground/window.cc
87+++ b/playground/window.cc
88@@ -41,14 +41,13 @@ int main() {
89 Window window = createWindow();
90 window.parseHTML(ss);
91
92-// Node document = window.document;
93+ Node document = window.document;
94
95- window.setWidth(1000);
96- window.setHeight(700);
97- window.setEM(16);
98+ document.cacheWidth(1000);
99+ document.cacheHeight(700);
100+ document.cacheEM(16);
101
102- // std::vector<Unit> units = getUnit("clamp(100px, calc(30% / 2rem + 10px), 900px)");
103- std::vector<Unit> units = getUnit("clamp(100px, calc(30% / 2rem + 10px), 900px)");
104+ std::vector<Unit> units = getUnit("mod(13px, 5px)");
105
106- std::cout << WindowUnitLengthValue(&window, units, 0, units.size()) << std::endl;
107+ std::cout << UnitLengthValue(&document, units, 0, units.size()) << std::endl;
108 }
109diff --git a/src/grim.cc b/src/grim.cc
110index 1dcbc67..23940a2 100755
111--- a/src/grim.cc
112+++ b/src/grim.cc
113@@ -12,6 +12,7 @@
114 #include <optional>
115 #include <stdexcept>
116 #include <limits>
117+#include <cmath>
118
119 // +------------------------------------------------------+
120 // |(TOC) Table of contents |
121@@ -965,7 +966,7 @@ std::vector<Range> extractUnits(const std::vector<Unit>& units, size_t start, si
122 /*
123 CAUTION: These values are only correct for top level media queries
124 */
125-float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size_t start, size_t end) {
126+float UnitLengthValue(Node* n, const std::vector<Unit>& units, size_t start, size_t end) {
127 float sum = 0;
128 UnitType op = UnitType::ADD;
129
130@@ -980,6 +981,7 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
131 float v2 = 0;
132 float v3 = 0;
133 std::vector<Range> extracted;
134+ UnitType ut;
135
136 switch (type) {
137 case UnitType::ADD:
138@@ -988,8 +990,12 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
139 case UnitType::MULT:
140 op = type;
141 break;
142+ // DEG, TURN are not length values but are treated as generic units (PX)
143+ case UnitType::DEG:
144+ case UnitType::TURN:
145+ case UnitType::NUMBER:
146+ // NUMBER is any untyped numbers "2". Treat as a pixel
147 case UnitType::PX:
148- std::cout << "PX: " << unit.value.FLOAT << std::endl;
149 value = unit.value.FLOAT;
150 break;
151 case UnitType::CM:
152@@ -1021,27 +1027,27 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
153 case UnitType::RCH:
154 case UnitType::RIC:
155 case UnitType::RLH:
156- value = unit.value.FLOAT*window->getEM();
157+ value = unit.value.FLOAT*n->getEM();
158 break;
159 case UnitType::EX:
160 case UnitType::REX:
161- value = unit.value.FLOAT*(window->getEM()/2);
162+ value = unit.value.FLOAT*(n->getEM()/2);
163 break;
164 case UnitType::CQH:
165 case UnitType::VH:
166- value = window->getHeight()*(unit.value.FLOAT/100);
167+ value = n->getHeight()*(unit.value.FLOAT/100);
168 break;
169 case UnitType::CQW:
170 case UnitType::CQI:
171 case UnitType::CQB:
172 case UnitType::PERCENT:
173 case UnitType::VW:
174- value = window->getWidth()*(unit.value.FLOAT/100);
175+ value = n->getWidth()*(unit.value.FLOAT/100);
176 break;
177 case UnitType::VMAX:
178 case UnitType::CQMAX:
179- w = window->getWidth();
180- h = window->getHeight();
181+ w = n->getWidth();
182+ h = n->getHeight();
183 if (w > h) {
184 value = unit.value.FLOAT*w;
185 } else {
186@@ -1050,8 +1056,8 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
187 break;
188 case UnitType::VMIN:
189 case UnitType::CQMIN:
190- w = window->getWidth();
191- h = window->getHeight();
192+ w = n->getWidth();
193+ h = n->getHeight();
194 if (w < h) {
195 value = unit.value.FLOAT*w;
196 } else {
197@@ -1060,10 +1066,10 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
198 break;
199 case UnitType::VB:
200 case UnitType::VI:
201- value = unit.value.FLOAT*window->getWidth();
202+ value = unit.value.FLOAT*n->getWidth();
203 break;
204 case UnitType::CALC:
205- value = WindowUnitLengthValue(window, units, i+1, i+1+unit.value.INT);
206+ value = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
207 i += unit.value.INT;
208 break;
209 case UnitType::MIN:
210@@ -1072,7 +1078,7 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
211 v1 = std::numeric_limits<float>::max(); // Store of the min value
212
213 for (size_t j = 0; j < extracted.size(); j++) {
214- v2 = WindowUnitLengthValue(window, units, extracted[j].start, extracted[j].end);
215+ v2 = UnitLengthValue(n, units, extracted[j].start, extracted[j].end);
216
217 if (v2 < v1) {
218 v1 = v2;
219@@ -1089,7 +1095,7 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
220 v1 = std::numeric_limits<float>::min(); // Store of the max value
221
222 for (size_t j = 0; j < extracted.size(); j++) {
223- v2 = WindowUnitLengthValue(window, units, extracted[j].start, extracted[j].end);
224+ v2 = UnitLengthValue(n, units, extracted[j].start, extracted[j].end);
225
226 if (v2 > v1) {
227 v1 = v2;
228@@ -1104,13 +1110,9 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
229 extracted = extractUnits(units, i+1, i+1+unit.value.INT);
230
231 if (extracted.size() >= 3) {
232- v1 = WindowUnitLengthValue(window, units, extracted[0].start, extracted[0].end);
233- v2 = WindowUnitLengthValue(window, units, extracted[1].start, extracted[1].end);
234- v3 = WindowUnitLengthValue(window, units, extracted[2].start, extracted[2].end);
235-
236- std::cout << "V1: " << v1 << std::endl;
237- std::cout << "V2: " << v2 << std::endl;
238- std::cout << "V3: " << v3 << std::endl;
239+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
240+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
241+ v3 = UnitLengthValue(n, units, extracted[2].start, extracted[2].end);
242
243 if (v2 < v1) {
244 v2 = v1;
245@@ -1118,16 +1120,202 @@ float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size
246 if (v2 > v3) {
247 v2 = v3;
248 }
249- }
250
251- value = v2;
252-
253+ value = v2;
254+ }
255
256 i += unit.value.INT;
257 break;
258 case UnitType::ROUND:
259+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
260+
261+ ut = UnitType::NEAREST;
262+
263+ if (extracted.size() == 3) {
264+ ut = units[extracted[0].start].type;
265+
266+ v1 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
267+ v2 = UnitLengthValue(n, units, extracted[2].start, extracted[2].end);
268+ } else if (extracted.size() == 2) {
269+ ut = UnitType::NEAREST;
270+
271+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
272+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
273+ } else if (extracted.size() == 1) {
274+ ut = UnitType::NEAREST;
275+
276+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
277+ v2 = 1.0f;
278+ }
279+
280+
281+ if (ut == UnitType::NEAREST) {
282+ value = std::round(v1/v2)*v2;
283+ } else if (ut == UnitType::UP) {
284+ value = std::ceil(v1/v2)*v2;
285+ } else if (ut == UnitType::DOWN) {
286+ value = std::floor(v1/v2)*v2;
287+ } else if (ut == UnitType::TO_ZERO) {
288+ value = std::trunc(v1/v2)*v2;
289+ }
290+
291+ i += unit.value.INT;
292+ break;
293+ case UnitType::MOD:
294+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
295+
296+ if (extracted.size() == 2) {
297+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
298+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
299+
300+ value = std::fmod(v1, v2);
301+ }
302+
303+ i += unit.value.INT;
304+ break;
305+ case UnitType::PROGRESS:
306+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
307+
308+ if (extracted.size() >= 3) {
309+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
310+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
311+ v3 = UnitLengthValue(n, units, extracted[2].start, extracted[2].end);
312+
313+ // (progress - progress start) / (progress end - progress start)
314+ value = (v1-v2)/(v3-v2);
315+ }
316+
317+
318+ i += unit.value.INT;
319+ break;
320+ case UnitType::REMD:
321+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
322+
323+ if (extracted.size() == 2) {
324+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
325+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
326+
327+ value = std::fmodf(v1, v2);
328+ }
329+
330+ i += unit.value.INT;
331+ break;
332+ case UnitType::SIN:
333+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
334+ value = std::sinf(v1);
335+ i += unit.value.INT;
336+ break;
337+ case UnitType::COS:
338+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
339+ value = std::cosf(v1);
340+ i += unit.value.INT;
341+ break;
342+ case UnitType::TAN:
343+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
344+ value = std::tanf(v1);
345+ i += unit.value.INT;
346+ break;
347+ case UnitType::ASIN:
348+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
349+ value = std::asinf(v1);
350+ i += unit.value.INT;
351+ break;
352+ case UnitType::ACOS:
353+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
354+ value = std::acosf(v1);
355+ i += unit.value.INT;
356+ break;
357+ case UnitType::ATAN:
358+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
359+ value = std::atanf(v1);
360+ i += unit.value.INT;
361+ break;
362+ case UnitType::ATAN2:
363+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
364+
365+ if (extracted.size() == 2) {
366+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
367+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
368+
369+ value = std::atan2f(v1, v2);
370+ }
371+ i += unit.value.INT;
372+ break;
373+ case UnitType::POW:
374+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
375+
376+ if (extracted.size() == 2) {
377+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
378+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
379+
380+ value = std::powf(v1, v2);
381+ }
382+ i += unit.value.INT;
383+ break;
384+ case UnitType::SQRT:
385+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
386+ value = std::sqrtf(v1);
387+ i += unit.value.INT;
388+ break;
389+ case UnitType::HYPOT:
390+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
391+
392+ v2 = 0;
393+ for (size_t j = 0; j < extracted.size(); j++) {
394+ v1 = UnitLengthValue(n, units, extracted[j].start, extracted[j].end);
395+
396+ v2 += std::powf(v1, 2);
397+ }
398+
399+ value = std::sqrtf(v2);
400+
401+ i += unit.value.INT;
402+ break;
403+ case UnitType::LOG:
404+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
405+
406+ if (extracted.size() == 2) {
407+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
408+ v2 = UnitLengthValue(n, units, extracted[1].start, extracted[1].end);
409+
410+ value = std::logf(v1) / std::logf(v2);
411+ } else if (extracted.size() == 1) {
412+ v1 = UnitLengthValue(n, units, extracted[0].start, extracted[0].end);
413+
414+ value = std::logf(v1);
415+ }
416+
417+ i += unit.value.INT;
418+ break;
419+ case UnitType::EXP:
420+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
421+ value = std::expf(v1);
422+ i += unit.value.INT;
423+ break;
424+ case UnitType::ABS:
425+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
426+
427+ if (v1 < 0) v1 *= -1;
428+ value = v1;
429
430- // Need to add variable injection
431+ i += unit.value.INT;
432+ break;
433+ case UnitType::SIGN:
434+ v1 = UnitLengthValue(n, units, i+1, i+1+unit.value.INT);
435+
436+ if (v1 > 0) {
437+ value = 1;
438+ } else if (v1 < 0) {
439+ value = -1;
440+ } else {
441+ value = 0;
442+ }
443+
444+ i += unit.value.INT;
445+ break;
446+ case UnitType::ATTR
447+
448+ // Need to add variable injection and group handling
449 }
450
451 if (op != type) {
452diff --git a/src/parser.cc b/src/parser.cc
453index 2c156df..78fa6a0 100755
454--- a/src/parser.cc
455+++ b/src/parser.cc
456@@ -946,6 +946,10 @@ static const std::unordered_map<std::string_view, UnitType> kMatch{
457 {"revert", UnitType::REVERT},
458 {"revert-layer", UnitType::REVERT_LAYER},
459 {"unset", UnitType::UNSET},
460+ {"nearest", UnitType::NEAREST},
461+ {"up", UnitType::UP},
462+ {"down", UnitType::DOWN},
463+ {"to-zero", UnitType::TO_ZERO},
464 };
465
466 static const std::unordered_map<std::string_view, UnitType> kFunc{
467@@ -962,7 +966,7 @@ static const std::unordered_map<std::string_view, UnitType> kFunc{
468 {"hypot", UnitType::HYPOT},
469 {"log", UnitType::LOG},
470 {"exp", UnitType::EXP},
471- {"ABS", UnitType::ABS},
472+ {"abs", UnitType::ABS},
473 {"sin", UnitType::SIN},
474 {"cos", UnitType::COS},
475 {"tan", UnitType::TAN},
476diff --git a/tests/windowunitlength.cc b/tests/windowunitlength.cc
477new file mode 100644
478index 0000000..495aa9b
479--- /dev/null
480+++ b/tests/windowunitlength.cc
481@@ -0,0 +1,201 @@
482+#include <catch_amalgamated.hpp>
483+#include "../include/grim.h"
484+#include "../include/parser.h"
485+#include <string>
486+#include <sstream>
487+#include <vector>
488+#include <iostream>
489+
490+TEST_CASE("WindowUnitLengthValue") {
491+ std::string html = "<!DOCTYPE html>"
492+ "<html>"
493+ "<head>"
494+ "<title>This is a simple test</title>"
495+ "<link rel=\"stylesheet\" href=\'tests/style.css\'/>"
496+ "<style>h1 {background: red;}</style>"
497+ "</head>"
498+ "<body>"
499+ "<h1 id=\"h1Tag\">Header</h1>"
500+ "<p id=\"paragraph\" class=\"class1 class2\">This is some text for a simple test</p>"
501+ "<div contenteditable=\"true\">"
502+ "<p>this is a only child</p>"
503+ "</div>"
504+ "<input type=\"text\" checked required/>"
505+ "<ul>"
506+ "<li>one</li>"
507+ "<li>two</li>"
508+ "<li>three</li>"
509+ "<li>four</li>"
510+ "</ul>"
511+ "<input type=\"radio\" checked required/>"
512+ "<input type=\"text\" readonly/>"
513+ "<input type=\"text\" disabled/>"
514+ "<textarea></textarea>"
515+ "<textarea readonly></textarea>"
516+ "<textarea disabled></textarea>"
517+ "</body>"
518+ "</html>";
519+
520+
521+ std::stringstream ss(html);
522+
523+ Window window = createWindow();
524+ window.parseHTML(ss);
525+
526+ Node document = window.document;
527+
528+ document.cacheWidth(1000);
529+ document.cacheHeight(500);
530+ document.cacheEM(16);
531+
532+ SECTION("Absolute Physical Units") {
533+ // Testing conversions: CM (37.8), MM (3.78), IN (96)
534+ REQUIRE(UnitLengthValue(&document, getUnit("1cm"), 0, 1) == Catch::Approx(37.8f));
535+ REQUIRE(UnitLengthValue(&document, getUnit("10mm"), 0, 1) == Catch::Approx(37.8f));
536+ REQUIRE(UnitLengthValue(&document, getUnit("1in"), 0, 1) == Catch::Approx(96.0f));
537+ REQUIRE(UnitLengthValue(&document, getUnit("72pt"), 0, 1) == Catch::Approx(93.6f)); // 72 * 1.3
538+ }
539+
540+ SECTION("Viewport Units") {
541+ // VW = 1% of Width (1000), VH = 1% of Height (500)
542+ REQUIRE(UnitLengthValue(&document, getUnit("10vw"), 0, 1) == Catch::Approx(100.0f));
543+ REQUIRE(UnitLengthValue(&document, getUnit("50vh"), 0, 1) == Catch::Approx(250.0f));
544+
545+ // VMIN (smaller of 1000, 500) = 500. 10% of 500 = 50.
546+ // Note: Your VMIN code does unit.value * document (not /100), check if logic matches CSS spec
547+ REQUIRE(UnitLengthValue(&document, getUnit("0.1vmin"), 0, 1) == Catch::Approx(50.0f));
548+ }
549+
550+ SECTION("Basic Arithmetic (Sequential)") {
551+ // Tests the 'op' switching logic (ADD, SUB, MULT, DIV)
552+ std::vector<Unit> units = getUnit("10px + 20px * 2");
553+ // Note: Your function processes sequentially: (10 + 20) * 2 = 60
554+ // Standard CSS calc() has precedence, but your loop is linear.
555+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(60.0f));
556+ }
557+
558+ SECTION("Recursive Calc Blocks") {
559+ // Tests UnitType::CALC which uses unit.value.INT for jumping
560+ std::vector<Unit> units = getUnit("calc(10px + 5px)");
561+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(15.0f));
562+ }
563+
564+ SECTION("Min / Max Functions") {
565+ // min(100px, 200px, 50px)
566+ std::vector<Unit> unitsMin = getUnit("min(100px, 200px, 50px)");
567+ REQUIRE(UnitLengthValue(&document, unitsMin, 0, unitsMin.size()) == Catch::Approx(50.0f));
568+
569+ // max(100px, 200px, 50px)
570+ std::vector<Unit> unitsMax = getUnit("max(100px, 200px, 50px)");
571+ REQUIRE(UnitLengthValue(&document, unitsMax, 0, unitsMax.size()) == Catch::Approx(200.0f));
572+ }
573+
574+ SECTION("Clamp Function") {
575+ // clamp(MIN, VAL, MAX) -> clamp(100, 50, 200) should return 100
576+ std::vector<Unit> unitsClamp = getUnit("clamp(100px, 50px, 200px)");
577+ REQUIRE(UnitLengthValue(&document, unitsClamp, 0, unitsClamp.size()) == Catch::Approx(100.0f));
578+
579+ // clamp(100, 150, 200) should return 150
580+ std::vector<Unit> unitsMid = getUnit("clamp(100px, 150px, 200px)");
581+ REQUIRE(UnitLengthValue(&document, unitsMid, 0, unitsMid.size()) == Catch::Approx(150.0f));
582+ }
583+
584+ SECTION("Rounding Function") {
585+ // round(13px, 5px) -> Nearest 5 is 15px
586+ std::vector<Unit> unitsRound = getUnit("round(13px, 5px)");
587+ REQUIRE(UnitLengthValue(&document, unitsRound, 0, unitsRound.size()) == Catch::Approx(15.0f));
588+
589+ // round(down, 13px, 5px) -> 10px
590+ // Note: This requires your getUnit/Parser to support the Enum strategy correctly
591+ std::vector<Unit> unitsDown = getUnit("round(down, 13px, 5px)");
592+ REQUIRE(UnitLengthValue(&document, unitsDown, 0, unitsDown.size()) == Catch::Approx(10.0f));
593+ }
594+
595+ SECTION("Mod Function") {
596+ std::vector<Unit> units = getUnit("mod(18px, 4px)");
597+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(2.0f));
598+
599+ units = getUnit("mod(135deg, 90deg)");
600+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(45.0f));
601+ }
602+
603+ SECTION("Progress Function") {
604+ std::vector<Unit> units = getUnit("progress(300, 0, 1000)");
605+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(0.3f));
606+ }
607+
608+ SECTION("Rem Function") {
609+ std::vector<Unit> units = getUnit("rem(18px, 5px)");
610+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(3.0f));
611+ }
612+
613+ SECTION("Sin Function") {
614+ std::vector<Unit> units = getUnit("sin(45deg)");
615+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(0.8509035245341184f));
616+ }
617+
618+ SECTION("Cos Function") {
619+ std::vector<Unit> units = getUnit("cos(45deg)");
620+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(0.5253219888177297f));
621+ }
622+
623+ SECTION("Tan Function") {
624+ std::vector<Unit> units = getUnit("tan(45deg)");
625+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(1.6197751905438615f));
626+ }
627+ SECTION("ASin Function") {
628+ std::vector<Unit> units = getUnit("asin(-0.2deg)");
629+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(-0.2013579207903308f));
630+ }
631+
632+ SECTION("ACos Function") {
633+ std::vector<Unit> units = getUnit("acos(-0.2deg)");
634+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(1.7721542475852274f));
635+ }
636+
637+ SECTION("ATan Function") {
638+ std::vector<Unit> units = getUnit("atan(-0.2deg)");
639+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(-0.19739555984988078f));
640+ }
641+
642+ SECTION("ATan2 Function") {
643+ std::vector<Unit> units = getUnit("atan2(3, 2)");
644+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(0.982793723247329f));
645+ }
646+
647+ SECTION("Pow Function") {
648+ std::vector<Unit> units = getUnit("pow(3, 2)");
649+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(9.0f));
650+ }
651+
652+ SECTION("Sqrt Function") {
653+ std::vector<Unit> units = getUnit("sqrt(3)");
654+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(1.7320508075688772f));
655+ }
656+
657+ SECTION("Hypot Function") {
658+ std::vector<Unit> units = getUnit("hypot(1em, 10px)");
659+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(18.867962264113206f));
660+
661+ units = getUnit("hypot(3px, 4px, 5px)");
662+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(7.0710678118654755f));
663+ }
664+
665+ SECTION("Exp Function") {
666+ std::vector<Unit> units = getUnit("exp(-1)");
667+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(0.367879441171442f));
668+
669+ units = getUnit("calc(100px * exp(-1))");
670+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(36.787944794f));
671+ }
672+
673+ SECTION("Abs Function") {
674+ std::vector<Unit> units = getUnit("abs(-3)");
675+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(3.0f));
676+ }
677+
678+ SECTION("Sign Function") {
679+ std::vector<Unit> units = getUnit("sign(-3)");
680+ REQUIRE(UnitLengthValue(&document, units, 0, units.size()) == Catch::Approx(-1.0f));
681+ }
682+};