Browse Source

Trade: add a parentFor() object-oriented accessor as well.

For a lack of better API right now, this will be used to check if an
object belongs to a certain scene. In the future, when BitArray is a
thing, there will be a dedicated field with a mask telling which objects
belong to the scene and which not, with a fallback that goes through all
field object mappings and collects objects that have at least one field.
pull/525/head
Vladimír Vondruš 5 years ago
parent
commit
409b49391e
  1. 22
      src/Magnum/Trade/SceneData.cpp
  2. 27
      src/Magnum/Trade/SceneData.h
  3. 48
      src/Magnum/Trade/Test/SceneDataTest.cpp

22
src/Magnum/Trade/SceneData.cpp

@ -1588,7 +1588,27 @@ std::size_t SceneData::fieldFor(const SceneFieldData& field, const std::size_t o
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
Containers::Array<UnsignedInt> SceneData::childrenFor(Int object) const {
Containers::Optional<Int> SceneData::parentFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Parent);
if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId];
const std::size_t offset = fieldFor(field, 0, object);
if(offset == field._size) return {};
Int index[1];
parentsIntoInternal(fieldId, offset, index);
if(*index == -1) return -1;
UnsignedInt parent[1];
objectsIntoInternal(fieldId, *index, parent);
return Int(*parent);
}
Containers::Array<UnsignedInt> SceneData::childrenFor(const Int object) const {
CORRADE_ASSERT(object >= -1 && object < Long(_objectCount),
"Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});

27
src/Magnum/Trade/SceneData.h

@ -102,7 +102,8 @@ enum class SceneField: UnsignedInt {
* Note that the index points to the parent array itself and isn't the
* actual object index --- the object mapping is stored in the
* corresponding @ref SceneData::objects() array.
* @see @ref SceneData::parentsAsArray(), @ref SceneData::childrenFor()
* @see @ref SceneData::parentsAsArray(), @ref SceneData::parentFor(),
* @ref SceneData::childrenFor()
*/
Parent = 1,
@ -1358,7 +1359,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* is larger than the max representable 32-bit value, this
* function can't be used, only an appropriately typed
* @ref field(SceneField) const.
* @see @ref parentsInto(), @ref hasField(), @ref childrenFor()
* @see @ref parentsInto(), @ref hasField(), @ref parentFor(),
* @ref childrenFor()
*/
Containers::Array<Int> parentsAsArray() const;
@ -1719,6 +1721,26 @@ class MAGNUM_TRADE_EXPORT SceneData {
*/
std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
/**
* @brief Parent for given object
* @m_since_latest
*
* Looks up the @ref SceneField::Parent field for @p object. The lookup
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$
* being the field count and @f$ n @f$ the size of the parent field,
* thus for retrieving parent info for many objects it's recommended to
* access the field data directly with @ref parentsAsArray() and
* related APIs.
*
* If the @ref SceneField::Parent field is not present or if there's no
* parent for @p object, returns @ref Containers::NullOpt. If @p object
* is top-level, returns @cpp -1 @ce.
*
* The @p object is expected to be less than @ref objectCount().
* @see @ref childrenFor()
*/
Containers::Optional<Int> parentFor(UnsignedInt object) const;
/**
* @brief Children for given object
* @m_since_latest
@ -1737,6 +1759,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* an empty array. Pass @cpp -1 @ce to get a list of top-level objects.
*
* The @p object is expected to be less than @ref objectCount().
* @see @ref parentFor()
*/
Containers::Array<UnsignedInt> childrenFor(Int object) const;

48
src/Magnum/Trade/Test/SceneDataTest.cpp

@ -151,9 +151,10 @@ struct SceneDataTest: TestSuite::Tester {
void fieldWrongType();
void fieldWrongArrayAccess();
/* Different object types checked just for the childrenFor(), other APIs
/* Different object types checked just for the parentFor(), other APIs
use the same helper */
template<class T> void childrenFor();
template<class T> void parentFor();
void childrenFor();
void transformation2DFor();
void transformation2DForTRS();
template<class T> void transformation2DForBut3DType();
@ -360,10 +361,11 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::fieldWrongType,
&SceneDataTest::fieldWrongArrayAccess,
&SceneDataTest::childrenFor<UnsignedByte>,
&SceneDataTest::childrenFor<UnsignedShort>,
&SceneDataTest::childrenFor<UnsignedInt>,
&SceneDataTest::childrenFor<UnsignedLong>,
&SceneDataTest::parentFor<UnsignedByte>,
&SceneDataTest::parentFor<UnsignedShort>,
&SceneDataTest::parentFor<UnsignedInt>,
&SceneDataTest::parentFor<UnsignedLong>,
&SceneDataTest::childrenFor,
&SceneDataTest::transformation2DFor,
&SceneDataTest::transformation2DForTRS,
&SceneDataTest::transformation2DForBut3DType<Matrix4x4>,
@ -4040,12 +4042,39 @@ void SceneDataTest::fieldWrongArrayAccess() {
"Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n");
}
template<class T> void SceneDataTest::childrenFor() {
template<class T> void SceneDataTest::parentFor() {
setTestCaseTemplateName(NameTraits<T>::name());
struct Field {
T object;
Int parent;
} fields[]{
{3, -1},
{4, 0},
{2, 1},
{4, 2} /* duplicate, ignored */
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{Implementation::sceneObjectTypeFor<T>(), 7, {}, fields, {
SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
CORRADE_COMPARE(scene.parentFor(2), 4);
CORRADE_COMPARE(scene.parentFor(3), -1);
/* Duplicate entries -- only the first one gets used, it doesn't traverse
further */
CORRADE_COMPARE(scene.parentFor(4), 3);
/* Object that's not in the array at all */
CORRADE_COMPARE(scene.parentFor(1), Containers::NullOpt);
}
void SceneDataTest::childrenFor() {
struct Field {
UnsignedInt object;
Int parent;
} fields[]{
{4, -1},
{3, 0},
@ -4056,7 +4085,7 @@ template<class T> void SceneDataTest::childrenFor() {
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{Implementation::sceneObjectTypeFor<T>(), 7, {}, fields, {
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
@ -4486,6 +4515,7 @@ void SceneDataTest::skinsFor() {
void SceneDataTest::fieldForFieldMissing() {
SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}};
CORRADE_COMPARE(scene.parentFor(6), Containers::NullOpt);
CORRADE_COMPARE_AS(scene.childrenFor(6),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
@ -4516,6 +4546,7 @@ void SceneDataTest::fieldForInvalidObject() {
std::ostringstream out;
Error redirectError{&out};
scene.parentFor(7);
scene.childrenFor(-2);
scene.childrenFor(7);
scene.transformation2DFor(7);
@ -4527,6 +4558,7 @@ void SceneDataTest::fieldForInvalidObject() {
scene.camerasFor(7);
scene.skinsFor(7);
CORRADE_COMPARE(out.str(),
"Trade::SceneData::parentFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::childrenFor(): object -2 out of bounds for 7 objects\n"
"Trade::SceneData::childrenFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::transformation2DFor(): object 7 out of bounds for 7 objects\n"

Loading…
Cancel
Save