/**************************************************************************** * Copyright (c) 2017-2023 by the ArborX authors * * All rights reserved. * * * * This file is part of the ArborX library. ArborX is * * distributed under a BSD 3-clause license. For the licensing terms see * * the LICENSE file in the top-level directory. * * * * SPDX-License-Identifier: BSD-3-Clause * ****************************************************************************/ #ifndef ARBORX_DETAILS_TREE_VISUALIZATION_HPP #define ARBORX_DETAILS_TREE_VISUALIZATION_HPP #include #include #include #include #include // ignore namespace ArborX { namespace Details { std::ostream &operator<<(std::ostream &os, Point const &p) { os << "(" << p[0] << "," << p[1] << ")"; return os; } struct TreeVisualization { template static std::string getNodeLabel(Tree const &tree, int node) { auto const node_is_leaf = HappyTreeFriends::isLeaf(tree, node); auto const node_index = node_is_leaf ? HappyTreeFriends::getValue(tree, node).index : node; std::string label = node_is_leaf ? "l" : "i"; label.append(std::to_string(node_index)); return label; } template static std::string getNodeAttributes(Tree const &tree, int node) { return HappyTreeFriends::isLeaf(tree, node) ? "[leaf]" : "[internal]"; } template static std::string getEdgeAttributes(Tree const &tree, int /*parent*/, int child) { return HappyTreeFriends::isLeaf(tree, child) ? "[pendant]" : "[edge]"; } // Produces node and edges statements to be listed for a graph in DOT // format: // ``` // digraph g { // root = i0; // // } // ``` struct GraphvizVisitor { std::ostream &_os; template void visit(Tree const &tree, int node) const { visitNode(tree, node); visitEdgesStartingFromNode(tree, node); } template void visitNode(Tree const &tree, int node) const { auto const node_label = getNodeLabel(tree, node); auto const node_attributes = getNodeAttributes(tree, node); _os << " " << node_label << " " << node_attributes << ";\n"; } template void visitEdgesStartingFromNode(Tree const &tree, int node) const { auto const node_label = getNodeLabel(tree, node); auto const node_is_internal = !HappyTreeFriends::isLeaf(tree, node); if (node_is_internal) for (auto const child : {HappyTreeFriends::getLeftChild(tree, node), HappyTreeFriends::getRightChild(tree, node)}) { auto const child_label = getNodeLabel(tree, child); auto const edge_attributes = getEdgeAttributes(tree, node, child); _os << " " << node_label << " -> " << child_label << " " << edge_attributes << ";\n"; } } }; // Produce commands to enclose in a tikzpicture in a LateX document: // ``` // \begin{tikzpicture} // // \end{tikzpicture} // ``` // NB ensure TikZ styles have been defined. struct TikZVisitor { std::ostream &_os; template void visit(Tree const &tree, int node) const { auto const node_label = getNodeLabel(tree, node); auto const node_attributes = getNodeAttributes(tree, node); auto const bounding_volume = HappyTreeFriends::isLeaf(tree, node) ? HappyTreeFriends::getIndexable(tree, node) : HappyTreeFriends::getInternalBoundingVolume(tree, node); auto const min_corner = bounding_volume.minCorner(); auto const max_corner = bounding_volume.maxCorner(); _os << R"(\draw)" << node_attributes << " " << min_corner << " rectangle " << max_corner << " node {" << node_label << "};\n"; } }; template static void visitAllIterative(Tree const &tree, Visitor const &visitor) { Stack stack; stack.emplace(HappyTreeFriends::getRoot(tree)); while (!stack.empty()) { auto const node = stack.top(); stack.pop(); visitor.visit(tree, node); if (!HappyTreeFriends::isLeaf(tree, node)) for (auto const child : {HappyTreeFriends::getLeftChild(tree, node), HappyTreeFriends::getRightChild(tree, node)}) stack.push(child); } } template struct VisitorCallback { template KOKKOS_FUNCTION void operator()(Query const &, typename TreeType::value_type const &value) const { _visitor.visit(_tree, permute(value.index)); } TreeType _tree; VisitorType _visitor; Permute permute; }; template static void visit(Tree const &tree, Predicate const &pred, Visitor const &visitor) { // The preprocessor directives below are intended to silence the // warnings about calling a __host__ function from a __host__ // __device__ function emitted by nvcc. #if defined(__CUDA_ARCH__) std::ignore = tree; std::ignore = pred; std::ignore = visitor; throw std::runtime_error("visit() is not meant to execute on the GPU"); #else using ExecutionSpace = Kokkos::DefaultHostExecutionSpace; using Predicates = Kokkos::View; using Permute = Kokkos::View; using Callback = VisitorCallback; ExecutionSpace space; int const n = tree.size(); Permute permute(Kokkos::view_alloc(space, Kokkos::WithoutInitializing, "ArborX::permute"), n); Kokkos::parallel_for( "ArborX::Viz::compute_permutation", Kokkos::RangePolicy(space, 0, n), KOKKOS_LAMBDA(int i) { permute(HappyTreeFriends::getValue(tree, i).index) = i; }); Predicates predicates(Kokkos::view_alloc(space, Kokkos::WithoutInitializing, "ArborX::predicates"), 1); predicates(0) = pred; TreeTraversal tree_traversal(ExecutionSpace{}, tree, predicates, Callback{tree, visitor, permute}); #endif } }; } // namespace Details } // namespace ArborX #endif