Skip to content

COWPlacement

Description

Define the parameters that label, color and position a vtkCameraOrientationWidget. This allows us to use multiple camera orientation widgets in a window with the provision to relabel the axes labels and change axis colors.

To use the snippet, click the Copy to clipboard at the upper right of the code blocks.

Declaration Section

#include <vtkCameraOrientationRepresentation.h>
#include <vtkCameraOrientationWidget.h>
#include <vtkNamedColors.h>
#include <vtkStringFormatter.h>

#include <set>

namespace {

  class COWPlacement
  {

  public:
    COWPlacement()
    {
      MakeLabelColor();
    }

    virtual ~COWPlacement() = default;

  public:
    /**
     * @brief Make a camera orientation widget for a given renderer.
     *
     * position has these values
     *   0: LowerLeft,
     *   1: UpperLeft,
     *   2: LowerRight,
     *   3: UpperRight - the default
     *
     * @param ren The renderer.
     * @param lcKey The key specifying the desired labels and colors for the axes.
     * @param position Where to position the camera orientation widget.
     * @return The camera orientation widget.
     */
    vtkNew<vtkCameraOrientationWidget>
    MakeCOW(vtkRenderer* ren, std::string lcKey = "XYZ", int const& position = 3);

  private:
    typedef std::map<std::string, std::string> TAP;
    typedef std::map<std::string, TAP> TAPS;
    std::map<std::string, std::pair<TAP, TAP>> labelColor;

    /**
     * @brief Gather the defined axes labels and colors into a map.
     *
     */
    void MakeLabelColor();
  };

} // namespace

Implementation Section

namespace {
  void COWPlacement::MakeLabelColor()
  {
    vtkNew<vtkNamedColors> nc;

    // Define the axes labels and colors.
    // The three digit key refers to the positive X,Y,Z axes.
    // clang-format off
    TAPS labels {
      // Labels are: Anterior, Posterior, Dorsal, Ventral, Left, Right
      {"ADL",{{"+X", "A"}, {"+Y", "D"}, {"+Z", "L"}, {"-X", "P"}, {"-Y", "V"}, {"-Z", "R"}}},
      {"ALV",{{"+X", "A"}, {"+Y", "L"}, {"+Z", "V"}, {"-X", "P"}, {"-Y", "R"}, {"-Z", "D"}}},
      {"ADR",{{"+X", "A"}, {"+Y", "D"}, {"+Z", "R"}, {"-X", "P"}, {"-Y", "V"}, {"-Z", "L"}}},
      {"ARV",{{"+X", "A"}, {"+Y", "R"}, {"+Z", "V"}, {"-X", "P"}, {"-Y", "L"}, {"-Z", "D"}}},
      {"PDL",{{"+X", "P"}, {"+Y", "D"}, {"+Z", "L"}, {"-X", "A"}, {"-Y", "V"}, {"-Z", "R"}}},
      // Labels are: Left, Right, Superior, Inferior, Anterior, Posterior
      {"LAS",{{"+X", "L"}, {"+Y", "A"}, {"+Z", "S"}, {"-X", "R"}, {"-Y", "P"}, {"-Z", "I"}}},
      {"LPA",{{"+X", "L"}, {"+Y", "P"}, {"+Z", "A"}, {"-X", "R"}, {"-Y", "A"}, {"-Z", "S"}}},
      {"LPS",{{"+X", "L"}, {"+Y", "P"}, {"+Z", "S"}, {"-X", "R"}, {"-Y", "A"}, {"-Z", "I"}}},
      {"LSA",{{"+X", "L"}, {"+Y", "S"}, {"+Z", "A"}, {"-X", "R"}, {"-Y", "I"}, {"-Z", "A"}}},
      {"RAP",{{"+X", "R"}, {"+Y", "A"}, {"+Z", "P"}, {"-X", "L"}, {"-Y", "I"}, {"-Z", "S"}}},
      {"RAS",{{"+X", "R"}, {"+Y", "A"}, {"+Z", "S"}, {"-X", "L"}, {"-Y", "P"}, {"-Z", "I"}}},
      {"RPS",{{"+X", "R"}, {"+Y", "P"}, {"+Z", "S"}, {"-X", "L"}, {"-Y", "A"}, {"-Z", "I"}}},
      {"SAL",{{"+X", "S"}, {"+Y", "A"}, {"+Z", "L"}, {"-X", "I"}, {"-Y", "P"}, {"-Z", "R"}}},
      // Default labels.
      {"XYZ", {}},
    };
    TAP c1{
      {"+X", "IndianRed"},{"-X", "FireBrick"},
      {"+Y", "LimeGreen"},{"-Y", "DarkGreen"},
      {"+Z", "SkyBlue"}, {"-Z", "DeepSkyBlue"}
    };
    TAPS colors {
      {"ADL", c1},
      {"ALV", c1},
      {"ADR", c1},
      {"ARV", c1},
      {"PDL", c1},
      // Default colors.
      {"LAS", {}},
      {"LPS", {}},
      {"LPA", {}},
      {"LSA", {}},
      {"RAP", {}},
      {"RAS", {}},
      {"RPS", {}},
      {"SAL", {}},
      // The default.
      {"XYZ", {}},
    };
    // clang-format on

    // The keys must be the same in the labels and colors variables.
    // Get the keys.
    std::set<std::string> labelKeys;
    for (auto&& label : labels)
    {
      labelKeys.insert(label.first);
    }
    std::set<std::string> colorKeys;
    for (auto&& label : colors)
    {
      colorKeys.insert(label.first);
    }
    std::vector<std::string> commonKeys;
    std::set_intersection(labelKeys.begin(), labelKeys.end(), colorKeys.begin(),
                          colorKeys.end(), std::back_inserter(commonKeys));
    for (auto&& k : commonKeys)
    {
      this->labelColor[k] = {labels[k], colors[k]};
    }
  };

  vtkNew<vtkCameraOrientationWidget>
  COWPlacement::MakeCOW(vtkRenderer* ren, std::string lcKey, int const& position)
  {
    std::set<std::string> keys;
    for (auto&& label : this->labelColor)
    {
      keys.insert(label.first);
    }
    auto it = keys.find(lcKey);
    if (it == keys.end())
    {
      std::string res =
      "Invalid key for axes labels and colors.\nValid keys are: ";
      for (const auto& [key, value] : this->labelColor)
      {
        res += fmt::format("{:s}, ", key);
      }
      if (res.length() >= 2)
      {
        auto pos = res.length() - 2;
        res.replace(pos, 2, "");
      }
      res += "\nUsing the key: XYZ";
      vtk::println("{:}", res);
      lcKey = "XYZ";
    }
    auto lc = this->labelColor[lcKey];

    vtkNew<vtkCameraOrientationRepresentation> rep;

    switch (position)
    {
      case 0:
        rep->AnchorToLowerLeft();
        break;
      case 1:
        rep->AnchorToUpperLeft();
        break;
      case 2:
        rep->AnchorToLowerRight();
        break;
      default:
        rep->AnchorToUpperRight();
    }

    if (!lc.first.empty())
    {
      rep->SetXPlusLabelText(lc.first["+X"]);
      rep->SetXMinusLabelText(lc.first["-X"]);
      rep->SetYPlusLabelText(lc.first["+Y"]);
      rep->SetYMinusLabelText(lc.first["-Y"]);
      rep->SetZPlusLabelText(lc.first["+Z"]);
      rep->SetZMinusLabelText(lc.first["-Z"]);
    }

    if (!lc.second.empty())
    {
      vtkNew<vtkNamedColors> colors;

      rep->SetXAxisColor(colors->GetColor3d(lc.second["+X"]).GetData());
      rep->SetYAxisColor(colors->GetColor3d(lc.second["+Y"]).GetData());
      rep->SetZAxisColor(colors->GetColor3d(lc.second["+Z"]).GetData());
    }

    vtkNew<vtkCameraOrientationWidget> cow;
    cow->SetParentRenderer(ren);
    cow->SetRepresentation(rep);
    cow->Off();

    return cow;
  }

} // namespace

Usage

int main(int argc, char* argv[])
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkRenderer> ren;
  ren->SetBackground(colors->GetColor3d("ParaViewBlueGrayBkg").GetData());
  vtkNew<vtkRenderWindow> renWin;
  renWin->AddRenderer(ren);
  renWin->SetSize(640, 480);
  renWin->SetWindowName("COWPlacement");

  vtkNew<vtkRenderWindowInteractor> iRen;
  iRen->SetRenderWindow(renWin);

  auto owp = COWPlacement();
  // Important: The interactor must be set prior to enabling the widget.
  auto cow = owp.MakeCOW(ren);
  cow->On();

  auto category = "ADR";
  auto cow1 = owp.MakeCOW(ren, category, 1);
  cow1->On();

  category = "SAL";
  auto cow2 = owp.MakeCOW(ren, category, 0);
  cow2->On();

  iRen->Initialize();
  iRen->Start();

  return EXIT_SUCCESS;
}