vtk-m/vtkm/worklet/contourtree_distributed/PrintGraph.h
2021-10-20 14:46:21 -07:00

1442 lines
69 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//=======================================================================================
//
// Parallel Peak Pruning v. 2.0
//
// Started June 15, 2017
//
// Copyright Hamish Carr, University of Leeds
//
// PrintGraph.h - routines for outputting dot files for debug purposes
//
//=======================================================================================
//
// COMMENTS: We have dot output scattered among multiple files, when it is primarily
// used for debug. Moreover, our principal data classes are public so that
// the algorithmic construction can be separated into factory classes.
// So we can collect all of the code for dot output into one place and be
// consistent about how we implement it
//
// We will want dot output for the following classes:
// BoundaryTree
// ContourTree
// ContourTreeMesh
// HierarchicalContourTree
// InteriorForest / InteriorForest
//
// In addition, it may be useful to be able to print out:
// MergeTree
//
// but as of the time of writing (30/07/2020) it is not priority for debug
//
// Factory classes will typically have multiple temporary arrays
// and will therefore primarily be debugged by printing the arrays
//
// However, since these classes are often indexed on our the main data classes
// we should also have a version of the graph output that shows a designated
// piece of text on each node
//
// The general design (initially) will be to produce all-purpose illustrations
// While these will probably be overkill, experience shows that confusion between
// the different indices is common, so it is better to be absolutely explicit
// and print out every piece of information possible in the graph.
// However, for the ContourTree and HierarchicalContourTree, we also want to have
// the option to show regular / super / hyper structure separately.
//
// NOTE ON CONST USAGE:
// I had originally planned to use NULL vectors for parameters not being passed
// but the compiler doesn't like the possibility of non-const temporary parameters
// so the flags arrays must be const.
// If I do the same with Mesh & ContourTree, I need to set functions elsewhere to
// be const. I will not do that unilaterally.
// All parameters of these print functions should on principal be const, except
// the stream parameter
//
//=======================================================================================
#ifndef vtk_m_worklet_contourtree_distributed_print_graph_h
#define vtk_m_worklet_contourtree_distributed_print_graph_h
#include <vtkm/Types.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/worklet/contourtree_augmented/ContourTree.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_augmented/meshtypes/ContourTreeMesh.h>
#include <vtkm/worklet/contourtree_distributed/BoundaryTree.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/InteriorForest.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
/// \brief Routines for printing various tree data structures in graphviz .dot format
///
/// These routines are primarily for debug at the moment, and share a number of constants and
/// software patterns.
/// They are therefore collected in a single unit rather than distributed in each clase
// Routines to print out a contour tree in regular and super/hyper format
// The older code ended up with separate versions depending on mesh type, but there are now
// accessor functions, and I will try to avoid this. However, this means templating everything
// to do with contour trees
// Routines for contour trees:
// 1. Routine to print super structure
// generic, with colour set according to iteration
// with option to print grey / white for boundary / interior nodes (regular tree only)
// with option to show BoundaryTree / Forest as different styles (super tree only)
// with option to show an arbitrary additional value passed as an array
// Routines for ContourTreeMesh:
// 2. Simple routine to dump out nodes / edges for cross-checking
// no options should be needed
// Routines for BoundaryTree:
// 3. All purpose routine to dump out the contents for comparison with contour tree
// Includes option to show how the InteriorForest connects to it
// Routines for HierarchicalContourTree:
// 4. Routine to print regular/super/hyper structure with similar options to contour tree
// Routines for contour trees:
// 1. Routine to print super structure
// generic, with colour set according to iteration
// with option to print grey / white for boundary / interior nodes (regular tree only)
// with option to show BoundaryTree / Forest as different styles (super tree only)
// with option to show an arbitrary additional value passed as an array
constexpr vtkm::Id INDEX_WIDTH = 6;
constexpr vtkm::Id NO_PER_NODE_VALUES = 0;
constexpr vtkm::Id PER_REGULAR_NODE_VALUES = 1;
constexpr vtkm::Id PER_REGULAR_NODE_BOUNDARY_FLAGS = 2;
constexpr vtkm::Id PER_SUPER_NODE_VALUES = 3;
constexpr vtkm::Id PER_SUPER_NODE_BOUNDARY_FLAGS = 4;
constexpr vtkm::Id PER_HYPER_NODE_VALUES = 5;
constexpr vtkm::Id BAD_PER_NODE_VALUES = 6;
constexpr vtkm::Id NODE_TYPE_REGULAR = 0;
constexpr vtkm::Id NODE_TYPE_SUPER = 1;
constexpr vtkm::Id NODE_TYPE_HYPER = 2;
// bitflags for various components
constexpr vtkm::Id SHOW_REGULAR_STRUCTURE = 0x00000001;
constexpr vtkm::Id SHOW_SUPER_STRUCTURE = 0x00000002;
constexpr vtkm::Id SHOW_HYPER_STRUCTURE = 0x00000004;
constexpr vtkm::Id SHOW_BOUNDARY_NODES = 0x00000010;
constexpr vtkm::Id SHOW_CRITICAL_BOUNDARY_NODES = 0x00000020;
constexpr vtkm::Id SHOW_NECESSARY_SUPERNODES = 0x00000040;
constexpr vtkm::Id SHOW_GLOBAL_ID = 0x00000100;
constexpr vtkm::Id SHOW_DATA_VALUE = 0x00000200;
constexpr vtkm::Id SHOW_MESH_REGULAR_ID = 0x00000400;
constexpr vtkm::Id SHOW_MESH_SORT_ID = 0x00000800;
constexpr vtkm::Id SHOW_NODE_ID = 0x00001000;
constexpr vtkm::Id SHOW_SUPERPARENT = 0x00002000;
constexpr vtkm::Id SHOW_ARC_ID = 0x00004000;
constexpr vtkm::Id SHOW_EXTRA_DATA = 0x00008000;
constexpr vtkm::Id SHOW_SUPERNODE_ID = 0x00010000;
constexpr vtkm::Id SHOW_HYPERPARENT = 0x00020000;
constexpr vtkm::Id SHOW_SUPERARC_ID = 0x0004000;
constexpr vtkm::Id SHOW_ITERATION = 0x00080000;
constexpr vtkm::Id SHOW_HYPERNODE_ID = 0x00100000;
constexpr vtkm::Id SHOW_HYPERARC_ID = 0x00200000;
// bit flags used for structures other than the contour tree
// the BoundaryTree has a vertex index, but doesn't have the contour tree's nodes, so we will reuse that bit flag
constexpr vtkm::Id SHOW_BOUNDARY_TREE_VERTEX_ID = SHOW_NODE_ID;
// others are just relabelling the same ID flag
constexpr vtkm::Id SHOW_BOUNDARY_TREE_GLOBAL_ID = SHOW_GLOBAL_ID;
constexpr vtkm::Id SHOW_BOUNDARY_TREE_DATA_VALUE = SHOW_DATA_VALUE;
constexpr vtkm::Id SHOW_BOUNDARY_TREE_MESH_REGULAR_ID = SHOW_MESH_REGULAR_ID;
constexpr vtkm::Id SHOW_BOUNDARY_TREE_MESH_SORT_ID = SHOW_MESH_SORT_ID;
constexpr vtkm::Id SHOW_BOUNDARY_TREE_ARC_ID = SHOW_ARC_ID;
constexpr vtkm::Id SHOW_BOUNDARY_TREE_ALL =
(SHOW_BOUNDARY_TREE_VERTEX_ID | SHOW_BOUNDARY_TREE_GLOBAL_ID | SHOW_BOUNDARY_TREE_DATA_VALUE |
SHOW_BOUNDARY_TREE_MESH_REGULAR_ID | SHOW_BOUNDARY_TREE_MESH_SORT_ID |
SHOW_BOUNDARY_TREE_ARC_ID);
// Similarly, relabel the IDs for use with contour tree meshes
constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_VERTEX_ID = SHOW_NODE_ID;
// others are just relabelling the same ID flag
constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_GLOBAL_ID = SHOW_GLOBAL_ID;
constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_DATA_VALUE = SHOW_DATA_VALUE;
constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_ALL =
(SHOW_CONTOUR_TREE_MESH_VERTEX_ID | SHOW_CONTOUR_TREE_MESH_GLOBAL_ID |
SHOW_CONTOUR_TREE_MESH_DATA_VALUE);
// InteriorForest is slightly tricky, but wants to be use the same set as BoundaryTree
constexpr vtkm::Id SHOW_INTERIOR_FOREST_VERTEX_ID = SHOW_SUPERNODE_ID;
constexpr vtkm::Id SHOW_INTERIOR_FOREST_GLOBAL_ID = SHOW_BOUNDARY_TREE_GLOBAL_ID;
constexpr vtkm::Id SHOW_INTERIOR_FOREST_DATA_VALUE = SHOW_BOUNDARY_TREE_DATA_VALUE;
constexpr vtkm::Id SHOW_INTERIOR_FOREST_MESH_REGULAR_ID = SHOW_BOUNDARY_TREE_MESH_REGULAR_ID;
constexpr vtkm::Id SHOW_INTERIOR_FOREST_MESH_SORT_ID = SHOW_BOUNDARY_TREE_MESH_SORT_ID;
constexpr vtkm::Id SHOW_INTERIOR_FOREST_ALL =
(SHOW_INTERIOR_FOREST_VERTEX_ID | SHOW_INTERIOR_FOREST_GLOBAL_ID |
SHOW_INTERIOR_FOREST_DATA_VALUE | SHOW_INTERIOR_FOREST_MESH_REGULAR_ID |
SHOW_INTERIOR_FOREST_MESH_SORT_ID);
constexpr vtkm::Id SHOW_ALL_STRUCTURE =
(SHOW_REGULAR_STRUCTURE | SHOW_SUPER_STRUCTURE | SHOW_HYPER_STRUCTURE);
constexpr vtkm::Id SHOW_BASIC_IDS = (SHOW_DATA_VALUE | SHOW_MESH_SORT_ID);
constexpr vtkm::Id SHOW_ALL_IDS =
(SHOW_GLOBAL_ID | SHOW_DATA_VALUE | SHOW_MESH_REGULAR_ID | SHOW_MESH_SORT_ID | SHOW_NODE_ID |
SHOW_SUPERPARENT | SHOW_ARC_ID);
constexpr vtkm::Id SHOW_BASIC_SUPERIDS = (SHOW_SUPERNODE_ID | SHOW_ITERATION);
constexpr vtkm::Id SHOW_ALL_SUPERIDS =
(SHOW_SUPERNODE_ID | SHOW_HYPERPARENT | SHOW_ITERATION | SHOW_SUPERARC_ID);
constexpr vtkm::Id SHOW_BASIC_HYPERIDS = SHOW_HYPERNODE_ID;
constexpr vtkm::Id SHOW_ALL_HYPERIDS = (SHOW_HYPERNODE_ID | SHOW_HYPERARC_ID);
constexpr vtkm::Id SHOW_REGULAR_SIMPLE = (SHOW_REGULAR_STRUCTURE | SHOW_BASIC_IDS);
constexpr vtkm::Id SHOW_REGULAR_BOUNDARY =
(SHOW_REGULAR_STRUCTURE | SHOW_BASIC_IDS | SHOW_BOUNDARY_NODES);
constexpr vtkm::Id SHOW_REGULAR_CRITICAL_BOUNDARY =
(SHOW_REGULAR_STRUCTURE | SHOW_BASIC_IDS | SHOW_CRITICAL_BOUNDARY_NODES);
constexpr vtkm::Id SHOW_SUPER_SIMPLE =
(SHOW_SUPER_STRUCTURE | SHOW_BASIC_IDS | SHOW_BASIC_SUPERIDS);
constexpr vtkm::Id SHOW_BOUNDARY_INTERIOR_DIVISION =
(SHOW_SUPER_STRUCTURE | SHOW_BASIC_IDS | SHOW_BASIC_SUPERIDS | SHOW_NECESSARY_SUPERNODES);
constexpr vtkm::Id SHOW_SUPER_AND_HYPER_SIMPLE =
(SHOW_SUPER_SIMPLE | SHOW_HYPER_STRUCTURE | SHOW_HYPERNODE_ID);
constexpr vtkm::Id SHOW_ALL_STANDARD =
(SHOW_ALL_STRUCTURE | SHOW_ALL_IDS | SHOW_ALL_SUPERIDS | SHOW_ALL_HYPERIDS);
constexpr vtkm::Id SHOW_HIERARCHICAL_STANDARD =
(SHOW_SUPER_STRUCTURE | SHOW_HYPER_STRUCTURE | SHOW_ALL_IDS | SHOW_ALL_SUPERIDS |
SHOW_ALL_HYPERIDS);
// 1. Routine for printing dot for contour tree regular / super / hyper structure
VTKM_CONT
template <typename T, typename StorageType, typename MeshType, typename VectorType>
std::string ContourTreeDotGraphPrint(
const std::string& label, // the label to use as title for the graph
MeshType& mesh, // the underlying mesh for the contour tree
const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler*
localToGlobalIdRelabeler, // relabler needed to compute global ids
const vtkm::cont::ArrayHandle<T, StorageType>& field,
vtkm::worklet::contourtree_augmented::ContourTree& contourTree, // the contour tree itself
const vtkm::Id showMask = SHOW_ALL_STANDARD, // mask with flags for what elements to show
// const vtkm::worklet::contourtree_augmented::IdArrayType &necessaryFlags = vtkm::worklet::contourtree_augmented::IdArrayType(),
// array with flags for "necessary"
const VectorType& perNodeValues = VectorType()) // an arbitrary vector of values
{ // ContourTreeSuperDotGraphPrint()
// initialise a string stream to capture the output
std::stringstream outStream;
// now grab portals to all the variables we will need
auto nodesPortal = contourTree.Nodes.ReadPortal();
auto arcsPortal = contourTree.Arcs.ReadPortal();
auto superparentsPortal = contourTree.Superparents.ReadPortal();
auto supernodesPortal = contourTree.Supernodes.ReadPortal();
auto superarcsPortal = contourTree.Superarcs.ReadPortal();
auto hyperparentsPortal = contourTree.Hyperparents.ReadPortal();
auto whenTransferredPortal = contourTree.WhenTransferred.ReadPortal();
auto hypernodesPortal = contourTree.Hypernodes.ReadPortal();
auto hyperarcsPortal = contourTree.Hyperarcs.ReadPortal();
// auto necessaryFlagsPortal = necessaryFlags.ReadPortal();
auto perNodeValuesPortal = perNodeValues.ReadPortal();
// work out how long the computed value is
int nodeValueType = vtkm::worklet::contourtree_distributed::BAD_PER_NODE_VALUES;
vtkm::Id perNodeSize = perNodeValues.GetNumberOfValues();
if (perNodeSize == 0)
nodeValueType = vtkm::worklet::contourtree_distributed::NO_PER_NODE_VALUES;
else if (perNodeSize == contourTree.Nodes.GetNumberOfValues())
nodeValueType = vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES;
else if (perNodeSize == contourTree.Supernodes.GetNumberOfValues())
nodeValueType = vtkm::worklet::contourtree_distributed::PER_SUPER_NODE_VALUES;
else if (perNodeSize == contourTree.Hypernodes.GetNumberOfValues())
nodeValueType = vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES;
else
{ // error message
outStream << "ERROR in ContourTreeDotGraphPrint().\n";
outStream << "Per node values array must be empty, or\n";
outStream << "Same length as regular nodes ("
<< std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< contourTree.Nodes.GetNumberOfValues() << "), or\n";
outStream << "Same length as super nodes ("
<< std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< contourTree.Supernodes.GetNumberOfValues() << "), or\n";
outStream << "Same length as hyper nodes ("
<< std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< contourTree.Hypernodes.GetNumberOfValues() << ")\n";
outStream << "Actual length was ("
<< std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< perNodeValues.GetNumberOfValues() << ")\n";
} // error message
// print the header information
outStream << "digraph ContourTree\n\t{\n";
outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n";
outStream << "\t// Nodes" << std::endl;
auto meshSortOrderPortal = mesh.SortOrder.ReadPortal();
auto globalIds = mesh.GetGlobalIdsFromSortIndices(contourTree.Nodes, localToGlobalIdRelabeler);
auto globalIdsPortal = globalIds.ReadPortal();
auto dataValuesPortal = field.ReadPortal();
// loop through all of the nodes in the regular list
for (vtkm::Id node = 0; node < contourTree.Nodes.GetNumberOfValues(); node++)
{ // per node
// the nodes array is actually sorted by superarc, but the superarcs array is not
// so we ignore the nodes array and work directly with the node #
vtkm::Id sortID = nodesPortal.Get(node);
// retrieve the regular ID
vtkm::Id regularID = meshSortOrderPortal.Get(sortID);
// retrieve the global ID
vtkm::Id globalID = globalIdsPortal.Get(sortID);
// retrieve the values
auto dataValue = dataValuesPortal.Get(regularID);
// retrieve the superparent
vtkm::Id superparent = superparentsPortal.Get(sortID);
// and retrieve the iteration #
vtkm::Id iteration =
vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(superparent));
// work out the super ID & hyper ID
vtkm::Id superID = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
vtkm::Id hyperID = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
vtkm::Id nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR;
// test for super
if (supernodesPortal.Get(superparent) == sortID)
{ // at least super
// set super ID
superID = superparent;
// set hyperparent
hyperparent = hyperparentsPortal.Get(superID);
// set nodetype
nodeType = NODE_TYPE_SUPER;
// test for hyper
if (hypernodesPortal.Get(hyperparent) == superID)
{ // hyper node
nodeType = NODE_TYPE_HYPER;
hyperID = hyperparent;
} // hyper node
} // at least super
// now, if we don't want the regular nodes, we want to skip them entirely, so
bool showNode = false;
// regular structure always shows all nodes
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE)
showNode = true;
// super structure shows super & hyper nodes only
else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE)
showNode = (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR);
else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE)
showNode = (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER);
// if we didn't set the flag, skip the node
if (!showNode)
continue;
// print the vertex ID, which should be the sort ID & needs to be left-justified to work
outStream << "\ts" << std::setw(1) << sortID;
// print the style characteristics - node is filled and fixed size
outStream << " [style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\"";
// specify the style based on the type of node
if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR)
outStream << ",height=\"1.7in\",width=\"1.7in\",penwidth=5";
else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_SUPER)
outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=10";
else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER)
outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=15";
// shape should always be circular.
outStream << ",shape=circle";
// fill colour is grey for boundary or necessary, if these are passed in
bool isGrey = false;
// TODO: Add liesOnBoundary and isNecessary so we can define the gray value
/*
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_NODES)
isGrey = (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) && mesh.liesOnBoundary(regularID);
else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CRITICAL_BOUNDARY_NODES)
isGrey = (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) && mesh.isNecessary(regularID);
else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_NECESSARY_SUPERNODES)
isGrey = (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE) // skip if superstructure not shown
&& !vtkm::worklet::contourtree_augmented::NoSuchElement(superID) // ignore non-super nodes
&& (necessaryFlags.GetNumberOfValues() == contourTree.Supernodes.GetNumberOfValues()) // skip if necessary flags array is wrong size
&& necessaryFlagsPortal.Get(superID);
*/
// after setting the flag, its easy
outStream << (isGrey ? ",fillcolor=grey" : ",fillcolor=white");
// stroke colour depends on iteration
outStream << ",color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS];
// start printing the label
outStream << ",label=\"";
// print the global ID
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_GLOBAL_ID)
outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< globalID << "\\n";
// print the value
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_DATA_VALUE)
outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< dataValue << "\\n";
// print the regular & sort IDs
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_REGULAR_ID)
outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< regularID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_SORT_ID)
outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID
<< "\\n";
// and the node ID
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_NODE_ID)
outStream << "n " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << node
<< "\\n";
// print the superparent
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERPARENT)
outStream << "sp" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< superparent << "\\n";
// add arbitrary per node value if it is regular in nature
if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) &&
(nodeValueType == vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES))
outStream << "x " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< perNodeValuesPortal.Get(regularID) << "\\n";
// we now want to add labelling information specific to supernodes, but also present in hypernodes
if (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR)
{ // at least super
// print the super node ID
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERNODE_ID)
outStream << "SN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< superID << "\\n";
// print the hyperparent as well
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERPARENT)
outStream << "HP" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< hyperparent << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_ITERATION)
outStream << "IT" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< iteration << "\\n";
// add arbitrary per node value if it is super in nature
if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) &&
(nodeValueType == vtkm::worklet::contourtree_distributed::PER_SUPER_NODE_VALUES))
outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< perNodeValuesPortal.Get(superID) << "\\n";
} // at least super
// now add even more for hypernodes
if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER)
{ // hyper node
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERNODE_ID)
outStream << "HN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< hyperID << "\\n";
// add arbitrary per node value if it is hyper in nature
if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) &&
(nodeValueType == vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES))
outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< perNodeValuesPortal.Get(hyperID) << "\\n";
} // hyper node
outStream << "\"]" << std::endl;
} // per node
// always show the null node
outStream << "\t// Null Node" << std::endl;
outStream
<< "\tNULL "
"[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=\"0.5in\","
"width=\"0.5in\",penwidth=1,shape=circle,fillcolor=white,color=black,label=\"NULL\"]"
<< std::endl;
// start the arcs
outStream << "\t// Arcs" << std::endl;
// now add regular arcs (if requested)
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE)
for (vtkm::Id node = 0; node < contourTree.Nodes.GetNumberOfValues(); node++)
{ // per node
// retrieve the "to" end
vtkm::Id to = arcsPortal.Get(node);
// if "to" is NSE, it's the root node
if (vtkm::worklet::contourtree_augmented::NoSuchElement(to))
outStream << "\ts" << std::setw(1) << node << " -> NULL [penwidth=2";
else
{ // actual node
// mask out the flags to get the target node
to = vtkm::worklet::contourtree_augmented::MaskedIndex(to);
// since we're using sort IDs, we compare them
if (node < to)
outStream << "\ts" << std::setw(1) << to << " -> s" << std::setw(1) << node
<< " [dir=back,penwidth=3";
else
outStream << "\ts" << std::setw(1) << node << " -> s" << std::setw(1) << to
<< " [penwidth=3";
} // actual node
// set the color based on the from vertex
// retrieve the superparent
vtkm::Id superparent = superparentsPortal.Get(node);
vtkm::Id iteration =
vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(superparent));
outStream << ",color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS];
if (showMask & SHOW_ARC_ID)
outStream << ",label=\"A" << node << "\"";
outStream << "]" << std::endl;
} // per node
// show superarcs if requested
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE)
for (vtkm::Id supernode = 0; supernode < contourTree.Supernodes.GetNumberOfValues();
supernode++)
{ // per supernode
// retrieve the sort ID
vtkm::Id from = supernodesPortal.Get(supernode);
// retrieve the "to" end
vtkm::Id toSuper = superarcsPortal.Get(supernode);
// test for "NSE"
if (vtkm::worklet::contourtree_augmented::NoSuchElement(toSuper))
outStream << "\ts" << std::setw(1) << from << " -> NULL [penwidth=4";
else
{ // supernode
// mask out the ascending flag & convert to sort ID
vtkm::Id to =
supernodesPortal.Get(vtkm::worklet::contourtree_augmented::MaskedIndex(toSuper));
// now test for ascending with sort IDs as before
if (from < to)
outStream << "\ts" << std::setw(1) << to << " -> s" << std::setw(1) << from
<< " [dir=back,penwidth=7";
else
outStream << "\ts" << std::setw(1) << from << " -> s" << std::setw(1) << to
<< " [penwidth=7";
} // supernode
// set the color based on the from vertex
vtkm::Id iteration =
vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(supernode));
outStream << ",color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS];
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERARC_ID)
outStream << ",label=\"SA" << supernode << "\"";
outStream << "]" << std::endl;
} // per supernode
// add hyper arcs if requested
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE)
for (vtkm::Id hypernode = 0; hypernode < contourTree.Hypernodes.GetNumberOfValues();
hypernode++)
{ // per hypernode
// retrieve the sort ID
vtkm::Id fromSuper = hypernodesPortal.Get(hypernode);
vtkm::Id from = supernodesPortal.Get(fromSuper);
// retrieve the "to" end
vtkm::Id toSuper = hyperarcsPortal.Get(hypernode);
// test for "NSE"
if (vtkm::worklet::contourtree_augmented::NoSuchElement(toSuper))
outStream << "\ts" << std::setw(1) << from << " -> NULL [penwidth=6";
else
{ // hypernode
// mask out the ascending flag & convert to sort ID
vtkm::Id to =
supernodesPortal.Get(vtkm::worklet::contourtree_augmented::MaskedIndex(toSuper));
// now test for ascending with sort IDs as before
if (from < to)
outStream << "\ts" << std::setw(1) << to << " -> s" << std::setw(1) << from
<< " [dir=back,penwidth=12";
else
outStream << "\ts" << std::setw(1) << from << " -> s" << std::setw(1) << to
<< " [penwidth=12";
} // hypernode
// set the color based on the from vertex
vtkm::Id iteration =
vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(fromSuper));
outStream << ",color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS];
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERARC_ID)
outStream << ",label=\"HA" << hypernode << "\"";
outStream << "]" << std::endl;
} // per hypernode
// print the footer information
outStream << "\t}\n";
// now return the string
return outStream.str();
} // ContourTreeSuperDotGraphPrint()
// 2. Simple routine to dump out nodes / edges for cross-checking
VTKM_CONT
template <typename FieldType>
std::string ContourTreeMeshDotGraphPrint(
const std::string& label, // the label to use as title for the graph
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh, // the mesh itself
const vtkm::Id showMask = SHOW_CONTOUR_TREE_MESH_ALL) // mask with flags for what elements to show
{ // ContourTreeMeshDotGraphPrint()
// initialise a string stream to capture the output
std::stringstream outStream;
// now grab portals to all the variables we will need
auto globalMeshIndexPortal = mesh.GlobalMeshIndex.ReadPortal();
auto meshSortedValuesPortal = mesh.SortedValues.ReadPortal();
auto meshNeighborConnectivityPortal = mesh.NeighborConnectivity.ReadPortal();
auto meshNeighborOffsetsPortal = mesh.NeighborOffsets.ReadPortal();
// print the header information
outStream << "digraph ContourTreeMesh\n\t{\n";
outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n";
outStream << "\t// Nodes" << std::endl;
// loop through all vertices
for (vtkm::Id vertex = 0; vertex < mesh.GetNumberOfVertices(); vertex++)
{ // per vertex
// work out the various ID's
vtkm::Id globalID = globalMeshIndexPortal.Get(vertex);
auto dataValue = meshSortedValuesPortal.Get(vertex);
// print the vertex
outStream << "\tr" << std::setw(1) << vertex;
outStream << "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height="
"\"1.7in\",width=\"1.7in\",penwidth=5,shape=circle";
outStream << ",fillcolor=white";
outStream << ",label=\"";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_VERTEX_ID)
outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << vertex
<< "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_GLOBAL_ID)
outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< globalID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_DATA_VALUE)
outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< dataValue << "\\n";
outStream << "\"];\n";
} // per vertex
// now print out the edges
for (vtkm::Id vertex = 0; vertex < mesh.NeighborOffsets.GetNumberOfValues(); vertex++)
{ // per vertex
// find iterators for the block of edges for this vertex
vtkm::Id neighboursBegin = meshNeighborOffsetsPortal.Get(vertex);
vtkm::Id neighboursEnd = (vertex < mesh.GetNumberOfVertices() - 1)
? meshNeighborOffsetsPortal.Get(vertex + 1)
: mesh.NeighborConnectivity.GetNumberOfValues();
// now loop through the neighbours
for (vtkm::Id whichNbr = neighboursBegin; whichNbr != neighboursEnd; ++whichNbr)
{ // per neighbour
vtkm::Id nbrID = meshNeighborConnectivityPortal.Get(whichNbr);
// skip if the neighbour is higher (use sim. of simp.)
if ((meshSortedValuesPortal.Get(nbrID) > meshSortedValuesPortal.Get(vertex)) ||
((meshSortedValuesPortal.Get(nbrID) == meshSortedValuesPortal.Get(vertex)) &&
(nbrID > vertex)))
// output the edge
outStream << "\tr" << std::setw(1) << nbrID << " -> r" << std::setw(1) << vertex
<< " [penwidth=3]" << std::endl;
else
outStream << "\tr" << std::setw(1) << vertex << " -> r" << std::setw(1) << nbrID
<< " [dir=back,penwidth=3]" << std::endl;
} // per neighbour
} // per vertex
// close the graph
outStream << "\t}" << std::endl;
// now return the string
return outStream.str();
} // ContourTreeMeshDotGraphPrint()
// 3. All purpose routine to dump out the contents for comparison with contour tree
VTKM_CONT
template <typename T, typename StorageType, typename MeshType, typename MeshBoundaryExecObjType>
std::string BoundaryTreeDotGraphPrint(
const std::string& label, // the label to use as title for the graph
MeshType& mesh, // the underlying mesh for the contour tree
MeshBoundaryExecObjType&
meshBoundaryExecutionObject, // the boundary description need to determin if a vertex is on the boundary
vtkm::worklet::contourtree_distributed::BoundaryTree& boundaryTree, // the boundary tree itself
const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler*
localToGlobalIdRelabeler, // relabler needed to compute global ids
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::Id showMask = vtkm::worklet::contourtree_distributed::
SHOW_BOUNDARY_TREE_ALL, // mask with flags for what elements to show
const bool printHeaderAndFooter = true)
{ // BoundaryTreeDotGraphPrint()
// initialise a string stream to capture the output
std::stringstream outStream;
// now grab portals to all the variables we will need
auto vertexIndexPortal = boundaryTree.VertexIndex.ReadPortal();
auto superarcsPortal = boundaryTree.Superarcs.ReadPortal();
// if requested
if (printHeaderAndFooter)
{ // print header
// print the header information
outStream << "digraph BoundaryTree\n\t{\n";
outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n";
outStream << "\t// Nodes" << std::endl;
} // print header
// prercompute the mesh boundary
// TODO: This should be done in parallel. We have the basic code but for printing this is fine for now
vtkm::cont::ArrayHandle<bool> liesOnBoundary;
{
vtkm::worklet::contourtree_augmented::IdArrayType boundaryVertexArray;
vtkm::worklet::contourtree_augmented::IdArrayType boundaryVertexSortIndexArray;
mesh.GetBoundaryVertices(boundaryVertexArray, // output
boundaryVertexSortIndexArray, // output
&meshBoundaryExecutionObject //input
);
// TODO Add option for boundary critical only
auto boundaryVertexArrayPortal = boundaryVertexArray.ReadPortal();
// vtkm::cont::ArrayHandle<vtkm::Range> rangeArray = vtkm::cont::ArrayRangeCompute(mesh.SortOrder);
// vtkm::Id maxId = static_cast<vtkm::Id>(rangeArray.ReadPortal().Get(0).Max) + 1;
liesOnBoundary.Allocate(mesh.SortOrder.GetNumberOfValues());
auto liesOnBoundaryWritePortal = liesOnBoundary.WritePortal();
vtkm::cont::Algorithm::Copy(
vtkm::cont::ArrayHandleConstant<bool>(false, liesOnBoundary.GetNumberOfValues()),
liesOnBoundary);
for (vtkm::Id i = 0; i < boundaryVertexArray.GetNumberOfValues(); ++i)
{
liesOnBoundaryWritePortal.Set(boundaryVertexArrayPortal.Get(i), true);
}
}
auto liesOnBoundaryPortal = liesOnBoundary.ReadPortal();
// loop through all nodes
auto meshSortOrderPortal = mesh.SortOrder.ReadPortal();
auto globalIds = mesh.GetGlobalIdsFromSortIndices(mesh.SortOrder, localToGlobalIdRelabeler);
auto globalIdsPortal = globalIds.ReadPortal();
auto dataValuesPortal = field.ReadPortal();
for (vtkm::Id node = 0; node < boundaryTree.VertexIndex.GetNumberOfValues(); node++)
{ // per node
// work out the node and it's value
vtkm::Id sortID = vertexIndexPortal.Get(node);
vtkm::Id regularID = meshSortOrderPortal.Get(sortID);
// NOTE: globalIdsPortal already looked up by meshSortOrder so we need to
// look up globalID now by node not sortID
vtkm::Id globalID = globalIdsPortal.Get(node);
auto dataValue = dataValuesPortal.Get(regularID);
// print the vertex (using global ID to simplify things for the residue)
outStream << "\tg" << std::setw(1) << globalID;
outStream << "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height="
"\"1.7in\",width=\"1.7in\",penwidth=5,shape=circle";
outStream << ",fillcolor=" << (liesOnBoundaryPortal.Get(regularID) ? "grey" : "white");
outStream << ",label=\"";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_VERTEX_ID)
outStream << "b " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << node
<< "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_GLOBAL_ID)
outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< globalID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_DATA_VALUE)
outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< dataValue << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_MESH_REGULAR_ID)
outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< regularID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_MESH_SORT_ID)
outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID
<< "\\n";
outStream << "\"];\n";
} // per vertex
// always show the null node
outStream << "\t// Null Node" << std::endl;
outStream
<< "\tNULL "
"[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=\"0.5in\","
"width=\"0.5in\",penwidth=1,shape=circle,fillcolor=white,color=black,label=\"NULL\"]"
<< std::endl;
// now print out the edges
for (vtkm::Id node = 0; node < boundaryTree.Superarcs.GetNumberOfValues(); node++)
{ // per node
// retrieve global ID of node
vtkm::Id sortID = vertexIndexPortal.Get(node);
vtkm::Id globalID = globalIdsPortal.Get(sortID);
// retrieve ID of target supernode
vtkm::Id to = superarcsPortal.Get(node);
// if this is true, it is the last pruned vertex & is omitted
if (vtkm::worklet::contourtree_augmented::NoSuchElement(to))
outStream << "\tg" << std::setw(1) << globalID << " -> NULL [penwidth=2";
else
{ // actual superarc
vtkm::Id toSort = vertexIndexPortal.Get(to);
vtkm::Id toGlobal = globalIdsPortal.Get(toSort);
if (node < to)
outStream << "\tg" << std::setw(1) << toGlobal << " -> g" << std::setw(1) << globalID
<< " [dir=back,penwidth=3";
else
outStream << "\tg" << std::setw(1) << globalID << " -> g" << std::setw(1) << toGlobal
<< " [penwidth=3";
} // actual superarc
// now tidy up
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_ARC_ID)
outStream << ",label=\"BA" << node << "\"";
outStream << "]" << std::endl;
} // per node
if (printHeaderAndFooter)
{ // print footer
outStream << "\t}" << std::endl;
} // print footer
// now return the string
return outStream.str();
} // BoundaryTreeDotGraphPrint()
// Routines for InteriorForest:
// 4. All purpose routine to dump out the contents for comparison with contour tree
VTKM_CONT
template <typename T, typename StorageType, typename MeshType, typename MeshBoundaryExecObjType>
std::string InteriorForestDotGraphPrint(
const std::string& label, // the label to use as title for the graph
vtkm::worklet::contourtree_distributed::InteriorForest& forest, // the forest in question
vtkm::worklet::contourtree_augmented::ContourTree&
contourTree, // the contour tree to which it belongs
vtkm::worklet::contourtree_distributed::BoundaryTree& boundaryTree, // the boundary tree
MeshType& mesh, // the underlying mesh for the contour tree
MeshBoundaryExecObjType&
meshBoundaryExecutionObject, // the boundary description need to determin if a vertex is on the boundary
const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler*
localToGlobalIdRelabeler, // relabler needed to compute global ids
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::Id& showMask =
vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_ALL) // mask for what to show
{ // InteriorForestDotGraphPrint()
// initialise a string stream to capture the output
std::stringstream outStream;
// now grab portals to all the variables we will need
auto supernodesPortal = contourTree.Supernodes.ReadPortal();
auto superarcsPortal = contourTree.Superarcs.ReadPortal();
auto forestAbovePortal = forest.Above.ReadPortal();
auto forestBelowPortal = forest.Below.ReadPortal();
auto forestIsNecessaryPortal = forest.IsNecessary.ReadPortal();
// print the header information
outStream << "digraph InteriorForest\n\t{\n";
outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n";
outStream << "\t// Nodes" << std::endl;
// call the boundary tree routine first, telling it to omit the header and footer
// note that since we define our mask in the same bits as BRACT, we can pass through the mask
outStream << BoundaryTreeDotGraphPrint(
label,
mesh,
meshBoundaryExecutionObject,
boundaryTree,
localToGlobalIdRelabeler,
field,
vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_ALL,
false);
// now we need to show the forest and how it relates to the boundary tree
// note - we will ignore the boundary tree Mesh Indices array for now
auto meshSortOrderPortal = mesh.SortOrder.ReadPortal();
auto globalIds = mesh.GetGlobalIdsFromSortIndices(mesh.SortOrder, localToGlobalIdRelabeler);
auto globalIdsPortal = globalIds.ReadPortal();
auto dataValuesPortal = field.ReadPortal();
// loop through all of the supernodes in the contour tree
for (vtkm::Id supernode = 0; supernode < contourTree.Supernodes.GetNumberOfValues(); supernode++)
{ // per supernode
// retrieve the various IDs for the supernode
vtkm::Id sortID = supernodesPortal.Get(supernode);
vtkm::Id regularID = meshSortOrderPortal.Get(sortID);
// NOTE: globalIdsPortal already looked up by meshSortOrder so we need to
// look up globalID now by supernode not sortID
vtkm::Id globalID = globalIdsPortal.Get(supernode);
auto dataValue = dataValuesPortal.Get(regularID);
// vertices marked "necessary" are in the interior of the BRACT, but not all are in the BRACT
// but the ones in the BRACT always have above/below pointing to themselves, so we test that
if (forestIsNecessaryPortal.Get(supernode) && (forestAbovePortal.Get(supernode) == globalID) &&
(forestBelowPortal.Get(supernode) == globalID))
continue;
// now print out the node
// print the vertex
outStream << "\tg" << std::setw(1) << globalID;
outStream << "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height="
"\"1.7in\",width=\"1.7in\",penwidth=5,shape=circle";
outStream << ",fillcolor=white";
outStream << ",label=\"";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_VERTEX_ID)
outStream << "SN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< supernode << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_GLOBAL_ID)
outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< globalID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_DATA_VALUE)
outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< dataValue << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_MESH_REGULAR_ID)
outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< regularID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_MESH_SORT_ID)
outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID
<< "\\n";
outStream << "\"];\n";
} // per supernode
// now loop through the superarcs in the contour tree
for (vtkm::Id supernode = 0; supernode < contourTree.Supernodes.GetNumberOfValues(); supernode++)
{ // per supernode / superarc
// retrieve the various IDs for the supernode
vtkm::Id sortID = supernodesPortal.Get(supernode);
vtkm::Id globalID = globalIdsPortal.Get(sortID);
// for nodes not necessary, show their superarc
if (!forestIsNecessaryPortal.Get(supernode))
{ // not necessary
// retrieve the target of its superarc
vtkm::Id superarc = superarcsPortal.Get(supernode);
// check to see if it exists
if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarc))
continue;
// separate out the ID
vtkm::Id superTo = vtkm::worklet::contourtree_augmented::MaskedIndex(superarc);
vtkm::Id toSort = supernodesPortal.Get(superTo);
vtkm::Id toGlobal = globalIdsPortal.Get(toSort);
// then print out the edge
if (contourtree_augmented::IsAscending(superTo))
outStream << "\tg" << std::setw(1) << toGlobal << " -> g" << globalID
<< "[dir=back,penwidth=3]" << std::endl;
else
outStream << "\tg" << std::setw(1) << globalID << " -> g" << toGlobal << "[penwidth=3]"
<< std::endl;
} // not necessary
else if ((forestAbovePortal.Get(supernode) != globalID) ||
(forestBelowPortal.Get(supernode) != globalID))
{ // attachment point
// all others are attachment points and have a valid above / below
outStream << "\tg" << std::setw(1) << forestAbovePortal.Get(supernode) << " -> g"
<< std::setw(1) << globalID << "[penwidth=1,style=dotted,label=above,dir=back]"
<< std::endl;
outStream << "\tg" << std::setw(1) << globalID << " -> g" << forestBelowPortal.Get(supernode)
<< "[penwidth=1,style=dotted,label=below]" << std::endl;
} // attachment point
} // per supernode / superarc
// print the footer
outStream << "\t}" << std::endl;
// now return the string
return outStream.str();
} // InteriorForestDotGraphPrint()
// 5. Routine to print regular/super/hyper structure with similar options to contour tree
VTKM_CONT
template <typename FieldType>
// template <typename FieldType, typename VectorType>
std::string HierarchicalContourTreeDotGraphPrint(
const std::string& label, // the label to use as title for the graph
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>&
hierarchicalTree, // the hierarchical contour tree itself
const vtkm::Id showMask = vtkm::worklet::contourtree_distributed::
SHOW_HIERARCHICAL_STANDARD) // mask with flags for what elements to show
// const unsigned long showMask = vtkm::worklet::contourtree_distributed::SHOW_HIERARCHICAL_STANDARD, // mask with flags for what elements to show
// const VectorType &perNodeValues = VectorType(0)) // an arbitrary vector of values
{ // HierarchicalContourTreeDotGraphPrint()
// initialise a string stream to capture the output
std::stringstream outStream;
// now grab portals to all the variables we will need
auto regularNodeGlobalIdsPortal = hierarchicalTree.RegularNodeGlobalIds.ReadPortal();
auto dataValuesPortal = hierarchicalTree.DataValues.ReadPortal();
auto regularNodeSortOrderPortal = hierarchicalTree.RegularNodeSortOrder.ReadPortal();
auto regular2supernodePortal = hierarchicalTree.Regular2Supernode.ReadPortal();
auto superparentsPortal = hierarchicalTree.Superparents.ReadPortal();
auto supernodesPortal = hierarchicalTree.Supernodes.ReadPortal();
auto superarcsPortal = hierarchicalTree.Superarcs.ReadPortal();
auto hyperparentsPortal = hierarchicalTree.Hyperparents.ReadPortal();
auto super2hypernodePortal = hierarchicalTree.Super2Hypernode.ReadPortal();
auto whichRoundPortal = hierarchicalTree.WhichRound.ReadPortal();
auto whichIterationPortal = hierarchicalTree.WhichIteration.ReadPortal();
auto hypernodesPortal = hierarchicalTree.Hypernodes.ReadPortal();
auto hyperarcsPortal = hierarchicalTree.Hyperarcs.ReadPortal();
// TODO: Resolve passing conventions for per node values
// auto perNodeValuesPortal = perNodeValues.ReadPortal();
// work out how long the computed value is
// int nodeValueType = vtkm::worklet::contourtree_distributed::BAD_PER_NODE_VALUES;
// vtkm::Id perNodeSize = perNodeValues.GetNumberOfValues();
// if (perNodeSize == 0)
// nodeValueType = vtkm::worklet::contourtree_distributed::NO_PER_NODE_VALUES;
// else if (perNodeSize == hierarchicalTree.Nodes.GetNumberOfValues())
// nodeValueType = vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES;
// else if (perNodeSize == hierarchicalTree.Supernodes.GetNumberOfValues())
// nodeValueType = vtkm::worklet::contourtree_distributed::PER_SUPER_NODE_VALUES;
// else if (perNodeSize == hierarchicalTree.Hypernodes.GetNumberOfValues())
// nodeValueType = vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES;
// else
// { // error message
// outStream << "ERROR in HierarchicalContourTreeDotGraphPrint().\n";
// outStream << "Per node values array must be empty, or\n";
// outStream << "Same length as regular nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hierarchicalTree.Nodes.GetNumberOfValues() << "), or\n";
// outStream << "Same length as super nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hierarchicalTree.Supernodes.GetNumberOfValues() << "), or\n";
// outStream << "Same length as hyper nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hierarchicalTree.Hypernodes.GetNumberOfValues() << ")\n";
// outStream << "Actual length was (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues.GetNumberOfValues() << ")\n";
// } // error message
// print the header information
outStream << "digraph HierarchicalContourTree\n\t{\n";
outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n";
outStream << "\t// Nodes" << std::endl;
// loop through all of the nodes in the regular list
for (vtkm::Id node = 0; node < hierarchicalTree.RegularNodeGlobalIds.GetNumberOfValues(); node++)
{ // per node
// Since the superparent for an attachment point is set to another supernode, reset the calculation here
//vtkm::Id whichRound = maskedIndex(hierarchicalTree.WhichRound[superID]);
//vtkm::Id whichIteration = maskedIndex(hierarchicalTree.WhichIteration[superID]);
// the regular ID in this case is the node itself
vtkm::Id regularID = node;
// for a sort ID, we will take the sort order vector
vtkm::Id sortID = regularNodeSortOrderPortal.Get(node);
// retrieve the global ID
vtkm::Id globalID = regularNodeGlobalIdsPortal.Get(node);
// retrieve the values
auto dataValue = dataValuesPortal.Get(node);
// retrieve the superparent
vtkm::Id superparent = superparentsPortal.Get(node);
// and retrieve the iteration #
vtkm::Id whichRound =
vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(superparent));
vtkm::Id whichIteration =
vtkm::worklet::contourtree_augmented::MaskedIndex(whichIterationPortal.Get(superparent));
// work out the super ID & hyper ID
vtkm::Id superID = regular2supernodePortal.Get(node);
vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
vtkm::Id hyperID = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
vtkm::Id nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR;
// test for super
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(superID))
{ // at least super
// set hyperparent
hyperparent = hyperparentsPortal.Get(superID);
// set nodetype
nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_SUPER;
// retrieve hyper ID
hyperID = super2hypernodePortal.Get(superparent);
// test it
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(hyperID))
{ // hyper node
nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER;
} // hyper node
} // at least super
// now, if we don't want the regular nodes, we want to skip them entirely, so
bool showNode = false;
// regular structure always shows all nodes
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE)
showNode = true;
// super structure shows super & hyper nodes only
else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE)
showNode = (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR);
else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE)
showNode = (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER);
// if we didn't set the flag, skip the node
if (!showNode)
continue;
// print the vertex ID, which should be the sort ID & needs to be left-justified to work
outStream << "\ts" << std::setw(1) << sortID;
// print the style characteristics - node is filled and fixed size
outStream << " [style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\"";
// specify the style based on the type of node
if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR)
outStream << ",height=\"1.7in\",width=\"1.7in\",penwidth=5";
else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_SUPER)
outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=10";
else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER)
outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=15";
// shape should always be circular.
outStream << ",shape=circle";
// after setting the flag, its easy
outStream << ",fillcolor=white";
// stroke colour depends on which round
outStream << ",color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS];
// start printing the label
outStream << ",label=\"";
// print the global ID
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_GLOBAL_ID)
outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< globalID << "\\n";
// print the value
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_DATA_VALUE)
outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< dataValue << "\\n";
// print the regular & sort IDs
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_REGULAR_ID)
outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< regularID << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_SORT_ID)
outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID
<< "\\n";
// print the superparent
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERPARENT)
outStream << "sp" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< superparent << "\\n";
// add arbitrary per node value if it is regular in nature
// if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES))
// outStream << "x " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues[regularID] << "\\n";
// we now want to add labelling information specific to supernodes, but also present in hypernodes
if (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR)
{ // at least super
// print the super node ID
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERNODE_ID)
outStream << "SN" << std::setw(INDEX_WIDTH) << superID << "\\n";
// print the hyperparent as well
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERPARENT)
outStream << "HP" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< hyperparent << "\\n";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_ITERATION)
outStream << "IT" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< whichRound << "." << whichIteration << "\\n";
// add arbitrary per node value if it is super in nature
// if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == PER_SUPER_NODE_VALUES))
// outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues[superID] << "\\n";
} // at least super
// now add even more for hypernodes
if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER)
{ // hyper node
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERNODE_ID)
outStream << "HN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH)
<< hyperID << "\\n";
// add arbitrary per node value if it is hyper in nature
// if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES))
// outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues[hyperID] << "\\n";
} // hyper node
outStream << "\"]" << std::endl;
} // per node
// always show the null node
outStream << "\t// Null Node" << std::endl;
outStream
<< "\tNULL "
"[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=\"0.5in\","
"width=\"0.5in\",penwidth=1,shape=circle,fillcolor=white,color=black,label=\"NULL\"]"
<< std::endl;
// now show superarc nodes
outStream << "\t// Superarc nodes\n";
// now repeat to create nodes for the middle of each superarc (to represent the superarcs themselves)
for (vtkm::Id superarc = 0; superarc < hierarchicalTree.Superarcs.GetNumberOfValues(); superarc++)
{ // per superarc
// retrieve ID of target superarc
vtkm::Id superarcFrom = superarc;
vtkm::Id superarcTo = superarcsPortal.Get(superarcFrom);
// and retrieve the iteration #
vtkm::Id whichRound =
vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(superarcFrom));
// if this is true, it is the last pruned vertex (attachment point or root) and has no superarc vertex
if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarcTo))
continue;
// print the superarc vertex
outStream << "\tSA" << std::setw(1) << superarc;
outStream << "[shape=circle,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS];
outStream << ",fillcolor=white";
outStream << ",fixedsize=true";
outStream << ",height=0.8,width=0.8";
outStream << ",label=\"";
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERARC_ID)
outStream << "SA" << std::setw(1) << superarc;
outStream << "\"];" << std::endl;
} // per superarc
// now show regular arcs - since we do not maintain a sort, they will all attach to the parent superarc
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE)
{ // showing regular nodes
outStream << "\t// Superarc nodes\n";
for (vtkm::Id regularID = 0;
regularID < hierarchicalTree.RegularNodeGlobalIds.GetNumberOfValues();
regularID++)
{ // per regular node
// if it has a superID, then we don't want to attach it to a superarc
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(
regular2supernodePortal.Get(regularID)))
continue;
// retrieve the sort ID
vtkm::Id sortID = regularNodeSortOrderPortal.Get(regularID);
// retrieve the superparent
vtkm::Id superparent = superparentsPortal.Get(regularID);
// and connect to the superarc
outStream << "\ts" << sortID << " -> SA" << superparent << "[style=dotted]" << std::endl;
} // per regular node
} // showing regular nodes
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE)
{ // showing superstructure
outStream << "\t// Superarc edges\n";
// loop through all superarcs to draw them
for (vtkm::Id superarc = 0; superarc < hierarchicalTree.Superarcs.GetNumberOfValues();
superarc++)
{ // per superarc
// retrieve ID of target supernode
vtkm::Id superarcFrom = superarc;
// retrieve the sort ID
vtkm::Id fromRegular = supernodesPortal.Get(superarcFrom);
vtkm::Id fromSort = regularNodeSortOrderPortal.Get(fromRegular);
// and retrieve the destination
vtkm::Id superarcTo = superarcsPortal.Get(superarcFrom);
// and retrieve the iteration #
vtkm::Id whichRound =
vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(superarcFrom));
// if this is true, it may be the last pruned vertex
if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarcTo))
{ // no superarc
// if it occurred on the final round, it's the global root and is shown as the NULL node
if (whichRound == hierarchicalTree.NumRounds)
outStream << "\ts" << fromSort << " -> NULL[label=\"SA" << superarc << "\",style=dotted]"
<< std::endl;
else
{ // attachment point
// otherwise, the target is actually a superarc vertex not a supernode vertex
// so we use the regular ID to retrieve the superparent which tells us which superarc we insert into
vtkm::Id regularFrom = supernodesPortal.Get(superarcFrom);
superarcTo = superparentsPortal.Get(regularFrom);
// output a suitable edge
outStream << "\ts" << fromSort << " -> SA" << superarcTo << "[label=\"S" << superarc
<< "\",style=dotted,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
} // attachment point
} // no superarc
else
{ // there is a superarc
// retrieve the ascending flag
bool ascendingSuperarc = vtkm::worklet::contourtree_augmented::IsAscending(superarcTo);
// strip out the flags
superarcTo = vtkm::worklet::contourtree_augmented::MaskedIndex(superarcTo);
// retrieve the sort ID for the to end
vtkm::Id toRegular = supernodesPortal.Get(superarcTo);
vtkm::Id toSort = regularNodeSortOrderPortal.Get(toRegular);
// how we print depends on whether the superarc ascends
if (ascendingSuperarc)
{ // ascending arc
outStream << "\ts" << toSort << " -> SA" << superarc << "[label=\"SA" << superarc
<< "\",dir=back";
outStream << ",penwidth=3,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
outStream << "\tSA" << superarc << " -> s" << fromSort << "[label=\"SA" << superarc
<< "\",dir=back";
outStream << ",penwidth=3,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
} // ascending arc
else
{ // descending arc
outStream << "\ts" << fromSort << " -> SA" << superarc << "[label=\"SA" << superarc
<< "\"";
outStream << ",penwidth=3,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
outStream << "\tSA" << superarc << " -> s" << toSort << "[label=\"SA" << superarc << "\"";
outStream << ",penwidth=3,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
} // descending arc
} // there is a superarc
} // per superarc
} // showing superstructure
if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE)
{ // show hyperstructure
outStream << "\t// Hyperarcs\n";
// now loop through the hyperarcs to draw them
for (vtkm::Id hyperarc = 0; hyperarc < hierarchicalTree.Hyperarcs.GetNumberOfValues();
hyperarc++)
{ // per hyperarc
// down convert to a sort ID
vtkm::Id fromSuper = hypernodesPortal.Get(hyperarc);
vtkm::Id fromRegular = supernodesPortal.Get(fromSuper);
vtkm::Id fromSort = regularNodeSortOrderPortal.Get(fromRegular);
// and retrieve the iteration #
vtkm::Id whichRound =
vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(fromSuper));
// and do the same with the to end
vtkm::Id toSuper = hyperarcsPortal.Get(hyperarc);
// if this is true, it is the last pruned vertex & connects to NULL
if (vtkm::worklet::contourtree_augmented::NoSuchElement(toSuper))
outStream << "\ts" << fromSort << " -> NULL[label=\"HA" << hyperarc
<< "\",penwidth=3.0,style=dotted]" << std::endl;
else
{ // not the last one
// otherwise, retrieve the ascending flag
bool ascendingHyperarc = vtkm::worklet::contourtree_augmented::IsAscending(toSuper);
// strip out the flags
toSuper = vtkm::worklet::contourtree_augmented::MaskedIndex(toSuper);
// retrieve the sort index
vtkm::Id toRegular = supernodesPortal.Get(toSuper);
vtkm::Id toSort = regularNodeSortOrderPortal.Get(toRegular);
// how we print depends on whether the hyperarc ascends
if (ascendingHyperarc)
{ // ascending arc
outStream << "\ts" << toSort << " -> s" << fromSort << "[label=\"HA" << hyperarc
<< "\",dir=back";
outStream << ",penwidth=5.0,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
} // ascending arc
else
{ // descending arc
outStream << "\ts" << fromSort << " -> s" << toSort << "[label=\"HA" << hyperarc << "\"";
outStream << ",penwidth=5.0,color="
<< vtkm::worklet::contourtree_augmented::NODE_COLORS
[whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]
<< "]" << std::endl;
} // descending arc
} // not the last one
} // per hyperarc
} // show hyperstructure
// print the footer information
outStream << "\t}\n";
// now return the string
return outStream.str();
} // HierarchicalContourTreeDotGraphPrint()
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif