float CollisionTile::placeFeature(const CollisionFeature &feature, const bool allowOverlap, const bool avoidEdges) {

    float minPlacementScale = minScale;

    for (auto& box : feature.boxes) {
        const auto anchor = box.anchor.matMul(rotationMatrix);

        if (!allowOverlap) {
            std::vector<CollisionTreeBox> blockingBoxes;
            tree.query(bgi::intersects(getTreeBox(anchor, box)), std::back_inserter(blockingBoxes));

            for (auto& blockingTreeBox : blockingBoxes) {
                const auto& blocking = std::get<1>(blockingTreeBox);
                auto blockingAnchor = blocking.anchor.matMul(rotationMatrix);

                minPlacementScale = findPlacementScale(minPlacementScale, anchor, box, blockingAnchor, blocking);
                if (minPlacementScale >= maxScale) return minPlacementScale;
            }
        }

        if (avoidEdges) {
            const vec2<float> tl = { box.x1, box.y1 };
            const vec2<float> tr = { box.x2, box.y1 };
            const vec2<float> bl = { box.x1, box.y2 };
            const vec2<float> br = { box.x2, box.y2 };
            const vec2<float> rtl = tl.matMul(reverseRotationMatrix);
            const vec2<float> rtr = tr.matMul(reverseRotationMatrix);
            const vec2<float> rbl = bl.matMul(reverseRotationMatrix);
            const vec2<float> rbr = br.matMul(reverseRotationMatrix);
            CollisionBox rotatedBox(box.anchor,
                    ::fmin(::fmin(rtl.x, rtr.x), ::fmin(rbl.x, rbr.x)),
                    ::fmin(::fmin(rtl.y, rtr.y), ::fmin(rbl.y, rbr.y)),
                    ::fmax(::fmax(rtl.x, rtr.x), ::fmax(rbl.x, rbr.x)),
                    ::fmax(::fmax(rtl.y, rtr.y), ::fmax(rbl.y, rbr.y)),
                    box.maxScale);

            for (auto& blocking : edges) {
                minPlacementScale = findPlacementScale(minPlacementScale, box.anchor, rotatedBox, blocking.anchor, blocking);

                if (minPlacementScale >= maxScale) return minPlacementScale;
            }
        }
    }

    return minPlacementScale;
}
float CollisionTile::placeFeature(const CollisionFeature& feature, const bool allowOverlap, const bool avoidEdges) {

    float minPlacementScale = minScale;

    for (auto& box : feature.boxes) {
        const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor);

        if (!allowOverlap) {
            for (auto it = tree.qbegin(bgi::intersects(getTreeBox(anchor, box))); it != tree.qend(); ++it) {
                const CollisionBox& blocking = std::get<1>(*it);
                Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor);

                minPlacementScale = findPlacementScale(minPlacementScale, anchor, box, blockingAnchor, blocking);
                if (minPlacementScale >= maxScale) return minPlacementScale;
            }
        }

        if (avoidEdges) {
            const Point<float> tl = { box.x1, box.y1 };
            const Point<float> tr = { box.x2, box.y1 };
            const Point<float> bl = { box.x1, box.y2 };
            const Point<float> br = { box.x2, box.y2 };
            const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, tl);
            const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, tr);
            const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, bl);
            const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, br);
            CollisionBox rotatedBox(box.anchor,
                    ::fmin(::fmin(rtl.x, rtr.x), ::fmin(rbl.x, rbr.x)),
                    ::fmin(::fmin(rtl.y, rtr.y), ::fmin(rbl.y, rbr.y)),
                    ::fmax(::fmax(rtl.x, rtr.x), ::fmax(rbl.x, rbr.x)),
                    ::fmax(::fmax(rtl.y, rtr.y), ::fmax(rbl.y, rbr.y)),
                    box.maxScale);

            for (auto& blocking : edges) {
                minPlacementScale = findPlacementScale(minPlacementScale, box.anchor, rotatedBox, blocking.anchor, blocking);

                if (minPlacementScale >= maxScale) return minPlacementScale;
            }
        }
    }

    return minPlacementScale;
}