HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
axis_aligned_rectangle.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
9#pragma once
10
11#include "alignment.hpp"
12#include "extent.hpp"
13#include "point.hpp"
14#include "../SIMD/module.hpp"
15#include "../utility/module.hpp"
16#include "../concurrency/module.hpp"
17#include <concepts>
18#include <mutex>
19
20namespace hi { inline namespace v1 {
21namespace geo {
25template<typename T>
27public:
28 using value_type = T;
29 using array_type = simd<value_type, 4>;
30
31 constexpr axis_aligned_rectangle() noexcept : v() {}
32 constexpr axis_aligned_rectangle(axis_aligned_rectangle const& rhs) noexcept = default;
33 constexpr axis_aligned_rectangle& operator=(axis_aligned_rectangle const& rhs) noexcept = default;
34 constexpr axis_aligned_rectangle(axis_aligned_rectangle&& rhs) noexcept = default;
35 constexpr axis_aligned_rectangle& operator=(axis_aligned_rectangle&& rhs) noexcept = default;
36
39 [[nodiscard]] constexpr static axis_aligned_rectangle large() noexcept
40 {
41 return {
42 point<value_type, 2>{-large_number_v<value_type>, -large_number_v<value_type>},
43 point<value_type, 2>{large_number_v<value_type>, large_number_v<value_type>}};
44 }
45
46 constexpr explicit axis_aligned_rectangle(array_type const& other) noexcept : v(other)
47 {
49 }
50
58 constexpr axis_aligned_rectangle(value_type x, value_type y, value_type width, value_type height) noexcept :
59 v{x, y, x + width, y + height}
60 {
62 }
63
68 constexpr explicit axis_aligned_rectangle(extent<value_type, 2> const& extent) noexcept :
69 v(static_cast<array_type>(extent)._00xy())
70 {
72 }
73
78 constexpr axis_aligned_rectangle(point<value_type, 2> const& p0, point<value_type, 2> const& p3) noexcept :
79 v(static_cast<array_type>(p0).xy00() + static_cast<array_type>(p3)._00xy())
80 {
81 hi_axiom(p0.holds_invariant());
82 hi_axiom(p3.holds_invariant());
84 }
85
93 constexpr axis_aligned_rectangle(point<value_type, 2> const& p0, extent<value_type, 2> const& extent) noexcept :
94 v(static_cast<array_type>(p0).xyxy() + static_cast<array_type>(extent)._00xy())
95 {
97 }
98
99 constexpr explicit operator array_type() const noexcept
100 {
101 return v;
102 }
103
107 [[nodiscard]] constexpr bool holds_invariant() const noexcept
108 {
109 return (v <= v.zwzw()).mask() == 0b1111;
110 }
111
114 [[nodiscard]] constexpr bool empty() const noexcept
115 {
116 return (v == v.zwxy()).mask() == 0b1111;
117 }
118
121 [[nodiscard]] constexpr explicit operator bool() const noexcept
122 {
123 return not empty();
124 }
125
132 {
133 return *this = *this | rhs;
134 }
135
141 constexpr axis_aligned_rectangle& operator|=(point<value_type, 2> const& rhs) noexcept
142 {
143 return *this = *this | rhs;
144 }
145
146 [[nodiscard]] constexpr point<value_type, 2> operator[](std::size_t i) const noexcept
147 {
148 switch (i) {
149 case 0:
150 return point<value_type, 2>{v.xy01()};
151 case 1:
152 return point<value_type, 2>{v.zy01()};
153 case 2:
154 return point<value_type, 2>{v.xw01()};
155 case 3:
156 return point<value_type, 2>{v.zw01()};
157 default:
159 }
160 }
161
162 template<int I>
163 [[nodiscard]] constexpr friend point<value_type, 2> get(axis_aligned_rectangle const& rhs) noexcept
164 {
165 if constexpr (I == 0) {
166 return point<value_type, 2>{rhs.v.xy01()};
167 } else if constexpr (I == 1) {
168 return point<value_type, 2>{rhs.v.zy01()};
169 } else if constexpr (I == 2) {
170 return point<value_type, 2>{rhs.v.xw01()};
171 } else if constexpr (I == 3) {
172 return point<value_type, 2>{rhs.v.zw01()};
173 } else {
175 }
176 }
177
182 [[nodiscard]] constexpr extent<value_type, 2> size() const noexcept
183 {
184 return extent<value_type, 2>{v.zwzw() - v};
185 }
186
187 [[nodiscard]] constexpr value_type x() const noexcept
188 {
189 return v.x();
190 }
191
192 [[nodiscard]] constexpr value_type y() const noexcept
193 {
194 return v.y();
195 }
196
197 [[nodiscard]] constexpr value_type width() const noexcept
198 {
199 return (v.zwzw() - v).x();
200 }
201
202 [[nodiscard]] constexpr value_type height() const noexcept
203 {
204 return (v.zwzw() - v).y();
205 }
206
207 [[nodiscard]] constexpr value_type bottom() const noexcept
208 {
209 return v.y();
210 }
211
212 [[nodiscard]] constexpr value_type top() const noexcept
213 {
214 return v.w();
215 }
216
217 [[nodiscard]] constexpr value_type left() const noexcept
218 {
219 return v.x();
220 }
221
222 [[nodiscard]] constexpr value_type right() const noexcept
223 {
224 return v.z();
225 }
226
229 [[nodiscard]] constexpr value_type middle() const noexcept
230 {
231 return (bottom() + top()) / value_type{2};
232 }
233
236 [[nodiscard]] constexpr value_type center() const noexcept
237 {
238 return (left() + right()) / value_type{2};
239 }
240
243 [[nodiscard]] constexpr friend point<value_type, 2> midpoint(axis_aligned_rectangle const& rhs) noexcept
244 {
245 return midpoint(get<0>(rhs), get<3>(rhs));
246 }
247
248 constexpr axis_aligned_rectangle& set_width(value_type newWidth) noexcept
249 {
250 v = v.xyxw() + array_type{value_type{0}, value_type{0}, newWidth, value_type{0}};
251 return *this;
252 }
253
254 constexpr axis_aligned_rectangle& set_height(value_type newHeight) noexcept
255 {
256 v = v.xyzy() + array_type{value_type{0}, value_type{0}, value_type{0}, newHeight};
257 return *this;
258 }
259
264 [[nodiscard]] constexpr bool contains(point<value_type, 2> const& rhs) const noexcept
265 {
266 // No need to check with empty due to half open range check.
267 return (static_cast<array_type>(rhs).xyxy() >= v).mask() == 0b0011;
268 }
269
275 [[nodiscard]] constexpr bool contains(point<value_type, 3> const& rhs) const noexcept
276 {
277 return contains(point<value_type, 2>{rhs});
278 }
279
286 [[nodiscard]] friend constexpr axis_aligned_rectangle
287 align(axis_aligned_rectangle haystack, extent<value_type, 2> needle, alignment alignment) noexcept
288 {
289 auto x = value_type{0};
290 if (alignment == horizontal_alignment::left) {
291 x = haystack.left();
292
293 } else if (alignment == horizontal_alignment::right) {
294 x = haystack.right() - needle.width();
295
296 } else if (alignment == horizontal_alignment::center) {
297 x = haystack.center() - needle.width() / value_type{2};
298
299 } else {
301 }
302
303 auto y = value_type{0};
304 if (alignment == vertical_alignment::bottom) {
305 y = haystack.bottom();
306
307 } else if (alignment == vertical_alignment::top) {
308 y = haystack.top() - needle.height();
309
310 } else if (alignment == vertical_alignment::middle) {
311 y = haystack.middle() - needle.height() / value_type{2};
312
313 } else {
315 }
316
317 return {point<value_type, 2>{x, y}, needle};
318 }
319
326 [[nodiscard]] friend constexpr axis_aligned_rectangle
327 align(axis_aligned_rectangle haystack, axis_aligned_rectangle needle, alignment alignment) noexcept
328 {
329 return align(haystack, needle.size(), alignment);
330 }
331
334 [[nodiscard]] static constexpr axis_aligned_rectangle
336 {
337 return align(outside, inside, alignment);
338 }
339
340 [[nodiscard]] friend constexpr bool operator==(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
341 {
342 return equal(lhs.v, rhs.v);
343 }
344
345 [[nodiscard]] friend constexpr bool overlaps(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
346 {
347 if (lhs.empty() or rhs.empty()) {
348 return false;
349 }
350
351 hilet rhs_swap = rhs.v.zwxy();
352
353 // lhs.p0.x > rhs.p3.x | lhs.p0.y > rhs.p3.y
354 if (((lhs.v > rhs_swap).mask() & 0b0011) != 0) {
355 return false;
356 }
357
358 // lhs.p3.x < rhs.p0.x | lhs.p3.y < rhs.p0.y
359 if (((lhs.v < rhs_swap).mask() & 0b1100) != 0) {
360 return false;
361 }
362
363 return true;
364 }
365
366 [[nodiscard]] friend constexpr axis_aligned_rectangle
367 operator|(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
368 {
369 if (!lhs) {
370 return rhs;
371 } else if (!rhs) {
372 return lhs;
373 } else {
374 return axis_aligned_rectangle{min(get<0>(lhs), get<0>(rhs)), max(get<3>(lhs), get<3>(rhs))};
375 }
376 }
377
378 [[nodiscard]] friend constexpr axis_aligned_rectangle
379 operator|(axis_aligned_rectangle const& lhs, point<value_type, 2> const& rhs) noexcept
380 {
381 if (!lhs) {
382 return axis_aligned_rectangle{rhs, rhs};
383 } else {
384 return axis_aligned_rectangle{min(get<0>(lhs), rhs), max(get<3>(lhs), rhs)};
385 }
386 }
387
393 [[nodiscard]] friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const& lhs, value_type rhs) noexcept
394 {
395 hilet new_extent = lhs.size() * rhs;
396 hilet diff = vector<value_type, 2>{new_extent} - vector<value_type, 2>{lhs.size()};
397 hilet offset = diff * 0.5f;
398
399 hilet p0 = get<0>(lhs) - offset;
400 hilet p3 = max(get<3>(lhs) + offset, p0);
401 return axis_aligned_rectangle{p0, p3};
402 }
403
410 [[nodiscard]] friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const& lhs, value_type rhs) noexcept
411 {
412 return axis_aligned_rectangle{lhs.v + neg<0b0011>(array_type::broadcast(rhs))};
413 }
414
421 [[nodiscard]] friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const& lhs, value_type rhs) noexcept
422 {
423 return lhs + -rhs;
424 }
425
426 [[nodiscard]] friend constexpr axis_aligned_rectangle round(axis_aligned_rectangle const& rhs) noexcept
427 requires std::is_same_v<value_type, float>
428 {
429 hilet p0 = round(get<0>(rhs));
430 hilet size = round(rhs.size());
431 return axis_aligned_rectangle{p0, size};
432 }
433
436 [[nodiscard]] friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const& rhs) noexcept
437 requires std::is_same_v<value_type, float>
438 {
439 hilet p0 = floor(get<0>(rhs));
440 hilet p3 = ceil(get<3>(rhs));
441 return axis_aligned_rectangle{p0, p3};
442 }
443
446 [[nodiscard]] friend constexpr axis_aligned_rectangle
447 ceil(axis_aligned_rectangle const& lhs, extent<value_type, 2> const& rhs) noexcept
448 {
449 hilet p0 = floor(get<0>(lhs), rhs);
450 hilet p3 = ceil(get<3>(lhs), rhs);
451 return axis_aligned_rectangle{p0, p3};
452 }
453
456 [[nodiscard]] friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const& rhs) noexcept
457 requires std::is_same_v<value_type, float>
458 {
459 hilet p0 = ceil(get<0>(rhs));
460 hilet p3 = floor(get<3>(rhs));
461 return axis_aligned_rectangle{p0, p3};
462 }
463
464 [[nodiscard]] friend constexpr axis_aligned_rectangle bounding_rectangle(axis_aligned_rectangle const& rhs) noexcept
465 {
466 return rhs;
467 }
468
472 [[nodiscard]] friend constexpr axis_aligned_rectangle
474 {
475 hilet p0 = max(get<0>(lhs), get<0>(rhs));
476 hilet p3 = min(get<3>(lhs), get<3>(rhs));
477 if (p0.x() < p3.x() && p0.y() < p3.y()) {
478 return {p0, p3};
479 } else {
480 return {};
481 }
482 }
483
484 [[nodiscard]] constexpr friend value_type
485 distance(axis_aligned_rectangle const& lhs, point<value_type, 2> const& rhs) noexcept
486 {
487 hilet lhs_ = static_cast<array_type>(lhs);
488 hilet rhs_ = static_cast<array_type>(rhs);
489 // Only (x,y) of subsequent calculations are valid, (z,w) have garbage values.
490 hilet closest_point = max(min(rhs_, lhs_.zwzw()), lhs_);
491 hilet v_closest_point = closest_point - rhs_;
492 return hypot<0b0011>(v_closest_point);
493 }
494
495private:
501 array_type v;
502};
503
504} // namespace geo
505
506using aarectangle = geo::axis_aligned_rectangle<float>;
507using aarectanglei = geo::axis_aligned_rectangle<int>;
508
516[[nodiscard]] aarectangle fit(aarectangle const& bounds, aarectangle const& rectangle) noexcept;
517
525[[nodiscard]] aarectanglei fit(aarectanglei const& bounds, aarectanglei const& rectangle) noexcept;
526
527template<>
528[[nodiscard]] constexpr aarectanglei narrow_cast(aarectangle const& rhs) noexcept
529{
530 return {narrow_cast<int>(rhs.x()), narrow_cast<int>(rhs.y()), narrow_cast<int>(rhs.width()), narrow_cast<int>(rhs.height())};
531}
532
533template<>
534[[nodiscard]] constexpr aarectangle narrow_cast(aarectanglei const& rhs) noexcept
535{
536 return {
537 narrow_cast<float>(rhs.x()),
538 narrow_cast<float>(rhs.y()),
539 narrow_cast<float>(rhs.width()),
540 narrow_cast<float>(rhs.height())};
541}
542
543}} // namespace hi::v1
544
545template<typename T>
546class std::atomic<hi::geo::axis_aligned_rectangle<T>> {
547public:
548 using value_type = hi::geo::axis_aligned_rectangle<T>;
549 static constexpr bool is_always_lock_free = false;
550
551 constexpr atomic() noexcept = default;
552 atomic(atomic const&) = delete;
553 atomic(atomic&&) = delete;
554 atomic& operator=(atomic const&) = delete;
555 atomic& operator=(atomic&&) = delete;
556
557 constexpr atomic(value_type const& rhs) noexcept : _value(rhs) {}
558 atomic& operator=(value_type const& rhs) noexcept
559 {
560 store(rhs);
561 return *this;
562 }
563
564 operator value_type() const noexcept
565 {
566 return load();
567 }
568
569 [[nodiscard]] bool is_lock_free() const noexcept
570 {
571 return is_always_lock_free;
572 }
573
574 void store(value_type desired, std::memory_order = std::memory_order_seq_cst) noexcept
575 {
576 hilet lock = std::scoped_lock(_mutex);
577 _value = desired;
578 }
579
580 value_type load(std::memory_order = std::memory_order_seq_cst) const noexcept
581 {
582 hilet lock = std::scoped_lock(_mutex);
583 return _value;
584 }
585
586 value_type exchange(value_type desired, std::memory_order = std::memory_order_seq_cst) noexcept
587 {
588 hilet lock = std::scoped_lock(_mutex);
589 return std::exchange(_value, desired);
590 }
591
592 bool compare_exchange_weak(value_type& expected, value_type desired, std::memory_order, std::memory_order) noexcept
593 {
594 hilet lock = std::scoped_lock(_mutex);
595 if (_value == expected) {
596 _value = desired;
597 return true;
598 } else {
599 expected = _value;
600 return false;
601 }
602 }
603
605 value_type& expected,
606 value_type desired,
607 std::memory_order success,
608 std::memory_order failure) noexcept
609 {
610 return compare_exchange_weak(expected, desired, success, failure);
611 }
612
613 bool
614 compare_exchange_weak(value_type& expected, value_type desired, std::memory_order order = std::memory_order_seq_cst) noexcept
615 {
616 return compare_exchange_weak(expected, desired, order, order);
617 }
618
620 value_type& expected,
621 value_type desired,
622 std::memory_order order = std::memory_order_seq_cst) noexcept
623 {
624 return compare_exchange_strong(expected, desired, order, order);
625 }
626
627 value_type fetch_or(value_type arg, std::memory_order = std::memory_order_seq_cst) noexcept
628 {
629 hilet lock = std::scoped_lock(_mutex);
630 auto tmp = _value;
631 _value = tmp | arg;
632 return tmp;
633 }
634
635 value_type operator|=(value_type arg) noexcept
636 {
637 hilet lock = std::scoped_lock(_mutex);
638 return _value |= arg;
639 }
640
641private:
642 value_type _value;
643 mutable hi::unfair_mutex _mutex;
644};
645
646template<typename CharT>
647struct std::formatter<hi::geo::axis_aligned_rectangle<float>, CharT> {
648 auto parse(auto& pc)
649 {
650 return pc.end();
651 }
652
653 auto format(hi::geo::axis_aligned_rectangle<float> const& t, auto& fc)
654 {
655 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
656 }
657};
658
659template<typename CharT>
660struct std::formatter<hi::geo::axis_aligned_rectangle<int>, CharT> {
661 auto parse(auto& pc)
662 {
663 return pc.end();
664 }
665
666 auto format(hi::geo::axis_aligned_rectangle<int> const& t, auto& fc)
667 {
668 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
669 }
670};
Defined the geo::extent, extent2 and extent3 types.
types and utilities for alignment.
#define hi_static_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:308
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:264
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
@ other
The gui_event does not have associated data.
@ rectangle
The gui_event has rectangle data.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
aarectangle fit(aarectangle const &bounds, aarectangle const &rectangle) noexcept
Make a rectangle fit inside bounds.
@ inside
The border is drawn inside the edge of a quad.
@ outside
The border is drawn outside the edge of a quad.
An unfair mutex This is a fast implementation of a mutex which does not fairly arbitrate between mult...
Definition unfair_mutex.hpp:36
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:26
static constexpr axis_aligned_rectangle large() noexcept
Create a large axis aligned rectangle.
Definition axis_aligned_rectangle.hpp:39
friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const &lhs, value_type rhs) noexcept
Shrink the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:421
static constexpr axis_aligned_rectangle _align(axis_aligned_rectangle outside, axis_aligned_rectangle inside, alignment alignment) noexcept
Need to call the hidden friend function from within another class.
Definition axis_aligned_rectangle.hpp:335
friend constexpr axis_aligned_rectangle intersect(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
Return the overlapping part of two rectangles.
Definition axis_aligned_rectangle.hpp:473
constexpr axis_aligned_rectangle(point< value_type, 2 > const &p0, extent< value_type, 2 > const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:93
friend constexpr axis_aligned_rectangle align(axis_aligned_rectangle haystack, axis_aligned_rectangle needle, alignment alignment) noexcept
Align a rectangle within another rectangle.
Definition axis_aligned_rectangle.hpp:327
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by expanding to pixel edge.
Definition axis_aligned_rectangle.hpp:436
constexpr bool contains(point< value_type, 3 > const &rhs) const noexcept
Check if a 3D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:275
constexpr extent< value_type, 2 > size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:182
constexpr bool empty() const noexcept
Check if the rectangle has no area.
Definition axis_aligned_rectangle.hpp:114
constexpr bool holds_invariant() const noexcept
Make sure p0 is left/bottom from p3.
Definition axis_aligned_rectangle.hpp:107
constexpr bool contains(point< value_type, 2 > const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:264
friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by shrinking to pixel edge.
Definition axis_aligned_rectangle.hpp:456
constexpr friend point< value_type, 2 > midpoint(axis_aligned_rectangle const &rhs) noexcept
Get the center of the rectangle.
Definition axis_aligned_rectangle.hpp:243
constexpr value_type center() const noexcept
The center on the x-axis between left and right.
Definition axis_aligned_rectangle.hpp:236
constexpr axis_aligned_rectangle & operator|=(axis_aligned_rectangle const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:131
constexpr axis_aligned_rectangle(value_type x, value_type y, value_type width, value_type height) noexcept
Create a box from the position and size.
Definition axis_aligned_rectangle.hpp:58
friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const &lhs, value_type rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:393
friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const &lhs, value_type rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:410
constexpr value_type middle() const noexcept
The middle on the y-axis between bottom and top.
Definition axis_aligned_rectangle.hpp:229
constexpr axis_aligned_rectangle(point< value_type, 2 > const &p0, point< value_type, 2 > const &p3) noexcept
Create a rectangle from the left-bottom and right-top points.
Definition axis_aligned_rectangle.hpp:78
friend constexpr axis_aligned_rectangle align(axis_aligned_rectangle haystack, extent< value_type, 2 > needle, alignment alignment) noexcept
Align a rectangle within another rectangle.
Definition axis_aligned_rectangle.hpp:287
constexpr axis_aligned_rectangle & operator|=(point< value_type, 2 > const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:141
constexpr axis_aligned_rectangle(extent< value_type, 2 > const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:68
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &lhs, extent< value_type, 2 > const &rhs) noexcept
Round rectangle by expanding to a certain granularity.
Definition axis_aligned_rectangle.hpp:447
A high-level geometric extent.
Definition extent.hpp:30
T compare_exchange_weak(T... args)
T exchange(T... args)
T fetch_or(T... args)
T is_lock_free(T... args)
T load(T... args)
T lock(T... args)
T max(T... args)
T min(T... args)
T operator=(T... args)
T operator|=(T... args)
T store(T... args)