text: /*=========================================================================
   *
   *  Copyright NumFOCUS
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *         http://www.apache.org/licenses/LICENSE-2.0.txt
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   *
   *=========================================================================*/
  #ifndef itkMetaDTITubeConverter_hxx
  #define itkMetaDTITubeConverter_hxx
  
  #include "itkMath.h"
  
  namespace itk
  {
  
  template <unsigned int VDimension>
  auto
  MetaDTITubeConverter<VDimension>::CreateMetaObject() -> MetaObjectType *
  {
    return dynamic_cast<MetaObjectType *>(new DTITubeMetaObjectType);
  }
  
  /** Convert a MetaDTITube into an Tube SpatialObject  */
  template <unsigned int VDimension>
  auto
  MetaDTITubeConverter<VDimension>::MetaObjectToSpatialObject(const MetaObjectType * mo) -> SpatialObjectPointer
  {
    const auto * tube = dynamic_cast<const MetaDTITube *>(mo);
    if (tube == nullptr)
    {
      itkExceptionMacro(<< "Can't downcast MetaObject to MetaDTITube");
    }
    DTITubeSpatialObjectPointer tubeSO = DTITubeSpatialObjectType::New();
  
    tubeSO->SetTypeName("DTITubeSpatialObject");
    tubeSO->GetProperty().SetName(tube->Name());
    tubeSO->SetParentPoint(tube->ParentPoint());
    tubeSO->SetId(tube->ID());
    tubeSO->SetParentId(tube->ParentID());
    tubeSO->GetProperty().SetRed(tube->Color()[0]);
    tubeSO->GetProperty().SetGreen(tube->Color()[1]);
    tubeSO->GetProperty().SetBlue(tube->Color()[2]);
    tubeSO->GetProperty().SetAlpha(tube->Color()[3]);
  
    using TubePointType = itk::DTITubeSpatialObjectPoint<VDimension>;
  
    auto it2 = tube->GetPoints().begin();
  
    itk::CovariantVector<double, VDimension> v;
    v.Fill(0.0);
    itk::Vector<double, VDimension> t;
    t.Fill(0.0);
  
    for (unsigned int identifier = 0; identifier < tube->GetPoints().size(); ++identifier)
    {
      TubePointType pnt;
  
      using PointType = typename DTITubeSpatialObjectType::PointType;
      PointType point;
  
      for (unsigned int ii = 0; ii < VDimension; ++ii)
      {
        point[ii] = (*it2)->m_X[ii] * tube->ElementSpacing(ii);
      }
  
      // Get the fields from the metaIO
      const DTITubePnt::FieldListType & metaFields = (*it2)->GetExtraFields();
      auto                              extraIt = metaFields.begin();
      while (extraIt != metaFields.end())
      {
        // Do not add the optional fields
        if (((*extraIt).first != "r") && ((*extraIt).first != "v1x") && ((*extraIt).first != "v1y") &&
            ((*extraIt).first != "v1z") && ((*extraIt).first != "v2x") && ((*extraIt).first != "v2y") &&
            ((*extraIt).first != "v2z") && ((*extraIt).first != "tx") && ((*extraIt).first != "ty") &&
            ((*extraIt).first != "tz") && ((*extraIt).first != "red") && ((*extraIt).first != "green") &&
            ((*extraIt).first != "blue") && ((*extraIt).first != "alpha") && ((*extraIt).first != "id"))
        {
          pnt.AddField((*extraIt).first.c_str(), (*extraIt).second);
        }
        extraIt++;
      }
  
      pnt.SetPositionInObjectSpace(point);
  
      auto * tensor = new float[6];
  
      for (unsigned int ii = 0; ii < 6; ++ii)
      {
        tensor[ii] = (*it2)->m_TensorMatrix[ii];
      }
      pnt.SetTensorMatrix(tensor);
  
      delete[] tensor;
  
      // This attribute is optional
      if (Math::NotExactlyEquals((*it2)->GetField("r"), -1))
      {
        pnt.SetRadiusInObjectSpace((*it2)->GetField("r") * tube->ElementSpacing(0));
      }
  
      char vnd[] = "v1x";
      if (Math::NotExactlyEquals((*it2)->GetField(vnd), -1))
      {
        v[0] = (*it2)->GetField(vnd);
        for (unsigned int ii = 1; ii < VDimension; ++ii)
        {
          ++(vnd[2]); // x -> y -> z
          v[ii] = (*it2)->GetField(vnd);
        }
        pnt.SetNormal1InObjectSpace(v);
      }
  
      vnd[1] = '2';
      vnd[2] = 'x';
      if (Math::NotExactlyEquals((*it2)->GetField(vnd), -1))
      {
        v[0] = (*it2)->GetField(vnd);
        for (unsigned int ii = 1; ii < VDimension; ++ii)
        {
          ++(vnd[2]); // x -> y -> z
          v[ii] = (*it2)->GetField(vnd);
        }
        pnt.SetNormal1InObjectSpace(v);
      }
  
      char td[] = "tx";
      if (Math::NotExactlyEquals((*it2)->GetField(td), -1))
      {
        t[0] = (*it2)->GetField(td);
        for (unsigned int ii = 1; ii < VDimension; ++ii)
        {
          ++(td[1]); // x -> y -> z
          t[ii] = (*it2)->GetField(td);
        }
        pnt.SetTangentInObjectSpace(t);
      }
  
      if (Math::NotExactlyEquals((*it2)->GetField("red"), -1))
      {
        pnt.SetRed((*it2)->GetField("red"));
      }
  
      if (Math::NotExactlyEquals((*it2)->GetField("green"), -1))
      {
        pnt.SetGreen((*it2)->GetField("green"));
      }
  
      if (Math::NotExactlyEquals((*it2)->GetField("blue"), -1))
      {
        pnt.SetBlue((*it2)->GetField("blue"));
      }
  
      if (Math::NotExactlyEquals((*it2)->GetField("alpha"), -1))
      {
        pnt.SetAlpha((*it2)->GetField("alpha"));
      }
  
      if (Math::NotExactlyEquals((*it2)->GetField("id"), -1))
      {
        pnt.SetId(static_cast<int>((*it2)->GetField("id")));
      }
  
      tubeSO->AddPoint(pnt);
  
      it2++;
    }
    return tubeSO.GetPointer();
  }
  
  /** Convert a Tube SpatialObject into a MetaDTITube */
  template <unsigned int VDimension>
  auto
  MetaDTITubeConverter<VDimension>::SpatialObjectToMetaObject(const SpatialObjectType * spatialObject) -> MetaObjectType *
  {
    DTITubeSpatialObjectConstPointer DTITubeSO = dynamic_cast<const DTITubeSpatialObjectType *>(spatialObject);
    if (DTITubeSO.IsNull())
    {
      itkExceptionMacro(<< "Can't downcast SpatialObject to DTITubeSpatialObject");
    }
  
    auto * tube = new MetaDTITube(VDimension);
  
    // Check what are the fields to be written
    bool writeNormal1 = false;
    bool writeNormal2 = false;
    bool writeTangent = false;
    bool writeRadius = false;
    bool writeColor = false;
    bool writeAlpha = false;
    bool writeID = false;
  
    typename DTITubeSpatialObjectType::DTITubePointListType::const_iterator it;
    for (it = DTITubeSO->GetPoints().begin(); it != DTITubeSO->GetPoints().end(); ++it)
    {
      // Optional fields (written only if not default values)
      if ((*it).GetId() != -1)
      {
        writeID = true;
      }
  
      if ((*it).GetRadiusInObjectSpace() != 0.0f)
      {
        writeRadius = true;
      }
  
      unsigned int d;
      for (d = 0; d < VDimension; ++d)
      {
        if (Math::NotExactlyEquals((*it).GetNormal1InObjectSpace()[d], 0))
        {
          writeNormal1 = true;
        }
        if (Math::NotExactlyEquals((*it).GetNormal2InObjectSpace()[d], 0))
        {
          writeNormal2 = true;
        }
        if (Math::NotExactlyEquals((*it).GetTangentInObjectSpace()[d], 0))
        {
          writeTangent = true;
        }
      }
  
      // write the color if changed
      if (((*it).GetRed() != 1.0) || ((*it).GetGreen() != 0.0) || ((*it).GetBlue() != 0.0))
      {
        writeColor = true;
      }
  
      if ((*it).GetAlpha() != 1.0)
      {
        writeAlpha = true;
      }
    }
  
    // fill in the tube information
    for (it = DTITubeSO->GetPoints().begin(); it != DTITubeSO->GetPoints().end(); ++it)
    {
      auto * pnt = new DTITubePnt(VDimension);
  
      for (unsigned int d = 0; d < VDimension; ++d)
      {
        pnt->m_X[d] = (*it).GetPositionInObjectSpace()[d];
      }
  
      const DTITubePnt::FieldListType & metaFields = (*it).GetFields();
      auto                              extraIt = metaFields.begin();
      while (extraIt != metaFields.end())
      {
        pnt->AddField((*extraIt).first.c_str(), (*extraIt).second);
        extraIt++;
      }
  
      for (unsigned int d = 0; d < 6; ++d)
      {
        pnt->m_TensorMatrix[d] = (*it).GetTensorMatrix()[d];
      }
  
      // Optional fields (written only if not default values)
      if (writeID)
      {
        pnt->AddField("id", (*it).GetId());
      }
  
      if (writeRadius)
      {
        pnt->AddField("r", (*it).GetRadiusInObjectSpace());
      }
  
      if (writeNormal1)
      {
        pnt->AddField("v1x", (*it).GetNormal1InObjectSpace()[0]);
        pnt->AddField("v1y", (*it).GetNormal1InObjectSpace()[1]);
        if (VDimension == 3)
        {
          pnt->AddField("v1z", (*it).GetNormal1InObjectSpace()[2]);
        }
      }
  
      if (writeNormal2)
      {
        pnt->AddField("v2x", (*it).GetNormal2InObjectSpace()[0]);
        pnt->AddField("v2y", (*it).GetNormal2InObjectSpace()[1]);
        if (VDimension == 3)
        {
          pnt->AddField("v2z", (*it).GetNormal2InObjectSpace()[2]);
        }
      }
  
      if (writeTangent)
      {
        pnt->AddField("tx", (*it).GetTangentInObjectSpace()[0]);
        pnt->AddField("ty", (*it).GetTangentInObjectSpace()[1]);
        if (VDimension == 3)
        {
          pnt->AddField("tz", (*it).GetTangentInObjectSpace()[2]);
        }
      }
  
      // write the color if changed
      if (writeColor)
      {
        pnt->AddField("red", (*it).GetRed());
        pnt->AddField("green", (*it).GetGreen());
        pnt->AddField("blue", (*it).GetBlue());
      }
  
      if (writeAlpha)
      {
        pnt->AddField("alpha", (*it).GetAlpha());
      }
  
      tube->GetPoints().push_back(pnt);
    }
  
    tube->PointDim("x y z tensor1 tensor2 tensor3 tensor4 tensor5 tensor6");
  
    float color[4];
    for (unsigned int ii = 0; ii < 4; ++ii)
    {
      color[ii] = DTITubeSO->GetProperty().GetColor()[ii];
    }
  
    tube->Color(color);
    tube->ID(DTITubeSO->GetId());
  
    if (DTITubeSO->GetParent())
    {
      tube->ParentID(DTITubeSO->GetParent()->GetId());
    }
    tube->ParentPoint(DTITubeSO->GetParentPoint());
    tube->NPoints(static_cast<int>(tube->GetPoints().size()));
  
    return tube;
  }
  
  } // end namespace itk
  
  #endif
  