til::rectangle (#4912)
## Summary of the Pull Request Introduces convenience type `til::rectangle` which automatically implements our best practices for rectangle-related types and provides automatic conversions in/out of the relevant types. ## PR Checklist * [x] In support of Differential Rendering #778 * [X] I work here. * [x] Tests added/passed * [x] I'm a core contributor. ## Detailed Description of the Pull Request / Additional comments - Automatically converts in from anything with a Left/Top/Right/Bottom or left/top/right/bottom (Win32 `RECT`) - Automatically converts Console type `SMALL_RECT` and shifts it from **inclusive** to **exclusive** on instantiation - Automatically converts out to `SMALL_RECT` (converting back to **inclusive**), `RECT`, or `D2D1_RECT_F`. - Constructs from bare integers written into source file - Constructs from a single `til::point` as a 1x1 size rectangle with top-left corner (origin) at that point - Constructs from a single `til::size` as a WxH size rectangle with top-left corner (origin) at 0,0 - Constructs from a `til::point` and a `til::size` representing the top-left corner and the width by height. - Constructs from a `til::point` and another `til::point` representing the top-left corner and the **exclusive** bottom-right corner. - Default constructs to empty - Uses Chromium numerics for all basic math operations (+, -, *, /) - Provides equality tests - Provides `operator bool` to know when it's valid (has an area > 0) and `empty()` to know the contrary - Accessors for left/top/right/bottom - Type converting accessors (that use safe conversions and throw) for left/top/right/bottom - Convenience methods for finding width/height (with Chromium numerics operations) and type-converting templates (with Chromium numerics conversions). - Accessors for origin (top-left point) and the size/dimensions (as a `til::size`). - Intersect operation on `operator &` to find where two `til::rectangle`s overlap, returned as a `til::rectangle`. - Union operation on `operator |` to find the total area covered by two `til::rectangles`, returned as a `til::rectangle`. - Subtract operation on `operator -` to find the area remaining after one `til::rectangle` is removed from another, returned as a `til::some<til::rectangle, 4>`. - TAEF/WEX Output and Comparators so they will print very nicely with `VERIFY` and `Log` macros in our testing suite. - Additional comparators, TAEF/WEX output, and tests written on `til::some` to support the Subtract operation. - A natvis ## Validation Steps Performed - See automated tests of functionality.
This commit is contained in:
parent
57a80aa531
commit
f5ab042939
|
@ -8,6 +8,7 @@
|
|||
#include "til/some.h"
|
||||
#include "til/size.h"
|
||||
#include "til/point.h"
|
||||
#include "til/rectangle.h"
|
||||
#include "til/u8u16convert.h"
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
|
|
|
@ -39,14 +39,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
{
|
||||
}
|
||||
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertable to an integer value
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
|
||||
point(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to size from anything that has a x and a y field that appear convertable to an integer value
|
||||
// This template will convert to size from anything that has a x and a y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().x)> && std::is_integral_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
|
||||
point(static_cast<ptrdiff_t>(other.x), static_cast<ptrdiff_t>(other.y))
|
||||
|
|
484
src/inc/til/rectangle.h
Normal file
484
src/inc/til/rectangle.h
Normal file
|
@ -0,0 +1,484 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "point.h"
|
||||
#include "size.h"
|
||||
#include "some.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class RectangleTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
class rectangle
|
||||
{
|
||||
public:
|
||||
constexpr rectangle() noexcept :
|
||||
rectangle(til::point{ 0, 0 }, til::point{ 0, 0 })
|
||||
{
|
||||
}
|
||||
|
||||
// On 64-bit processors, int and ptrdiff_t are different fundamental types.
|
||||
// On 32-bit processors, they're the same which makes this a double-definition
|
||||
// with the `ptrdiff_t` one below.
|
||||
#if defined(_M_AMD64) || defined(_M_ARM64)
|
||||
constexpr rectangle(int left, int top, int right, int bottom) noexcept :
|
||||
rectangle(til::point{ left, top }, til::point{ right, bottom })
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
rectangle(size_t left, size_t top, size_t right, size_t bottom) :
|
||||
rectangle(til::point{ left, top }, til::point{ right, bottom })
|
||||
{
|
||||
}
|
||||
|
||||
constexpr rectangle(ptrdiff_t left, ptrdiff_t top, ptrdiff_t right, ptrdiff_t bottom) noexcept :
|
||||
rectangle(til::point{ left, top }, til::point{ right, bottom })
|
||||
{
|
||||
}
|
||||
|
||||
// Creates a 1x1 rectangle with the given top-left corner.
|
||||
rectangle(til::point topLeft) :
|
||||
_topLeft(topLeft)
|
||||
{
|
||||
_bottomRight = _topLeft + til::point{ 1, 1 };
|
||||
}
|
||||
|
||||
// Creates a rectangle where you specify the top-left corner (included)
|
||||
// and the bottom-right corner (excluded)
|
||||
constexpr rectangle(til::point topLeft, til::point bottomRight) noexcept :
|
||||
_topLeft(topLeft),
|
||||
_bottomRight(bottomRight)
|
||||
{
|
||||
}
|
||||
|
||||
// Creates a rectangle with the given size where the top-left corner
|
||||
// is set to 0,0.
|
||||
constexpr rectangle(til::size size) noexcept :
|
||||
_topLeft(til::point{ 0, 0 }),
|
||||
_bottomRight(til::point{ size.width(), size.height() })
|
||||
{
|
||||
}
|
||||
|
||||
// Creates a rectangle at the given top-left corner point X,Y that extends
|
||||
// down (+Y direction) and right (+X direction) for the given size.
|
||||
rectangle(til::point topLeft, til::size size) :
|
||||
_topLeft(topLeft),
|
||||
_bottomRight(topLeft + til::point{ size.width(), size.height() })
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef _WINCONTYPES_
|
||||
// This extra specialization exists for SMALL_RECT because it's the only rectangle in the world that we know of
|
||||
// with the bottom and right fields INCLUSIVE to the rectangle itself.
|
||||
// It will perform math on the way in to ensure that it is represented as EXCLUSIVE.
|
||||
rectangle(SMALL_RECT sr)
|
||||
{
|
||||
_topLeft = til::point{ static_cast<ptrdiff_t>(sr.Left), static_cast<ptrdiff_t>(sr.Top) };
|
||||
|
||||
_bottomRight = til::point{ static_cast<ptrdiff_t>(sr.Right), static_cast<ptrdiff_t>(sr.Bottom) } + til::point{ 1, 1 };
|
||||
}
|
||||
#endif
|
||||
|
||||
// This template will convert to rectangle from anything that has a Left, Top, Right, and Bottom field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr rectangle(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().Top)> && std::is_integral_v<decltype(std::declval<TOther>().Left)> && std::is_integral_v<decltype(std::declval<TOther>().Bottom)> && std::is_integral_v<decltype(std::declval<TOther>().Right)>, int> /*sentinel*/ = 0) :
|
||||
rectangle(til::point{ static_cast<ptrdiff_t>(other.Left), static_cast<ptrdiff_t>(other.Top) }, til::point{ static_cast<ptrdiff_t>(other.Right), static_cast<ptrdiff_t>(other.Bottom) })
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to rectangle from anything that has a left, top, right, and bottom field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr rectangle(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().top)> && std::is_integral_v<decltype(std::declval<TOther>().left)> && std::is_integral_v<decltype(std::declval<TOther>().bottom)> && std::is_integral_v<decltype(std::declval<TOther>().right)>, int> /*sentinel*/ = 0) :
|
||||
rectangle(til::point{ static_cast<ptrdiff_t>(other.left), static_cast<ptrdiff_t>(other.top) }, til::point{ static_cast<ptrdiff_t>(other.right), static_cast<ptrdiff_t>(other.bottom) })
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool operator==(const rectangle& other) const noexcept
|
||||
{
|
||||
return _topLeft == other._topLeft &&
|
||||
_bottomRight == other._bottomRight;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const rectangle& other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept
|
||||
{
|
||||
return _topLeft.x() < _bottomRight.x() &&
|
||||
_topLeft.y() < _bottomRight.y();
|
||||
}
|
||||
|
||||
// OR = union
|
||||
constexpr rectangle operator|(const rectangle& other) const noexcept
|
||||
{
|
||||
const auto thisEmpty = empty();
|
||||
const auto otherEmpty = other.empty();
|
||||
|
||||
// If both are empty, return empty rect.
|
||||
if (thisEmpty && otherEmpty)
|
||||
{
|
||||
return rectangle{};
|
||||
}
|
||||
|
||||
// If this is empty but not the other one, then give the other.
|
||||
if (thisEmpty)
|
||||
{
|
||||
return other;
|
||||
}
|
||||
|
||||
// If the other is empty but not this, give this.
|
||||
if (otherEmpty)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
// If we get here, they're both not empty. Do math.
|
||||
const auto l = std::min(left(), other.left());
|
||||
const auto t = std::min(top(), other.top());
|
||||
const auto r = std::max(right(), other.right());
|
||||
const auto b = std::max(bottom(), other.bottom());
|
||||
return rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
// AND = intersect
|
||||
constexpr rectangle operator&(const rectangle& other) const noexcept
|
||||
{
|
||||
const auto l = std::max(left(), other.left());
|
||||
const auto r = std::min(right(), other.right());
|
||||
|
||||
// If the width dimension would be empty, give back empty rectangle.
|
||||
if (l >= r)
|
||||
{
|
||||
return rectangle{};
|
||||
}
|
||||
|
||||
const auto t = std::max(top(), other.top());
|
||||
const auto b = std::min(bottom(), other.bottom());
|
||||
|
||||
// If the height dimension would be empty, give back empty rectangle.
|
||||
if (t >= b)
|
||||
{
|
||||
return rectangle{};
|
||||
}
|
||||
|
||||
return rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
// - = subtract
|
||||
some<rectangle, 4> operator-(const rectangle& other) const
|
||||
{
|
||||
some<rectangle, 4> result;
|
||||
|
||||
// We could have up to four rectangles describing the area resulting when you take removeMe out of main.
|
||||
// Find the intersection of the two so we know which bits of removeMe are actually applicable
|
||||
// to the original rectangle for subtraction purposes.
|
||||
const auto intersect = *this & other;
|
||||
|
||||
// If there's no intersect, there's nothing to remove.
|
||||
if (intersect.empty())
|
||||
{
|
||||
// Just put the original rectangle into the results and return early.
|
||||
result.push_back(*this);
|
||||
}
|
||||
// If the original rectangle matches the intersect, there is nothing to return.
|
||||
else if (*this != intersect)
|
||||
{
|
||||
// Generate our potential four viewports that represent the region of the original that falls outside of the remove area.
|
||||
// We will bias toward generating wide rectangles over tall rectangles (if possible) so that optimizations that apply
|
||||
// to manipulating an entire row at once can be realized by other parts of the console code. (i.e. Run Length Encoding)
|
||||
// In the following examples, the found remaining regions are represented by:
|
||||
// T = Top B = Bottom L = Left R = Right
|
||||
//
|
||||
// 4 Sides but Identical:
|
||||
// |-----------this-----------| |-----------this-----------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | ======> | intersect | ======> early return of nothing
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |-----------other----------| |--------------------------|
|
||||
//
|
||||
// 4 Sides:
|
||||
// |-----------this-----------| |-----------this-----------| |--------------------------|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
|
||||
// | |other | | ======> | |intersect| | ======> |LLLLLLLL| |RRRRRRR|
|
||||
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
|
||||
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
|
||||
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
|
||||
// |--------------------------| |--------------------------| |--------------------------|
|
||||
//
|
||||
// 3 Sides:
|
||||
// |-----------this-----------| |-----------this-----------| |--------------------------|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
|
||||
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
|
||||
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
|
||||
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
|
||||
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
|
||||
// |--------------------------| |--------------------------| |--------------------------|
|
||||
//
|
||||
// 2 Sides:
|
||||
// |-----------this-----------| |-----------this-----------| |--------------------------|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
|
||||
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
|
||||
// | | | | | | |LLLLLLLL| |
|
||||
// | | | | | | |LLLLLLLL| |
|
||||
// | | | | | | |LLLLLLLL| |
|
||||
// |--------| | |--------------------------| |--------------------------|
|
||||
// | |
|
||||
// |--------------------|
|
||||
//
|
||||
// 1 Side:
|
||||
// |-----------this-----------| |-----------this-----------| |--------------------------|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
|
||||
// |-----------------------------| |--------------------------| |--------------------------|
|
||||
// | other | ======> | intersect | ======> | |
|
||||
// | | | | | |
|
||||
// | | | | | |
|
||||
// | | | | | |
|
||||
// | | |--------------------------| |--------------------------|
|
||||
// | |
|
||||
// |-----------------------------|
|
||||
//
|
||||
// 0 Sides:
|
||||
// |-----------this-----------| |-----------this-----------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | ======> | | ======> early return of this
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |--------------------------| |--------------------------|
|
||||
//
|
||||
//
|
||||
// |---------------|
|
||||
// | other |
|
||||
// |---------------|
|
||||
|
||||
// We generate these rectangles by the original and intersect points, but some of them might be empty when the intersect
|
||||
// lines up with the edge of the original. That's OK. That just means that the subtraction didn't leave anything behind.
|
||||
// We will filter those out below when adding them to the result.
|
||||
const til::rectangle t{ left(), top(), right(), intersect.top() };
|
||||
const til::rectangle b{ left(), intersect.bottom(), right(), bottom() };
|
||||
const til::rectangle l{ left(), intersect.top(), intersect.left(), intersect.bottom() };
|
||||
const til::rectangle r{ intersect.right(), intersect.top(), right(), intersect.bottom() };
|
||||
|
||||
if (!t.empty())
|
||||
{
|
||||
result.push_back(t);
|
||||
}
|
||||
|
||||
if (!b.empty())
|
||||
{
|
||||
result.push_back(b);
|
||||
}
|
||||
|
||||
if (!l.empty())
|
||||
{
|
||||
result.push_back(l);
|
||||
}
|
||||
|
||||
if (!r.empty())
|
||||
{
|
||||
result.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t top() const noexcept
|
||||
{
|
||||
return _topLeft.y();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T top() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t bottom() const noexcept
|
||||
{
|
||||
return _bottomRight.y();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T bottom() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t left() const noexcept
|
||||
{
|
||||
return _topLeft.x();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T left() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t right() const noexcept
|
||||
{
|
||||
return _bottomRight.x();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T right() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptrdiff_t width() const
|
||||
{
|
||||
ptrdiff_t ret;
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(right(), left()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T width() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptrdiff_t height() const
|
||||
{
|
||||
ptrdiff_t ret;
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(bottom(), top()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T height() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr point origin() const noexcept
|
||||
{
|
||||
return _topLeft;
|
||||
}
|
||||
|
||||
size size() const
|
||||
{
|
||||
return til::size{ width(), height() };
|
||||
}
|
||||
|
||||
constexpr bool empty() const noexcept
|
||||
{
|
||||
return !operator bool();
|
||||
}
|
||||
|
||||
#ifdef _WINCONTYPES_
|
||||
// NOTE: This will convert back to INCLUSIVE on the way out because
|
||||
// that is generally how SMALL_RECTs are handled in console code and via the APIs.
|
||||
operator SMALL_RECT() const
|
||||
{
|
||||
SMALL_RECT ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.Left));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.Top));
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(right(), 1).AssignIfValid(&ret.Right));
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(bottom(), 1).AssignIfValid(&ret.Bottom));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WINDEF_
|
||||
operator RECT() const
|
||||
{
|
||||
RECT ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.left));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.top));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret.right));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret.bottom));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DCOMMON_H_INCLUDED
|
||||
constexpr operator D2D1_RECT_F() const noexcept
|
||||
{
|
||||
return D2D1_RECT_F{ gsl::narrow_cast<FLOAT>(left()), gsl::narrow_cast<FLOAT>(top()), gsl::narrow_cast<FLOAT>(right()), gsl::narrow_cast<FLOAT>(bottom()) };
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
til::point _topLeft;
|
||||
til::point _bottomRight;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::RectangleTests;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<::til::rectangle>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::rectangle& rect)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.width(), rect.height());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<::til::rectangle, ::til::rectangle>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::rectangle& expected, const ::til::rectangle& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::rectangle& expected, const ::til::rectangle& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::rectangle& expectedLess, const ::til::rectangle& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::rectangle& expectedGreater, const ::til::rectangle& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::rectangle& object) noexcept
|
||||
{
|
||||
return object == til::rectangle{};
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
|
@ -39,14 +39,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
{
|
||||
}
|
||||
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertable to an integer value
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
|
||||
size(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to size from anything that has a cx and a cy field that appear convertable to an integer value
|
||||
// This template will convert to size from anything that has a cx and a cy field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().cx)> && std::is_integral_v<decltype(std::declval<TOther>().cy)>, int> /*sentinel*/ = 0) :
|
||||
size(static_cast<ptrdiff_t>(other.cx), static_cast<ptrdiff_t>(other.cy))
|
||||
|
|
|
@ -50,6 +50,16 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
_used = init.size();
|
||||
}
|
||||
|
||||
constexpr bool operator==(const til::some<T, N>& other) const noexcept
|
||||
{
|
||||
return std::equal(cbegin(), cend(), other.cbegin(), other.cend());
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const til::some<T, N>& other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void fill(const T& _Value)
|
||||
{
|
||||
_array.fill(_Value);
|
||||
|
@ -182,3 +192,51 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<class T, size_t N>
|
||||
class VerifyOutputTraits<::til::some<T, N>>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::some<T, N>& some)
|
||||
{
|
||||
auto str = WEX::Common::NoThrowString().Format(L"\r\nSome contains %d of max size %d:\r\nElements:\r\n", some.size(), some.max_size());
|
||||
|
||||
for (auto& item : some)
|
||||
{
|
||||
const auto itemStr = WEX::TestExecution::VerifyOutputTraits<T>::ToString(item);
|
||||
str.AppendFormat(L"\t- %ws\r\n", (const wchar_t*)itemStr);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, size_t N>
|
||||
class VerifyCompareTraits<::til::some<T, N>, ::til::some<T, N>>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::some<T, N>& expected, const ::til::some<T, N>& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::some<T, N>& expected, const ::til::some<T, N>& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::some<T, N>& expectedLess, const ::til::some<T, N>& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::some<T, N>& expectedGreater, const ::til::some<T, N>& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::some<T, N>& object) noexcept
|
||||
{
|
||||
return object == til::some<T, N>{};
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
|
|
932
src/til/ut_til/RectangleTests.cpp
Normal file
932
src/til/ut_til/RectangleTests.cpp
Normal file
|
@ -0,0 +1,932 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "til/rectangle.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
class RectangleTests
|
||||
{
|
||||
TEST_CLASS(RectangleTests);
|
||||
|
||||
TEST_METHOD(DefaultConstruct)
|
||||
{
|
||||
const til::rectangle rc;
|
||||
VERIFY_ARE_EQUAL(0, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(0, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(0, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(0, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(RawConstruct)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(5, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(10, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(15, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(20, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(UnsignedConstruct)
|
||||
{
|
||||
Log::Comment(L"0.) Normal unsigned construct.");
|
||||
{
|
||||
const size_t l = 5;
|
||||
const size_t t = 10;
|
||||
const size_t r = 15;
|
||||
const size_t b = 20;
|
||||
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
VERIFY_ARE_EQUAL(5, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(10, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(15, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(20, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Unsigned construct overflow on left.");
|
||||
{
|
||||
constexpr size_t l = std::numeric_limits<size_t>().max();
|
||||
const size_t t = 10;
|
||||
const size_t r = 15;
|
||||
const size_t b = 20;
|
||||
|
||||
auto fn = [&]() {
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Unsigned construct overflow on top.");
|
||||
{
|
||||
const size_t l = 5;
|
||||
constexpr size_t t = std::numeric_limits<size_t>().max();
|
||||
const size_t r = 15;
|
||||
const size_t b = 20;
|
||||
|
||||
auto fn = [&]() {
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Unsigned construct overflow on right.");
|
||||
{
|
||||
const size_t l = 5;
|
||||
const size_t t = 10;
|
||||
constexpr size_t r = std::numeric_limits<size_t>().max();
|
||||
const size_t b = 20;
|
||||
|
||||
auto fn = [&]() {
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Unsigned construct overflow on bottom.");
|
||||
{
|
||||
const size_t l = 5;
|
||||
const size_t t = 10;
|
||||
const size_t r = 15;
|
||||
constexpr size_t b = std::numeric_limits<size_t>().max();
|
||||
|
||||
auto fn = [&]() {
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(SignedConstruct)
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
const ptrdiff_t t = 10;
|
||||
const ptrdiff_t r = 15;
|
||||
const ptrdiff_t b = 20;
|
||||
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
VERIFY_ARE_EQUAL(5, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(10, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(15, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(20, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(SinglePointConstruct)
|
||||
{
|
||||
Log::Comment(L"0.) Normal Case");
|
||||
{
|
||||
const til::rectangle rc{ til::point{ 4, 8 } };
|
||||
VERIFY_ARE_EQUAL(4, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(8, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(5, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(9, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Overflow x-dimension case.");
|
||||
{
|
||||
auto fn = [&]() {
|
||||
constexpr ptrdiff_t x = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t y = 0;
|
||||
const til::rectangle rc{ til::point{ x, y } };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Overflow y-dimension case.");
|
||||
{
|
||||
auto fn = [&]() {
|
||||
const ptrdiff_t x = 0;
|
||||
constexpr ptrdiff_t y = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::rectangle rc{ til::point{ x, y } };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TwoPointsConstruct)
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
const ptrdiff_t t = 10;
|
||||
const ptrdiff_t r = 15;
|
||||
const ptrdiff_t b = 20;
|
||||
|
||||
const til::rectangle rc{ til::point{ l, t }, til::point{ r, b } };
|
||||
VERIFY_ARE_EQUAL(5, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(10, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(15, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(20, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(SizeOnlyConstruct)
|
||||
{
|
||||
// Size will match bottom right point because
|
||||
// til::rectangle is exclusive.
|
||||
const auto sz = til::size{ 5, 10 };
|
||||
const til::rectangle rc{ sz };
|
||||
VERIFY_ARE_EQUAL(0, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(0, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(sz.width(), rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(sz.height(), rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(PointAndSizeConstruct)
|
||||
{
|
||||
const til::point pt{ 4, 8 };
|
||||
|
||||
Log::Comment(L"0.) Normal Case");
|
||||
{
|
||||
const til::rectangle rc{ pt, til::size{ 2, 10 } };
|
||||
VERIFY_ARE_EQUAL(4, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(8, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(6, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(18, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Overflow x-dimension case.");
|
||||
{
|
||||
auto fn = [&]() {
|
||||
constexpr ptrdiff_t x = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t y = 0;
|
||||
const til::rectangle rc{ pt, til::size{ x, y } };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Overflow y-dimension case.");
|
||||
{
|
||||
auto fn = [&]() {
|
||||
const ptrdiff_t x = 0;
|
||||
constexpr ptrdiff_t y = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::rectangle rc{ pt, til::size{ x, y } };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(SmallRectConstruct)
|
||||
{
|
||||
SMALL_RECT sr;
|
||||
sr.Left = 5;
|
||||
sr.Top = 10;
|
||||
sr.Right = 14;
|
||||
sr.Bottom = 19;
|
||||
|
||||
const til::rectangle rc{ sr };
|
||||
VERIFY_ARE_EQUAL(5, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(10, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(15, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(20, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(ExclusiveCapitalStructConstruct)
|
||||
{
|
||||
struct TestStruct
|
||||
{
|
||||
char Left;
|
||||
char Top;
|
||||
char Right;
|
||||
char Bottom;
|
||||
};
|
||||
|
||||
const TestStruct ts{ 1, 2, 3, 4 };
|
||||
|
||||
const til::rectangle rc{ ts };
|
||||
|
||||
VERIFY_ARE_EQUAL(1, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(2, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(3, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(4, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(Win32RectConstruct)
|
||||
{
|
||||
const RECT win32rc{ 5, 10, 15, 20 };
|
||||
const til::rectangle rc{ win32rc };
|
||||
|
||||
VERIFY_ARE_EQUAL(5, rc._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(10, rc._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(15, rc._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(20, rc._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(Assignment)
|
||||
{
|
||||
til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 5, 6, 7, 8 };
|
||||
|
||||
VERIFY_ARE_EQUAL(1, a._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(2, a._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(3, a._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(4, a._bottomRight.y());
|
||||
|
||||
a = b;
|
||||
|
||||
VERIFY_ARE_EQUAL(5, a._topLeft.x());
|
||||
VERIFY_ARE_EQUAL(6, a._topLeft.y());
|
||||
VERIFY_ARE_EQUAL(7, a._bottomRight.x());
|
||||
VERIFY_ARE_EQUAL(8, a._bottomRight.y());
|
||||
}
|
||||
|
||||
TEST_METHOD(Equality)
|
||||
{
|
||||
Log::Comment(L"0.) Equal.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_TRUE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Left A changed.");
|
||||
{
|
||||
const til::rectangle a{ 9, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Top A changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 9, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Right A changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 9, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Bottom A changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 9 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"5.) Left B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 9, 2, 3, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"6.) Top B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 9, 3, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"7.) Right B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 9, 4 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
|
||||
Log::Comment(L"8.) Bottom B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 9 };
|
||||
VERIFY_IS_FALSE(a == b);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Inequality)
|
||||
{
|
||||
Log::Comment(L"0.) Equal.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_FALSE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Left A changed.");
|
||||
{
|
||||
const til::rectangle a{ 9, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Top A changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 9, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Right A changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 9, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Bottom A changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 9 };
|
||||
const til::rectangle b{ 1, 2, 3, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"5.) Left B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 9, 2, 3, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"6.) Top B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 9, 3, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"7.) Right B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 9, 4 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
|
||||
Log::Comment(L"8.) Bottom B changed.");
|
||||
{
|
||||
const til::rectangle a{ 1, 2, 3, 4 };
|
||||
const til::rectangle b{ 1, 2, 3, 9 };
|
||||
VERIFY_IS_TRUE(a != b);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Boolean)
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:left", L"{0,10}")
|
||||
TEST_METHOD_PROPERTY(L"Data:top", L"{0,10}")
|
||||
TEST_METHOD_PROPERTY(L"Data:right", L"{0,10}")
|
||||
TEST_METHOD_PROPERTY(L"Data:bottom", L"{0,10}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
ptrdiff_t left, top, right, bottom;
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"left", left));
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"top", top));
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"right", right));
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"bottom", bottom));
|
||||
|
||||
const bool expected = left < right && top < bottom;
|
||||
const til::rectangle actual{ left, top, right, bottom };
|
||||
VERIFY_ARE_EQUAL(expected, (bool)actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(OrUnion)
|
||||
{
|
||||
const til::rectangle one{ 4, 6, 10, 14 };
|
||||
const til::rectangle two{ 5, 2, 13, 10 };
|
||||
|
||||
const til::rectangle expected{ 4, 2, 13, 14 };
|
||||
const auto actual = one | two;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(AndIntersect)
|
||||
{
|
||||
const til::rectangle one{ 4, 6, 10, 14 };
|
||||
const til::rectangle two{ 5, 2, 13, 10 };
|
||||
|
||||
const til::rectangle expected{ 5, 6, 10, 10 };
|
||||
const auto actual = one & two;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractSame)
|
||||
{
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
const auto removal = original;
|
||||
|
||||
// Since it's the same rectangle, nothing's left. We should get no results.
|
||||
const til::some<til::rectangle, 4> expected;
|
||||
const auto actual = original - removal;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractNoOverlap)
|
||||
{
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
const til::rectangle removal{ 12, 12, 15, 15 };
|
||||
|
||||
// Since they don't overlap, we expect the original to be given back.
|
||||
const til::some<til::rectangle, 4> expected{ original };
|
||||
const auto actual = original - removal;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractOne)
|
||||
{
|
||||
// +--------+
|
||||
// | result |
|
||||
// | |
|
||||
// +-------------------------------------+
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | |original| |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | +--------+ |
|
||||
// | |
|
||||
// | |
|
||||
// | removal |
|
||||
// | |
|
||||
// +-------------------------------------+
|
||||
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
const til::rectangle removal{ -12, 3, 15, 15 };
|
||||
|
||||
const til::some<til::rectangle, 4> expected{
|
||||
til::rectangle{ original.left(), original.top(), original.right(), removal.top() }
|
||||
};
|
||||
const auto actual = original - removal;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractTwo)
|
||||
{
|
||||
// +--------+
|
||||
// |result0 |
|
||||
// | |
|
||||
// |~~~~+-----------------+
|
||||
// |res1| | |
|
||||
// | | | |
|
||||
// |original| |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// +--------+ |
|
||||
// | |
|
||||
// | |
|
||||
// | removal |
|
||||
// +-----------------+
|
||||
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
const til::rectangle removal{ 3, 3, 15, 15 };
|
||||
|
||||
const til::some<til::rectangle, 4> expected{
|
||||
til::rectangle{ original.left(), original.top(), original.right(), removal.top() },
|
||||
til::rectangle{ original.left(), removal.top(), removal.left(), original.bottom() }
|
||||
};
|
||||
const auto actual = original - removal;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractThree)
|
||||
{
|
||||
// +--------+
|
||||
// |result0 |
|
||||
// | |
|
||||
// |~~~~+---------------------------+
|
||||
// |res2| | removal |
|
||||
// |original| |
|
||||
// |~~~~+---------------------------+
|
||||
// |result1 |
|
||||
// | |
|
||||
// +--------+
|
||||
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
const til::rectangle removal{ 3, 3, 15, 6 };
|
||||
|
||||
const til::some<til::rectangle, 4> expected{
|
||||
til::rectangle{ original.left(), original.top(), original.right(), removal.top() },
|
||||
til::rectangle{ original.left(), removal.bottom(), original.right(), original.bottom() },
|
||||
til::rectangle{ original.left(), removal.top(), removal.left(), removal.bottom() }
|
||||
};
|
||||
const auto actual = original - removal;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractFour)
|
||||
{
|
||||
// (original)---+
|
||||
// |
|
||||
// v
|
||||
// + --------------------------+
|
||||
// | result0 |
|
||||
// | o r i |
|
||||
// | |
|
||||
// |~~~~~~~+-----------+~~~~~~~|
|
||||
// | res2 | | res3 |
|
||||
// | g | removal | i |
|
||||
// | | | |
|
||||
// |~~~~~~~+-----------+~~~~~~~|
|
||||
// | result1 |
|
||||
// | n a l |
|
||||
// | |
|
||||
// +---------------------------+
|
||||
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
const til::rectangle removal{ 3, 3, 6, 6 };
|
||||
|
||||
const til::some<til::rectangle, 4> expected{
|
||||
til::rectangle{ original.left(), original.top(), original.right(), removal.top() },
|
||||
til::rectangle{ original.left(), removal.bottom(), original.right(), original.bottom() },
|
||||
til::rectangle{ original.left(), removal.top(), removal.left(), removal.bottom() },
|
||||
til::rectangle{ removal.right(), removal.top(), original.right(), removal.bottom() }
|
||||
};
|
||||
const auto actual = original - removal;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(Top)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(rc._topLeft.y(), rc.top());
|
||||
}
|
||||
|
||||
TEST_METHOD(TopCast)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(static_cast<SHORT>(rc._topLeft.y()), rc.top<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Bottom)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(rc._bottomRight.y(), rc.bottom());
|
||||
}
|
||||
|
||||
TEST_METHOD(BottomCast)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(static_cast<SHORT>(rc._bottomRight.y()), rc.bottom<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Left)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(rc._topLeft.x(), rc.left());
|
||||
}
|
||||
|
||||
TEST_METHOD(LeftCast)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(static_cast<SHORT>(rc._topLeft.x()), rc.left<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Right)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(rc._bottomRight.x(), rc.right());
|
||||
}
|
||||
|
||||
TEST_METHOD(RightCast)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(static_cast<SHORT>(rc._bottomRight.x()), rc.right<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Width)
|
||||
{
|
||||
Log::Comment(L"0.) Width that should be in bounds.");
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(15 - 5, rc.width());
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Width that should go out of bounds on subtraction.");
|
||||
{
|
||||
constexpr ptrdiff_t bigVal = std::numeric_limits<ptrdiff_t>().min();
|
||||
const ptrdiff_t normalVal = 5;
|
||||
const til::rectangle rc{ normalVal, normalVal, bigVal, normalVal };
|
||||
|
||||
auto fn = [&]() {
|
||||
rc.width();
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(WidthCast)
|
||||
{
|
||||
const SHORT expected = 15 - 5;
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(expected, rc.width<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Height)
|
||||
{
|
||||
Log::Comment(L"0.) Height that should be in bounds.");
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(20 - 10, rc.height());
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Height that should go out of bounds on subtraction.");
|
||||
{
|
||||
constexpr ptrdiff_t bigVal = std::numeric_limits<ptrdiff_t>().min();
|
||||
const ptrdiff_t normalVal = 5;
|
||||
const til::rectangle rc{ normalVal, normalVal, normalVal, bigVal };
|
||||
|
||||
auto fn = [&]() {
|
||||
rc.height();
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(HeightCast)
|
||||
{
|
||||
const SHORT expected = 20 - 10;
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
VERIFY_ARE_EQUAL(expected, rc.height<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Origin)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
const til::point expected{ rc._topLeft };
|
||||
VERIFY_ARE_EQUAL(expected, rc.origin());
|
||||
}
|
||||
|
||||
TEST_METHOD(Size)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
const til::size expected{ 10, 10 };
|
||||
VERIFY_ARE_EQUAL(expected, rc.size());
|
||||
}
|
||||
|
||||
TEST_METHOD(Empty)
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:left", L"{0,10}")
|
||||
TEST_METHOD_PROPERTY(L"Data:top", L"{0,10}")
|
||||
TEST_METHOD_PROPERTY(L"Data:right", L"{0,10}")
|
||||
TEST_METHOD_PROPERTY(L"Data:bottom", L"{0,10}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
ptrdiff_t left, top, right, bottom;
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"left", left));
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"top", top));
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"right", right));
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"bottom", bottom));
|
||||
|
||||
const bool expected = !(left < right && top < bottom);
|
||||
const til::rectangle actual{ left, top, right, bottom };
|
||||
VERIFY_ARE_EQUAL(expected, actual.empty());
|
||||
}
|
||||
|
||||
TEST_METHOD(CastToSmallRect)
|
||||
{
|
||||
Log::Comment(L"0.) Typical situation.");
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
SMALL_RECT val = rc;
|
||||
VERIFY_ARE_EQUAL(5, val.Left);
|
||||
VERIFY_ARE_EQUAL(10, val.Top);
|
||||
VERIFY_ARE_EQUAL(14, val.Right);
|
||||
VERIFY_ARE_EQUAL(19, val.Bottom);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Overflow on left.");
|
||||
{
|
||||
constexpr ptrdiff_t l = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t t = 10;
|
||||
const ptrdiff_t r = 15;
|
||||
const ptrdiff_t b = 20;
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
auto fn = [&]() {
|
||||
SMALL_RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Overflow on top.");
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
constexpr ptrdiff_t t = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t r = 15;
|
||||
const ptrdiff_t b = 20;
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
auto fn = [&]() {
|
||||
SMALL_RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Overflow on right.");
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
const ptrdiff_t t = 10;
|
||||
constexpr ptrdiff_t r = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t b = 20;
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
auto fn = [&]() {
|
||||
SMALL_RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Overflow on bottom.");
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
const ptrdiff_t t = 10;
|
||||
const ptrdiff_t r = 15;
|
||||
constexpr ptrdiff_t b = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
auto fn = [&]() {
|
||||
SMALL_RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastToRect)
|
||||
{
|
||||
Log::Comment(L"0.) Typical situation.");
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
RECT val = rc;
|
||||
VERIFY_ARE_EQUAL(5, val.left);
|
||||
VERIFY_ARE_EQUAL(10, val.top);
|
||||
VERIFY_ARE_EQUAL(15, val.right);
|
||||
VERIFY_ARE_EQUAL(20, val.bottom);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Fit max left into RECT (may overflow).");
|
||||
{
|
||||
constexpr ptrdiff_t l = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t t = 10;
|
||||
const ptrdiff_t r = 15;
|
||||
const ptrdiff_t b = 20;
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
// On some platforms, ptrdiff_t will fit inside l/t/r/b
|
||||
const bool overflowExpected = l > std::numeric_limits<decltype(RECT::left)>().max();
|
||||
|
||||
if (overflowExpected)
|
||||
{
|
||||
auto fn = [&]() {
|
||||
RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT val = rc;
|
||||
VERIFY_ARE_EQUAL(l, val.left);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Fit max top into RECT (may overflow).");
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
constexpr ptrdiff_t t = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t r = 15;
|
||||
const ptrdiff_t b = 20;
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
// On some platforms, ptrdiff_t will fit inside l/t/r/b
|
||||
const bool overflowExpected = t > std::numeric_limits<decltype(RECT::top)>().max();
|
||||
|
||||
if (overflowExpected)
|
||||
{
|
||||
auto fn = [&]() {
|
||||
RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT val = rc;
|
||||
VERIFY_ARE_EQUAL(t, val.top);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Fit max right into RECT (may overflow).");
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
const ptrdiff_t t = 10;
|
||||
constexpr ptrdiff_t r = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t b = 20;
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
// On some platforms, ptrdiff_t will fit inside l/t/r/b
|
||||
const bool overflowExpected = r > std::numeric_limits<decltype(RECT::right)>().max();
|
||||
|
||||
if (overflowExpected)
|
||||
{
|
||||
auto fn = [&]() {
|
||||
RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT val = rc;
|
||||
VERIFY_ARE_EQUAL(r, val.right);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Fit max bottom into RECT (may overflow).");
|
||||
{
|
||||
const ptrdiff_t l = 5;
|
||||
const ptrdiff_t t = 10;
|
||||
const ptrdiff_t r = 15;
|
||||
constexpr ptrdiff_t b = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::rectangle rc{ l, t, r, b };
|
||||
|
||||
// On some platforms, ptrdiff_t will fit inside l/t/r/b
|
||||
const bool overflowExpected = b > std::numeric_limits<decltype(RECT::bottom)>().max();
|
||||
|
||||
if (overflowExpected)
|
||||
{
|
||||
auto fn = [&]() {
|
||||
RECT val = rc;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT val = rc;
|
||||
VERIFY_ARE_EQUAL(b, val.bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastToD2D1RectF)
|
||||
{
|
||||
Log::Comment(L"0.) Typical situation.");
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
D2D1_RECT_F val = rc;
|
||||
VERIFY_ARE_EQUAL(5, val.left);
|
||||
VERIFY_ARE_EQUAL(10, val.top);
|
||||
VERIFY_ARE_EQUAL(15, val.right);
|
||||
VERIFY_ARE_EQUAL(20, val.bottom);
|
||||
}
|
||||
|
||||
// All ptrdiff_ts fit into a float, so there's no exception tests.
|
||||
}
|
||||
};
|
|
@ -28,6 +28,38 @@ class SomeTests
|
|||
VERIFY_THROWS(f(), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_METHOD(Equality)
|
||||
{
|
||||
til::some<int, 2> a{ 1, 2 };
|
||||
til::some<int, 2> b{ 1, 2 };
|
||||
VERIFY_IS_TRUE(a == b);
|
||||
|
||||
til::some<int, 2> c{ 3, 2 };
|
||||
VERIFY_IS_FALSE(a == c);
|
||||
|
||||
til::some<int, 2> d{ 2, 3 };
|
||||
VERIFY_IS_FALSE(a == d);
|
||||
|
||||
til::some<int, 2> e{ 1 };
|
||||
VERIFY_IS_FALSE(a == e);
|
||||
}
|
||||
|
||||
TEST_METHOD(Inequality)
|
||||
{
|
||||
til::some<int, 2> a{ 1, 2 };
|
||||
til::some<int, 2> b{ 1, 2 };
|
||||
VERIFY_IS_FALSE(a != b);
|
||||
|
||||
til::some<int, 2> c{ 3, 2 };
|
||||
VERIFY_IS_TRUE(a != c);
|
||||
|
||||
til::some<int, 2> d{ 2, 3 };
|
||||
VERIFY_IS_TRUE(a != d);
|
||||
|
||||
til::some<int, 2> e{ 1 };
|
||||
VERIFY_IS_TRUE(a != e);
|
||||
}
|
||||
|
||||
TEST_METHOD(Fill)
|
||||
{
|
||||
til::some<int, 4> s;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="PointTests.cpp" />
|
||||
<ClCompile Include="RectangleTests.cpp" />
|
||||
<ClCompile Include="SizeTests.cpp" />
|
||||
<ClCompile Include="ColorTests.cpp" />
|
||||
<ClCompile Include="SomeTests.cpp" />
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<ClCompile Include="SizeTests.cpp" />
|
||||
<ClCompile Include="ColorTests.cpp" />
|
||||
<ClCompile Include="PointTests.cpp" />
|
||||
<ClCompile Include="RectangleTests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
|
|
|
@ -89,6 +89,10 @@
|
|||
<DisplayString>{{X: {_x,d}, Y: {_y,d}}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="til::rectangle">
|
||||
<DisplayString>{{L: {_topLeft._x}, T: {_topLeft._y}, R: {_bottomRight._x} B: {_bottomRight._y} [W: {_bottomRight._x - _topLeft._x} x H: {_bottomRight._y - _topLeft._y} -> A: {(_bottomRight._x - _topLeft._x) * (_bottomRight._y - _topLeft._y)}]}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="til::color">
|
||||
<DisplayString>{{RGB: {(int)r,d}, {(int)g,d}, {(int)b,d}; α: {(int)a,d}}}</DisplayString>
|
||||
</Type>
|
||||
|
|
Loading…
Reference in a new issue