Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) TriangleMesh

 

Technical facts

 

 

 

 

 

 

./CppTriangleMesh/CppTriangleMesh.pri

 

INCLUDEPATH += \
    ../../Classes/CppTriangleMesh

SOURCES += \
    ../../Classes/CppTriangleMesh/trianglemeshtemplate.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshpoint.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshfaceorientation.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshfacefactory.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshface.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcellscreator.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcellfactory.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcell.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshhelper.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshbuilder.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshpointfactory.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcellscreatorfactory.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshwinding.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshwindings.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcreateverticalfacesstrategy.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcreateverticalfacesstrategies.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshdialog.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshbuilderimpl.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshtemplateimpl.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshcellimpl.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshbuilderimpl_pout.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshbuilderimpl_pin.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshbuilderimpl_mut.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshbuilderimpl_k.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshfaceimpl.cpp \
    ../../Classes/CppTriangleMesh/trianglemeshhelper_test.cpp

HEADERS  += \
    ../../Classes/CppTriangleMesh/trianglemeshtemplate.h \
    ../../Classes/CppTriangleMesh/trianglemeshpoint.h \
    ../../Classes/CppTriangleMesh/trianglemeshfwd.h \
    ../../Classes/CppTriangleMesh/trianglemeshfaceorientation.h \
    ../../Classes/CppTriangleMesh/trianglemeshfacefactory.h \
    ../../Classes/CppTriangleMesh/trianglemeshhelper.h \
    ../../Classes/CppTriangleMesh/trianglemeshface.h \
    ../../Classes/CppTriangleMesh/trianglemeshcellscreator.h \
    ../../Classes/CppTriangleMesh/trianglemeshcellfactory.h \
    ../../Classes/CppTriangleMesh/trianglemeshcell.h \
    ../../Classes/CppTriangleMesh/trianglemeshbuilder.h \
    ../../Classes/CppTriangleMesh/trianglemeshpointfactory.h \
    ../../Classes/CppTriangleMesh/trianglemeshcellscreatorfactory.h \
    ../../Classes/CppTriangleMesh/trianglemeshwinding.h \
    ../../Classes/CppTriangleMesh/trianglemeshwindings.h \
    ../../Classes/CppTriangleMesh/trianglemeshcreateverticalfacesstrategy.h \
    ../../Classes/CppTriangleMesh/trianglemeshcreateverticalfacesstrategies.h \
    ../../Classes/CppTriangleMesh/trianglemeshdialog.h \
    ../../Classes/CppTriangleMesh/trianglemeshbuilderimpl.h \
    ../../Classes/CppTriangleMesh/trianglemeshtemplateimpl.h \
    ../../Classes/CppTriangleMesh/trianglemeshcellimpl.h \
    ../../Classes/CppTriangleMesh/trianglemeshfaceimpl.h

OTHER_FILES += \
    ../../Classes/CppTriangleMesh/Licence.txt

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilder.h

 

#ifndef RIBI_TRIANGLEMESHBUILDER_H
#define RIBI_TRIANGLEMESHBUILDER_H

#include <string>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include "openfoamfwd.h"
#include "openfoampatchfieldtype.h"
#include "trianglemeshfwd.h"
#include "trianglemeshcreateverticalfacesstrategy.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///TriangleMeshBuilder creates all files needed by OpenFOAM
///(in the correct folder) from a collection of Cells
///These Cells can be created by TriangleMeshCellCreator
///The Cells' Faces are assigned a Boundary, but these
///may be reassigned before feeding the cells to TriangleMeshBuilder
///Use the pimpl idiom, implementation is in TriangleMeshBuilderImpl
class TriangleMeshBuilder
{
  friend class Dialog;

  explicit TriangleMeshBuilder(
    const std::vector<boost::shared_ptr<ribi::trim::Cell>>& cells,
    const std::string& mesh_filename,
    const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function,
    const CreateVerticalFacesStrategy strategy,
    const bool verbose
  );

  int CountCells() const noexcept;
  int CountFaces() const noexcept;

  private:
  TriangleMeshBuilder(const TriangleMeshBuilder& ) = delete;
  //TriangleMeshBuilder(      TriangleMeshBuilder&&) = delete;
  TriangleMeshBuilder& operator=(const TriangleMeshBuilder& ) = delete;
  //TriangleMeshBuilder& operator=(      TriangleMeshBuilder&&) = delete;
  ~TriangleMeshBuilder() noexcept;

  const std::unique_ptr<TriangleMeshBuilderImpl> m_impl;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend void boost::checked_delete<>(      TriangleMeshBuilder*);
  friend void boost::checked_delete<>(const TriangleMeshBuilder*);
  friend class boost::detail::sp_ms_deleter<      TriangleMeshBuilder>;
  friend class boost::detail::sp_ms_deleter<const TriangleMeshBuilder>;
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHBUILDER_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilder.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "trianglemeshbuilder.h"

#include <cassert>

#include <boost/make_shared.hpp>

#include "testtimer.h"
#include "trace.h"
#include "trianglemeshbuilderimpl.h"

#pragma GCC diagnostic pop

ribi::trim::TriangleMeshBuilder::TriangleMeshBuilder(
  const std::vector<boost::shared_ptr<Cell>>& cells,
  const std::string& mesh_filename,
  const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function,
  const CreateVerticalFacesStrategy strategy,
  const bool verbose
) : m_impl{std::make_unique<TriangleMeshBuilderImpl>(
        cells,
        mesh_filename,
        boundary_to_patch_field_type_function,
        strategy,
        verbose
      )
   }
{
  #ifndef NDEBUG
  Test();
  #endif
}

ribi::trim::TriangleMeshBuilder::~TriangleMeshBuilder() noexcept
{
  //To prevent incomplete TriangleMeshBuilderImpl errors
}

int ribi::trim::TriangleMeshBuilder::CountCells() const noexcept
{
  assert(m_impl);
  return m_impl->CountCells();
}

int ribi::trim::TriangleMeshBuilder::CountFaces() const noexcept
{
  assert(m_impl);
  return m_impl->CountFaces();
}

#ifndef NDEBUG
void ribi::trim::TriangleMeshBuilder::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilderimpl.h

 

#ifndef TRIANGLEMESHBUILDERIMPL_H
#define TRIANGLEMESHBUILDERIMPL_H

#include <string>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include "openfoamfwd.h"
#include "openfoampatchfieldtype.h"
#include "trianglemeshfwd.h"
#include "trianglemeshcreateverticalfacesstrategy.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

struct TriangleMeshBuilderImpl
{
  explicit TriangleMeshBuilderImpl(
    const std::vector<boost::shared_ptr<ribi::trim::Cell>>& cells,
    const std::string& mesh_filename,
    const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function,
    const CreateVerticalFacesStrategy strategy,
    const bool verbose
  );
  ~TriangleMeshBuilderImpl();
  int CountCells() const noexcept;
  int CountFaces() const noexcept;

  private:
  std::vector<boost::shared_ptr<Cell>> m_cells;
  std::vector<boost::shared_ptr<Face>> m_faces;
  const std::vector<boost::shared_ptr<Point>> m_points;

  std::string CreateBoundary(
    const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function
  ) const noexcept;
  std::pair<std::string,std::string> CreateCells() const noexcept;
  std::string CreateFaces() const noexcept;
  std::string CreateHeader() const noexcept;
  std::string CreateNodes() const noexcept;

  std::string CreateOpenFoamFaces() const noexcept;
  std::string CreateOpenFoamHeader(
    const std::string& class_name,
    const std::string& object,
    const std::string& location,
    const std::string& note = "") const noexcept;

  std::string CreateOpenFoamK() const noexcept;
  std::string CreateOpenFoamMut() const noexcept;
  std::string CreateOpenFoamNodes() const noexcept;
  std::string CreateOpenFoamPin() const noexcept;
  std::string CreateOpenFoamPout() const noexcept;
  std::string CreateOpenFoamRasProperties() const noexcept;
  std::string CreateOpenFoamT() const noexcept;

  static std::vector<boost::shared_ptr<Face>> ExtractFaces(
    const std::vector<boost::shared_ptr<Cell>>& cells
  ) noexcept;

  static std::vector<boost::shared_ptr<Point>> ExtractPoints(
    const std::vector<boost::shared_ptr<Cell>>& cells
  ) noexcept;

  std::vector<std::string> GetAllFolders() const noexcept;

  static std::string Implode(
    const std::string& seperator,
    const std::vector<ribi::foam::PointIndex>& v) noexcept;

  static std::string Implode(
    const std::string& seperator,
    const std::vector<int>& v) noexcept;

  ///no_patch_field comes first, name comes second
  static bool OrderByPatchFieldType(
    const std::string lhs_name, const std::string rhs_name,
    const ribi::foam::PatchFieldType lhs_type, const ribi::foam::PatchFieldType rhs_type
  ) noexcept;

  static std::vector<boost::shared_ptr<Face>> SortByBoundary(
    std::vector<boost::shared_ptr<Face>> faces,
    const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function
  ) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace trim
} //~namespace ribi

#endif // TRIANGLEMESHBUILDERIMPL_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilderimpl.cpp

 

#include "trianglemeshbuilderimpl.h"

#include <fstream>
#include <sstream>

#include "trianglemeshcell.h"
#include "trianglemeshface.h"
#include "fileio.h"
#include "openfoamboundaryfile.h"
#include "openfoamboundaryfileitem.h"
#include "openfoamfilenames.h"
#include "openfoamfaceindex.h"
#include "openfoampointindex.h"
#include "php.h"
#include "testtimer.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trace.h"

ribi::trim::TriangleMeshBuilderImpl::TriangleMeshBuilderImpl(
  const std::vector<boost::shared_ptr<Cell>>& cells,
  const std::string& mesh_filename,
  const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function,
  const CreateVerticalFacesStrategy
  #ifndef NDEBUG
    strategy
  #endif
  ,
  const bool verbose
) : m_cells(cells),
    //#1: Partition faces in boundaries
    m_faces(SortByBoundary(ExtractFaces(cells),boundary_to_patch_field_type_function)),
    m_points(ExtractPoints(cells))
{
  #ifndef NDEBUG
  Test();
  #endif
  const bool verbose_show_faces{false};
  const bool verbose_show_cell_indices{false};
  for (const std::string& folder: GetAllFolders())
  {
    if (!ribi::fileio::FileIo().IsFolder(folder))
    {
      ribi::fileio::FileIo().CreateFolder(folder);
    }
    assert(ribi::fileio::FileIo().IsFolder(folder));
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Remove cells with less than 8 faces or less than 8 faces with an owner"
      << std::endl
    ;
  }

  assert(
    std::count_if(m_cells.begin(),m_cells.end(),
      [strategy](const boost::shared_ptr<Cell> cell)
      {
        const std::vector<boost::shared_ptr<Face>> faces { cell->GetFaces() };
        const int n_faces_expected {
          strategy == CreateVerticalFacesStrategy::one_face_per_square ? 5 : 8
        };
        assert(static_cast<int>(faces.size()) == n_faces_expected);
        return std::count_if(faces.begin(),faces.end(),
          [](const boost::shared_ptr<Face> face)
          {
            assert(face);
            assert(face->GetConstOwner());
            return face->GetConstOwner();
          }
        ) < n_faces_expected;
      }
    ) == 0
    && "So the code below can be removed #1"
  );
  #define SO_THIS_CAN_BE_REMOVED_1
  #ifndef SO_THIS_CAN_BE_REMOVED_1
  m_cells.erase(
    std::remove_if(m_cells.begin(),m_cells.end(),
      [strategy](const boost::shared_ptr<Cell> cell)
      {
        const std::vector<boost::shared_ptr<Face>> faces { cell->GetFaces() };
        const int n_faces_expected {
          strategy == CreateVerticalFacesStrategy::one_face_per_square ? 5 : 8
        };
        assert(static_cast<int>(faces.size()) == n_faces_expected);
        return std::count_if(faces.begin(),faces.end(),
          [](const boost::shared_ptr<Face> face)
          {
            assert(face);
            assert(face->GetConstOwner());
            return face->GetConstOwner();
          }
        ) < n_faces_expected;
      }
    ),
    m_cells.end()
  );
  #endif

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Remove faces without owners"
      << std::endl
    ;
  }
  assert(
    std::count_if(m_faces.begin(),m_faces.end(),
      [](const boost::shared_ptr<const Face> face)
      {
        return !face->GetConstOwner();
      }
    ) == 0
    && "So the code below can be removed #2"
  );

  #define SO_THIS_CAN_BE_REMOVED_2
  #ifndef SO_THIS_CAN_BE_REMOVED_2
  m_faces.erase(
    std::remove_if(m_faces.begin(),m_faces.end(),
      [](const boost::shared_ptr<const Face> face)
      {
        return !face->GetConstOwner();
      }
    ),
    m_faces.end()
  );
  #endif // SO_THIS_CAN_BE_REMOVED_2

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Remove cells with less than 8 faces or less than 8 faces with an owner"
      << std::endl
    ;
  }

  assert(
    std::count_if(m_cells.begin(),m_cells.end(),
      [strategy](const boost::shared_ptr<Cell> cell)
      {
        const std::vector<boost::shared_ptr<Face>> faces { cell->GetFaces() };
        const int n_faces_expected {
          CreateVerticalFacesStrategies().GetFacesPerCell(strategy)
        };

        assert(static_cast<int>(faces.size()) == n_faces_expected);
        return std::count_if(faces.begin(),faces.end(),
          [](const boost::shared_ptr<Face> face)
          {
            assert(face);
            assert(face->GetConstOwner());
            return face->GetConstOwner();
          }
        ) < n_faces_expected;
      }
    ) == 0
    && "So the code below can be removed #3"
  );

  #define SO_THIS_CAN_BE_REMOVED_2
  #ifndef SO_THIS_CAN_BE_REMOVED_2
  m_cells.erase(
    std::remove_if(m_cells.begin(),m_cells.end(),
      [strategy](const boost::shared_ptr<Cell> cell)
      {
        const std::vector<boost::shared_ptr<Face>> faces { cell->GetFaces() };
        const int n_faces_expected {
          CreateVerticalFacesStrategies().GetFacesPerCell(strategy)
        };

        assert(static_cast<int>(faces.size()) == n_faces_expected);
        return std::count_if(faces.begin(),faces.end(),
          [](const boost::shared_ptr<Face> face)
          {
            assert(face);
            assert(face->GetConstOwner());
            return face->GetConstOwner();
          }
        ) < n_faces_expected;
      }
    ),
    m_cells.end()
  );
  #endif // SO_THIS_CAN_BE_REMOVED_2

  #ifndef NDEBUG
  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Check that all Faces know they belong to their Cell"
      << std::endl
    ;
  }
  for (const auto& cell: m_cells)
  {
    for (const auto& face: cell->GetFaces())
    {
      assert(face->GetConstOwner() == cell
        || face->GetNeighbour() == cell
      );
    }
  }
  #endif

  #ifndef NDEBUG
  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Check that all Cells know they own their Faces"
      << std::endl
    ;
  }
  for (const auto& face: m_faces)
  {
    assert(face);
    const auto owner = face->GetConstOwner();
    assert(owner);
    const auto neighbour = face->GetNeighbour();
    if (!neighbour)
    {
      const auto owner_faces = owner->GetFaces();
      assert(std::count(owner_faces.begin(),owner_faces.end(),face) == 1);
    }
    else
    {
      const auto owner_faces = owner->GetFaces();
      const auto neighbour_faces = neighbour->GetFaces();
      assert(
          std::count(owner_faces.begin(),owner_faces.end(),face)
        + std::count(neighbour_faces.begin(),neighbour_faces.end(),face)
        != 0 && "A Face with a neighbour is known by two cells, instead of zero"
      );
      assert(
          std::count(owner_faces.begin(),owner_faces.end(),face)
        + std::count(neighbour_faces.begin(),neighbour_faces.end(),face)
        != 1 && "A Face with a neighbour is known by two cells, instead of one"
      );
      assert(
          std::count(owner_faces.begin(),owner_faces.end(),face)
        + std::count(neighbour_faces.begin(),neighbour_faces.end(),face)
        == 2
      );
    }
  }
  #endif

  //Start setting the indices
  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Unset all Cell indices"
      << std::endl
    ;
  }
  for (const auto& cell: m_cells) { cell->SetIndex(Cell::sm_cell_no_index); }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Unset all Face indices"
      << std::endl
    ;
  }
  for (const auto& face: m_faces) { face->SetIndex(Face::sm_face_no_index); }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Set Cell indices"
      << std::endl
    ;
  }
  //#2: For each face, find its owner (a cell), and assign these increasing cell indices
  //Set all Cell indices, following the Face order:
  //The faces are ordered correctly, by the boundary.
  //The faces' owners must be an increasing value, to prevent
  //'upper triangular order' errors
  {
    int cell_index = 0;
    const int n_faces = static_cast<int>(m_faces.size());
    for (int i=0; i!=n_faces; ++i)
    {
      //+---+---+---+---+---------------------------------------------------+
      //| S | 1 | 2 | 3 | Action                                            |
      //+---+---+---+---+---------------------------------------------------+
      //| A | N | Y | - | Assume owner's index is less then index           |
      //| B |   | N | - | Assign index to owner                             |
      //+---+---+---+---+---------------------------------------------------+
      //| C | Y | Y | N | Assign index to neighbour, transfer ownership if  |
      //|   |   |   |   |   neighbour's index is less than owner's index    |
      //| D |   | Y | Y | Assume owner's index is less than neighbour's     |
      //|   |   |   |   |   index, assume owner's index is less then index  |
      //| E |   | N | Y | Assign index to owner, transfer ownership if      |
      //|   |   |   |   |   neighbour's index is less than owner's index    |
      //| F |   | N | N | Assign index to owner                             |
      //+---+---+---+---+---------------------------------------------------+
      // * S = Scenario
      // * 1 = Does it have a neighbour?
      // * 2 = Assigned owner index yes/no?
      // * 3 = Assigned neighbour index yes/no?
      auto this_face = m_faces[i];
      if (this_face->GetNeighbour())
      {
        const int owner_index = this_face->GetConstOwner()->GetIndex();
        const int neighbour_index = this_face->GetNeighbour()->GetIndex();
        if (owner_index == Cell::sm_cell_no_index)
        {
          //Scenario E or F
          if (neighbour_index == Cell::sm_cell_no_index)
          {
            //Scenario F: No owner index, no neighbour index
            assert(owner_index == Cell::sm_cell_no_index);
            //Assign index to owner
            this_face->GetNonConstOwner()->SetIndex(cell_index);
            ++cell_index;
          }
          else
          {
            //Scenario E: No owner index, a neighbour index
            assert(owner_index == Cell::sm_cell_no_index);
            assert(neighbour_index != Cell::sm_cell_no_index);
            //Assign index to owner, transfer ownership if
            //neighbour's index is less than owner's index
            this_face->GetNonConstOwner()->SetIndex(cell_index);
            ++cell_index;
          }
        }
        else
        {
          //Scenario C or D
          assert(owner_index != Cell::sm_cell_no_index);
          if (neighbour_index == Cell::sm_cell_no_index)
          {
            //Scenario C: An owner index, no neighbour index
            //Assign index to neighbour, transfer ownership if
            //neighbour's index is less than owner's index
            this_face->GetNonConstNeighbour()->SetIndex(cell_index);
            ++cell_index;
          }
          else
          {
            //Scenario D: An owner index, a neighbour index
            //Assume owner's index is less than neighbour's
            //index, assume owner's index is less then index
            assert(neighbour_index != Cell::sm_cell_no_index);
            assert(owner_index < neighbour_index);
          }
        }
      }
      else
      {
        //Scenario A or B
        //No neighbour at all
        assert(!this_face->GetNeighbour());

        auto owner = this_face->GetNonConstOwner();
        assert(owner);
        if (owner->GetIndex() == Cell::sm_cell_no_index)
        {
          //Scenario B: no owner index
          //Assign index to owner
          owner->SetIndex(cell_index);
          ++cell_index;
        }
        else
        {
          //Scenario A: an owner index
          //Assume owner's index is less then index
          assert(owner->GetIndex() < cell_index);
        }
      }
      assert(this_face->GetConstOwner()->GetIndex() != Cell::sm_cell_no_index);
    }

    //Fixed #221:
    //Assign the cells without an index an index
    for (auto& cell: m_cells)
    {
      if(cell->GetIndex() == Cell::sm_cell_no_index)
      {
        cell->SetIndex(cell_index);
        ++cell_index;
      }
    }
  }

  #ifndef NDEBUG
  for (const auto& cell: m_cells)
  {
    assert(cell->GetIndex() != Cell::sm_cell_no_index
      && "All cells must have been assigned an index, #221");
  }
  #endif

  //Show all cells' indices
  if (verbose_show_cell_indices)
  {
    const int n_cells = static_cast<int>(m_cells.size());
    TRACE(n_cells);
    for (int i=0; i!=n_cells; ++i)
    {
      assert(m_cells[i]);
      {
        std::stringstream s;
        s << "m_cells[" << i << "] has index " << m_cells[i]->GetIndex()
          << " and " << m_cells[i]->GetFaces().size() << " faces:";
        TRACE(s.str());
      }
      for (const auto& face: m_cells[i]->GetFaces())
      {
        std::stringstream s;
        s << "owner: " << face->GetConstOwner()->GetIndex()
          << ", neighbour: " << (face->GetNeighbour() ? face->GetNeighbour()->GetIndex() : -1)
        ;
        TRACE(s.str());
      }
    }
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Order all cells by their index"
      << std::endl
    ;
  }
  {
    //const int n_cells = static_cast<int>(m_cells.size());
    for (int i=0; i!=static_cast<int>(m_cells.size()); ++i)
    {
      while (1)
      {
        assert(i >= 0);
        assert(i < static_cast<int>(m_cells.size()));
        assert(m_cells[i]);
        const auto this_index = m_cells[i]->GetIndex();
        #ifndef NDEBUG
        if (this_index == Cell::sm_cell_no_index)
        {
          TRACE("ERROR");
          TRACE(m_cells.size());
          TRACE(m_faces.size());
          TRACE(m_cells.max_size());
          TRACE(m_faces.max_size());
          TRACE(i);
          TRACE(m_cells[i]->GetFaces().size());
          for (const auto& face: m_cells[i]->GetFaces())
          {
            std::stringstream s;
            s << "owner: " << face->GetConstOwner()->GetIndex()
              << ", neighbour: " << (face->GetNeighbour() ? face->GetNeighbour()->GetIndex() : -1)
            ;
            TRACE(s.str());
          }
          TRACE("BREAK"); //#221
        }
        #endif
        assert(this_index != Cell::sm_cell_no_index);
        if (i != this_index)
        {
          assert(this_index >= 0);
          assert(this_index < static_cast<int>(m_cells.size()));
          if (verbose_show_cell_indices)
          {
            std::stringstream s;
            s << "i != this_index <-> " << i << " != " << this_index;
            TRACE(s.str());
          }
          std::swap(m_cells[i],m_cells[this_index]);
        }
        else
        {
          assert(i == this_index);
          if (verbose_show_cell_indices)
          {
            std::stringstream s;
            s << "i == this_index == " << i;
            TRACE(s.str());
          }
          //Everything is OK
          break; //Next
        }
      }
    }
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "#3: Go though all cells by increasing index. For each cell,"
      << "    find the faces it owns, assign an increasing face index"
      << std::endl
    ;
  }
  {
    int face_index = 0;
    const int max_cell_index
      = m_cells.empty()
      ? 0
      : (*std::max_element(m_cells.begin(),m_cells.end(),
          [](const boost::shared_ptr<Cell>& a, const boost::shared_ptr<Cell>& b)
          {
            return a->GetIndex() < b->GetIndex();
          }
          )
        )->GetIndex() + 1;

    for (int cell_index=0; cell_index!=max_cell_index; ++cell_index)
    {
      for (const auto& cell: m_cells)
      {
        if (cell->GetIndex() != cell_index) continue;
        for (const auto& face: cell->GetFaces())
        {
          if (face->GetIndex() == Face::sm_face_no_index)
          {
            face->SetIndex(face_index);
            ++face_index;
          }
        }
      }
    }
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "#4: Within each boundary, sort the faces by"
      << "    1) boundary type"
      << "    2) its owner its (cell)index"
      << "    3) its neighbour its (cell)index"
      << std::endl
    ;
  }
  if (!m_faces.empty())
  {
    const auto f = [](
      const boost::shared_ptr<Face>& lhs,
      const boost::shared_ptr<Face>& rhs) -> bool
    {
      if (!lhs && (rhs || !rhs)) return false;
      if (!rhs) return true;
      assert(lhs);
      assert(rhs);
      {
        const auto lhs_boundary_type = lhs->GetBoundaryType();
        const auto rhs_boundary_type = rhs->GetBoundaryType();
        if (lhs_boundary_type != rhs_boundary_type)
        {
          return lhs_boundary_type < rhs_boundary_type;
        }
      }
      {
        const auto lhs_owner_index = lhs->GetConstOwner()->GetIndex();
        const auto rhs_owner_index = rhs->GetConstOwner()->GetIndex();
        if (lhs_owner_index != rhs_owner_index)
        {
          return lhs_owner_index < rhs_owner_index;
        }
      }
      const auto lhs_neighbour = lhs->GetNeighbour();
      const auto rhs_neighbour = lhs->GetNeighbour();
      if ( lhs_neighbour && !rhs_neighbour) return true;
      if (!lhs_neighbour && (rhs_neighbour || !rhs_neighbour)) return false;
      return lhs_neighbour->GetIndex() < rhs_neighbour->GetIndex();
    };
    std::sort(std::begin(m_faces),std::end(m_faces),f);
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "#5: Set the Faces' indices equal to their position in the vector"
      << std::endl
    ;
  }
  {
    const int n_faces = static_cast<int>(m_faces.size());
    for (int i=0; i!=n_faces; ++i)
    {
      m_faces[i]->SetIndex(i);
    }
  }
  //Order all faces by their index
  /*
  {
    const int n_faces = static_cast<int>(m_faces.size());
    for (int i=0; i!=n_faces; ++i)
    {
      while (1)
      {
        const int this_index = m_faces[i]->GetIndex();
        if (i == this_index) break;
        std::swap(m_faces[i],m_faces[this_index]);
      }
    }
  }
  */

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Set the " << m_faces.size() << " Faces their points in the correct order"
      << std::endl
    ;
  }
  {
    const int n = static_cast<int>(m_faces.size());
    const int p = n / 100 == 0 ? 1 : n / 100;
    for (int i=0; i!=n; ++i)
    //const auto j = std::end(m_faces);
    //for (const boost::shared_ptr<Face>& face: m_faces)
    //for (auto i = std::begin(m_faces); i!=j; ++i)
    {
      assert(m_faces[i]);
      m_faces[i]->SetCorrectWinding();
      assert(p != 0);
      if (verbose) { if (i % p == 0) { std::clog << "%"; } }
    }
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Set all Point indices"
      << std::endl
    ;
  }
  {
    const int n_points = static_cast<int>(m_points.size());
    for (int i=0; i!=n_points; ++i)
    {
      m_points[i]->SetIndex(i);
    }
  }

  //Show the faces
  if (verbose_show_faces)
  {
    const int n_faces = static_cast<int>(m_faces.size());
    TRACE(n_faces);
    for (int i=0; i!=n_faces; ++i)
    {
      std::stringstream s;
      s
        << "#" << i << ": boundary type: "
        << m_faces[i]->GetBoundaryType() << ", owner index: "
        << m_faces[i]->GetConstOwner()->GetIndex()
        << ", neighbour index: "
      ;
      if (m_faces[i]->GetNeighbour())
      {
        s << m_faces[i]->GetNeighbour()->GetIndex();
      }
      else
      {
        s << "[no neighbour]";
      }
      TRACE(s.str());
    }
  }

  //Check
  #ifndef NDEBUG
  {
    const int cell_usecount = m_cells.empty() ? 0 : m_cells[0].use_count();
    for (const auto& cell: m_cells)
    {
      assert(cell);
      assert(cell.use_count() == cell_usecount && "Every Cell must have an equal use_count");
      //All Cells must have existing indices
      assert(cell->GetIndex() >= 0);
      assert(cell->GetIndex() <  static_cast<int>(m_cells.size()));
      //const int face_usecount = cell->GetFaces().empty() ? 0 : cell->GetFaces()[0].use_count();
      for (const auto& face: cell->GetFaces())
      {
        assert(face);
        //assert(std::abs(face_usecount - face.use_count()) <= 1 && "Face are used once or twice");
        //All Cells must exist of Faces with an existing index
        assert(face->GetIndex() >= 0);
        assert(face->GetIndex() <  static_cast<int>(m_faces.size()));
        //All Faces must have a Cell that owns them with an existing index
        assert(face->GetConstOwner()->GetIndex() >= 0);
        //assert(face->GetOwner()->GetIndex() <  static_cast<int>(m_cells.size())
        // && "Index actually might be bigger than the size");
        //All Faces must have either no Neighbout or a Neighbour with an existing index
        assert(!face->GetNeighbour() || face->GetNeighbour()->GetIndex() >= 0);
        //assert(!face->GetNeighbour() || face->GetNeighbour()->GetIndex() <  static_cast<int>(m_cells.size())
        // && "Index actually might be bigger than the size");
        for (const auto& point: face->GetPoints())
        {
          assert(point);
          //All Faces must exists of Points with an existing index
          assert(point->GetIndex() >= 0);
          assert(point->GetIndex() <  static_cast<int>(m_points.size()));
        }
      }
    }
  }
  #endif

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      "Start writing output\n"
      << std::endl
    ;
  }
  //Mesh
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Start generating mesh (.ply)"
      << std::endl
      ;
    }


    std::ofstream f(mesh_filename.c_str());
    f << CreateHeader();
    f << CreateNodes();
    f << CreateFaces();
  }
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Start creating file 'points'"
        << std::endl
      ;
    }

    std::ofstream f(ribi::foam::Filenames().GetPoints().c_str());
    f << CreateOpenFoamHeader("vectorField","points","constant/polyMesh");
    f << CreateOpenFoamNodes();
  }
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Start creating file 'faces'"
        << std::endl
      ;
    }

    std::ofstream fp(ribi::foam::Filenames().GetFaces().c_str());

    fp << CreateOpenFoamHeader("faceList","faces","constant/polyMesh");
    fp << CreateOpenFoamFaces();
  }
  {
    const int n_cells = static_cast<int>(m_cells.size());

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Start creating files 'owner' and 'neighbour' ("
        << n_cells << " cells)"
        << std::endl
      ;
    }

    std::ofstream fo(ribi::foam::Filenames().GetOwner().c_str());
    std::ofstream fn(ribi::foam::Filenames().GetNeighbour().c_str());

    std::stringstream fs;
    fs
      << "nPoints: " << m_points.size()
      << " nCells: " << m_cells.size()
      << " nFaces: " << m_faces.size()
    ;

    fo << CreateOpenFoamHeader(
        "labelList",
        "owner",
        "constant/polyMesh",
        fs.str()
      );
    fn << CreateOpenFoamHeader(
      "labelList",
      "neighbour",
      "constant/polyMesh",
      fs.str()
      );

    const auto p = CreateCells();
    const std::string& out_owner { p.first };
    const std::string& out_neighbour { p.second};
    fo << out_owner;
    fn << out_neighbour;
  }
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Start creating file 'boundary'"
        << std::endl
      ;
    }

    std::ofstream f(ribi::foam::Filenames().GetBoundary().c_str());
    f << CreateBoundary(boundary_to_patch_field_type_function);
  }
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Start creating file 'case.foam'"
        << std::endl
      ;
    }

    std::ofstream f(ribi::foam::Filenames().GetCase().c_str());
    //Need nothing to stream
  }
}

ribi::trim::TriangleMeshBuilderImpl::~TriangleMeshBuilderImpl()
{
  //OK
}

int ribi::trim::TriangleMeshBuilderImpl::CountCells() const noexcept
{
  return static_cast<int>(m_cells.size());
}

int ribi::trim::TriangleMeshBuilderImpl::CountFaces() const noexcept
{
  return static_cast<int>(m_faces.size());
}

std::string ribi::trim::TriangleMeshBuilderImpl::CreateBoundary(
    const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function
  ) const noexcept
{

  //Tally all boundary names
  std::map<std::string,int> sorted_tally;

  for (const auto& face: m_faces)
  {
    const std::string s { face->GetBoundaryType() };
    const auto iter(sorted_tally.find(s));
    if (iter == sorted_tally.end())
    {
      sorted_tally.insert(sorted_tally.begin(),std::make_pair(s,1));
    }
    else
    {
      ++(*iter).second;
    }
  }

  //Create a tally sorted as such that the PatchFieldType::no_patch_field comes first
  //so these can be omitted
  typedef std::pair<std::string,int> Pair;
  std::vector<Pair> tally;
  std::copy(sorted_tally.begin(),sorted_tally.end(),std::back_inserter(tally));

  std::sort(tally.begin(),tally.end(),
    [boundary_to_patch_field_type_function](const Pair& lhs, const Pair& rhs)
    {
      return OrderByPatchFieldType(
        lhs.first,
        rhs.first,
        boundary_to_patch_field_type_function(lhs.first),
        boundary_to_patch_field_type_function(rhs.first)
      );
    }
  );

  //Create the items
  std::vector<ribi::foam::BoundaryFileItem> items;
  int start_index = 0;
  for (const auto& p: tally)
  {
    const std::string boundary_name = p.first;
    const int n_faces = p.second;
    const ribi::foam::BoundaryFileItem item(
      boundary_name,
      boundary_to_patch_field_type_function(boundary_name),
      n_faces,
      ribi::foam::FaceIndex(start_index)
    );
    items.push_back(item);
    start_index += p.second;
  }

  ribi::foam::BoundaryFile file(
    ribi::foam::BoundaryFile::GetDefaultHeader(),
    items
  );

  std::stringstream s;
  s << file;
  return s.str();
}

std::pair<std::string,std::string> ribi::trim::TriangleMeshBuilderImpl::CreateCells() const noexcept
{
  std::stringstream out_owner;
  out_owner
    << static_cast<int>(m_faces.size())
    << "\n(\n";

  std::stringstream out_neighbour;
  out_neighbour
    << m_faces.size()
    << "\n(\n";


  for (const auto& face: m_faces)
  {
    assert(face);
    assert(face->GetConstOwner());
    out_owner << face->GetConstOwner()->GetIndex() << "\n";
    if(!face->GetNeighbour())
    {
      out_neighbour << "-1\n";
    }
    else
    {
      out_neighbour << face->GetNeighbour()->GetIndex() << "\n";
    }
  }

  out_owner << ")";
  out_neighbour << ")";
  return std::make_pair(out_owner.str(),out_neighbour.str());
}


std::string ribi::trim::TriangleMeshBuilderImpl::CreateFaces() const noexcept
{
  std::stringstream s;
  s << std::setprecision(17);
  for (const auto& face: m_faces)
  {
    s << face->GetPoints().size();

    face->SetCorrectWinding();

    for (const boost::shared_ptr<const Point> point: face->GetPoints())
    {
      s << " " << point->GetIndex();
    }
    s << "\n";
  }
  return s.str();
}



std::string ribi::trim::TriangleMeshBuilderImpl::CreateHeader() const noexcept
{
  std::stringstream s;
  s << ""
    << "ply\n"
    << "format ascii 1.0\n"
    << "element vertex " << m_points.size() << "\n"
    << "property float x\n"
    << "property float y\n"
    << "property float z\n"
    << "element face " << m_faces.size() << "\n"
    << "property list uchar int vertex_index\n"
    << "end_header\n";
  return s.str();
}

std::string ribi::trim::TriangleMeshBuilderImpl::CreateNodes() const noexcept
{
  using boost::geometry::get;

  std::string text;
  for (const auto& point: m_points)
  {
    std::stringstream s;
    int cnt = 0;
    for (const double p:
      {
        get<0>(*point->GetCoordinat()),
        get<1>(*point->GetCoordinat()),
        point->GetZ().value()
      }
    )
    {
      s << std::setprecision(cnt != 2 ? 17 : 3);
      s << p << " ";
      ++cnt;
    }
    std::string t = s.str();
    assert(t[t.size() - 1] == ' '); //Replace last space
    t[t.size() - 1] = '\n';
    text += t;

  }
  return text;
}

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamFaces() const noexcept
{
  std::stringstream s;
  s
    << std::setprecision(17)
    << m_faces.size()
    << "\n(\n";

  //Build a list of nodes
  for (const auto& face: m_faces)
  {
    assert(face);
    std::vector<int> points_indices;
    for (const auto& point: face->GetPoints())
    {
      points_indices.push_back(point->GetIndex());
    }
    s
      << points_indices.size()
      << "("
      << Implode(" ",points_indices)
      << ")\n";
  }
  s << ")";

  return s.str();
}

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamHeader(
  const std::string& class_name,
  const std::string& object,
  const std::string& location,
  const std::string& note) const noexcept
{
  std::stringstream s;
  s << "FoamFile\n{\tversion\t2.0;\n\tformat\tascii;\n\tclass\t" << class_name << ";";

  if(!note.empty())
  {
    s << "\n\tnote\t\"" << note << "\";";
  }

  s << "\n\tlocation\t\"" << location << "\";\n\tobject\t" << object << ";\n}\n\n";
  return s.str();
}

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamNodes() const noexcept
{

  using boost::geometry::get;
  std::stringstream s;
  s
    << std::setprecision(17)
    << m_points.size()
    << "\n(\n";

  //Build a list of nodes
  for (const auto& point: m_points)
  {
    const std::array<double,3> co {
      get<0>(*point->GetCoordinat()),
      get<1>(*point->GetCoordinat()),
      point->GetZ().value()
    };
    s <<  "(" << ribi::php::implode(" ",co) << ")\n";
  }

  s << ")";
  return s.str();
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::TriangleMeshBuilderImpl::ExtractFaces(
  const std::vector<boost::shared_ptr<Cell>>& cells
) noexcept
{
  const bool verbose{false};

  std::vector<boost::shared_ptr<Face>> v;
  for (const boost::shared_ptr<Cell>& cell: cells)
  {
    const auto w(cell->GetFaces());
    std::copy(w.begin(),w.end(),std::back_inserter(v));
  }
  if (verbose)
  {
    TRACE("n_face, non-unique:");
    TRACE(v.size());
  }

  std::sort(v.begin(),v.end(),Helper().OrderByIndex());
  const auto new_end = std::unique(std::begin(v),std::end(v));
  v.erase(new_end,v.end());
  assert(std::count(v.begin(),v.end(),nullptr) == 0);

  if (verbose)
  {
    TRACE("n_face, unique:");
    TRACE(v.size());
  }
  return v;


}

std::vector<boost::shared_ptr<ribi::trim::Point>> ribi::trim::TriangleMeshBuilderImpl::ExtractPoints(
  const std::vector<boost::shared_ptr<Cell>>& cells
) noexcept
{
  std::vector<boost::shared_ptr<Point>> v;
  for (const boost::shared_ptr<Cell>& cell: cells)
  {
    for (const auto& face: cell->GetFaces())
    {
      const std::vector<boost::shared_ptr<Point>> w { face->GetPoints() };
      std::copy(w.begin(),w.end(),std::back_inserter(v));
    }
  }

  std::sort(v.begin(),v.end(),Helper().OrderByX());
  const auto new_end = std::unique(std::begin(v),std::end(v));
  v.erase(new_end,v.end());
  assert(std::count(v.begin(),v.end(),nullptr) == 0);

  return v;
}


std::vector<std::string> ribi::trim::TriangleMeshBuilderImpl::GetAllFolders() const noexcept
{
  return {
    "0",
    "constant",
    "constant" + ribi::fileio::FileIo().GetPathSeperator() + "polyMesh",
    "system"
  };
}

std::string ribi::trim::TriangleMeshBuilderImpl::Implode(
  const std::string& seperator,
  const std::vector<ribi::foam::PointIndex>& v) noexcept
{
  std::stringstream s;
  s << std::setprecision(17);

  if (v.empty()) return s.str();
  s << v[0];
  const std::size_t sz = v.size();
  for (std::size_t i=1; i!=sz; ++i)
  {
    s << seperator << v[i];
  }
  return s.str();
}

std::string ribi::trim::TriangleMeshBuilderImpl::Implode(
  const std::string& seperator,
  const std::vector<int>& v) noexcept
{
  std::stringstream s;
  s << std::setprecision(17);

  if (v.empty()) return s.str();
  s << v[0];
  const std::size_t sz = v.size();
  for (std::size_t i=1; i!=sz; ++i)
  {
    s << seperator << v[i];
  }
  return s.str();
}

bool ribi::trim::TriangleMeshBuilderImpl::OrderByPatchFieldType(
  const std::string lhs_name, const std::string rhs_name,
  const ribi::foam::PatchFieldType lhs_type, const ribi::foam::PatchFieldType rhs_type
) noexcept
{
  if (lhs_type == ribi::foam::PatchFieldType::no_patch_field)
  {
    if (rhs_type == ribi::foam::PatchFieldType::no_patch_field)
    {
      return lhs_name > rhs_name;
    }
    else
    {
      return true;
    }
  }
  if (rhs_type == ribi::foam::PatchFieldType::no_patch_field)
  {
    return false;
  }
  return lhs_name > rhs_name;
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::TriangleMeshBuilderImpl::SortByBoundary(
  std::vector<boost::shared_ptr<Face>> faces,
  const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function
) noexcept
{


  std::sort(std::begin(faces),std::end(faces),
    [boundary_to_patch_field_type_function](const boost::shared_ptr<const Face> lhs, const boost::shared_ptr<const Face> rhs)
    {
      return OrderByPatchFieldType(
        lhs->GetBoundaryType(),
        rhs->GetBoundaryType(),
        boundary_to_patch_field_type_function(lhs->GetBoundaryType()),
        boundary_to_patch_field_type_function(rhs->GetBoundaryType())
      );
    }
  );
  return faces;
}

#ifndef NDEBUG
void ribi::trim::TriangleMeshBuilderImpl::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilderimpl_k.cpp

 

#include "trianglemeshbuilderimpl.h"

#include <sstream>

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamK() const noexcept
{
  std::stringstream s;
  s
    << "FoamFile\n"
    << "{\n"
    << "    version     2.0;\n"
    << "    format      ascii;\n"
    << "    class       volScalarField;\n"
    << "    location    \"0\";\n"
    << "    object      k;\n"
    << "}\n"
    << "\n"
    << "dimensions      [ 0 2 -2 0 0 0 0 ];\n"
    << "\n"
    << "internalField   uniform 0;\n"
    << "\n"
    << "boundaryField\n"
    << "{\n"
    << "     inlet\n"
    << "     {\n"
    << " type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     outlet\n"
    << "     {\n"
    << "         type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     side_CW\n"
    << "     {\n"
    << "         type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     side_CCW\n"
    << "     {\n"
    << "       type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     defaultFaces\n"
    << "     {\n"
    << "       type compressible::kqRWallFunction;\n"
    << "         value uniform 0;\n"
    << "     }\n"
    << "}\n"
  ;
  return s.str();
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilderimpl_mut.cpp

 

#include "trianglemeshbuilderimpl.h"

#include <sstream>

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamMut() const noexcept
{
  std::stringstream s;
  s
    << "FoamFile\n"
    << "{\n"
    << "    version     2.0;\n"
    << "    format      ascii;\n"
    << "    class       volScalarField;\n"
    << "    location    \"0\";\n"
    << "    object      mut;\n"
    << "}\n"
    << "\n"
    << "dimensions      [ 1 -1 -1 0 0 0 0 ];\n"
    << "\n"
    << "internalField   uniform 0;\n"
    << "\n"
    << "boundaryField\n"
    << "{\n"
    << "     inlet\n"
    << "     {\n"
    << " type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     outlet\n"
    << "     {\n"
    << "         type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     side_CW\n"
    << "     {\n"
    << "         type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     side_CCW\n"
    << "     {\n"
    << "         type zeroGradient;\n"
    << "     }\n"
    << "\n"
    << "     defaultFaces\n"
    << "     {\n"
    << "         type mutkWallFunction;\n"
    << "         value uniform 0;\n"
    << "     }\n"
    << "}\n"
  ;
  return s.str();
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilderimpl_pin.cpp

 

#include "trianglemeshbuilderimpl.h"

#include <sstream>

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamPin() const noexcept
{
  std::stringstream s;
  s
    << "101 \n"
    << "( \n"
    << "( 0 1.7000E+005 )\n"
    << "( 0.0000005 1.7120E+005 )\n"
    << "( 0.000001 1.7135E+005 )\n"
    << "( 0.0000015 1.7152E+005 )\n"
    << "( 0.000002 1.7172E+005 )\n"
    << "( 0.0000025 1.7193E+005 )\n"
    << "( 0.000003 1.7218E+005 )\n"
    << "( 0.0000035 1.7245E+005 )\n"
    << "( 0.000004 1.7277E+005 )\n"
    << "( 0.0000045 1.7312E+005 )\n"
    << "( 0.000005 1.7351E+005 )\n"
    << "( 0.0000055 1.7395E+005 )\n"
    << "( 0.000006 1.7445E+005 )\n"
    << "( 0.0000065 1.7501E+005 )\n"
    << "( 0.000007 1.7564E+005 )\n"
    << "( 0.0000075 1.7635E+005 )\n"
    << "( 0.000008 1.7715E+005 )\n"
    << "( 0.0000085 1.7804E+005 )\n"
    << "( 0.000009 1.7905E+005 )\n"
    << "( 0.0000095 1.8017E+005 )\n"
    << "( 0.00001 1.8144E+005 )\n"
    << "( 0.0000105 1.8285E+005 )\n"
    << "( 0.000011 1.8443E+005 )\n"
    << "( 0.0000115 1.8621E+005 )\n"
    << "( 0.000012 1.8818E+005 )\n"
    << "( 0.0000125 1.9039E+005 )\n"
    << "( 0.000013 1.9285E+005 )\n"
    << "( 0.0000135 1.9560E+005 )\n"
    << "( 0.000014 1.9864E+005 )\n"
    << "( 0.0000145 2.0202E+005 )\n"
    << "( 0.000015 2.0576E+005 )\n"
    << "( 0.0000155 2.0990E+005 )\n"
    << "( 0.000016 2.1446E+005 )\n"
    << "( 0.0000165 2.1948E+005 )\n"
    << "( 0.000017 2.2498E+005 )\n"
    << "( 0.0000175 2.3100E+005 )\n"
    << "( 0.000018 2.3755E+005 )\n"
    << "( 0.0000185 2.4467E+005 )\n"
    << "( 0.000019 2.5236E+005 )\n"
    << "( 0.0000195 2.6065E+005 )\n"
    << "( 0.00002 2.6953E+005 )\n"
    << "( 0.0000205 2.7901E+005 )\n"
    << "( 0.000021 2.8906E+005 )\n"
    << "( 0.0000215 2.9966E+005 )\n"
    << "( 0.000022 3.1078E+005 )\n"
    << "( 0.0000225 3.2237E+005 )\n"
    << "( 0.000023 3.3437E+005 )\n"
    << "( 0.0000235 3.4671E+005 )\n"
    << "( 0.000024 3.5932E+005 )\n"
    << "( 0.0000245 3.7212E+005 )\n"
    << "( 0.000025 3.8500E+005 )\n"
    << "( 0.0000255 3.9788E+005 )\n"
    << "( 0.000026 4.1068E+005 )\n"
    << "( 0.0000265 4.2329E+005 )\n"
    << "( 0.000027 4.3563E+005 )\n"
    << "( 0.0000275 4.4763E+005 )\n"
    << "( 0.000028 4.5922E+005 )\n"
    << "( 0.0000285 4.7034E+005 )\n"
    << "( 0.000029 4.8094E+005 )\n"
    << "( 0.0000295 4.9099E+005 )\n"
    << "( 0.00003 5.0047E+005 )\n"
    << "( 0.0000305 5.0935E+005 )\n"
    << "( 0.000031 5.1764E+005 )\n"
    << "( 0.0000315 5.2533E+005 )\n"
    << "( 0.000032 5.3245E+005 )\n"
    << "( 0.0000325 5.3900E+005 )\n"
    << "( 0.000033 5.4502E+005 )\n"
    << "( 0.0000335 5.5052E+005 )\n"
    << "( 0.000034 5.5554E+005 )\n"
    << "( 0.0000345 5.6010E+005 )\n"
    << "( 0.000035 5.6424E+005 )\n"
    << "( 0.0000355 5.6798E+005 )\n"
    << "( 0.000036 5.7136E+005 )\n"
    << "( 0.0000365 5.7440E+005 )\n"
    << "( 0.000037 5.7715E+005 )\n"
    << "( 0.0000375 5.7961E+005 )\n"
    << "( 0.000038 5.8182E+005 )\n"
    << "( 0.0000385 5.8379E+005 )\n"
    << "( 0.000039 5.8557E+005 )\n"
    << "( 0.0000395 5.8715E+005 )\n"
    << "( 0.00004 5.8856E+005 )\n"
    << "( 0.0000405 5.8983E+005 )\n"
    << "( 0.000041 5.9095E+005 )\n"
    << "( 0.0000415 5.9196E+005 )\n"
    << "( 0.000042 5.9285E+005 )\n"
    << "( 0.0000425 5.9365E+005 )\n"
    << "( 0.000043 5.9436E+005 )\n"
    << "( 0.0000435 5.9499E+005 )\n"
    << "( 0.000044 5.9555E+005 )\n"
    << "( 0.0000445 5.9605E+005 )\n"
    << "( 0.000045 5.9649E+005 )\n"
    << "( 0.0000455 5.9688E+005 )\n"
    << "( 0.000046 5.9723E+005 )\n"
    << "( 0.0000465 5.9755E+005 )\n"
    << "( 0.000047 5.9782E+005 )\n"
    << "( 0.0000475 5.9807E+005 )\n"
    << "( 0.000048 5.9828E+005 )\n"
    << "( 0.0000485 5.9848E+005 )\n"
    << "( 0.000049 5.9865E+005 )\n"
    << "( 0.0000495 5.9880E+005 )\n"
    << "( 0.00005 6.0000E+005 )\n"
    << ") \n"
  ;
  return s.str();
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshbuilderimpl_pout.cpp

 

#include "trianglemeshbuilderimpl.h"

#include <sstream>

std::string ribi::trim::TriangleMeshBuilderImpl::CreateOpenFoamPout() const noexcept
{
  std::stringstream s;
  s
    << "51 \n"
    << "( \n"
    << "( 0 1.7000E+005 )\n"
    << "( 0.0000005 1.6978E+005 )\n"
    << "( 0.000001 1.6972E+005 )\n"
    << "( 0.0000015 1.6965E+005 )\n"
    << "( 0.000002 1.6955E+005 )\n"
    << "( 0.0000025 1.6943E+005 )\n"
    << "( 0.000003 1.6928E+005 )\n"
    << "( 0.0000035 1.6908E+005 )\n"
    << "( 0.000004 1.6884E+005 )\n"
    << "( 0.0000045 1.6853E+005 )\n"
    << "( 0.000005 1.6814E+005 )\n"
    << "( 0.0000055 1.6765E+005 )\n"
    << "( 0.000006 1.6704E+005 )\n"
    << "( 0.0000065 1.6628E+005 )\n"
    << "( 0.000007 1.6534E+005 )\n"
    << "( 0.0000075 1.6418E+005 )\n"
    << "( 0.000008 1.6276E+005 )\n"
    << "( 0.0000085 1.6105E+005 )\n"
    << "( 0.000009 1.5900E+005 )\n"
    << "( 0.0000095 1.5659E+005 )\n"
    << "( 0.00001 1.5380E+005 )\n"
    << "( 0.0000105 1.5062E+005 )\n"
    << "( 0.000011 1.4708E+005 )\n"
    << "( 0.0000115 1.4324E+005 )\n"
    << "( 0.000012 1.3918E+005 )\n"
    << "( 0.0000125 1.3500E+005 )\n"
    << "( 0.000013 1.3082E+005 )\n"
    << "( 0.0000135 1.2676E+005 )\n"
    << "( 0.000014 1.2292E+005 )\n"
    << "( 0.0000145 1.1938E+005 )\n"
    << "( 0.000015 1.1620E+005 )\n"
    << "( 0.0000155 1.1341E+005 )\n"
    << "( 0.000016 1.1100E+005 )\n"
    << "( 0.0000165 1.0895E+005 )\n"
    << "( 0.000017 1.0724E+005 )\n"
    << "( 0.0000175 1.0582E+005 )\n"
    << "( 0.000018 1.0466E+005 )\n"
    << "( 0.0000185 1.0372E+005 )\n"
    << "( 0.000019 1.0296E+005 )\n"
    << "( 0.0000195 1.0235E+005 )\n"
    << "( 0.00002 1.0186E+005 )\n"
    << "( 0.0000205 1.0147E+005 )\n"
    << "( 0.000021 1.0116E+005 )\n"
    << "( 0.0000215 1.0092E+005 )\n"
    << "( 0.000022 1.0072E+005 )\n"
    << "( 0.0000225 1.0057E+005 )\n"
    << "( 0.000023 1.0045E+005 )\n"
    << "( 0.0000235 1.0035E+005 )\n"
    << "( 0.000024 1.0028E+005 )\n"
    << "( 0.0000245 1.0022E+005 )\n"
    << "( 0.000025 1.0000E+005 )\n"
    << ") \n"
  ;
  return s.str();
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshcell.h

 

#ifndef RIBI_TRIANGLEMESHCELL_H
#define RIBI_TRIANGLEMESHCELL_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/checked_delete.hpp>
#include <boost/geometry.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include "trianglemeshfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///A Cell consists of Faces that surround the volume of the cell without openings
///Sure, the Faces can change...
struct Cell
{
  //friend class Dialog;
  //friend class CellsCreator;
  //friend class CellsCreatorFactory;
  //friend class Face;
  //friend class TriangleMeshBuilder;
  //friend class TriangleMeshBuilderImpl;
  //friend void CellsCheck(const std::vector<boost::shared_ptr<Cell>>& cells) noexcept;

  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;

  std::vector<boost::shared_ptr<const Face>> GetFaces() const noexcept;
  std::vector<boost::shared_ptr<      Face>> GetFaces()       noexcept { return m_faces; }

  Coordinat3D CalculateCenter() const noexcept;


  //#220: This is the number 2 slowest function
  int GetIndex() const noexcept { return m_index; }

  ///Sets the Faces of a Cell by their index
  void SetCorrectOrder() noexcept;

  void SetIndex(const int index) noexcept;

  static const int sm_cell_no_index = -2;

  private:
  ~Cell() noexcept;
  friend void boost::checked_delete<>(      Cell* x);
  friend void boost::checked_delete<>(const Cell* x);

  std::vector<boost::shared_ptr<Face>> m_faces;
  int m_index;

  friend class CellFactory;
  //Enforce that only CellFactory can create a Cell
  explicit Cell(
    const std::vector<boost::shared_ptr<Face>>& faces,
    const int index,
    const CellFactory& lock
  );
  Cell(const Cell&) = delete;
  //Cell(      Cell&&) = delete;
  Cell& operator=(const Cell& ) = delete;
  //Cell& operator=(      Cell&&) = delete;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend bool operator==(const Cell& lhs, const Cell& rhs) noexcept;
  friend bool operator!=(const Cell& lhs, const Cell& rhs) noexcept;
  friend std::ostream& operator<<(std::ostream& os, const Cell& cell) noexcept;
};

bool operator==(const Cell& lhs, const Cell& rhs) noexcept;
bool operator!=(const Cell& lhs, const Cell& rhs) noexcept;
std::ostream& operator<<(std::ostream& os, const Cell& cell) noexcept;

bool operator<(const boost::shared_ptr<const Cell>& lhs, const boost::shared_ptr<      Cell>& rhs) = delete;
bool operator<(const boost::shared_ptr<const Cell>& lhs, const boost::shared_ptr<const Cell>& rhs) = delete;
bool operator<(const boost::shared_ptr<      Cell>& lhs, const boost::shared_ptr<      Cell>& rhs) = delete;
bool operator<(const boost::shared_ptr<      Cell>& lhs, const boost::shared_ptr<const Cell>& rhs) = delete;

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHCELL_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcell.cpp

 

#include "trianglemeshcell.h"

#include <iostream>



#include "geometry.h"
#include "testtimer.h"
#include "trianglemeshcellfactory.h"
#include "trianglemeshface.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trace.h"
#include "xml.h"

ribi::trim::Cell::Cell(
  const std::vector<boost::shared_ptr<Face>>& faces,
  const int index,
  const CellFactory&)
  :
    m_faces(faces),
    m_index{index}
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(faces.size() == 5 || faces.size() == 8);
}

ribi::trim::Cell::~Cell() noexcept
{

}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> ribi::trim::Cell::CalculateCenter() const noexcept
{
  
  Coordinat3D center(0.0,0.0,0.0);
  int cnt = 0;
  for(const boost::shared_ptr<const Face> face: m_faces)
  {
    assert(face);
    for(const auto point: face->GetPoints())
    {
      assert(point);
      center += point->GetCoordinat3D();
      ++cnt;
    }
  }
  center /= static_cast<double>(cnt);
  return center;
}


std::vector<boost::shared_ptr<const ribi::trim::Face>> ribi::trim::Cell::GetFaces() const noexcept
{
  return AddConst(m_faces);
}

void ribi::trim::Cell::SetCorrectOrder() noexcept
{
  std::sort(m_faces.begin(), m_faces.end(),
    [](const boost::shared_ptr<Face>& lhs, const boost::shared_ptr<Face>& rhs)
    {
      const int priority_lhs { lhs->CalcPriority() };
      const int priority_rhs { rhs->CalcPriority() };
      assert((priority_lhs != priority_rhs || priority_lhs == priority_rhs)
        && "Priorities can be equal");
      if (priority_lhs > priority_rhs) return true;
      if (priority_lhs < priority_rhs) return false;
      //Sort on Face indices
      assert(lhs->GetIndex() != rhs->GetIndex());
      return lhs->GetIndex() < rhs->GetIndex();
    }
  );
}

void ribi::trim::Cell::SetIndex(const int index) noexcept
{
  assert( (index != Cell::sm_cell_no_index || index == Cell::sm_cell_no_index)
    && "Cell indices are set and reset"
  );

  m_index = index;

  //If there is a Face that has this cell as its neighbour, yet that Face its Owner
  //does not have an index yet, transfer the Face its ownership from neighbour to owner
  for (const auto& face: m_faces)
  {
    assert(face->GetConstOwner());

    if (face->GetNeighbour()
      && face->GetNeighbour().get() == this
      && face->GetConstOwner()->GetIndex() == Face::sm_face_no_index)
    {
      face->TransferOwnership();
    }
  }
}

#ifndef NDEBUG
void ribi::trim::Cell::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  CellFactory().CreateTestPrism(CreateVerticalFacesStrategy::one_face_per_square);
  CellFactory().CreateTestCube(CreateVerticalFacesStrategy::one_face_per_square);

  const TestTimer test_timer(__func__,__FILE__,1.0);
  //Test that in a prism-shaped Cell, all Faces are owned, and no faces have a neighbour
  for (const auto& strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const boost::shared_ptr<Cell> prism {
      CellFactory().CreateTestPrism(strategy)
    };
    assert(prism);
    const std::vector<boost::shared_ptr<Face>> faces {
      prism->GetFaces()
    };
    assert(
      std::count_if(faces.begin(),faces.end(),
        [](const boost::shared_ptr<Face> face)
        {
          assert(face);
          assert(face->GetConstOwner());
          return face->GetNeighbour().get();
        }
      ) == 0
    );
  }
  //Test that in a prism-shaped Cell, all Faces are owned, and no faces have a neighbour
  for (CreateVerticalFacesStrategy strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const std::vector<boost::shared_ptr<Cell>> cube {
      CellFactory().CreateTestCube(strategy)
    };
    assert(cube.size() == 2 && "A cube consists out of two prisms");
    //Concatenate the faces
    std::vector<boost::shared_ptr<Face>> faces {
      cube[0]->GetFaces()
    };
    const std::vector<boost::shared_ptr<Face>> other_faces { cube[1]->GetFaces() };
    std::copy(std::begin(other_faces),std::end(other_faces),
      std::back_inserter(faces)
    );
    
    std::sort(faces.begin(),faces.end(),Helper().OrderByIndex());
    assert(std::is_sorted(faces.begin(),faces.end(),Helper().OrderByIndex()));
    assert(
      (
           (strategy == CreateVerticalFacesStrategy::one_face_per_square  && faces.size() == 10)
        || (strategy == CreateVerticalFacesStrategy::two_faces_per_square && faces.size() == 16)
      )
      && "One or two faces are in both Cells, and must be made unique"
    );
    assert(std::count(std::begin(faces),std::end(faces),nullptr) == 0);
    assert(std::is_sorted(faces.begin(),faces.end(),Helper().OrderByIndex()));
    faces.erase(std::unique(std::begin(faces),std::end(faces)),faces.end());
    assert(std::is_sorted(faces.begin(),faces.end(),Helper().OrderByIndex()));
    faces.erase(std::remove(std::begin(faces),std::end(faces),nullptr),faces.end()); //OBLIGATORY! std::unique creates nullptrs!
    assert(std::is_sorted(faces.begin(),faces.end(),Helper().OrderByIndex()));
    if (!
      (
        (
             (strategy == CreateVerticalFacesStrategy::one_face_per_square  && faces.size() ==  9)
          || (strategy == CreateVerticalFacesStrategy::two_faces_per_square && faces.size() == 14)
        )
        && "One or two faces were in both Cells, and are now present only once"
      )
    )
    {
      TRACE("ERROR");
      TRACE(faces.size());
      TRACE(CreateVerticalFacesStrategies().ToStr(strategy));
      TRACE("BREAK");
    }

    assert(
      (
           (strategy == CreateVerticalFacesStrategy::one_face_per_square  && faces.size() ==  9)
        || (strategy == CreateVerticalFacesStrategy::two_faces_per_square && faces.size() == 14)
      )
      && "One or two faces were in both Cells, and are now present only once"
    );

    const int n_faces_with_neighbours {
      std::count_if(faces.begin(),faces.end(),
        [](const boost::shared_ptr<Face> face)
        {
          assert(face);
          assert(face->GetConstOwner());
          return face->GetNeighbour().get();
        }
      )
    };
    assert(n_faces_with_neighbours == 1 || n_faces_with_neighbours == 2);
  }

  //Test that CalcCenter returns the same value each time
  //Failed once...
  for (const auto& strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const auto center(CellFactory().CreateTestPrism(strategy)->CalculateCenter());
    assert(Geometry().IsEqual(center,CellFactory().CreateTestPrism(strategy)->CalculateCenter()));
    assert(Geometry().IsEqual(center,CellFactory().CreateTestPrism(strategy)->CalculateCenter()));
  }
}
#endif

bool ribi::trim::operator==(const ribi::trim::Cell& lhs, const ribi::trim::Cell& rhs) noexcept
{
  return lhs.GetFaces() == rhs.GetFaces();
}

bool ribi::trim::operator!=(const ribi::trim::Cell& lhs, const ribi::trim::Cell& rhs) noexcept
{
  return !(lhs == rhs);
}

std::ostream& ribi::trim::operator<<(std::ostream& os, const ribi::trim::Cell& cell) noexcept
{
  const auto faces = cell.GetFaces();
  std::stringstream s;
  s
    << ribi::xml::ToXml("cell_index",cell.GetIndex())
  ;
  {
    std::stringstream t;
    const int n_faces { static_cast<int>(faces.size()) };
    for (int i=0; i!=n_faces; ++i)
    {
      t << ribi::xml::ToXml("face" + boost::lexical_cast<std::string>(i),faces[i]->GetIndex());
    }
    s << ribi::xml::ToXml("faces",t.str());
  }
  const std::vector<std::string> v { ribi::xml::XmlToPretty(s.str()) };
  std::copy(v.begin(),v.end(),std::ostream_iterator<std::string>(os,"\n"));

  return os;
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellfactory.h

 

#ifndef RIBI_TRIANGLEMESHCELLFACTORY_H
#define RIBI_TRIANGLEMESHCELLFACTORY_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/checked_delete.hpp>
#include <boost/shared_ptr.hpp>
#include "trianglemeshfwd.h"
#include "trianglemeshcreateverticalfacesstrategy.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///The only class to use Cell its constructor
//It has no state, so Pimpl is unnecessary
class CellFactory
{
  friend class Dialog;
  friend class Cell;
  friend class CellsCreator;

  CellFactory();

  boost::shared_ptr<Cell> Create(
    const std::vector<boost::shared_ptr<Face>>& faces,
    const CreateVerticalFacesStrategy strategy
  );


  ///Create a cell in the shape of a prism
  /*

      +
     /|\
    +---+
    | | |
    | + |
    |/ \|
    +---+

  */
  boost::shared_ptr<Cell> CreateTestPrism(
    const CreateVerticalFacesStrategy strategy
  ) const noexcept;


  ///Create two prims-shaped cell to form a cube
  /*

      +---+
     /|\ /|
    +---+ |
    | | | |
    | +-|-+
    |/ \|/
    +---+

  */
  std::vector<boost::shared_ptr<Cell>> CreateTestCube(
    const CreateVerticalFacesStrategy strategy
  ) const noexcept;


  private:
  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHCELLFACTORY_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellfactory.cpp

 

#include "trianglemeshcellfactory.h"

#include <cassert>

#include "testtimer.h"
#include "trianglemeshcell.h"
#include "trianglemeshcellscreator.h"
#include "trianglemeshcellscreatorfactory.h"
#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshtemplate.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trace.h"

ribi::trim::CellFactory::CellFactory()
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::shared_ptr<ribi::trim::Cell> ribi::trim::CellFactory::Create(
  const std::vector<boost::shared_ptr<Face>>& faces,
  const CreateVerticalFacesStrategy
  #ifndef NDEBUG
    strategy
  #endif
)
{
  //Give every Cell some index at creation
  static int cnt = 1;
  const int n = cnt;
  ++cnt;

  #ifndef NDEBUG
  if (strategy == CreateVerticalFacesStrategy::one_face_per_square ) { assert(faces.size() == 5 && "A cell (in the shape of a prism) consists out of 5 faces"); }
  if (strategy == CreateVerticalFacesStrategy::two_faces_per_square) { assert(faces.size() == 8 && "A cell (in the shape of a prism) consists out of 8 faces"); }
  #endif
  const boost::shared_ptr<Cell> cell {
    new Cell(faces,n,*this)
  };
  assert(cell);

  for (const auto& face: faces)
  {
    assert(face);
    face->AddBelongsTo(cell);
  }

  return cell;
}

std::vector<boost::shared_ptr<ribi::trim::Cell>> ribi::trim::CellFactory::CreateTestCube(
  const CreateVerticalFacesStrategy strategy
) const noexcept
{
  const boost::shared_ptr<Template> my_template {
    Template::CreateTest(1)
  };
  assert(my_template);
  assert(my_template->CountFaces() == 2);
  const int n_cell_layers = 1;
  const bool verbose{false};
  const boost::shared_ptr<CellsCreator> cells_creator {
    CellsCreatorFactory().Create(
      my_template,
      n_cell_layers,
      1.0 * boost::units::si::meter,
      strategy,
      verbose
    )
  };
  const std::vector<boost::shared_ptr<Cell>> cells { cells_creator->GetCells() };

  assert(cells.size() == 2 && "A cube consists out of two prisms");
  #ifndef NDEBUG
  for (int i=0; i!=2; ++i)
  {
    const std::vector<boost::shared_ptr<Face>> faces { cells[i]->GetFaces() };
    for (const auto& face: faces)
    {
      assert(face);
      assert(face->GetPoints().size() == 3 || face->GetPoints().size() == 4);
    }
  }
  #endif

  return cells;
}

boost::shared_ptr<ribi::trim::Cell> ribi::trim::CellFactory::CreateTestPrism(
  const CreateVerticalFacesStrategy strategy
) const noexcept
{
  const std::vector<boost::shared_ptr<Face>> faces {
    FaceFactory().CreateTestPrism(strategy)
  };
  const boost::shared_ptr<Cell> prism {
    CellFactory().Create(faces,strategy)
  };
  assert(prism);
  return prism;
}

#ifndef NDEBUG
void ribi::trim::CellFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  using boost::units::si::meter;
  FaceFactory();
  CellFactory().CreateTestPrism(CreateVerticalFacesStrategy::one_face_per_square);
  CellsCreatorFactory();
  CellsCreatorFactory().Create(Template::CreateTest(0),1,1.0 * meter,CreateVerticalFacesStrategy::one_face_per_square,false);


  const TestTimer test_timer(__func__,__FILE__,1.0);
  //Create prism
  for (const auto& strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const boost::shared_ptr<Cell> prism {
      CellFactory().CreateTestPrism(strategy)
    };
    if (strategy == CreateVerticalFacesStrategy::one_face_per_square ) { assert(prism->GetFaces().size() == 5 && "A prism has 5 faces (as the vertical faces square)"); }
    if (strategy == CreateVerticalFacesStrategy::two_faces_per_square) { assert(prism->GetFaces().size() == 8 && "A prism has 5 or 8 faces (as the vertical faces are split into 2 triangles)"); }
    for (auto& face: prism->GetFaces())
    {
      face->SetCorrectWinding();
    }
  }
  //Create cube
  for (CreateVerticalFacesStrategy strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const std::vector<boost::shared_ptr<Cell>> cube {
      CellFactory().CreateTestCube(strategy)
    };
    assert(cube.size() == 2 && "A cube consists of two prisms");
  }
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellimpl.h

 

#ifndef TRIANGLEMESHCELLIMPL_H
#define TRIANGLEMESHCELLIMPL_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

class CellImpl
{
  friend class Cell;
  CellImpl();

  private:
};

} //~namespace trim
} //~namespace ribi

#endif // TRIANGLEMESHCELLIMPL_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellimpl.cpp

 

#include "trianglemeshcellimpl.h"

ribi::trim::CellImpl::CellImpl()
{
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellscheck.h

 

#ifndef RIBI_TRIANGLEMESHCELLSCHECK_H
#define RIBI_TRIANGLEMESHCELLSCHECK_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "trianglemeshfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///Checks if Cells are OK
void CellsCheck(const std::vector<boost::shared_ptr<Cell>>& cells) noexcept;

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHCELLSCHECK_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellscheck.cpp

 

#include "trianglemeshcellscheck.h"

#include <cassert>

#include "trianglemeshcell.h"
#include "trianglemeshcellscreator.h"
#include "trianglemeshcellscreatorfactory.h"
#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshtemplate.h"
#include "trace.h"

void ribi::trim::CellsCheck(
  const std::vector<boost::shared_ptr<Cell>>& cells
) noexcept
{
  if (cells.empty()) return;

  //All Cells must be in use once
  #ifndef NDEBUG
  {
    const int use_count = cells[0].use_count();
    assert(use_count == 1);
    for (const auto& cell: cells)
    {
      assert(cell.use_count() == use_count
        && "All Cells must have an equal use_count");
    }
  }
  #endif

  for (const auto cell: cells)
  {
    assert(cell);
    assert(cell->GetFaces().size() == 5 || cell->GetFaces().size() == 8);
    for (const auto face: cell->GetFaces())
    {
      assert(face);
      assert(face->GetPoints().size() == 3 || face->GetPoints().size() == 4);
      for (const auto point: face->GetPoints())
      {
        assert(point);
      }
    }
  }
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellscreator.h

 

#ifndef RIBI_TRIANGLEMESHCELLSCREATOR_H
#define RIBI_TRIANGLEMESHCELLSCREATOR_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si/length.hpp>

#include "trianglemeshfwd.h"
#include "trianglemeshcreateverticalfacesstrategy.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///CellsCreator creates multiple layers of cells from a Template
///
///The next step will be
/// - freely delete the Cells that are not in the final mesh
/// - pass the desired cells to TriangleMeshBuilder to create the OpenFOAM files
struct CellsCreator
{

  //friend class CellFactory;
  //friend class Dialog;

  //n_face_layers - 1 == n_cell_layers
  explicit CellsCreator(
    const boost::shared_ptr<const Template> t,
    const int n_cell_layers,
    const boost::units::quantity<boost::units::si::length> layer_height,
    const CreateVerticalFacesStrategy strategy,
    const bool verbose,
    const CellsCreatorFactory& lock //to force creation by CellsCreatorFactory
  );

  CellsCreator(const CellsCreator&) = delete;
  CellsCreator(CellsCreator&&) = delete;
  CellsCreator& operator=(const CellsCreator&) = delete;
  CellsCreator& operator=(const CellsCreator&&) = delete;

  void Clear() noexcept { m_cells.clear(); }

  std::vector<boost::shared_ptr<Cell>> GetCells() noexcept;

  private:
  friend class CellsCreatorFactory;

  ~CellsCreator() noexcept {}

  std::vector<boost::shared_ptr<Cell>> m_cells;

  const CreateVerticalFacesStrategy m_strategy;

  #ifndef NDEBUG
  static void CheckCells(const std::vector<boost::shared_ptr<Cell>>& cells) noexcept;
  #endif // NDEBUG

  //Must be static: it is used in the constructor
  //n_face_layers - 1 == n_cell_layers
  static std::vector<boost::shared_ptr<Cell>> CreateCells(
    const boost::shared_ptr<const Template> t,
    const int n_face_layers,
    const boost::units::quantity<boost::units::si::length> layer_height,
    const CreateVerticalFacesStrategy strategy,
    const bool verbose
  ) noexcept;

  static std::vector<boost::shared_ptr<Face>> CreateHorizontalFaces(
    const boost::shared_ptr<const Template> t,
    const std::vector<boost::shared_ptr<Point>>& points,
    const int n_face_layers
  );

  static std::vector<boost::shared_ptr<Point>> CreatePoints(
    const boost::shared_ptr<const Template> t,
    const int n_face_layers,
    const boost::units::quantity<boost::units::si::length> layer_height
  );

  //Must be static: it is used in the constructor
  static std::vector<boost::shared_ptr<Face>> CreateVerticalFaces(
    const boost::shared_ptr<const Template> t,
    const std::vector<boost::shared_ptr<Point>>& points,
    const int n_face_layers,
    const boost::units::quantity<boost::units::si::length> layer_height,
    const CreateVerticalFacesStrategy strategy,
    const bool verbose
  ) noexcept;

  static std::vector<boost::shared_ptr<Face>> FindKnownFacesBetween(
    const boost::shared_ptr<const Face> a, const boost::shared_ptr<const Face> b
  );

  ///Adapter to call Geometry().IsPlane()
  static bool IsPlane(const std::vector<boost::shared_ptr<Point>>& v) noexcept;

  static bool IsSubset(
    std::vector<boost::shared_ptr<Point>> a,
    std::vector<boost::shared_ptr<Point>> b
  ) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend void boost::checked_delete<>(      CellsCreator* x);
  friend void boost::checked_delete<>(const CellsCreator* x);
  friend class boost::detail::sp_ms_deleter<      CellsCreator>;
  friend class boost::detail::sp_ms_deleter<const CellsCreator>;
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHCELLSCREATOR_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellscreator.cpp

 

#include "trianglemeshcellscreator.h"

#include <cassert>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/make_shared.hpp>



#include "geometry.h"
#include "testtimer.h"
#include "trianglemeshcell.h"
#include "trianglemeshcellfactory.h"
#include "trianglemeshcellscreator.h"
#include "trianglemeshcellscreatorfactory.h"
#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshpointfactory.h"
#include "trianglemeshtemplate.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::trim::CellsCreator::CellsCreator(
  const boost::shared_ptr<const Template> t,
  const int n_cell_layers,
  const boost::units::quantity<boost::units::si::length> layer_height,
  const CreateVerticalFacesStrategy strategy,
  const bool verbose,
  const CellsCreatorFactory&
) : m_cells(
      CreateCells(
        t,
        n_cell_layers + 1, //n_face_layers - 1 == n_cell_layers
        layer_height,
        strategy,
        verbose
      )
    ),
    m_strategy(strategy)
{
  #ifndef NDEBUG
  Test();
  assert(t);
  assert(strategy == m_strategy);
  #endif
}

#ifndef NDEBUG
void ribi::trim::CellsCreator::CheckCells(const std::vector<boost::shared_ptr<Cell>>& cells) noexcept
{
  for (const auto& cell: cells)
  {
    assert(cell);
    assert(cell->GetFaces().size() == 5 || cell->GetFaces().size() == 8);
  }
}
#endif // NDEBUG

std::vector<boost::shared_ptr<ribi::trim::Cell>> ribi::trim::CellsCreator::CreateCells(
  const boost::shared_ptr<const Template> t,
  const int n_face_layers,
  const boost::units::quantity<boost::units::si::length> layer_height,
  const CreateVerticalFacesStrategy strategy,
  const bool verbose
) noexcept
{
  assert(t);

  if (n_face_layers < 2
    || t->GetPoints().empty()
  )
  {
    std::vector<boost::shared_ptr<ribi::trim::Cell>> no_cells; return no_cells;
  }
  assert(n_face_layers >= 2);

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Create points" << std::endl
    ;
  }
  const std::vector<boost::shared_ptr<Point>> all_points
    = CreatePoints(t,n_face_layers,layer_height);

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Create horizontal faces" << std::endl
    ;
  }
  const std::vector<boost::shared_ptr<Face>> hor_faces
    = CreateHorizontalFaces(t,all_points,n_face_layers);

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Create vertical faces" << std::endl
    ;
  }

  const std::vector<boost::shared_ptr<Face>> ver_faces
    = CreateVerticalFaces(t,all_points,n_face_layers,layer_height,strategy,verbose);

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Created " << ver_faces.size() << " vertical faces" << std::endl
    ;
  }

  if (ver_faces.empty())
  {
    std::vector<boost::shared_ptr<ribi::trim::Cell>> no_cells;
    return no_cells;
  }

  #ifndef NDEBUG
  for(const auto f:ver_faces) { assert(f); }
  #endif

  const int n_hor_faces_per_layer = static_cast<int>(t->GetFaces().size());
  const int n_cells_per_layer = n_hor_faces_per_layer;

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Creating cells" << std::endl
    ;
  }
  std::vector<boost::shared_ptr<Cell>> cells;
  for (int layer=0; layer!=n_face_layers-1; ++layer) //-1 because there are no points above the top layer
  {
    if (verbose) { std::clog << "."; }
    for (int i=0; i!=n_cells_per_layer; ++i)
    {

      const int bottom_face_index = ((layer + 0) * n_hor_faces_per_layer) + i;
      const int top_face_index    = ((layer + 1) * n_hor_faces_per_layer) + i;
      assert(bottom_face_index >= 0);
      assert(top_face_index    >= 0);
      assert(bottom_face_index < static_cast<int>(hor_faces.size()));
      assert(top_face_index    < static_cast<int>(hor_faces.size()));
      const std::vector<boost::shared_ptr<Face>> these_ver_faces {
        FindKnownFacesBetween(
          hor_faces[bottom_face_index],
          hor_faces[top_face_index]
        )
      };

      if (strategy == CreateVerticalFacesStrategy::one_face_per_square )
      {
        #ifndef NDEBUG
        if (these_ver_faces.size() != 3)
        {
          TRACE("BREAK");
        }
        #endif
        assert(these_ver_faces.size() == 3);
        assert(hor_faces[bottom_face_index]);
        assert(hor_faces[top_face_index]);
        assert(these_ver_faces[0]);
        assert(these_ver_faces[1]);
        assert(these_ver_faces[2]);
        const boost::shared_ptr<Cell> cell(
          CellFactory().Create(
            {
              hor_faces[bottom_face_index],
              hor_faces[top_face_index],
              these_ver_faces[0],
              these_ver_faces[1],
              these_ver_faces[2]
            },
            strategy
          )
        );
        assert(hor_faces[bottom_face_index]);
        assert(hor_faces[top_face_index]);
        assert(Helper().IsHorizontal(*hor_faces[bottom_face_index]));
        assert(Helper().IsHorizontal(*hor_faces[top_face_index]));
        assert(Helper().IsVertical(*these_ver_faces[0]));
        assert(Helper().IsVertical(*these_ver_faces[1]));
        assert(Helper().IsVertical(*these_ver_faces[2]));

        cells.push_back(cell);
      }
      else
      {
        assert(these_ver_faces.size() == 6);
        const boost::shared_ptr<Cell> cell {
          CellFactory().Create(
            {
              hor_faces[bottom_face_index],
              hor_faces[top_face_index],
              these_ver_faces[0],
              these_ver_faces[1],
              these_ver_faces[2],
              these_ver_faces[3],
              these_ver_faces[4],
              these_ver_faces[5]
            },
            strategy
          )
        };
        assert(hor_faces[bottom_face_index]);
        assert(hor_faces[top_face_index]);
        assert(Helper().IsHorizontal(*hor_faces[bottom_face_index]));
        assert(Helper().IsHorizontal(*hor_faces[top_face_index]));
        assert(Helper().IsVertical(*these_ver_faces[0]));
        assert(Helper().IsVertical(*these_ver_faces[1]));
        assert(Helper().IsVertical(*these_ver_faces[2]));
        assert(Helper().IsVertical(*these_ver_faces[3]));
        assert(Helper().IsVertical(*these_ver_faces[4]));
        assert(Helper().IsVertical(*these_ver_faces[5]));

        cells.push_back(cell);
      }
    }
  }
  #ifndef NDEBUG
  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Checking cells" << std::endl
    ;
  }
  CheckCells(cells);
  #endif // NDEBUG
  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Done creating cells" << std::endl
    ;
  }
  return cells;
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::CellsCreator::CreateHorizontalFaces(
  const boost::shared_ptr<const Template> t,
  const std::vector<boost::shared_ptr<Point>>& all_points,
  const int n_face_layers
)
{
  const bool verbose{false};
  std::vector<boost::shared_ptr<Face>> v;
  assert(t);
  #ifndef NDEBUG
  if (all_points.empty())
  {
    TRACE("ERROR");
    TRACE("BREAK");
  }
  #endif
  assert(!all_points.empty());

  const int n_points_per_layer{static_cast<int>(t->GetPoints().size())};
  #ifndef NDEBUG
  const int n_faces_per_layer{static_cast<int>(t->GetFaces().size())};
  assert(n_face_layers > 0);
  #endif
  v.reserve(n_face_layers * n_points_per_layer);

  for (int layer=0; layer!=n_face_layers; ++layer)
  {
    const int point_offset{n_points_per_layer * layer};
    for (const std::vector<int>& face_point_indices: t->GetFacePointIndices())
    {
      #ifndef NDEBUG
      const int face_index{static_cast<int>(v.size())};
      assert(face_point_indices.size() == 3); //Triangulation
      #endif
      std::vector<boost::shared_ptr<Point>> face_points;
      for (int point_index: face_point_indices)
      {
        assert(point_index + point_offset < static_cast<int>(all_points.size()));
        face_points.push_back(all_points[point_index + point_offset]);
        #ifndef NDEBUG
        if (face_points.size() >= 2 && face_points[0]->CanGetZ())
        {
          assert(face_points.front()->GetZ() == face_points.back()->GetZ());
        }
        #endif
      }

      assert(layer == 0 || face_index - n_faces_per_layer >= 0);
      assert(layer == 0 || face_index - n_faces_per_layer < static_cast<int>(v.size()));
      if ( (layer % 2 == 0 && !Helper().IsClockwiseHorizontal(face_points))
        || (layer % 2 == 1 &&  Helper().IsClockwiseHorizontal(face_points))
      )
      {
        std::reverse(face_points.begin(),face_points.end());
      }

      if(!Helper().IsConvex(face_points)) { Helper().MakeConvex(face_points); }
      assert(Helper().IsConvex(face_points));
      //const FaceFactory face_factory;
      const boost::shared_ptr<Face> face {
        FaceFactory().Create(
          face_points,
          FaceOrientation::horizontal,
          verbose
        )
      };
      v.push_back(face);
    }
  }
  return v;
}

std::vector<boost::shared_ptr<ribi::trim::Point>> ribi::trim::CellsCreator::CreatePoints(
  const boost::shared_ptr<const Template> t,
  const int n_face_layers,
  const boost::units::quantity<boost::units::si::length> layer_height
)
{
  std::vector<boost::shared_ptr<Point>> v;

  for (int i=0; i!=n_face_layers; ++i)
  {
    for (const boost::shared_ptr<const Point> point: t->GetPoints())
    {
      const PointFactory point_factory;
      const auto new_point(point_factory.Create(point->GetCoordinat()));
      new_point->SetZ(static_cast<double>(i) * layer_height );
      v.push_back(new_point);
    }
  }
  assert(static_cast<int>(v.size()) == static_cast<int>(t->GetPoints().size()) * n_face_layers);

  return v;
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::CellsCreator::CreateVerticalFaces(
  const boost::shared_ptr<const Template> t,
  const std::vector<boost::shared_ptr<Point>>& all_points,
  const int n_face_layers,
  const boost::units::quantity<boost::units::si::length> layer_height,
  const CreateVerticalFacesStrategy strategy,
  const bool verbose
) noexcept
{
  assert(t);

  assert(n_face_layers > 0);
  if (n_face_layers < 2)
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Too few layers to create vertical faces" << std::endl
      ;
    }
    std::vector<boost::shared_ptr<ribi::trim::Face>> no_faces;
    return no_faces;
  }
  #ifndef NDEBUG
  const FaceFactory face_factory;

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Checking points" << std::endl
    ;
  }

  for (const auto& point: all_points) { assert(point); }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Get edges" << std::endl
    ;
  }
  #endif
  const std::vector<std::pair<int,int>> edges = t->GetEdges();


  assert(!edges.empty());
  const int n_edges = static_cast<int>(edges.size());
  const int n_points_per_layer = static_cast<int>(t->GetPoints().size());
  assert(n_points_per_layer > 0);
  const int n_ver_faces
    = strategy == CreateVerticalFacesStrategy::one_face_per_square
    ? 1 * n_edges
    : 2 * n_edges //For every horizontal edge, two triangles are used instead
  ;

  std::vector<boost::shared_ptr<Face>> v;
  #ifndef NDEBUG
  const int n_reserve = n_ver_faces * (n_face_layers - 1);
  #endif
  assert(n_reserve > 0);
  assert(n_reserve < static_cast<int>(v.max_size()));
  v.reserve(n_ver_faces * (n_face_layers - 1));

  assert(n_face_layers > 0);
  if (n_face_layers == 1)
  {
    std::vector<boost::shared_ptr<ribi::trim::Face>> no_faces;
    return no_faces;
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Start building " << (n_face_layers-1) //Number of cell layers
      << " layers" << std::endl
    ;
  }

  for (int layer=0; layer!=n_face_layers-1; ++layer) //-1 because there are no points above the top layer
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << (layer+1) //Human-based
        << "/" << (n_face_layers-1) //Number of cell layers
        << std::endl
      ;
    }

    const int points_offset = n_points_per_layer * layer;
    assert(points_offset >= 0);
    const auto z_here  = static_cast<double>(layer + 0) * layer_height;
    const auto z_above = static_cast<double>(layer + 1) * layer_height;
    for (const std::pair<int,int>& edge: edges)
    {
      assert(edge.first < edge.second);

      assert(points_offset + edge.first  < static_cast<int>(all_points.size()));
      assert(points_offset + edge.second < static_cast<int>(all_points.size()));
      assert(points_offset + edge.first  + n_points_per_layer < static_cast<int>(all_points.size()));
      assert(points_offset + edge.second + n_points_per_layer < static_cast<int>(all_points.size()));
      if (strategy == CreateVerticalFacesStrategy::one_face_per_square)
      {
        //Ordering cannot be known for sure to be convex from these indices
        assert(all_points[points_offset + edge.first]);
        assert(all_points[points_offset + edge.second]);
        assert(all_points[points_offset + edge.first  + n_points_per_layer]);
        assert(all_points[points_offset + edge.second + n_points_per_layer]);
        std::vector<boost::shared_ptr<Point>> face_points;
        face_points.push_back(all_points[points_offset + edge.first]);
        face_points.push_back(all_points[points_offset + edge.second]);
        face_points.push_back(all_points[points_offset + edge.first  + n_points_per_layer]);
        face_points.push_back(all_points[points_offset + edge.second + n_points_per_layer]);
        assert(face_points.size() == 4);
        assert(face_points[0]);
        assert(face_points[1]);
        assert(face_points[2]);
        assert(face_points[3]);
        assert(face_points[0] != face_points[1]);
        assert(face_points[0] != face_points[2]);
        assert(face_points[0] != face_points[3]);
        assert(face_points[1] != face_points[2]);
        assert(face_points[1] != face_points[3]);
        assert(face_points[2] != face_points[3]);
        face_points[0]->SetZ(z_here);
        face_points[1]->SetZ(z_here);
        face_points[2]->SetZ(z_above);
        face_points[3]->SetZ(z_above);
        #ifndef NDEBUG
        if(!IsPlane(face_points))
        {
          TRACE("ERROR");
          std::stringstream s;
          s
            << face_points.size() << '\n'
            << std::setprecision(99)
          ;
          for (const auto& point: face_points) { s << (*point) << " "; }
          TRACE(s.str());
          TRACE("BREAK");
        }
        #endif
        assert(IsPlane(face_points));

        //Order face_points
        if (!Helper().IsConvex(face_points))
        {
          Helper().MakeConvex(face_points);
        }

        assert(Helper().IsConvex(face_points));

        //Cannot order face winding yet, need Cells for this
        const boost::shared_ptr<Face> face {
          FaceFactory().Create(
            face_points,
            FaceOrientation::vertical,
            verbose
          )
        };
        assert(face);
        v.push_back(face);
      }
      else
      {
        assert(all_points[points_offset + edge.first]);
        assert(all_points[points_offset + edge.second]);
        assert(all_points[points_offset + edge.first + n_points_per_layer]);
        const std::vector<boost::shared_ptr<Point>> face_points_1 {
          all_points[points_offset + edge.first],
          all_points[points_offset + edge.second],
          all_points[points_offset + edge.first + n_points_per_layer]
        };
        assert(face_points_1[0] != face_points_1[1]);
        assert(face_points_1[0] != face_points_1[2]);
        assert(face_points_1[1] != face_points_1[2]);

        face_points_1[0]->SetZ(z_here);
        face_points_1[1]->SetZ(z_here);
        face_points_1[2]->SetZ(z_above);

        assert(Helper().IsConvex(face_points_1)
          && "FaceFactory expects convex ordered points");

        //Cannot order face winding yet, need Cells for this
        const boost::shared_ptr<Face> face_1 {
          FaceFactory().Create(
            face_points_1,
            FaceOrientation::vertical,
            verbose
          )
        };
        assert(face_1);
        v.push_back(face_1);


        assert(all_points[points_offset + edge.second]);
        assert(all_points[points_offset + edge.second + n_points_per_layer]);
        assert(all_points[points_offset + edge.first  + n_points_per_layer]);
        std::vector<boost::shared_ptr<Point>> face_points_2 {
          all_points[points_offset + edge.second],
          all_points[points_offset + edge.second + n_points_per_layer],
          all_points[points_offset + edge.first  + n_points_per_layer]
        };
        assert(face_points_2[0] != face_points_2[1]);
        assert(face_points_2[0] != face_points_2[2]);
        assert(face_points_2[1] != face_points_2[2]);

        face_points_2[0]->SetZ(z_here );
        face_points_2[1]->SetZ(z_above);
        face_points_2[2]->SetZ(z_above);

        #ifndef NDEBUG
        if (!Helper().IsConvex(face_points_2))
        {
          TRACE("ERROR");

          for (const auto& point:face_points_2) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
        }
        #endif

        assert(Helper().IsConvex(face_points_2)
          && "FaceFactory expects convex ordered points");

        const boost::shared_ptr<Face> face_2 {
          FaceFactory().Create(
            face_points_2,
            FaceOrientation::vertical,
            verbose
          )
        };
        assert(face_2);
        v.push_back(face_2);
      }
    }
  }

  assert(n_ver_faces * (n_face_layers - 1) == static_cast<int>(v.size()));

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Done building " << (n_face_layers-1) //Number of cell layers
      << " layers" << std::endl
    ;
  }

  return v;
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::CellsCreator::FindKnownFacesBetween(
  const boost::shared_ptr<const Face> a, const boost::shared_ptr<const Face> b
)
{
  
  assert(a->GetOrientation() == FaceOrientation::horizontal);
  assert(b->GetOrientation() == FaceOrientation::horizontal);
  assert(a->GetPoints().size() == 3);
  assert(b->GetPoints().size() == 3);

  //Collect the points the candidates must be a subset of
  std::vector<boost::shared_ptr<Point>> points {
    a->GetPoints()
  };
  for (const auto& p: b->GetPoints()) { points.push_back(p); }

  std::sort(points.begin(),points.end(),Helper().OrderByX());
  assert(std::unique(points.begin(),points.end()) == points.end());
  assert(std::count(points.begin(),points.end(),nullptr) == 0);

  //Collect the candidates
  std::vector<boost::weak_ptr<Face>> weak_candidates;
  for (const auto& p: a->GetPoints()) { for (const auto& q: p->GetConnected()) { weak_candidates.push_back(q); } }
  for (const auto& p: b->GetPoints()) { for (const auto& q: p->GetConnected()) { weak_candidates.push_back(q); } }
  std::vector<boost::shared_ptr<Face>> candidates;
  for (const auto& p: weak_candidates) { const auto q = p.lock(); if (q) candidates.push_back(q); }
  //std::vector<boost::shared_ptr<Face>> candidates;
  //for (const auto& p: candidates) { const auto q = p.lock(); if (q) candidates.push_back(q); }
  std::sort(candidates.begin(),candidates.end(),Helper().OrderByIndex());
  candidates.erase(std::unique(candidates.begin(),candidates.end()),candidates.end());
  assert(std::count(candidates.begin(),candidates.end(),nullptr) == 0);

  //Collect the faces between
  std::vector<boost::shared_ptr<Face>> faces;
  for (const auto& c: candidates)
  {
    if (IsSubset(c->GetPoints(),points)) { faces.push_back(c); }
  }
  assert(std::is_sorted(faces.begin(),faces.end(),Helper().OrderByIndex()));
  assert(std::unique(faces.begin(),faces.end()) == faces.end());
  assert(std::count(faces.begin(),faces.end(),nullptr) == 0);

  //Remove the faces a and b
  assert(std::count(faces.begin(),faces.end(),a) == 1);
  assert(std::count(faces.begin(),faces.end(),b) == 1);
  std::remove(faces.begin(),faces.end(),a);
  faces.pop_back();
  std::remove(faces.begin(),faces.end(),b);
  faces.pop_back();
  return faces;
}

bool ribi::trim::CellsCreator::IsPlane(const std::vector<boost::shared_ptr<Point>>& v) noexcept
{
  std::vector<Point::Coordinat3D> w;
  std::transform(v.begin(),v.end(),std::back_inserter(w),
    [](const boost::shared_ptr<Point>& p)
    {
      assert(p);

      return p->GetCoordinat3D();
    }
  );
  assert(v.size() == w.size());
  return Geometry().IsPlane(w);
}

bool ribi::trim::CellsCreator::IsSubset(
  std::vector<boost::shared_ptr<Point>> v,
  std::vector<boost::shared_ptr<Point>> w
) noexcept
{
  

  std::sort(v.begin(),v.end(),Helper().OrderByX());
  std::sort(w.begin(),w.end(),Helper().OrderByX());
  assert(std::is_sorted(v.begin(),v.end(),Helper().OrderByX()));
  assert(std::is_sorted(w.begin(),w.end(),Helper().OrderByX()));
  assert(std::unique(std::begin(v),std::end(v)) == v.end());
  assert(std::unique(w.begin(),w.end()) == w.end());
  assert(std::count(v.begin(),v.end(),nullptr) == 0);
  assert(std::count(w.begin(),w.end(),nullptr) == 0);
  std::vector<boost::shared_ptr<Point>> x;
  std::set_intersection(
    v.begin(),v.end(),
    w.begin(),w.end(),
    std::back_inserter(x),
    Helper().OrderByX()
  );
  assert(std::count(x.begin(),x.end(),nullptr) == 0);

  return x.size() == std::min(v.size(),w.size());
}

std::vector<boost::shared_ptr<ribi::trim::Cell>> ribi::trim::CellsCreator::GetCells() noexcept
{
  return m_cells;
}

#ifndef NDEBUG
void ribi::trim::CellsCreator::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  CellFactory();
  FaceFactory();

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};

  /*
  if (testing_depth > 1)
  {
    if (verbose) { TRACE("Trying out to build cells from the hardest testing templates"); }
    {
      //This is the longest test by far
      //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
      for (CreateVerticalFacesStrategy strategy: CreateVerticalFacesStrategies().GetAll())
      {
        const boost::shared_ptr<Template> my_template {
          Template::CreateTest(3)
        };

        const int n_cell_layers = 2;
        const boost::shared_ptr<CellsCreator> cells_creator {
          CellsCreatorFactory().Create(
            my_template,
            n_cell_layers,
            1.0 * boost::units::si::meter,
            strategy,
            verbose
          )
        };
        const std::vector<boost::shared_ptr<Cell>> cells { cells_creator->GetCells() };
        assert(cells.size() > 0);
      }
    }
  }
  */
  if (verbose) { TRACE("Specific: check if a Face really loses its neighbour: remove a prism from a cube"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    for (CreateVerticalFacesStrategy strategy: CreateVerticalFacesStrategies().GetAll())
    {
      //Create a 2x1 cell block
      const boost::shared_ptr<Template> my_template {
        Template::CreateTest(1)
      };
      assert(my_template->CountFaces() == 2);
      const int n_cell_layers = 1;
      const boost::shared_ptr<CellsCreator> cells_creator {
        CellsCreatorFactory().Create(
          my_template,
          n_cell_layers,
          1.0 * boost::units::si::meter,
          strategy,
          verbose
        )
      };
      const std::vector<boost::shared_ptr<Cell>> cells { cells_creator->GetCells() };
      assert(cells.size() == 2);
      const std::vector<boost::shared_ptr<Face>> faces_1 { cells[0]->GetFaces() };
      const std::vector<boost::shared_ptr<Face>> faces_2 { cells[1]->GetFaces() };
      //Find the one/two Faces that have a neighbour
      {
        const int n_faces_with_neighbour {
          std::count_if(faces_1.begin(),faces_1.end(),
            [](const boost::shared_ptr<Face> face)
            {
              return face->GetNeighbour().get();
            }
          )
        };
        assert(
             (strategy == CreateVerticalFacesStrategy::one_face_per_square
               && n_faces_with_neighbour == 1)
          || (strategy == CreateVerticalFacesStrategy::two_faces_per_square
               && n_faces_with_neighbour == 2)
        );
      }
      {
        const int n_faces_with_neighbour {
          std::count_if(faces_2.begin(),faces_2.end(),
            [](const boost::shared_ptr<Face> face)
            {
              return face->GetNeighbour().get();
            }
          )
        };
        assert(
             (strategy == CreateVerticalFacesStrategy::one_face_per_square
               && n_faces_with_neighbour == 1)
          || (strategy == CreateVerticalFacesStrategy::two_faces_per_square
               && n_faces_with_neighbour == 2)
        );
      }
      if (verbose) { TRACE("Creating internal faces 1"); }

      Helper::FaceSet internal_faces_1 = Helper().CreateEmptyFaceSet();
      if (verbose) { TRACE("Creating internal faces 1, std::copy_if"); }
      std::copy_if(
        faces_1.begin(),faces_1.end(),
        std::inserter(internal_faces_1,internal_faces_1.begin()),
        [](const boost::shared_ptr<Face> face)
        {
          assert(face);
          const bool do_copy = face->GetNeighbour().get();
          return do_copy;
        }
      );

      if (verbose) { TRACE("Creating internal faces 2"); }
      Helper::FaceSet internal_faces_2 = Helper().CreateEmptyFaceSet();
      std::copy_if(faces_2.begin(),faces_2.end(),std::inserter(internal_faces_2,internal_faces_2.begin()),
        [](const boost::shared_ptr<Face> face)
        {
          return face->GetNeighbour().get();
        }
      );
      if (verbose) { TRACE("Creating internal faces 1"); }
      assert(
        std::equal(
          internal_faces_1.begin(),internal_faces_1.end(),
          internal_faces_2.begin(),
          [](boost::shared_ptr<Face> lhs, boost::shared_ptr<Face> rhs) { return *lhs == *rhs; }
        )
      );
    }
  }
  if (verbose) { TRACE("Create Face, from bug"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /*
    (1.17557,2.35781,5.0)
    (2.35114,3.23607,5.0)
    (1.17557,2.35781,6.0)
    (2.35114,3.23607,6.0)
    */
    //Ordering cannot be known for sure to be convex from these indices
    typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
    std::vector<boost::shared_ptr<Point>> face_points {
      PointFactory().Create(boost::make_shared<Coordinat2D>(1.17557,2.35781)),
      PointFactory().Create(boost::make_shared<Coordinat2D>(2.35114,3.23607)),
      PointFactory().Create(boost::make_shared<Coordinat2D>(1.17557,2.35781)),
      PointFactory().Create(boost::make_shared<Coordinat2D>(2.35114,3.23607))
    };
    face_points[0]->SetZ(5.0 * boost::units::si::meter);
    face_points[1]->SetZ(5.0 * boost::units::si::meter);
    face_points[2]->SetZ(6.0 * boost::units::si::meter);
    face_points[3]->SetZ(6.0 * boost::units::si::meter);

    //Order face_points
    if (!Helper().IsConvex(face_points)) { Helper().MakeConvex(face_points); }

    #ifndef NDEBUG
    if (!Helper().IsConvex(face_points))
    {
      TRACE("ERROR");
      for (const auto& p: face_points) { TRACE(*p); }
      TRACE("BREAK");
    }
    #endif

    assert(Helper().IsConvex(face_points));

    //Cannot order face winding yet, need Cells for this
    const boost::shared_ptr<Face> face {
      FaceFactory().Create(
        face_points,
        FaceOrientation::vertical,
        verbose
      )
    };
  }

  //From bug
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
    const double x1 = 0.0004035051226622692510832834944523028752882964909076690673828125;
    const double y1 = 0.00023296416881187433805568132161312178141088224947452545166015625;
    const double z1 = 0; //left out the '.0' intentionally

    const double x2 = 0.000403505141811931846741734464245610070065595209598541259765625;
    const double y2 = 0.00023296414405748076185791173298156309101614169776439666748046875;
    const double z2 = 0; //left out the '.0' intentionally

    const double x3 = 0.0004035051226622692510832834944523028752882964909076690673828125;
    const double y3 = 0.00023296416881187433805568132161312178141088224947452545166015625;
    const double z3 = 0.00025000000000000000520417042793042128323577344417572021484375;

    const double x4 = 0.000403505141811931846741734464245610070065595209598541259765625;
    const double y4 = 0.00023296414405748076185791173298156309101614169776439666748046875;
    const double z4 = 0.00025000000000000000520417042793042128323577344417572021484375;
    const auto c1 = boost::make_shared<Coordinat2D>(x1,y1);
    const auto c2 = boost::make_shared<Coordinat2D>(x2,y2);
    const auto c3 = boost::make_shared<Coordinat2D>(x3,y3);
    const auto c4 = boost::make_shared<Coordinat2D>(x4,y4);
    const auto p1 = PointFactory().Create(c1);
    const auto p2 = PointFactory().Create(c2);
    const auto p3 = PointFactory().Create(c3);
    const auto p4 = PointFactory().Create(c4);
    p1->SetZ(z1 * boost::units::si::meter);
    p2->SetZ(z2 * boost::units::si::meter);
    p3->SetZ(z3 * boost::units::si::meter);
    p4->SetZ(z4 * boost::units::si::meter);
    std::vector<boost::shared_ptr<Point>> face_points;
    face_points.push_back(p1);
    face_points.push_back(p2);
    face_points.push_back(p3);
    face_points.push_back(p4);
    assert(IsPlane(face_points));
  }
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellscreatorfactory.h

 

#ifndef RIBI_TRIANGLEMESHCELLSCREATORFACTORY_H
#define RIBI_TRIANGLEMESHCELLSCREATORFACTORY_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/checked_delete.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/quantity.hpp>
#include "trianglemeshfwd.h"
#include "trianglemeshcreateverticalfacesstrategy.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///Creation of Cell
struct CellsCreatorFactory
{
  //friend class CellFactory;
  //friend class CellsCreator;
  //friend class Dialog;

  CellsCreatorFactory() noexcept;

  boost::shared_ptr<CellsCreator> Create(
    const boost::shared_ptr<const Template> t,
    const int n_cell_layers,
    const boost::units::quantity<boost::units::si::length> layer_height,
    const CreateVerticalFacesStrategy strategy,
    const bool verbose
  ) const noexcept;


  ///Create a cell in the shape of a prism
  /*
        +
       /|\
      +---+
      | | |
      | + |
      |/ \|
      +---+
  */
  boost::shared_ptr<CellsCreator> CreateTestPrism(
    const CreateVerticalFacesStrategy strategy
  ) const noexcept;


  ///Create two prisms-shaped cell to form a cube
  /*
        +---+
       /|\ /|
      +---+ |
      | | | |
      | +-|-+
      |/ \|/
      +---+
  */
  boost::shared_ptr<CellsCreator> CreateTestCube(
    const CreateVerticalFacesStrategy strategy
  ) const noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace trim
} //~namespace ribi


#endif // RIBI_TRIANGLEMESHCELLSCREATORFACTORY_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcellscreatorfactory.cpp

 

#include "trianglemeshcellscreatorfactory.h"
#include <cassert>

#include "testtimer.h"
#include "trianglemeshcell.h"
#include "trianglemeshcellfactory.h"
#include "trianglemeshcellscreator.h"
#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshhelper.h"
#include "trianglemeshtemplate.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trace.h"

ribi::trim::CellsCreatorFactory::CellsCreatorFactory() noexcept
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::shared_ptr<ribi::trim::CellsCreator> ribi::trim::CellsCreatorFactory::Create(
  const boost::shared_ptr<const Template> t,
  const int n_cell_layers,
  const boost::units::quantity<boost::units::si::length> layer_height,
  const CreateVerticalFacesStrategy strategy,
  const bool verbose
) const noexcept
{
  assert(t);
  assert(n_cell_layers >= 0);
  const boost::shared_ptr<CellsCreator> creator(
    new CellsCreator(t,n_cell_layers,layer_height,strategy,verbose,*this)
  );
  assert(creator);
  return creator;
}

boost::shared_ptr<ribi::trim::CellsCreator> ribi::trim::CellsCreatorFactory::CreateTestCube(
  const CreateVerticalFacesStrategy strategy
) const noexcept
{
  const boost::shared_ptr<Template> my_template {
    Template::CreateTest(1)
  };
  assert(my_template->CountFaces() == 2);
  const int n_cell_layers = 1;
  const bool verbose{false};
  const boost::shared_ptr<CellsCreator> cells_creator {
    CellsCreatorFactory().Create(
      my_template,
      n_cell_layers,
      1.0 * boost::units::si::meter,
      strategy,
      verbose
    )
  };
  #ifndef NDEBUG
  const std::vector<boost::shared_ptr<Cell>> cells { cells_creator->GetCells() };
  assert(cells.size() == 2 && "A cube consists out of two prisms");
  for (int i=0; i!=2; ++i)
  {
    const std::vector<boost::shared_ptr<Face>> faces { cells[i]->GetFaces() };
    for (const auto& face: faces)
    {
      assert(face);
    }
  }
  #endif
  return cells_creator;
}

boost::shared_ptr<ribi::trim::CellsCreator> ribi::trim::CellsCreatorFactory::CreateTestPrism(
  const CreateVerticalFacesStrategy strategy
) const noexcept
{
  const CellsCreatorFactory cells_creator_factory;
  const boost::shared_ptr<Template> my_template
   = Template::CreateTest(0);
  assert(my_template);
  assert(my_template->CountFaces() == 1);
  const int n_cell_layers = 1;
  const bool verbose{false};
  const boost::shared_ptr<CellsCreator> cells_creator
    = cells_creator_factory.Create(
      my_template,
      n_cell_layers,
      1.0 * boost::units::si::meter,
      strategy,
      verbose
  );
  assert(cells_creator);
  #ifndef NDEBUG
  const std::vector<boost::shared_ptr<Cell>> cells
    = cells_creator->GetCells();
  assert(cells.size() == 1 && "A prism consists out of 1 prisms");
  for (int i=0; i!=1; ++i)
  {
    const std::vector<boost::shared_ptr<Face>> faces
      = cells[i]->GetFaces();
    for (const auto& face: faces)
    {
      assert(face);
    }
  }
  #endif
  return cells_creator;
}

#ifndef NDEBUG
void ribi::trim::CellsCreatorFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    Template::CreateTest(0);
    CellsCreatorFactory();
    CellsCreatorFactory().CreateTestPrism(CreateVerticalFacesStrategy::one_face_per_square);
    CellsCreator(Template::CreateTest(0),1,1.0 * boost::units::si::meter,CreateVerticalFacesStrategy::one_face_per_square,false,CellsCreatorFactory());
  }

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const CellsCreatorFactory cells_creator;
  const CreateVerticalFacesStrategies create_vertical_faces_strategies;
  //Create prism
  for (const auto strategy: create_vertical_faces_strategies.GetAll())
  {
    const boost::shared_ptr<CellsCreator> prism
      = cells_creator.CreateTestPrism(strategy);
    assert(prism);
    const auto cells = prism->GetCells();
    assert(cells.size() == 1 && "A prism consists of 1 prisms");
    assert(cells[0]);
  }
  //Create cube
  for (CreateVerticalFacesStrategy strategy: create_vertical_faces_strategies.GetAll())
  {
    boost::shared_ptr<CellsCreator> cube
      = cells_creator.CreateTestCube(strategy);
    assert(cube);
    const std::vector<boost::shared_ptr<Cell>> cells
      = cube->GetCells();
    assert(cells.size() == 2 && "A cube consists of 2 prisms");
    assert(cells[0]);
    assert(cells[1]);
  }
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshcreateverticalfacesstrategies.h

 

#ifndef RIBI_TRIANGLEMESHCREATEVERTICALFACESSTRATEGIES_H
#define RIBI_TRIANGLEMESHCREATEVERTICALFACESSTRATEGIES_H

#include <string>
#include <vector>

#include "trianglemeshcreateverticalfacesstrategy.h"

namespace ribi {
namespace trim {

struct CreateVerticalFacesStrategies
{
  //friend class Cell;
  //friend class CellFactory;
  //friend class CellsCreator;
  //friend class CellsCreatorFactory;
  //friend class Dialog;
  //friend class Face;
  //friend class FaceFactory;
  //friend class TriangleMeshBuilder;
  //friend class TriangleMeshBuilderImpl;

  CreateVerticalFacesStrategies() noexcept {}

  std::vector<CreateVerticalFacesStrategy> GetAll() const noexcept;

  int GetFacesPerCell(const CreateVerticalFacesStrategy s) const noexcept;

  std::string ToStr(const CreateVerticalFacesStrategy s) const noexcept;
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHCREATEVERTICALFACESSTRATEGIES_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcreateverticalfacesstrategies.cpp

 

#include "trianglemeshcreateverticalfacesstrategies.h"

#include <cassert>
#include <stdexcept>

std::vector<ribi::trim::CreateVerticalFacesStrategy> ribi::trim::CreateVerticalFacesStrategies::GetAll() const noexcept
{
  return {
    CreateVerticalFacesStrategy::one_face_per_square,
    CreateVerticalFacesStrategy::two_faces_per_square
  };
}

int ribi::trim::CreateVerticalFacesStrategies::GetFacesPerCell(const CreateVerticalFacesStrategy s) const noexcept
{
  switch (s)
  {
    case CreateVerticalFacesStrategy::one_face_per_square: return 5;
    case CreateVerticalFacesStrategy::two_faces_per_square: return 8;
    default:
      assert(!"Should not get here");
      throw std::logic_error("ribi::trim::CreateVerticalFacesStrategies::ToStr: unknown strategy");
  };
  assert(!"Should not get here");
  throw std::logic_error("ribi::trim::CreateVerticalFacesStrategies::ToStr: unknown strategy");
}


std::string ribi::trim::CreateVerticalFacesStrategies::ToStr(const CreateVerticalFacesStrategy s) const noexcept
{
  switch (s)
  {
    case CreateVerticalFacesStrategy::one_face_per_square: return "one_face_per_square";
    case CreateVerticalFacesStrategy::two_faces_per_square: return "two_faces_per_square";
    default:
      assert(!"Should not get here");
      throw std::logic_error("ribi::trim::CreateVerticalFacesStrategies::ToStr: unknown strategy");
  };
  assert(!"Should not get here");
  throw std::logic_error("ribi::trim::CreateVerticalFacesStrategies::ToStr: unknown strategy");
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshcreateverticalfacesstrategy.h

 

#ifndef RIBI_TRIANGLEMESHCREATEVERTICALFACESSTRATEGY_H
#define RIBI_TRIANGLEMESHCREATEVERTICALFACESSTRATEGY_H

namespace ribi {
namespace trim {

enum class CreateVerticalFacesStrategy
{
  one_face_per_square,
  two_faces_per_square
};

} //~namespace trim
} //~namespace ribi


#endif // RIBI_TRIANGLEMESHCREATEVERTICALFACESSTRATEGY_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshcreateverticalfacesstrategy.cpp

 

#include "trianglemeshcreateverticalfacesstrategy.h"

 

 

 

 

 

./CppTriangleMesh/trianglemeshdialog.h

 

//---------------------------------------------------------------------------
/*
TriangleMeshCreator, creates a 3D mesh using Triangle,
Copyright (C) 2014-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/ToolTriangleMeshCreator.htm
//---------------------------------------------------------------------------
#ifndef RIBI_TRIANGLEMESHDIALOG_H
#define RIBI_TRIANGLEMESHDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si/area.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/plane_angle.hpp>
#include "openfoampatchfieldtype.h"
#include "trianglemeshcreateverticalfacesstrategy.h"
#pragma GCC diagnostic pop

namespace ribi {

struct TriangleFile;

namespace trim {

struct Cell;


///Facade
///
///              SetTriangleParameters()                          SetMeshParameters()
///                CreateTriangleMesh()                              Create3DMesh()
/// SetShapes() ------------------------> GetTriangleMesh()      --------------------> Get3dMesh()
/// GetShapesAsSvg()                      GetTriangleMeshAsSvg()
/// GetShapesAsWkt()                      GetTriangleMeshAsWkt()
///
///1) SetShapes, CanGetShapes, GetShapes,
///   Initially, the Dialog contains no shapes, which is a valid state
///2) SetTriangleParameters, CanGetTriangleMesh, m_are_triangle_parameters_set
///3) SetMeshParameters, CanGet3dMesh, m_are_3d_mesh_parameters_set
///4) Create3dMesh
///
/*

  Shapes:  Triangle:  Mesh:

  *-----*  *---+-*    *---+-*
  |     |  |\ / /|    |\ / /|\
  | *-* |  | *-* |    | *-* | \
  |     |  |/ \ \|    |/ \ \|  \
  *-----*  *---+-*    *---+-*   *
                       \   \ \  |
                        \   \ \ |
                         \   \ \|
                          *-----*
*/
struct Dialog
{
  typedef boost::units::quantity<boost::units::si::plane_angle> Angle;
  typedef boost::units::quantity<boost::units::si::area> Area;
  typedef boost::units::quantity<boost::units::si::length> Length;
  typedef boost::geometry::model::d2::point_xy<double> Coordinat;
  typedef boost::geometry::model::polygon<Coordinat> Polygon;
  typedef boost::geometry::model::linestring<Coordinat> Linestring;
  typedef std::vector<Polygon> Polygons;
  typedef std::vector<Linestring> Linestrings;
  typedef std::pair<Polygons,Linestrings> Shapes;

  Dialog();

  ///Uses OpenFOAM's checkMesh to check the 3D mesh
  void Check3dMesh(const std::string& full_path_of_executable) const noexcept;

  void CreateTriangleMesh() noexcept;
  void Create3dMesh() noexcept;

  static std::function<void(std::vector<boost::shared_ptr<Cell>>&)> CreateDefaultAssignBoundaryFunction() noexcept;
  static std::function<::ribi::foam::PatchFieldType(const std::string&)> CreateDefaultBoundaryToPatchFieldTypeFunction() noexcept;

  void CreateDefaultControlDict() const noexcept;
  void CreateDefaultPressureField() const noexcept;
  void CreateDefaultTemperatureField() const noexcept;
  void CreateDefaultVelocityField() const noexcept;

  static std::function<void(std::vector<boost::shared_ptr<Cell>>&)> CreateSculptFunctionNone() noexcept;
  static std::function<void(std::vector<boost::shared_ptr<Cell>>&)> CreateSculptFunctionRemoveRandom(const double p) noexcept;

  ///The content of the .ply file created by Create3dMesh()
  const std::string& Get3dMesh() const noexcept { return m_3dmesh_output_ply; }

  ///Obtain the filename of the created mesh
  //std::string Get3dMeshFilename() const noexcept { return m_3dmesh_filename_result; }

  int GetNcells() const noexcept { return m_n_cells; }
  int GetNfaces() const noexcept { return m_n_faces; }

  ///Shapes are the input shapes
  const Shapes& GetShapes() const noexcept { return m_shapes; }
  std::string GetShapesAsSvg(const double line_width = 0.1) const noexcept;
  std::string GetShapesAsWkt() const noexcept;

  ///The .poly file used as input for Triangle
  boost::shared_ptr<const TriangleFile> GetTriangleFile() const noexcept { return m_triangle_file; }

  ///TriangleMesh is the 2D mesh generated by Triangle
  const Shapes& GetTriangleMesh() const noexcept { return m_triangle_shapes; }
  ///TriangleMesh is the 2D mesh generated by Triangle
  std::string GetTriangleMeshAsSvg(const double line_width = 0.1) const noexcept;
  ///TriangleMesh is the 2D mesh generated by Triangle
  std::string GetTriangleMeshAsWkt() const noexcept;
  ///Obtain the content of the .ele file that Triangle wrote part of its output to
  const std::string& GetTriangleOutputEle() const noexcept { return m_triangle_output_ele; }
  ///Obtain the content of the .node file that Triangle wrote part of its output to
  const std::string& GetTriangleOutputNode() const noexcept { return m_triangle_output_node; }
  ///Obtain the content of the .poly file that Triangle wrote part of its output to
  const std::string& GetTriangleOutputPoly() const noexcept { return m_triangle_output_poly; }


  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Step 3
  void Set3dMeshParameters(
    const int n_cell_layers,
    const Length layer_height,
    const ::ribi::trim::CreateVerticalFacesStrategy strategy,
    const std::function<void(std::vector<boost::shared_ptr<Cell>>&)>& sculpt_function,
    const std::function<void(std::vector<boost::shared_ptr<Cell>>&)>& assign_boundary_function,
    const std::function<::ribi::foam::PatchFieldType(const std::string&)>& boundary_to_patch_field_type_function,
    const bool verbose
  ) noexcept;

  ///Step 1
  void SetShapes(const Shapes& shapes) noexcept;
  void SetShapes(const std::string& wkt) noexcept;

  ///Step 2
  void SetTriangleParameters(
    const Angle triangle_min_angle,
    const Area triangle_max_area,
    const bool verbose
  ) noexcept;


  ///Show the 3D mesh result in MeshLab
  void Show3dMesh() const noexcept;

  private:
  std::function<void(std::vector<boost::shared_ptr<Cell>>&)> m_3dmesh_assign_boundary_function;
  std::function<::ribi::foam::PatchFieldType(const std::string&)> m_3dmesh_boundary_to_patch_field_type_function;
  Length m_3dmesh_layer_height;
  int m_3dmesh_n_cell_layers;
  ///The text put in a .ply file as output of TriangleMesh
  std::string m_3dmesh_output_ply;
  std::function<void(std::vector<boost::shared_ptr<Cell>>&)> m_3dmesh_sculpt_function;
  CreateVerticalFacesStrategy m_3dmesh_strategy;
  bool m_3dmesh_verbose;

  int m_n_cells;
  int m_n_faces;

  Shapes m_shapes; //The input shapes

  boost::shared_ptr<TriangleFile> m_triangle_file;

  ///The text put in a .poly file as input for Triangle.exe
  //std::string m_triangle_input_poly;

  Area m_triangle_max_area;
  Angle m_triangle_min_angle;

  ///The text put in a .ele file as output of Triangle.exe
  std::string m_triangle_output_ele;
  ///The text put in a .node file as output of Triangle.exe
  std::string m_triangle_output_node;
  ///The text put in a .poly file as output of Triangle.exe
  std::string m_triangle_output_poly;

  Shapes m_triangle_shapes; //The shapes generated by Triangle
  bool m_triangle_verbose;


  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHDIALOG_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshdialog.cpp

 

//---------------------------------------------------------------------------
/*
TriangleMeshCreator, creates a 3D mesh using Triangle,
Copyright (C) 2014-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/ToolTriangleMeshCreator.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "trianglemeshdialog.h"

#include <fstream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/units/systems/si.hpp>

#include "fileio.h"
#include "geometry.h"
#include "openfoamcontroldictfile.h"
#include "openfoamfilenames.h"
#include "openfoamfvschemesfile.h"
#include "openfoamfvsolutionfile.h"
#include "openfoampressurefile.h"
#include "openfoamtemperaturefile.h"
#include "openfoamthermophysicalpropertiesfile.h"
#include "openfoamvelocityfieldfile.h"
#include "polyfile.h"
#include "testtimer.h"
#include "trace.h"
#include "trianglefile.h"
#include "trianglemeshbuilder.h"
#include "trianglemeshcell.h"
#include "trianglemeshcellfactory.h"
#include "trianglemeshcellscreator.h"
#include "trianglemeshcellscreatorfactory.h"
#include "trianglemeshface.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshtemplate.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#pragma GCC diagnostic pop

ribi::trim::Dialog::Dialog()
  : m_3dmesh_assign_boundary_function{CreateDefaultAssignBoundaryFunction()},
    m_3dmesh_boundary_to_patch_field_type_function{CreateDefaultBoundaryToPatchFieldTypeFunction()},
    m_3dmesh_layer_height{0.0 * boost::units::si::meter},
    m_3dmesh_n_cell_layers{0},
    m_3dmesh_output_ply{},
    m_3dmesh_sculpt_function{CreateSculptFunctionNone()},
    m_3dmesh_strategy{CreateVerticalFacesStrategy::one_face_per_square},
    m_3dmesh_verbose{false},
    m_n_cells{0},
    m_n_faces{0},
    m_shapes{},
    m_triangle_file{},
    //m_triangle_input_poly{},
    m_triangle_max_area(1.0 * 1000.0 * 1000.0 * boost::units::si::square_meter), //Square kilometer
    m_triangle_min_angle(Angle( (20.0 * boost::math::constants::two_pi<double>() / 360.0) * boost::units::si::radians)),
    m_triangle_output_ele{},
    m_triangle_output_node{},
    m_triangle_output_poly{},
    m_triangle_shapes{},
    m_triangle_verbose{false}
{
  #ifndef NDEBUG
  Test();
  #endif

  //CreateTriangleMesh();
  //Create3dMesh();
}

void ribi::trim::Dialog::Check3dMesh(const std::string&
  #ifndef NDEBUG
  path
  #endif // NDEBUG
) const noexcept
{
  #ifndef NDEBUG
  if (!fileio::FileIo().IsRegularFile(path))
  {
    TRACE("ERROR");
    TRACE("BREAK");
  }

  assert(fileio::FileIo().IsRegularFile(path)
    && "Check3dMesh calls OpenFOAM its checkMesh, which needs to know the full path"
  );
  #endif


  const std::string ply_filename = fileio::FileIo().GetTempFileName(".ply");

  {
    std::ofstream f(ply_filename.c_str());
    f << m_3dmesh_output_ply;
  }

  //const auto verbose = m_3dmesh_verbose;
  const bool verbose{false};
  const std::string checkMesh_command(
    #ifdef _WIN32
      std::string(
      R"(C:\cfd\blueCFD-SingleCore-2.1\OpenFOAM-2.1\etc\batchrc.bat )")
    + R"("WM_COMPILER=mingw-w32" "WM_PRECISION_OPTION=DP" "WM_MPLIB=""" )"
      // Changing to drive D is important...
    + "&& D: "
      // ...although this also indicates the drive
    + "&& cd " + ribi::fileio::FileIo().GetPath(path) + " "
    + "&& cd .. "
    + (verbose ? "&& dir " : "")
    + "&& checkMesh"
    #else
      "checkMesh"
    #endif
  );

  if (verbose) { TRACE(checkMesh_command); }
  const int error = std::system(checkMesh_command.c_str());

  ribi::fileio::FileIo().DeleteFile(ply_filename);

  if (error)
  {
    TRACE("WARNING: cannot find checkMesh");
    //TRACE(path);
    //TRACE(ribi::fileio::FileIo().GetPath(path));
    //TRACE(checkMesh_command);
    //TRACE(error);
  }
  //assert(!error);
}

std::function<void(std::vector<boost::shared_ptr<ribi::trim::Cell>>&)>
  ribi::trim::Dialog::CreateDefaultAssignBoundaryFunction() noexcept
{
  return [](std::vector<boost::shared_ptr<ribi::trim::Cell>>& cells)
  {
    std::vector<boost::shared_ptr<ribi::trim::Face>> faces;
    for (const boost::shared_ptr<ribi::trim::Cell>& cell: cells)
    {
      const std::vector<boost::shared_ptr<ribi::trim::Face>> w { cell->GetFaces() };
      std::copy(w.begin(),w.end(),std::back_inserter(faces));
    }
    for (boost::shared_ptr<ribi::trim::Face> face: faces)
    {
      if (face->GetNeighbour())
      {
        assert(face->GetConstOwner());
        face->SetBoundaryType("inside");
        continue;
      }
      else
      {
        assert(face->GetConstOwner());
        assert(!face->GetNeighbour());
        //If Owner its center has a higher Z coordinat, this is a bottom face
        if (face->GetOrientation() == ribi::trim::FaceOrientation::horizontal)
        {
          const double owner_z = boost::geometry::get<2>(face->GetConstOwner()->CalculateCenter());
          const double face_z = face->GetPoint(0)->GetZ().value();
          if (face_z < owner_z)
          {
            face->SetBoundaryType("bottom");
          }
          else
          {
            face->SetBoundaryType("top");
          }
        }
        else
        {
          assert(face->GetOrientation() == ribi::trim::FaceOrientation::vertical);
          const auto center(face->CalcCenter());
          const double center_x = boost::geometry::get<0>(center);
          const double center_y = boost::geometry::get<1>(center);
          if (center_x < 0.0)
          {
            if (center_y < 0.0)
            {
              face->SetBoundaryType("front");
            }
            else
            {
              face->SetBoundaryType("back");
            }
          }
          else
          {
            if (center_y < 0.0)
            {
              face->SetBoundaryType("left");
            }
            else
            {
              face->SetBoundaryType("right");
            }
          }
        }
        continue;
      }
    }
  }
  ;
}

std::function<ribi::foam::PatchFieldType(const std::string&)>
  ribi::trim::Dialog::CreateDefaultBoundaryToPatchFieldTypeFunction() noexcept
{
  const std::function<ribi::foam::PatchFieldType(const std::string&)> boundary_to_patch_field_type_function
    = [](const std::string& patch_name)
    {
      if (patch_name == "inside") return ribi::foam::PatchFieldType::no_patch_field;
      if (patch_name == "top") return ribi::foam::PatchFieldType::zeroGradient;
      if (patch_name == "bottom") return ribi::foam::PatchFieldType::zeroGradient;
      if (patch_name == "front") return ribi::foam::PatchFieldType::zeroGradient;
      if (patch_name == "back") return ribi::foam::PatchFieldType::zeroGradient;
      if (patch_name == "left") return ribi::foam::PatchFieldType::zeroGradient;
      if (patch_name == "right") return ribi::foam::PatchFieldType::zeroGradient;
      //if (patch_name == "defaultFaces") return ribi::foam::PatchFieldType::wall;
      TRACE(patch_name);
      assert(!"CreateDefaultBoundaryToPatchFieldTypeFunction: unknown patch name");
      throw std::logic_error("CreateDefaultBoundaryToPatchFieldTypeFunction: unknown patch name");
    }
  ;
  return boundary_to_patch_field_type_function;
}

void ribi::trim::Dialog::CreateDefaultControlDict() const noexcept
{
  std::ofstream f(ribi::foam::Filenames().GetControlDict().c_str());
  ribi::foam::ControlDictFile file;
  file.SetAdjustTimeStep(false);
  file.SetApplication("sonicFoam");
  file.SetDeltaT(1.0);
  file.SetEndTime(10.0);
  file.SetPurgeWrite(0);
  file.SetRunTimeModifiable(true);
  file.SetStartFrom("latestTime");
  file.SetStartTime(0.0);
  file.SetStopAt("endTime");
  file.SetTimeFormat("general");
  file.SetTimePrecision(6);
  file.SetWriteCompression("uncompressed");
  file.SetWriteControl("adjustableRunTime");
  file.SetWriteFormat("ascii");
  file.SetWriteInterval(1.0);
  file.SetWritePrecision(6);
  f << file;
}

void ribi::trim::Dialog::CreateDefaultPressureField() const noexcept
{
  std::ofstream f(ribi::foam::Filenames().GetPressureField().c_str());
  ribi::foam::PressureFile file;
  //std::vector<std::pair<std::string,foam::PatchFieldType>> v;
  //v.push_back(std::make_pair("top",foam::PatchFieldType::zeroGradient));
  //v.push_back(std::make_pair("bottom",foam::PatchFieldType::zeroGradient));
  //v.push_back(std::make_pair("front",foam::PatchFieldType::zeroGradient));
  //v.push_back(std::make_pair("back",foam::PatchFieldType::zeroGradient));
  //v.push_back(std::make_pair("left",foam::PatchFieldType::zeroGradient));
  //v.push_back(std::make_pair("right",foam::PatchFieldType::zeroGradient));
  //v.push_back(std::make_pair("defaultFaces",foam::PatchFieldType::zeroGradient));
  //file.SetBoundaryField(v);
  file.SetBoundaryField(
    "  top\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }\n"
    "  bottom\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }\n"
    "  front\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }\n"
    "  back\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }\n"
    "  left\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }\n"
    "  right\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }\n"
    "  \n"
    "  defaultFaces\n"
    "  {\n"
    "    type zeroGradient;\n"
    "  }"
  );
  file.SetDimensions( {1,-1,-2,0,0,0,0} );
  file.SetInternalField("uniform 1.7e5");
  f << file;
}

void ribi::trim::Dialog::CreateDefaultTemperatureField() const noexcept
{
  std::ofstream f(ribi::foam::Filenames().GetTemperatureField().c_str());
  ribi::foam::TemperatureFile file;
  file.SetBoundaryField(
    "top\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "bottom\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "front\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "back\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "left\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "right\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
  );

  file.SetDimensions( {0,0,0,1,0,0,0} );
  file.SetInternalField("uniform 293");
  f << file;
}

void ribi::trim::Dialog::CreateDefaultVelocityField() const noexcept
{
  ribi::foam::VelocityFieldFile file;
  file.SetDimensions( {0,1,-1,0,0,0,0} );
  file.SetInternalField("uniform (0 0 0)");
  file.SetBoundaryField(
    "inside\n"
    "{\n"
    "  type slip;\n"
    "}\n"
    "\n"
    "top\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "bottom\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "front\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "back\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "left\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
    "right\n"
    "{\n"
    "  type zeroGradient;\n"
    "}\n"
  );
  std::ofstream f(ribi::foam::Filenames().GetVelocityField().c_str());
  f << file;
}

std::function<void(std::vector<boost::shared_ptr<ribi::trim::Cell>>&)>
  ribi::trim::Dialog::CreateSculptFunctionNone() noexcept
{
  return [](std::vector<boost::shared_ptr<ribi::trim::Cell>>& cells)
  {
    std::random_shuffle(cells.begin(),cells.end());
  }
  ;
}

std::function<void(std::vector<boost::shared_ptr<ribi::trim::Cell>>&)>
  ribi::trim::Dialog::CreateSculptFunctionRemoveRandom(const double p) noexcept
{
  assert(p >= 0.0);
  assert(p <= 1.0);
  return [p](std::vector<boost::shared_ptr<ribi::trim::Cell>>& cells)
  {
    std::random_shuffle(cells.begin(),cells.end());
    std::reverse(cells.begin(),cells.end());
    std::random_shuffle(cells.begin(),cells.end());
    cells.resize(static_cast<int>(static_cast<double>(cells.size()) * p));
  }
  ;
}

void ribi::trim::Dialog::CreateTriangleMesh() noexcept
{
  const auto verbose = m_triangle_verbose;
  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Write some geometries, let Triangle.exe work on it" << std::endl
    ;
  }

  m_triangle_shapes = Shapes();
  m_triangle_output_ele = "";
  m_triangle_output_node = "";
  m_triangle_output_poly = "";

  try
  {
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Create Triangle.exe input" << std::endl
      ;
    }

    //const auto file = boost::make_shared<TriangleFile>(m_shapes);
    m_triangle_file = boost::make_shared<TriangleFile>(m_shapes);

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Store the Triangle.exe input" << std::endl
      ;
    }

    //m_triangle_input_poly = file->GetTriangleInputPoly();

    std::string filename_node;
    std::string filename_ele;
    std::string filename_poly;

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Calling Triangle.exe" << std::endl
      ;
    }

    m_triangle_file->ExecuteTriangleExe(
      filename_node,
      filename_ele,
      filename_poly,
      m_triangle_min_angle,
      m_triangle_max_area,
      verbose
    );

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Saving Triangle.exe output" << std::endl
      ;
    }

    m_triangle_output_ele = fileio::FileIo().FileToStr(filename_ele);
    m_triangle_output_node = fileio::FileIo().FileToStr(filename_node);
    m_triangle_output_poly = fileio::FileIo().FileToStr(filename_poly);

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Read data from Triangle.exe output" << std::endl
      ;
    }

    const boost::shared_ptr<const ribi::trim::Template> t(
      new ribi::trim::Template(
        filename_node,
        filename_ele,
        verbose
      )
    );
    assert(t);

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Convert Triangle.exe output to polygons" << std::endl
      ;
    }

    for (const boost::shared_ptr<trim::Face> face: t->GetFaces())
    {
      std::vector<Coordinat> coordinats;
      for (const boost::shared_ptr<trim::Point> point: face->GetPoints())
      {
        coordinats.push_back(*point->GetCoordinat());
      }
      Polygon polygon;
      boost::geometry::append(polygon, coordinats);
      m_triangle_shapes.first.push_back(polygon);
    }

    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Delete Triangle.exe output" << std::endl
      ;
    }
    fileio::FileIo().DeleteFile(filename_ele);
    fileio::FileIo().DeleteFile(filename_node);
    fileio::FileIo().DeleteFile(filename_poly);


  }
  catch (std::exception& e)
  {
    std::stringstream s;
    s << "ribi::trim::Dialog::Dialog(" << __LINE__ << "): "
      << "Triangle.exe failed: " << e.what();
    throw std::runtime_error(s.str());
  }
}

void ribi::trim::Dialog::Create3dMesh() noexcept
{
  const auto verbose = m_3dmesh_verbose;

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Read data from Triangle.exe output" << std::endl
    ;
  }

  std::vector<boost::shared_ptr<ribi::trim::Cell>> cells;
  {
    const std::string filename_ele = fileio::FileIo().GetTempFileName(".ele");
    const std::string filename_node = fileio::FileIo().GetTempFileName(".node");

    { std::ofstream f(filename_ele.c_str()); f << m_triangle_output_ele; }
    { std::ofstream f(filename_node.c_str()); f << m_triangle_output_node; }

    const boost::shared_ptr<const ribi::trim::Template> t(
      new ribi::trim::Template(
        filename_node,
        filename_ele,
        verbose
      )
    );
    assert(t);

    fileio::FileIo().DeleteFile(filename_ele);
    fileio::FileIo().DeleteFile(filename_node);

    //Create cells from this template
    {
      const ribi::trim::CellsCreatorFactory cells_creator_factory;
      assert(t);
      const boost::shared_ptr<ribi::trim::CellsCreator> c
        = cells_creator_factory.Create(
          t,
          m_3dmesh_n_cell_layers,
          m_3dmesh_layer_height,
          m_3dmesh_strategy,
          verbose
      );
      assert(c);
      cells = c->GetCells();
      #ifndef NDEBUG
      for (const auto& cell:cells) { assert(cell); }
      #endif
    }

    //Sculpting
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Number of cells before sculpting: " << cells.size() << std::endl
      ;
    }
    m_3dmesh_sculpt_function(cells);

    m_n_cells = static_cast<int>(cells.size());
    if (verbose)
    {
      std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
        << "Number of cells after sculpting: " << m_n_cells << std::endl
      ;
    }

    //Remove weak faces
    {
      std::vector<boost::shared_ptr<ribi::trim::Face>> faces;
      for (const boost::shared_ptr<ribi::trim::Cell>& cell: cells)
      {
        const std::vector<boost::shared_ptr<ribi::trim::Face>> w { cell->GetFaces() };
        std::copy(w.begin(),w.end(),std::back_inserter(faces));
      }
      if (verbose)
      {
        std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
          << "Number of weak faces: " << faces.size() << std::endl
        ;
      }
      assert(std::unique(faces.begin(),faces.end()) == faces.end());
      assert(std::count(faces.begin(),faces.end(),nullptr) == 0);
      //Clean all weakened faces
      faces.erase(
        std::remove_if(faces.begin(),faces.end(),
          [](const boost::shared_ptr<const ribi::trim::Face> face)
          {
            return !face->GetConstOwner();
          }
        ),
        faces.end()
      );
      assert(std::count(faces.begin(),faces.end(),nullptr) == 0);

      m_n_faces = static_cast<int>(faces.size());
      if (verbose)
      {
        std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
          << "Number of strong faces: " << m_n_faces << std::endl
        ;
      }
      //const ribi::trim::Helper helper;
      std::sort(faces.begin(),faces.end(),ribi::trim::Helper().OrderByIndex());
      const auto new_end = std::unique(faces.begin(),faces.end());
      faces.erase(new_end,faces.end());
      assert(std::count(faces.begin(),faces.end(),nullptr) == 0);
    }

    //Assign boundaries to the strong faces
    m_3dmesh_assign_boundary_function(cells);
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Checking the cells" << std::endl
    ;
  }
  #ifndef NDEBUG
  {
    for (const auto& cell: cells)
    {
      assert(cell);
      for (const auto& face: cell->GetFaces())
      {
        assert(face);
        for (const auto& point: face->GetPoints())
        {
          assert(point);
        }
      }
    }
  }
  #endif //NDEBUG

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Build the OpenFOAM files" << std::endl
    ;
  }
  {
    std::string filename_result_mesh = fileio::FileIo().GetTempFileName(".ply");

    const boost::shared_ptr<ribi::trim::TriangleMeshBuilder> builder{
      new ribi::trim::TriangleMeshBuilder(
        cells,
        filename_result_mesh,
        m_3dmesh_boundary_to_patch_field_type_function,
        m_3dmesh_strategy,
        m_3dmesh_verbose
      )
    };
    assert(builder);

    m_3dmesh_output_ply = fileio::FileIo().FileToStr(filename_result_mesh);

    fileio::FileIo().DeleteFile(filename_result_mesh);
  }

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Create some files (checkMesh needs these)" << std::endl
    ;
  }
  CreateDefaultControlDict();
  CreateDefaultPressureField();
  CreateDefaultTemperatureField();
  CreateDefaultVelocityField();
}

std::string ribi::trim::Dialog::GetShapesAsSvg(const double line_width) const noexcept
{
  return Geometry().ToSvg(m_shapes,line_width);
}

std::string ribi::trim::Dialog::GetShapesAsWkt() const noexcept
{
  return Geometry().ToWkt(m_shapes);
}

std::string ribi::trim::Dialog::GetTriangleMeshAsSvg(const double line_width) const noexcept
{
  return Geometry().ToSvg(m_triangle_shapes,line_width);
}

std::string ribi::trim::Dialog::GetTriangleMeshAsWkt() const noexcept
{
  return Geometry().ToWkt(m_triangle_shapes);
}

std::string ribi::trim::Dialog::GetVersion() noexcept
{
  return "1.3";
}

std::vector<std::string> ribi::trim::Dialog::GetVersionHistory() noexcept
{
  return {
    "2014-06-24: version 1.0: initial version",
    "2014-06-25: version 1.1: checkMesh works again",
    "2014-07-02: version 1.2: changed O(n^2) to something better (#220)",
    "2014-07-18: version 1.3: the points of faces are wound clockwards, as in accordance with the OpenFOAM documentation"
  };
}

void ribi::trim::Dialog::Set3dMeshParameters(
  const int n_cell_layers,
  const Length layer_height,
  const ::ribi::trim::CreateVerticalFacesStrategy strategy,
  const std::function<void(std::vector<boost::shared_ptr<Cell>>&)>& sculpt_function,
  const std::function<void(std::vector<boost::shared_ptr<Cell>>&)>& assign_boundary_function,
  const std::function<::ribi::foam::PatchFieldType(const std::string&)>& boundary_to_patch_field_type_function,
  const bool verbose
) noexcept
{
  m_3dmesh_assign_boundary_function = assign_boundary_function;
  m_3dmesh_boundary_to_patch_field_type_function = boundary_to_patch_field_type_function;
  m_3dmesh_layer_height = layer_height;
  m_3dmesh_n_cell_layers = n_cell_layers;
  m_3dmesh_sculpt_function = sculpt_function;
  m_3dmesh_strategy = strategy;
  m_3dmesh_verbose = verbose;

}


void ribi::trim::Dialog::SetShapes(const Shapes& shapes) noexcept
{
  m_shapes = shapes;
}

void ribi::trim::Dialog::SetShapes(const std::string& wkt) noexcept
{
  SetShapes(Geometry().WktToShapes(wkt));
}

void ribi::trim::Dialog::SetTriangleParameters(
  const Angle triangle_min_angle,
  const Area triangle_max_area,
  const bool verbose
) noexcept
{
  m_triangle_min_angle = triangle_min_angle;
  m_triangle_max_area = triangle_max_area;
  m_triangle_verbose = verbose;

}

void ribi::trim::Dialog::Show3dMesh() const noexcept
{
  const std::string filename = fileio::FileIo().GetTempFileName(".ply");

  //Create file for MeshLab
  {
    std::ofstream f(filename.c_str());
    f << m_3dmesh_output_ply;
  }

  assert(ribi::fileio::FileIo().IsRegularFile(filename));
  std::stringstream s;
  s
    #ifdef _WIN32
    << "C:\\Progra~1\\VCG\\Meshlab\\meshlab.exe "
    #else
    << "meshlab "
    #endif
    << filename
  ;
  const int error = std::system(s.str().c_str());
  if (error)
  {
    std::cout << "WARNING: cannot display mesh" << '\n';
    TRACE("BREAK");
  }

  ribi::fileio::FileIo().DeleteFile(filename);
  assert(!ribi::fileio::FileIo().IsRegularFile(filename));
}

#ifndef NDEBUG
void ribi::trim::Dialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    PolyFile( Vertices() );
    PolyFileFromPolygons( Shapes() );
    TriangleFile( {} );
    CellsCreatorFactory();
    Template::CreateTest(0);
    CellFactory();
    TriangleMeshBuilder(
      { CellFactory().CreateTestPrism(CreateVerticalFacesStrategy::one_face_per_square) },
      "",
      Dialog::CreateDefaultBoundaryToPatchFieldTypeFunction(),
      CreateVerticalFacesStrategy::one_face_per_square,
      false
    );
    //foam::BoundaryFile():
    foam::ControlDictFile();
    //foam::FacesFile();
    foam::FvSchemesFile();
    foam::FvSolutionFile();
    //foam::NeighbourFile();
    //foam::OwnerFile();
    //foam::PointsFile();
    foam::PressureFile();
    foam::TemperatureFile();
    foam::ThermophysicalPropertiesFile();
    foam::VelocityFieldFile();
    //{ Dialog d; d.CreateTriangleMesh(); d.Create3dMesh(); } //TriangleMeshBuilder
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  //Flow of Dialog
  {
    Dialog d;
    d.SetShapes( Geometry().WktToShapes("POLYGON((1 1,-1 1,-1 -1,1 -1))") );
    d.CreateTriangleMesh();
    d.Create3dMesh();
    assert(!d.Get3dMesh().empty());
    //d.Check3dMesh();
  }
  {
    ribi::trim::CellsCreatorFactory().Create(
      ribi::trim::Template::CreateTest(0),
      2,
      1.0 * boost::units::si::meter,
      ribi::trim::CreateVerticalFacesStrategy::one_face_per_square,
      verbose
    );
  }

  //Create a simple mesh
  /*
  for (::ribi::trim::CreateVerticalFacesStrategy strategy: ribi::trim::CreateVerticalFacesStrategies().GetAll())
  {
    try
    {
      const double pi { boost::math::constants::pi<double>() };
      const Polygons polygons {
        Geometry().CreateShapePolygon(4,pi * 0.125,1.0) //1 cube
      };
      const Linestrings linestrings = {};
      const Shapes shapes(polygons,linestrings);
      const Angle triangle_min_angle
        = 20.0 //Default used by Triangle, in degrees
        * (boost::math::constants::two_pi<double>() / 360.0)
        * boost::units::si::radian
      ;
      const Area triangle_max_area = 1.0 * boost::units::si::square_meter;
      const int n_cell_layers = 1;
      const bool verbose{false};
      const ribi::trim::Dialog d(
        shapes,
        n_cell_layers,
        1.0 * boost::units::si::meter,
        strategy,
        triangle_min_angle,
        triangle_max_area,
        Dialog::CreateSculptFunctionRemoveRandom(0.75),
        Dialog::CreateDefaultAssignBoundaryFunction(),
        Dialog::CreateDefaultBoundaryToPatchFieldTypeFunction(),
        verbose
      );
    }
    catch (std::exception& e)
    {
      assert(!"Should not get here");
    }
    catch (...)
    {
      assert(!"Should not get here");
    }
  }
  */
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshedge.cpp

 

 

 

 

 

 

./CppTriangleMesh/trianglemeshface.h

 

#ifndef RIBI_TRIANGLEMESHFACE_H
#define RIBI_TRIANGLEMESHFACE_H

#include <iosfwd>
#include <set>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/checked_delete.hpp>
#include <boost/geometry.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include "trianglemeshfaceorientation.h"
#include "trianglemeshfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///Sure, its points can change...
struct Face
{
  //friend class Dialog;
  //friend class Cell;
  //friend class CellsCreator;
  //friend class Helper;
  //friend class Point;
  //friend class Template;
  //friend class TriangleMeshBuilderImpl;
  //friend void CellsCheck(const std::vector<boost::shared_ptr<Cell>>& cells) noexcept;

  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;
  typedef std::set<Coordinat3D,std::function<bool(Coordinat3D,Coordinat3D)>> Coordinat3dSet;

  Face(const Face& ) = delete;
  //Face(      Face&&) = delete;
  Face& operator=(const Face& ) = delete;
  //Face& operator=(      Face&&) = delete;

  Coordinat3D CalcCenter() const noexcept;

  int CalcPriority() const noexcept;

  ///When the Face its points know their Layers, call DoExtractCoordinats
  bool CanExtractCoordinats() const noexcept;

  ///Throws is the face its m_orientation and Points orientationare mismatch
  ///This function is called by the Faces' Points, after they received a Z value
  void CheckOrientation() const;

  ///When the Face its points know their Layers, call this member function
  void DoExtractCoordinats() const;

  Coordinat3dSet GetCoordinats() const noexcept { return m_coordinats; }

  int GetIndex() const noexcept { return m_index; }

  ///nullptr if no neighbour
  boost::shared_ptr<const Cell> GetNeighbour() const noexcept;
  boost::shared_ptr<      Cell> GetNonConstNeighbour()       noexcept;

  FaceOrientation GetOrientation() const noexcept { return m_orientation; }

  ///nullptr if no owner, a Volume::m_cellindex type
  boost::shared_ptr<const Cell> GetConstOwner() const noexcept;

  boost::shared_ptr<const Point> GetPoint(const int index) const noexcept;

  const std::vector<boost::shared_ptr<Point>>& GetPoints() const noexcept { return m_points; }

  void SetBoundaryType(const std::string type) const noexcept { m_type = type; }

  ///Before saving a Face to OpenFOAM, its Points' winding needs to be set to the correct order:
  /// - if the Face is a boundary face, the normal needs to point outwards;
  ///   going away from the mesh; its points needs to be ordered clockwise
  ///   when viewed from its cell's center
  /// - if the Face is an internal face, the normal needs to point inside
  ///   the cell with the heighest index
  void SetCorrectWinding() noexcept;

  ///If a Face has both an owner Cell and a Neighbour Cell, transfer
  ///ownership to the neighbour
  void TransferOwnership() noexcept;

  static const int sm_face_no_index = -2;

  private:
  ~Face() noexcept;
  friend void boost::checked_delete<>(Face* x);
  friend void boost::checked_delete<>(const Face* x);

  ///Cells this Face belongs to
  mutable std::vector<boost::weak_ptr<const Cell>> m_belongs_to;

  ///m_coordinats is used to speed up 'FaceExists', which compares a new Face
  ///with one already present, by comparing their sorted coordinats
  mutable Coordinat3dSet m_coordinats;

  ///Track the creation and deletion of faces
  static std::set<const Face*> sm_faces;

  ///The index of this Face in an TriangleMeshBuilder vector. It is determined at the end
  mutable int m_index;

  const FaceOrientation m_orientation;

  ///m_points must be a std::vector instead of a std::set, as
  ///their orders matter (and changed by ReversePoints)
  std::vector<boost::shared_ptr<Point>> m_points;

  ///The type of boundary this Face belongs to. It is determined at the end
  ///By default it is BoundaryType::internalMesh: a Face that is between two cells
  mutable std::string m_type;

  friend class FaceFactory;
  ///Enforce a Face is only created by a FaceFactory
  explicit Face(
    const std::vector<boost::shared_ptr<Point>>& points,
    const FaceOrientation any_orientation,
    const int index,
    const FaceFactory& lock
  );

  void OnCellDestroyed(const Cell* const cell) noexcept;

  friend class CellFactory;
  void AddBelongsTo(boost::weak_ptr<const Cell> cell);

  ///Determined in the end
  friend class TriangleMeshBuilderImpl;
  std::string GetBoundaryType() const noexcept { return m_type; }
  boost::shared_ptr<Cell> GetNonConstOwner() noexcept;
  void SetIndex(const int index) const noexcept { m_index = index; }

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend std::ostream& operator<<(std::ostream& os, const Cell& cell) noexcept;
  friend std::ostream& operator<<(std::ostream& os, const Face& f) noexcept;
  friend bool operator==(const Face& lhs, const Face& rhs) noexcept;
  friend bool operator!=(const Face& lhs, const Face& rhs) noexcept;
};

bool operator==(const Face& lhs, const Face& rhs) noexcept;
bool operator!=(const Face& lhs, const Face& rhs) noexcept;
std::ostream& operator<<(std::ostream& os, const Face& f) noexcept;

bool operator<(const boost::shared_ptr<const Face>& lhs, const boost::shared_ptr<      Face>& rhs) = delete;
bool operator<(const boost::shared_ptr<const Face>& lhs, const boost::shared_ptr<const Face>& rhs) = delete;
bool operator<(const boost::shared_ptr<      Face>& lhs, const boost::shared_ptr<      Face>& rhs) = delete;
bool operator<(const boost::shared_ptr<      Face>& lhs, const boost::shared_ptr<const Face>& rhs) = delete;


} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHFACE_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshface.cpp

 

#include "trianglemeshface.h"

#include <algorithm>
#include <fstream>
#include <set>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/prefixes.hpp>

#include "geometry.h"
#include "testtimer.h"
#include "trianglemeshcell.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trianglemeshpoint.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpointfactory.h"
#include "trianglemeshwinding.h"
#include "trianglemeshwindings.h"
#include "trace.h"
#include "xml.h"
#pragma GCC diagnostic pop

std::set<const ribi::trim::Face*> ribi::trim::Face::sm_faces;

ribi::trim::Face::Face(
  const std::vector<boost::shared_ptr<Point>>& any_points,
  const FaceOrientation any_orientation,
  const int index,
  const FaceFactory&
  )
  :
    m_belongs_to{},
    m_coordinats{},
    m_index{index},
    m_orientation{any_orientation},
    m_points{any_points},
    m_type{}
{
  #ifndef NDEBUG
  Test();
  assert(m_points == any_points);
  assert(!m_points.empty());
  assert(  m_points[0].use_count() >= 2);
  assert(any_points[0].use_count() >= 2);
  assert(Helper().IsPlane(m_points));
  if (!Helper().IsConvex(m_points))
  {
    TRACE("ERROR");
    for (const auto& point: m_points) TRACE(point->ToStr());
  }
  assert(sm_faces.count(this) == 0);
  #endif

  sm_faces.insert(this);

  #ifndef NDEBUG
  assert(Helper().IsConvex(m_points));
  if (m_orientation == FaceOrientation::horizontal)
  {
    const int n_points = static_cast<int>(m_points.size());
    assert(n_points > 2);
    for (int i=0; i!=n_points; ++i)
    {
      assert(m_points[i]);
    }
    if (m_points[0]->CanGetZ())
    {
      const auto z = m_points[0]->GetZ();
      for (const auto& p: m_points)
      {
        assert(p->CanGetZ());
        assert(z == p->GetZ());
      }
    }
  }
  #endif
}

ribi::trim::Face::~Face() noexcept
{
  assert(sm_faces.count(this) == 1);
  sm_faces.erase(this);
  assert(sm_faces.count(this) == 0);
}

void ribi::trim::Face::AddBelongsTo(boost::weak_ptr<const Cell> cell)
{
  assert(cell.lock());
  m_belongs_to.push_back(cell);
  assert(m_belongs_to.size() <= 2);
  assert(
       (m_belongs_to.size() == 1)
    || (m_belongs_to.size() == 2 && m_belongs_to[0].lock() != m_belongs_to[1].lock())
  );
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> ribi::trim::Face::CalcCenter() const noexcept
{
  assert(!m_points.empty());
  using boost::geometry::get;
  
  const Coordinat3D sum(
    std::accumulate(m_points.begin(),m_points.end(),
      Geometry().CreatePoint(0.0,0.0,0.0),
      [](const Coordinat3D& init, const boost::shared_ptr<const Point>& point)
      {
        assert(point);
        return init + point->GetCoordinat3D();
      }
    )
  );
  const Coordinat3D center(
    sum / static_cast<double>(m_points.size())
  );
  return center;
}

int ribi::trim::Face::CalcPriority() const noexcept
{
  assert(GetConstOwner());
  return GetConstOwner()->GetIndex();
}

bool ribi::trim::Face::CanExtractCoordinats() const noexcept
{
  for (const auto& point: m_points)
  {
    if (!point->CanGetZ()) return false;
  }
  return true;
}

void ribi::trim::Face::CheckOrientation() const
{
  assert(std::count(m_points.begin(),m_points.end(),nullptr) == 0);
  std::set<double> zs;
  for (const auto& point: m_points)
  {
    if (point->CanGetZ()) { zs.insert(zs.begin(),point->GetZ().value()); }
  }
  if (zs.size() > 1)
  {
    assert(m_orientation == FaceOrientation::vertical
      && "Only a vertical face has multiple Z values");
  }
}

void ribi::trim::Face::DoExtractCoordinats() const
{
  
  assert(CanExtractCoordinats());
  //assert(m_coordinats.empty()); //This is done multiple times in debugging
  
  m_coordinats = Helper().ExtractCoordinats(AddConst(m_points));
}

boost::shared_ptr<const ribi::trim::Cell> ribi::trim::Face::GetNeighbour() const noexcept
{
  //#220: This is the number 3 slowest function
  assert(m_belongs_to.size() <= 2);

  #define FIX_ISSUE_220
  #ifdef FIX_ISSUE_220
  assert(
    std::count_if(
      m_belongs_to.begin(),m_belongs_to.end(),
      [](const boost::weak_ptr<const Cell> cell) { return !cell.lock(); }
    ) == 0
    && "Ha, we can save the statement below to increase speed"
  );
  #else
  m_belongs_to.erase(
    std::remove_if(
      m_belongs_to.begin(),m_belongs_to.end(),
      [](const boost::weak_ptr<const Cell> cell) { return !cell.lock(); }
    ),
    m_belongs_to.end()
  );
  #endif
  boost::shared_ptr<const Cell> p;
  if (m_belongs_to.size() < 2) return p;
  assert(m_belongs_to[0].lock() != m_belongs_to[1].lock());
  p = m_belongs_to[1].lock();
  assert(p);
  return p;
}

boost::shared_ptr<ribi::trim::Cell> ribi::trim::Face::GetNonConstNeighbour() noexcept
{
  const boost::shared_ptr<const ribi::trim::Cell> const_cell
    = const_cast<const ribi::trim::Face*>(this)->GetNeighbour();
  return boost::const_pointer_cast<Cell>(const_cell);
}

boost::shared_ptr<const ribi::trim::Cell> ribi::trim::Face::GetConstOwner() const noexcept
{
  //#220: This is the number 1 slowest function
  assert(m_belongs_to.size() <= 2);

  assert(
    std::count_if(
      m_belongs_to.begin(),m_belongs_to.end(),
      [](const boost::weak_ptr<const Cell> cell) { return !cell.lock(); }
    ) >= 0
    && "Cells might and might not be deleted in other contexts"
  );

  m_belongs_to.erase(
    std::remove_if(
      m_belongs_to.begin(),m_belongs_to.end(),
      [](const boost::weak_ptr<const Cell> cell) { return !cell.lock(); }
    ),
    m_belongs_to.end()
  );

  assert( (m_belongs_to.empty() || m_belongs_to.size() > 0)
    && "There might have been no owner assigned yet");

  if (m_belongs_to.empty())
  {
    return nullptr;
  }
  assert(!m_belongs_to.empty());
  boost::shared_ptr<const Cell> p {
    m_belongs_to[0]
  };
  assert(p);
  return p;
}

boost::shared_ptr<ribi::trim::Cell> ribi::trim::Face::GetNonConstOwner() noexcept
{
  const boost::shared_ptr<const ribi::trim::Cell> const_cell
    = const_cast<const ribi::trim::Face*>(this)->GetConstOwner();
  return boost::const_pointer_cast<Cell>(const_cell);
}

boost::shared_ptr<const ribi::trim::Point> ribi::trim::Face::GetPoint(const int index) const noexcept
{
  
  assert(index >= 0);
  assert(index < static_cast<int>(GetPoints().size()));
  return GetPoints()[index];
}

void ribi::trim::Face::OnCellDestroyed(const Cell* const cell) noexcept
{
  assert(1==2);
  const auto new_end = std::remove_if(m_belongs_to.begin(),m_belongs_to.end(),
    [cell](const boost::weak_ptr<const Cell>& belongs_to) { return belongs_to.lock().get() == cell; }
  );
  m_belongs_to.erase(new_end,m_belongs_to.end());
}

void ribi::trim::Face::SetCorrectWinding() noexcept
{
  assert(m_points.size() == 3 || m_points.size() == 4);
  assert( (m_belongs_to.size() == 1 || m_belongs_to.size() == 2)
    && "A Face its winding can only be set if it belongs to a cell"
  );
  assert(Helper().IsPlane(m_points));
  assert(Helper().IsConvex(m_points));

  const boost::shared_ptr<const Cell> observer{
    !GetNeighbour()
    ? GetConstOwner()
    : GetConstOwner()->GetIndex() < GetNeighbour()->GetIndex() ? GetConstOwner() : GetNeighbour()
  };
  assert(observer);
  assert(Helper().IsPlane(m_points));

  //Must be ordered clockwise, according to the OpenFOAM documentation
  Helper().MakeClockwise(this->m_points,observer->CalculateCenter());

  assert(Helper().IsClockwise(m_points,observer->CalculateCenter()));
  assert(Helper().IsPlane(m_points));
  assert(Helper().IsConvex(m_points));
}

#ifndef NDEBUG
void ribi::trim::Face::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  FaceFactory();

  const TestTimer test_timer(__func__,__FILE__,1.0);
  //Check that a Face has no owner nor neighbour when not added to a Cell
  for (const auto& strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const std::vector<boost::shared_ptr<Face>> faces {
      FaceFactory().CreateTestPrism(strategy)
    };
    for (const auto& face: faces)
    {
      assert(!(face->GetConstOwner().get()) && "Faces obtain an owner when being added to a Cell");
      assert(!(face->GetNeighbour().get()) && "Faces obtain a neighbour when beging added to a Cell twice");
      //face->SetCorrectWinding(); //Cannot! A Face must belong to a Cell for this to work
    }
  }
  //Check that incorrect Faces cannot be constructed
  for (Winding winding: Windings().GetAll())
  {
    const std::vector<boost::shared_ptr<Point>> points {
      PointFactory().CreateTestSquare(winding)
    };
    
    assert(points.size() == 4);
    assert(Helper().IsConvex(points)
      == (winding == Winding::clockwise || winding == Winding::counter_clockwise)
    );
  }
}
#endif

void ribi::trim::Face::TransferOwnership() noexcept
{
  assert(m_belongs_to.size() == 2);
  std::swap(m_belongs_to[0],m_belongs_to[1]);
}

bool ribi::trim::operator==(const ribi::trim::Face& lhs, const ribi::trim::Face& rhs) noexcept
{
  return
       lhs.GetPoints() == rhs.GetPoints()
    && lhs.GetOrientation() == rhs.GetOrientation()
  ;
}

bool ribi::trim::operator!=(const ribi::trim::Face& lhs, const ribi::trim::Face& rhs) noexcept
{
  return !(lhs == rhs);
}

std::ostream& ribi::trim::operator<<(std::ostream& os, const ribi::trim::Face& f) noexcept
{
  os
    << ribi::xml::ToXml("face_index",f.GetIndex())
    << ribi::xml::ToXml("orientation",static_cast<int>(f.GetOrientation()))
  ;

  {
    std::stringstream s;
    const int n_points { static_cast<int>(f.GetPoints().size()) };
    for (int i=0; i!=n_points; ++i)
    {
      s << ribi::xml::ToXml("point" + boost::lexical_cast<std::string>(i),*f.GetPoint(i));
    }
    os << ribi::xml::ToXml("points",s.str());
  }

  return os;
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshfacefactory.h

 

#ifndef RIBI_TRIANGLEMESHFACEFACTORY_H
#define RIBI_TRIANGLEMESHFACEFACTORY_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/checked_delete.hpp>
#include <boost/geometry.hpp>
#include <boost/shared_ptr.hpp>
#include "trianglemeshcreateverticalfacesstrategy.h"
#include "trianglemeshfaceorientation.h"
#include "trianglemeshfwd.h"
#include "trianglemeshwinding.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

class FaceFactory
{
  //friend class Dialog;
  friend class CellFactory;
  friend class CellsCreator;
  friend class Face;
  friend class Template;

  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;

  FaceFactory();

  boost::shared_ptr<Face> Create(
    std::vector<boost::shared_ptr<Point>> points,
    const FaceOrientation any_orientation,
    const bool verbose
  ) const noexcept;

  ///Create the faces of a testing prism from points
  ///The indices are { top, bottom, a,b,c }
  /*

            5
           /|\
          3---4
          | | |
          | 2 |
          |/ \|
          0---1

  Folder out, with the bottom (marked #) at the center

          +---+
         /|\ 1|
        / | \ |
       /  |  \|
  +---+ 4 | 5 +
  |7 /|\  |  /
  | / | \ | /
  |/ 6|0 \|/
  +---+---+
      |\ 2|
      | \ |
      |3 \|
      +---+

          +---+
         / \ 1|
        /   \ |
       /     \|
  +---+   3   +
  |   |\     /
  | 4 | \   /
  |   |0 \ /
  +---+---+
      |   |
      | 2 |
      |   |
      +---+


  The front planes are 'a' and 'b', where 'a' has two nodes at the base

  */
  std::vector<boost::shared_ptr<Face>> CreateTestPrism(
    const CreateVerticalFacesStrategy strategy
  ) const noexcept;

  boost::shared_ptr<Face> CreateTestSquare(const Winding winding) const noexcept;

  private:

  std::vector<boost::shared_ptr<Face>> CreateTestPrismOneFacePerVerticalFace() const noexcept;
  std::vector<boost::shared_ptr<Face>> CreateTestPrismTwoFacesPerVerticalFace() const noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHFACEFACTORY_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshfacefactory.cpp

 

#include "trianglemeshfacefactory.h"

#include <cassert>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/make_shared.hpp>

#include "geometry.h"
#include "testtimer.h"
#include "trianglemeshcreateverticalfacesstrategies.h"
#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshpointfactory.h"
#include "trianglemeshwinding.h"
#include "trianglemeshwindings.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::trim::FaceFactory::FaceFactory()
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::shared_ptr<ribi::trim::Face> ribi::trim::FaceFactory::Create(
  std::vector<boost::shared_ptr<Point>> points,
  const FaceOrientation any_orientation,
  const bool verbose
) const noexcept
{
  
  //Give every Cell some index at creation
  static int cnt{1};
  const int n{cnt};
  ++cnt;

  assert(points.size() == 3 || points.size() == 4);
  assert(std::count(points.begin(),points.end(),nullptr) == 0);
  #ifndef NDEBUG
  if(!Helper().IsPlane(points))
  {
    TRACE("ERROR");
  }
  #endif //NDEBUG
  assert(Helper().IsPlane(points));

  if (!Helper().IsConvex(points))
  {
    TRACE("ERROR");
  }

  assert(Helper().IsConvex(points)
    && "FaceFactory must be called by a sorted and convex collection of points"
  );

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Creating face with index " << n << std::endl
    ;
  }

  const boost::shared_ptr<Face> face{
    new Face(
      points,
      any_orientation,
      n,
      *this
    )
  };
  assert(face);

  if (verbose)
  {
    std::clog << __FILE__ << "(" <<  (__LINE__) <<  ") : "
      << "Connecting all " << face->GetPoints().size()
      << " points to the face" << std::endl
    ;
  }

  for (const auto& point: face->GetPoints())
  {
    assert(point);
    point->AddConnected(face);
  }

  return face;
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::FaceFactory::CreateTestPrism(
  const CreateVerticalFacesStrategy strategy
) const noexcept
{
  switch (strategy)
  {
    case CreateVerticalFacesStrategy::one_face_per_square : return CreateTestPrismOneFacePerVerticalFace();
    case CreateVerticalFacesStrategy::two_faces_per_square: return CreateTestPrismTwoFacesPerVerticalFace();
  }
  assert(!"FaceFactory::CreateTestPrism: Should not get here");
  throw std::logic_error("FaceFactory::CreateTestPrism: Should not get here");
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::FaceFactory::CreateTestPrismOneFacePerVerticalFace() const noexcept
{
  const auto points = PointFactory().CreateTestPrism();
  const std::vector<boost::shared_ptr<Point>> points_bottom { points[0], points[2], points[1] };
  const std::vector<boost::shared_ptr<Point>> points_top    { points[3], points[4], points[5] };
  const std::vector<boost::shared_ptr<Point>> points_a      { points[0], points[1], points[4], points[3] };
  const std::vector<boost::shared_ptr<Point>> points_b      { points[1], points[2], points[5], points[4] };
  const std::vector<boost::shared_ptr<Point>> points_c      { points[2], points[0], points[3], points[5] };
  const bool verbose{false};

  //assert(Helper().IsClockwiseHorizontal(points_bottom));
  //assert(Helper().IsClockwiseHorizontal(points_top));
  //assert(Helper().IsConvex(points_bottom));
  //assert(Helper().IsConvex(points_top));
  //assert(Helper().IsConvex(points_a));
  //assert(Helper().IsConvex(points_b));
  //assert(Helper().IsConvex(points_c));
  const auto bottom = FaceFactory().Create(points_bottom,FaceOrientation::horizontal,verbose);
  const auto top = FaceFactory().Create(points_top,FaceOrientation::horizontal,verbose);
  const auto a = FaceFactory().Create(points_a,FaceOrientation::vertical,verbose);
  const auto b = FaceFactory().Create(points_b,FaceOrientation::vertical,verbose);
  const auto c = FaceFactory().Create(points_c,FaceOrientation::vertical,verbose);
  assert(bottom);
  assert(top);
  assert(a);
  assert(b);
  assert(c);
  bottom->SetBoundaryType("bottom");
  top->SetBoundaryType("top");
  a->SetBoundaryType("back");
  b->SetBoundaryType("left");
  c->SetBoundaryType("right");
  const std::vector<boost::shared_ptr<Face>> prism {
    top,bottom,a,b,c
  };
  return prism;
}

std::vector<boost::shared_ptr<ribi::trim::Face>> ribi::trim::FaceFactory::CreateTestPrismTwoFacesPerVerticalFace() const noexcept
{
  const std::vector<boost::shared_ptr<Point>> points {
    PointFactory().CreateTestPrism()
  };
        std::vector<boost::shared_ptr<Point>> points_bottom { points[0], points[1], points[2] };
        std::vector<boost::shared_ptr<Point>> points_top    { points[3], points[4], points[5] };
  const std::vector<boost::shared_ptr<Point>> points_a      { points[0], points[1], points[4] };
  const std::vector<boost::shared_ptr<Point>> points_b      { points[0], points[3], points[4] };
  const std::vector<boost::shared_ptr<Point>> points_c      { points[1], points[2], points[5] };
  const std::vector<boost::shared_ptr<Point>> points_d      { points[1], points[4], points[5] };
  const std::vector<boost::shared_ptr<Point>> points_e      { points[0], points[2], points[3] };
  const std::vector<boost::shared_ptr<Point>> points_f      { points[2], points[3], points[5] };
  const bool verbose{false};

  if (!Helper().IsClockwiseHorizontal(points_bottom))
  {
    std::reverse(points_bottom.begin(),points_bottom.end());
  }
  if (!Helper().IsClockwiseHorizontal(points_top))
  {
    std::reverse(points_top.begin(),points_top.end());
  }

  const auto bottom = FaceFactory().Create(points_bottom,FaceOrientation::horizontal,verbose);
  const auto top = FaceFactory().Create(points_top,FaceOrientation::horizontal,verbose);
  const auto a = FaceFactory().Create(points_a,FaceOrientation::vertical,verbose);
  const auto b = FaceFactory().Create(points_b,FaceOrientation::vertical,verbose);
  const auto c = FaceFactory().Create(points_c,FaceOrientation::vertical,verbose);
  const auto d = FaceFactory().Create(points_d,FaceOrientation::vertical,verbose);
  const auto e = FaceFactory().Create(points_e,FaceOrientation::vertical,verbose);
  const auto f = FaceFactory().Create(points_f,FaceOrientation::vertical,verbose);
  assert(bottom);
  assert(top);
  assert(a);
  assert(b);
  assert(c);
  assert(d);
  assert(e);
  assert(f);
  bottom->SetBoundaryType("bottom");
  top->SetBoundaryType("top");
  a->SetBoundaryType("back");
  b->SetBoundaryType("back");
  c->SetBoundaryType("left");
  d->SetBoundaryType("left");
  e->SetBoundaryType("right");
  f->SetBoundaryType("right");

  const std::vector<boost::shared_ptr<Face>> prism {
    top,bottom,a,b,c,d,e,f
  };
  return prism;
}

boost::shared_ptr<ribi::trim::Face> ribi::trim::FaceFactory::CreateTestSquare(const Winding winding) const noexcept
{
  const bool verbose{false};

  const std::vector<boost::shared_ptr<Point>> points {
    PointFactory().CreateTestSquare(winding)
  };
  assert(points.size() == 4);

  const boost::shared_ptr<Face> square {
    FaceFactory().Create(
      { points[0],points[1],points[2],points[3] },
      FaceOrientation::horizontal,
      verbose
    )
  };
  assert(square);
  //square->SetIndex(1);
  return square;
}

#ifndef NDEBUG
void ribi::trim::FaceFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  PointFactory();
  FaceFactory().CreateTestSquare(Winding::clockwise); //Face

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  if (verbose) TRACE("Create a testing prism");
  for (const auto& strategy: CreateVerticalFacesStrategies().GetAll())
  {
    const std::vector<boost::shared_ptr<Face>> prism {
      FaceFactory().CreateTestPrism(strategy)
    };
    switch (strategy)
    {
      case CreateVerticalFacesStrategy::one_face_per_square:
        assert(prism.size() == 5 && "A prism has 5 faces (when the vertical faces are kept as squares)");
        break;
      case CreateVerticalFacesStrategy::two_faces_per_square:
        assert(prism.size() == 8 && "A prism has 8 faces (as the vertical faces are split into 2 triangle)");
        break;
    }
  }
  if (verbose) TRACE("Check that incorrect Faces cannot be constructed");
  //(as this test is done in each Face its contructor)
  for (Winding winding: Windings().GetAll())
  {
    const std::vector<boost::shared_ptr<Point>> points {
      PointFactory().CreateTestSquare(winding)
    };
    assert(points.size() == 4);
    assert(Helper().IsConvex(points)
      == (winding == Winding::clockwise || winding == Winding::counter_clockwise)
    );
  }
  if (verbose) TRACE("Check that incorrect Faces cannot be constructed");
  //(as this test is done in each Face its contructor)
  {
    const std::vector<boost::shared_ptr<Point>> points {
      PointFactory().CreateTestInvalid()
    };
    assert(points.size() == 4);
    assert(!Helper().IsConvex(points));
  }
  #ifdef BRUTE_FORCE_ISSUE_168
  if (verbose) TRACE("IsConvex, issue 168");
  {
    typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
    boost::shared_ptr<Coordinat2D> c_0(new Coordinat2D(2.35114,3.23607));
    boost::shared_ptr<Coordinat2D> c_1(new Coordinat2D(1.17557,2.35781));
    boost::shared_ptr<Coordinat2D> c_2(new Coordinat2D(2.35114,3.23607));
    boost::shared_ptr<Coordinat2D> c_3(new Coordinat2D(1.17557,2.35781));
    boost::shared_ptr<Point> p0(PointFactory().Create(c_0));
    boost::shared_ptr<Point> p1(PointFactory().Create(c_1));
    boost::shared_ptr<Point> p2(PointFactory().Create(c_2));
    boost::shared_ptr<Point> p3(PointFactory().Create(c_3));
    p0->SetZ(5 * boost::units::si::meter); //Add no comma on purpose
    p1->SetZ(5 * boost::units::si::meter); //Add no comma on purpose
    p2->SetZ(6 * boost::units::si::meter); //Add no comma on purpose
    p3->SetZ(6 * boost::units::si::meter); //Add no comma on purpose
    std::vector<boost::shared_ptr<Point>> points { p0, p1, p2, p3 };
    assert(points.size() == 4);
    assert(!Helper().IsConvex(points) && "This a Z shape and thus not convex");
    Helper().MakeConvex(points);
    assert(Helper().IsConvex(points) && "FaceFactory only accepts convex points");
    const auto face(
      FaceFactory().Create(points,FaceOrientation::vertical,verbose)
    );
    assert(face && "But when creating a Face, the points are ordered");
    assert(face->GetIndex() > 0);
    //Shuffle these more often:
    //For every order, it must be possible to be made convex
    for (int i=0; i!=256; ++i)
    {
      std::random_shuffle(points.begin(),points.end());
      Helper().MakeConvex(points);
      assert(Helper().IsConvex(points));
    }
  }

  if (verbose) TRACE("IsConvex, issue 168");
  {
    typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
    boost::shared_ptr<Coordinat2D> c_0(new Coordinat2D(1.17557,2.35781));
    boost::shared_ptr<Coordinat2D> c_1(new Coordinat2D(2.35114,3.23607));
    boost::shared_ptr<Coordinat2D> c_2(new Coordinat2D(1.17557,2.35781));
    boost::shared_ptr<Coordinat2D> c_3(new Coordinat2D(2.35114,3.23607));
    boost::shared_ptr<Point> p0(PointFactory().Create(c_0));
    boost::shared_ptr<Point> p1(PointFactory().Create(c_1));
    boost::shared_ptr<Point> p2(PointFactory().Create(c_2));
    boost::shared_ptr<Point> p3(PointFactory().Create(c_3));
    p0->SetZ(5 * boost::units::si::meter); //Add no comma on purpose
    p1->SetZ(6 * boost::units::si::meter); //Add no comma on purpose
    p2->SetZ(6 * boost::units::si::meter); //Add no comma on purpose
    p3->SetZ(5 * boost::units::si::meter); //Add no comma on purpose
    std::vector<boost::shared_ptr<Point>> points { p0, p1, p2, p3 };
    assert(points.size() == 4);
    assert(!Helper().IsConvex(points) && "This a Z shape and thus not convex");
    Helper().MakeConvex(points);
    assert(Helper().IsConvex(points) && "FaceFactory only accepts convex points");
    const auto face(
      FaceFactory().Create(points,FaceOrientation::vertical,verbose)
    );
    assert(face && "But when creating a Face, the points are ordered");
    assert(face->GetIndex() > 0);
    //Shuffle these more often:
    //For every order, it must be possibleto be made convex
    for (int i=0; i!=256; ++i)
    {
      std::random_shuffle(points.begin(),points.end());
      Helper().MakeConvex(points);
      assert(Helper().IsConvex(points));
    }
  }
  #endif // BRUTE_FORCE_ISSUE_168
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshfaceimpl.h

 

#ifndef TRIANGLEMESHFACEIMPL_H
#define TRIANGLEMESHFACEIMPL_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

class FaceImpl
{
  friend class Face;
  FaceImpl();

  private:
};

} //~namespace trim
} //~namespace ribi


#endif // TRIANGLEMESHFACEIMPL_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshfaceimpl.cpp

 

#include "trianglemeshfaceimpl.h"

ribi::trim::FaceImpl::FaceImpl()
{

}

 

 

 

 

 

./CppTriangleMesh/trianglemeshfaceorientation.h

 

#ifndef RIBI_TRIANGLEMESHFACEORIENTATION_H
#define RIBI_TRIANGLEMESHFACEORIENTATION_H

namespace ribi {
namespace trim {

enum class FaceOrientation { horizontal, vertical };

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHFACEORIENTATION_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshfaceorientation.cpp

 

#include "trianglemeshfaceorientation.h"

 

 

 

 

 

./CppTriangleMesh/trianglemeshfwd.h

 

#ifndef RIBI_TRIANGLEMESHFWD_H
#define RIBI_TRIANGLEMESHFWD_H

namespace ribi {

namespace trim {

struct Cell;
struct CellFactory;
struct CellsCreator;
struct CellsCreatorFactory;
struct Dialog;
struct Face;
struct FaceFactory;
struct Point;
struct PointFactory;
struct Template;
struct TriangleMeshBuilder;
struct TriangleMeshBuilderImpl;

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHFWD_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshhelper.h

 

#ifndef TRIANGLEMESHHELPER_H
#define TRIANGLEMESHHELPER_H

#include <algorithm>
#include <cassert>
#include <functional>
#include <map>
#include <set>
#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/geometry.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include "trianglemeshfwd.h"
#include "trianglemeshwinding.h"

#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///Helper class for TriangleMesh
//It has no state, so Pimpl is unnecessary
class Helper
{
  friend class Cell;
  friend class CellsCreator;
  friend class Dialog;
  friend class Face;
  friend class FaceFactory;
  friend class Point;
  friend class PointFactory;
  friend class Template;
  friend class TriangleMeshBuilder;
  friend class TriangleMeshBuilderImpl;

  typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;
  typedef std::set<Coordinat3D,std::function<bool(Coordinat3D,Coordinat3D)>> Coordinat3dSet;
  typedef std::set<boost::shared_ptr<Face>,std::function<bool(boost::shared_ptr<const Face>,boost::shared_ptr<const Face>)>> FaceSet;

  Helper() noexcept;

  Coordinat3D CalcCenter(const std::vector<boost::shared_ptr<Point>>& points) const noexcept;

  std::vector<Coordinat2D> CalcProjection(
    const std::vector<boost::shared_ptr<const Point>>& v
  ) const;

  ///Find out the Winding of the edges
  ///knowing that all edges are in the same XY plane
  ///when viewed from above (at an infinite Z coordinat)
  Winding CalcWindingHorizontal(
    const std::vector<boost::shared_ptr<const Point>>& points
  ) const noexcept;

  FaceSet CreateEmptyFaceSet() const noexcept;

  Coordinat3dSet ExtractCoordinats(const Face& face) const noexcept;
  std::set<Coordinat3D,std::function<bool(Coordinat3D,Coordinat3D)>> ExtractCoordinats(const std::vector<boost::shared_ptr<const Point>>& points) const noexcept;

  ///Obtain the angle in radians between two deltas
  ///12 o'clock is 0.0 * pi
  /// 3 o'clock is 0.5 * pi
  /// 6 o'clock is 1.0 * pi
  /// 9 o'clock is 1.5 * pi

  /*
   Y          Y
   |    (11)  |  (1)
-2|          |
   |          |
-1| (10)     |      (2)
   |          |
  0+----------0--------X
   |          |
+1| (8)      |      (4)
   |          |
+2|          |
   |     (7)  |  (5)
   +----------+--------X
        - - -   + + +
        3 2 1 0 1 2 3

  Appriximate coordinat for a point for every hour, with the approximate angle
   1: ( 1,-2) :  1/6 * pi
   2: ( 2,-1) :  2/6 * pi
   3: ( 3, 0) :  3/6 * pi
   4: ( 2, 1) :  4/6 * pi
   5: ( 1, 2) :  5/6 * pi
   6: ( 0, 3) :  6/6 * pi
   7: (-1, 2) :  7/6 * pi
   8: (-2, 1) :  8/6 * pi
   9: (-3, 0) :  9/6 * pi
  10: (-2,-1) : 10/6 * pi
  11: (-1,-2) : 11/6 * pi
  12: ( 0,-3) : 12/6 * pi

  */
  //double GetAngle(const boost::shared_ptr<const Point> point) const noexcept;

  ///Obtain all permutations of a std::vector
  ///Examples:
  /// {1    } -> { {1} }
  /// {1,2  } -> { {1,2} , {2,1} }
  /// {1,2,3} -> { {1,2,3} , {1,3,2} , {2,1,3} , {2,3,1} , {3,1,2} , {3,2,1} }
  //From http://www.richelbilderbeek.nl/CppGetPermutations.htm
  std::vector<std::vector<int>> GetPermutations(std::vector<int> v) const noexcept;

  bool IsClockwise(
    const std::vector<boost::shared_ptr<const Point>>& points,
    const Coordinat3D& observer
  ) const noexcept;

  bool IsClockwise(
    const std::vector<boost::shared_ptr<Point>>& points,
    const Coordinat3D& observer
  ) const noexcept;

  ///Are the points ordered clockwise in the XY plane seen from above
  /// (e.g. from coordinat {0,0,1} )
  bool IsClockwiseHorizontal(const std::vector<boost::shared_ptr<Point>>& points) const noexcept;

  ///Are the points ordered clockwise in the XY plane seen from above
  /// (e.g. from coordinat {0,0,1} )
  bool IsClockwiseVertical(
    const std::vector<boost::shared_ptr<Point>>& points,
    const boost::shared_ptr<const Point>& observer
  ) const noexcept;

  bool IsConvex(const std::vector<boost::shared_ptr<const ribi::trim::Point>>& points) const noexcept;
  bool IsConvex(const std::vector<boost::shared_ptr<      ribi::trim::Point>>& points) const noexcept;

  bool IsCounterClockwise(
    const std::vector<boost::shared_ptr<const Point>>& points,
    const Coordinat3D& observer
  ) const noexcept;

  bool IsCounterClockwise(
    const std::vector<boost::shared_ptr<Point>>& points,
    const Coordinat3D& observer
  ) const noexcept;

  bool IsHorizontal(const Face& face) const noexcept;

  bool IsPlane(
    const std::vector<boost::shared_ptr<const Point>>& points
  ) const noexcept;

  bool IsPlane(
    const std::vector<boost::shared_ptr<Point>>& points
  ) const noexcept;

  bool IsVertical(const Face& face) const noexcept;

  ///Order the points so that these are convex
  void MakeConvex(std::vector<boost::shared_ptr<Point>>& points) const noexcept;

  //The points of face must be ordered clockwise, according to the documentation
  void MakeClockwise(std::vector<boost::shared_ptr<Point>>& points,const Coordinat3D& observer) const noexcept;
  void MakeCounterClockwise(std::vector<boost::shared_ptr<Point>>& points,const Coordinat3D& observer) const noexcept;

  std::function<bool(const boost::shared_ptr<const Face>& lhs, const boost::shared_ptr<const Face>& rhs)>
    OrderByIndex() const noexcept;

  std::function<bool(const boost::shared_ptr<const Point>& lhs, const boost::shared_ptr<const Point>& rhs)>
    OrderByX() const noexcept;

  std::vector<boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>>
    PointsToCoordinats3D(const std::vector<boost::shared_ptr<const ribi::trim::Point>>& points
  ) const noexcept;

  std::string ToStr(const std::vector<boost::shared_ptr<const Point>>& p) const noexcept;
  std::string ToXml(const boost::geometry::model::d2::point_xy<double>& p) const noexcept;


  private:
  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

///Help adding constness a bit
template <class T>
std::vector<boost::shared_ptr<const T>> AddConst(
  const std::vector<boost::shared_ptr<T>> v) noexcept
{
  assert(std::count(v.begin(),v.end(),nullptr) == 0);
  const std::vector<boost::shared_ptr<const T>> w(std::begin(v),std::end(v));
  assert(std::count(w.begin(),w.end(),nullptr) == 0);
  return w;
}

//From http://www.richelbilderbeek.nl/CppCanLexicalCast.htm
template <class TargetType>
bool CanLexicalCast(const std::string& from) noexcept
{
  try
  {
    boost::lexical_cast<TargetType>(from);
  }
  catch (boost::bad_lexical_cast&)
  {
    return false;
  }
  catch (...)
  {
    assert(!"Something unexpected happened");
    throw;
  }
  return true;
}

bool operator<(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept;

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> operator+(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept;

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& operator+=(
  boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept;

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& operator/=(
  boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& p,
  const double factor
) noexcept;

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> operator/(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& p,
  const double factor
) noexcept;

//Name for operator<
bool Less(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept;


//Name for operator+
boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Add(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept;


} //~namespace trim
} //~namespace ribi

#endif // TRIANGLEMESHHELPER_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshhelper.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "trianglemeshhelper.h"

#include <iomanip>
#include <iostream>
#include <sstream>

#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/math/constants/constants.hpp>


#include "geometry.h"
#include "trace.h"
#include "trianglemeshface.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshpointfactory.h"
#include "xml.h"
#pragma GCC diagnostic pop

ribi::trim::Helper::Helper::Helper() noexcept
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> ribi::trim::Helper::CalcCenter(
  const std::vector<boost::shared_ptr<Point>>& points
) const noexcept
{
  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Value;
  using boost::geometry::get;

  assert(!points.empty());
  Value sum(0.0,0.0,0.0);
  for (const auto& point: points)
  {
    assert(point);
    assert(point->GetCoordinat());
    assert(!std::isnan( get<0>(*point->GetCoordinat())));
    assert(!std::isnan( get<1>(*point->GetCoordinat())));
    assert(!point->CanGetZ() || !std::isnan(point->GetZ().value()));
    sum += point->GetCoordinat3D();
  }
  const double n { static_cast<double>(points.size()) };
  return sum / n;
}

std::vector<ribi::trim::Helper::Coordinat2D> ribi::trim::Helper::CalcProjection(
  const std::vector<boost::shared_ptr<const ribi::trim::Point>>& v) const
{
  
  return Geometry().CalcProjection(PointsToCoordinats3D(v));
}

ribi::trim::Winding ribi::trim::Helper::CalcWindingHorizontal(
  const std::vector<boost::shared_ptr<const Point>>& points
) const noexcept
{
  using boost::geometry::get;
  

  const int n_points { static_cast<int>(points.size()) };

  //Extract the points
  std::vector<Coordinat3D> coordinats;
  for (int i=0; i!=n_points; ++i)
  {
    Coordinat3D co(
      get<0>(*points[i]->GetCoordinat()),
      get<1>(*points[i]->GetCoordinat()),
      points[i]->GetZ().value()
    );
    coordinats.push_back(co);
  }

  assert(coordinats.size() == coordinats.size());

  const bool a { Geometry().IsClockwiseCartesianHorizontal(coordinats) };
  const bool b { Geometry().IsCounterClockwiseCartesianHorizontal(coordinats) };
  if ( a && !b) return Winding::clockwise;
  if (!a &&  b) return Winding::counter_clockwise;
  return Winding::indeterminate;
}

ribi::trim::Helper::FaceSet ribi::trim::Helper::CreateEmptyFaceSet() const noexcept
{
  ribi::trim::Helper::FaceSet s(OrderByIndex());
  return s;
}

std::set<
  boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>,
  std::function<
    bool(
      boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>,
      boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>
    )
  >
>
  ribi::trim::Helper::ExtractCoordinats(
  const std::vector<boost::shared_ptr<const Point>>& points
) const noexcept
{
  
  using boost::geometry::get;

  std::set<Coordinat3D,std::function<bool(Coordinat3D,Coordinat3D)>> s(
    [](const Coordinat3D& lhs, const Coordinat3D& rhs)
    {
      return ribi::trim::Less(lhs,rhs);
    }
  );
  for (const auto& point: points)
  {
    if (!point->CanGetZ())
    {
      TRACE("Extract these coordinats later: the Face must be assigned to a Layer first");
    }
    assert(point->CanGetZ());
    const Coordinat3D c(
      point->GetCoordinat3D()
    );
    s.insert(s.begin(),c);
  }
  return s;
}

ribi::trim::Helper::Coordinat3dSet ribi::trim::Helper::ExtractCoordinats(const ribi::trim::Face& face) const noexcept
{
  face.DoExtractCoordinats();
  return face.GetCoordinats();
}

/*
double ribi::trim::Helper::GetAngle(const boost::shared_ptr<const Point> point) const noexcept
{
  
  return Geometry().GetAngle(
    boost::geometry::get<0>(*point->GetCoordinat()),
    boost::geometry::get<1>(*point->GetCoordinat())
  );
}
*/

std::vector<std::vector<int>> ribi::trim::Helper::GetPermutations(std::vector<int> v) const noexcept
{
  std::sort(std::begin(v),std::end(v));
  std::vector<std::vector<int>> w;
  w.push_back(v);
  while(std::next_permutation(std::begin(v),std::end(v)))
  {
    w.push_back(v);
  }
  return w;
}

bool ribi::trim::Helper::IsClockwise(
  const std::vector<boost::shared_ptr<const Point>>& points,
  const Coordinat3D& observer) const noexcept
{
  
  std::vector<Coordinat3D> coordinats;
  for (const auto& point: points)
  {
    assert(point);
    coordinats.push_back(point->GetCoordinat3D());
  }
  assert(Geometry().IsPlane(coordinats));
  return Geometry().IsClockwiseCartesian(coordinats,observer);
}

bool ribi::trim::Helper::IsClockwise(
  const std::vector<boost::shared_ptr<Point>>& points,
  const Coordinat3D& observer) const noexcept
{
  return IsClockwise(AddConst(points),observer);
}

bool ribi::trim::Helper::IsClockwiseHorizontal(
  const std::vector<boost::shared_ptr<Point>>& points
) const noexcept
{
  using boost::geometry::get;
  
  assert(points.size() == 3);
  assert(points[0]);
  assert(points[1]);
  assert(points[2]);
  assert(points[0]->GetCoordinat());
  assert(points[1]->GetCoordinat());
  assert(points[2]->GetCoordinat());
  const Coordinat3D center(CalcCenter(points));
  const auto center_x(boost::geometry::get<0>(center));
  const auto center_y(boost::geometry::get<1>(center));
  assert(!std::isnan(center_x));
  assert(!std::isnan(center_y));

  const std::vector<double> angles {
    Geometry().GetAngleClockCartesian(
      get<0>(*points[0]->GetCoordinat()) - center_x,
      get<1>(*points[0]->GetCoordinat()) - center_y
    ),
    Geometry().GetAngleClockCartesian(
      get<0>(*points[1]->GetCoordinat()) - center_x,
      get<1>(*points[1]->GetCoordinat()) - center_y
    ),
    Geometry().GetAngleClockCartesian(
      get<0>(*points[2]->GetCoordinat()) - center_x,
      get<1>(*points[2]->GetCoordinat()) - center_y
    )
  };
  const bool a = Geometry().IsClockwise(angles[0],angles[1]);
  const bool b = Geometry().IsClockwise(angles[1],angles[2]);
  const bool is_clockwise { a && b };
  return is_clockwise;
}

bool ribi::trim::Helper::IsConvex(const std::vector<boost::shared_ptr<const ribi::trim::Point>>& points) const noexcept
{
  
  const std::vector<Coordinat3D> coordinats3d
    = PointsToCoordinats3D(points);
  return Geometry().IsConvex(coordinats3d);
}

bool ribi::trim::Helper::IsConvex(const std::vector<boost::shared_ptr<ribi::trim::Point>>& points) const noexcept
{
  const bool verbose{false};
  if (points.size() == 3)
  {
    if (verbose) { TRACE("Three points are always convex"); }
    return true;
  }

  #ifndef NDEBUG
  assert(points.size() == 4);
  if (verbose)
  {
    std::stringstream s;
    s << "{";
    for (const auto& point3d: points)
    {
      assert(point3d);
      s << (*point3d) << ",";
    }
    std::string po_str(s.str());
    po_str[po_str.size() - 1] = '}';
    TRACE(po_str);
  }
  #endif

  const auto const_points(AddConst(points));

  assert(points.size() == const_points.size());
  assert(boost::geometry::get<0>(*points[0]->GetCoordinat()) == boost::geometry::get<0>(*const_points[0]->GetCoordinat()));
  assert(boost::geometry::get<1>(*points[0]->GetCoordinat()) == boost::geometry::get<1>(*const_points[0]->GetCoordinat()));
  assert(points[0]->GetZ() == const_points[0]->GetZ());
  assert(boost::geometry::get<0>(*points[1]->GetCoordinat()) == boost::geometry::get<0>(*const_points[1]->GetCoordinat()));
  assert(boost::geometry::get<1>(*points[1]->GetCoordinat()) == boost::geometry::get<1>(*const_points[1]->GetCoordinat()));
  assert(points[1]->GetZ() == const_points[1]->GetZ());
  assert(boost::geometry::get<0>(*points[2]->GetCoordinat()) == boost::geometry::get<0>(*const_points[2]->GetCoordinat()));
  assert(boost::geometry::get<1>(*points[2]->GetCoordinat()) == boost::geometry::get<1>(*const_points[2]->GetCoordinat()));
  assert(points[2]->GetZ() == const_points[2]->GetZ());
  assert(boost::geometry::get<0>(*points[3]->GetCoordinat()) == boost::geometry::get<0>(*const_points[3]->GetCoordinat()));
  assert(boost::geometry::get<1>(*points[3]->GetCoordinat()) == boost::geometry::get<1>(*const_points[3]->GetCoordinat()));
  assert(points[3]->GetZ() == const_points[3]->GetZ());

  #ifndef NDEBUG
  for (const auto& point: const_points) { assert(point); }
  #endif
  return IsConvex(const_points);
}

bool ribi::trim::Helper::IsCounterClockwise(
  const std::vector<boost::shared_ptr<const Point>>& points,
  const Coordinat3D& observer) const noexcept
{
  
  assert(Geometry().IsPlane(PointsToCoordinats3D(points)));
  return Geometry().IsCounterClockwiseCartesian(PointsToCoordinats3D(points),observer);
}

bool ribi::trim::Helper::IsCounterClockwise(
  const std::vector<boost::shared_ptr<Point>>& points,
  const Coordinat3D& observer) const noexcept
{
  return IsCounterClockwise(AddConst(points),observer);
  //assert(Geometry().IsPlane(PointsToCoordinats3D(AddConst(points))));
  //return Geometry().IsCounterClockwise(PointsToCoordinats3D(AddConst(points)),observer);
}

bool ribi::trim::Helper::IsHorizontal(const ribi::trim::Face& face) const noexcept
{
  const bool answer = face.GetOrientation() == FaceOrientation::horizontal;
  return answer;
}

bool ribi::trim::Helper::IsPlane(
  const std::vector<boost::shared_ptr<const ribi::trim::Point>>& points
) const noexcept
{
  
  return Geometry().IsPlane(PointsToCoordinats3D(points));
}

bool ribi::trim::Helper::IsPlane(
  const std::vector<boost::shared_ptr<ribi::trim::Point>>& points
) const noexcept
{
  
  return Geometry().IsPlane(PointsToCoordinats3D(AddConst(points)));
}

bool ribi::trim::Helper::IsVertical(const ribi::trim::Face& face) const noexcept
{
  const bool answer_1 = face.GetOrientation() == FaceOrientation::vertical;
  #ifndef NDEBUG
  const bool answer_2 = !IsHorizontal(face);
  assert(answer_1 == answer_2);
  #endif
  return answer_1;
}

void ribi::trim::Helper::MakeConvex(
  std::vector<boost::shared_ptr<ribi::trim::Point>>& points
) const noexcept
{
  const bool verbose{false};
  #ifndef NDEBUG
  for (const auto& p: points) { assert(p); }
  assert(!points.empty());
  assert(points.size() == 4);
  #endif

  if (verbose)
  {
    TRACE(Helper().ToStr(AddConst(points)));
    for (const auto& p: points) { std::cerr << (*p) << std::endl; }
    TRACE("BREAK");
  }

  if (IsConvex(points))
  {
    if (verbose) { TRACE("MakeConvex: points were convex at start"); }
    return;
  }

  std::sort(points.begin(),points.end(),OrderByX());

  #ifndef NDEBUG
  const int max_i = (4*3*2*1) + 4; //Number of permutations, plus four to be sure
  for (int i=0; i!=max_i; ++i)
  #else
  while (1)
  #endif
  {
    #ifndef NDEBUG
    if (i == max_i-1)
    {
      TRACE("ERROR");
      TRACE(Helper().ToStr(AddConst(points)));
      for (const auto& p: points) { TRACE(*p); }
      TRACE("BREAK");
    }
    assert(i!=max_i-1 && "There must be a permutation of the points that renders them convex");
    #endif

    if (IsConvex(points))
    {
      break;
    }

    #ifndef NDEBUG
    const auto before = points;
    #endif

    std::next_permutation(points.begin(),points.end(),OrderByX());

    #ifndef NDEBUG
    const auto after = points;
    assert(before != after);
    #endif
  }

  #ifndef NDEBUG
  if(!IsConvex(points))
  {
    TRACE("ERROR");
    for (int i=0; i!=26; ++i)
    {
      std::next_permutation(points.begin(),points.end(),OrderByX());
      {
        std::stringstream s;
        assert(points.size() == 4);
        assert(points[0]); assert(points[1]); assert(points[2]); assert(points[3]);
        s << (IsConvex(points) ? "Convex" : "Not convex")
          << ": "
          << Geometry().ToStr(points[0]->GetCoordinat3D()) << "->"<< points[0]->GetIndex() << ","
          << Geometry().ToStr(points[1]->GetCoordinat3D()) << "->"<< points[1]->GetIndex() << ","
          << Geometry().ToStr(points[2]->GetCoordinat3D()) << "->"<< points[2]->GetIndex() << ","
          << Geometry().ToStr(points[3]->GetCoordinat3D()) << "->"<< points[3]->GetIndex() << '\n'
        ;
        TRACE(s.str());
      }
      {
        std::stringstream t;
        const std::vector<Coordinat2D> projected_points(CalcProjection(AddConst(points)));
        assert(projected_points.size() == 4);
        t << (IsConvex(points) ? "Convex" : "Not convex")
          << ": "
          << Geometry().ToStr(projected_points[0]) << ","
          << Geometry().ToStr(projected_points[1]) << ","
          << Geometry().ToStr(projected_points[2]) << ","
          << Geometry().ToStr(projected_points[3])
        ;
        TRACE(t.str());
      }
    }
  }
  #endif

  assert(Helper().IsConvex(points));
  return;
}

void ribi::trim::Helper::MakeClockwise(
  std::vector<boost::shared_ptr<Point>>& points,
  const Coordinat3D& observer) const noexcept
{
  //Sure, will be repeated later, but perhaps the points are already ordered by a smart client
  if (IsClockwise(points,observer)) return;

  assert(points.size() == 3 || points.size() == 4);

  const std::vector<boost::shared_ptr<Point>> original{points};

  assert(original == points);

  const int n_points{static_cast<int>(points.size())};
  const std::vector<int> indices{
    n_points == 3 ? std::vector<int>({0,1,2}) : std::vector<int>({0,1,2,3})
  };

  for (const auto& sequence: GetPermutations(indices))
  {
    for (int i{0}; i!=n_points; ++i)
    {
      points[i] = original[ sequence[i] ];
    }
    if (IsClockwise(points,observer)) return;
  }

  #ifndef NDEBUG
  TRACE("ERROR: failed making these points counterclockwards:");
  for (const auto& point: original) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
  TRACE(Geometry().ToStr(observer));
  TRACE(IsClockwise(original,observer));
  TRACE(IsPlane(original));
  TRACE(IsConvex(original));
  TRACE("Let's try again");
  for (const auto& point: points) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
  TRACE(IsClockwise(points,observer));

  for (const auto& sequence: GetPermutations(indices))
  {
    for (int i{0}; i!=n_points; ++i)
    {
      points[i] = original[ sequence[i] ];
    }
    for (const auto& point: points) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
    TRACE(IsClockwise(points,observer));
  }

  assert(!"Should not get here");
  assert(IsClockwise(points,observer));
  #endif
}

void ribi::trim::Helper::MakeCounterClockwise(
  std::vector<boost::shared_ptr<Point>>& points,
  const Coordinat3D& observer) const noexcept
{
  //Sure, will be repeated later, but perhaps the points are already ordered by a smart client
  if (IsCounterClockwise(points,observer)) return;

  assert(points.size() == 3 || points.size() == 4);

  const std::vector<boost::shared_ptr<Point>> original{points};

  assert(original == points);

  const int n_points{static_cast<int>(points.size())};
  const std::vector<int> indices{
    n_points == 3 ? std::vector<int>({0,1,2}) : std::vector<int>({0,1,2,3})
  };

  for (const auto& sequence: GetPermutations(indices))
  {
    for (int i{0}; i!=n_points; ++i)
    {
      points[i] = original[ sequence[i] ];
    }
    if (IsCounterClockwise(points,observer)) return;
  }

  #ifndef NDEBUG
  TRACE("ERROR: failed making these points counterclockwards:");
  for (const auto& point: original) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
  TRACE(Geometry().ToStr(observer));
  TRACE(IsCounterClockwise(original,observer));
  TRACE(IsPlane(original));
  TRACE(IsConvex(original));
  TRACE("Let's try again");
  for (const auto& point: points) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
  TRACE(IsCounterClockwise(points,observer));

  for (const auto& sequence: GetPermutations(indices))
  {
    for (int i{0}; i!=n_points; ++i)
    {
      points[i] = original[ sequence[i] ];
    }
    for (const auto& point: points) { TRACE(Geometry().ToStr(point->GetCoordinat3D())); }
    TRACE(IsCounterClockwise(points,observer));
  }

  assert(!"Should not get here");
  assert(IsCounterClockwise(points,observer));
  #endif
}

std::function<
    bool(
      const boost::shared_ptr<const ribi::trim::Face>& lhs,
      const boost::shared_ptr<const ribi::trim::Face>& rhs
    )
  >
  ribi::trim::Helper::OrderByIndex() const noexcept
{
  return [](const boost::shared_ptr<const Face>& lhs, const boost::shared_ptr<const Face>& rhs)
  {
    using boost::geometry::get;
    assert(lhs);
    assert(rhs);
    return lhs->GetIndex() < rhs->GetIndex();
  };

}

std::function<
    bool(
      const boost::shared_ptr<const ribi::trim::Point>& lhs,
      const boost::shared_ptr<const ribi::trim::Point>& rhs
    )
  >
  ribi::trim::Helper::OrderByX() const noexcept
{
  return [](const boost::shared_ptr<const Point>& lhs, const boost::shared_ptr<const Point>& rhs)
  {
    using boost::geometry::get;
    assert(lhs);
    assert(rhs);
    assert(lhs->GetCoordinat());
    assert(rhs->GetCoordinat());
    const auto lhs_coordinat = lhs->GetCoordinat();
    const auto rhs_coordinat = rhs->GetCoordinat();
    assert(lhs_coordinat);
    assert(rhs_coordinat);
    if (get<0>(*lhs_coordinat) < get<0>(*rhs_coordinat)) return true;
    if (get<0>(*lhs_coordinat) > get<0>(*rhs_coordinat)) return false;
    if (get<1>(*lhs_coordinat) < get<1>(*rhs_coordinat)) return true;
    if (get<1>(*lhs_coordinat) > get<1>(*rhs_coordinat)) return false;
    assert(lhs->CanGetZ());
    assert(rhs->CanGetZ());
    return lhs->GetZ() < rhs->GetZ();
  };
}

std::vector<boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>>
  ribi::trim::Helper::PointsToCoordinats3D(
    const std::vector<boost::shared_ptr<const ribi::trim::Point>>& points
) const noexcept
{
  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;
  std::vector<Coordinat3D> v;
  for (const auto& p: points)
  {
    assert(p);
    assert(p->GetCoordinat());
    v.push_back(p->GetCoordinat3D());
  }
  return v;
}



std::string ribi::trim::Helper::ToStr(
  const std::vector<boost::shared_ptr<const Point>>& points
  ) const noexcept
{
  std::stringstream s;
  s << " {";
  for (const auto& point:points) { s << point->GetIndex() << ","; }
  std::string t(s.str());
  t[ t.size() - 1] = '}';
  return t;
}

std::string ribi::trim::Helper::ToXml(const boost::geometry::model::d2::point_xy<double>& p) const noexcept
{
  using boost::geometry::get;
  std::stringstream s;
  s
    << ribi::xml::ToXml("0",get<0>(p))
    << ribi::xml::ToXml("1",get<1>(p))
  ;

  std::stringstream t;
  t << ribi::xml::ToXml("Coordinat2D",s.str());
  return t.str();
}

bool ribi::trim::operator<(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs) noexcept
{
  using boost::geometry::get;
  if (get<0>(lhs) < get<0>(rhs)) return true;
  if (get<0>(lhs) > get<0>(rhs)) return false;

  if (get<1>(lhs) < get<1>(rhs)) return true;
  if (get<1>(lhs) > get<1>(rhs)) return false;

  return get<2>(lhs) < get<2>(rhs);
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> ribi::trim::operator+(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept
{
  using boost::geometry::get;
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> result(
    get<0>(lhs) + get<0>(rhs),
    get<1>(lhs) + get<1>(rhs),
    get<2>(lhs) + get<2>(rhs)
  );
  assert(std::abs(get<0>(result) - (get<0>(lhs) + get<0>(rhs))) < 0.001);
  assert(std::abs(get<1>(result) - (get<1>(lhs) + get<1>(rhs))) < 0.001);
  assert(std::abs(get<2>(result) - (get<2>(lhs) + get<2>(rhs))) < 0.001);
  return result;
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& ribi::trim::operator+=(
  boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept
{
  lhs = lhs + rhs;
  return lhs;
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& ribi::trim::operator/=(
  boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& p,
  const double factor
) noexcept
{
  p = p / factor;
  return p;
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> ribi::trim::operator/(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& p,
  const double factor
) noexcept
{
  using boost::geometry::get;
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> result(
    get<0>(p) / factor,
    get<1>(p) / factor,
    get<2>(p) / factor
  );
  assert(std::abs(get<0>(result) - (get<0>(p) / factor)) < 0.001);
  assert(std::abs(get<1>(result) - (get<1>(p) / factor)) < 0.001);
  assert(std::abs(get<2>(result) - (get<2>(p) / factor)) < 0.001);
  return result;
}

boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> ribi::trim::Add(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept { return lhs + rhs; }

bool ribi::trim::Less(
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& lhs,
  const boost::geometry::model::point<double,3,boost::geometry::cs::cartesian>& rhs
) noexcept
{
  return ribi::trim::operator <(lhs,rhs);
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshhelper_test.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "trianglemeshhelper.h"

//#include <iomanip>
//#include <iostream>
#include <sstream>

//#include <boost/lexical_cast.hpp>
//#include <boost/make_shared.hpp>
#include <boost/math/constants/constants.hpp>

#include "geometry.h"
#include "testtimer.h"
#include "trace.h"
#include "trianglemeshface.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshpointfactory.h"
//#include "xml.h"
#pragma GCC diagnostic pop

#ifndef NDEBUG
void ribi::trim::Helper::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  Geometry();

  const TestTimer test_timer(__func__,__FILE__,1.0);
  typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
  using boost::geometry::get;
  const bool verbose{false};

  //CalcCenter
  //CalcWindingHorizontal
  const Helper h;
  //IsClockwiseHorizontal 1
  //IsClockwiseHorizontal 2
  //IsClockwiseVertical
  if (verbose) { TRACE("IsConvex, 2D, from error"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    std::vector<boost::shared_ptr<const Coordinat2D>> coordinats2d;
    {
      const auto coordinat
        = boost::make_shared<Coordinat2D>(9.2885,29.5639);
      assert(coordinat);
      coordinats2d.push_back(coordinat);
    }
    {
      const auto coordinat
        = boost::make_shared<Coordinat2D>(9.2885,40.6764);
      assert(coordinat);
      coordinats2d.push_back(coordinat);
    }
    {
      const auto coordinat
        = boost::make_shared<Coordinat2D>(17.497,44.4009);
      assert(coordinat);
      coordinats2d.push_back(coordinat);
    }
    {
      const auto coordinat
        = boost::make_shared<Coordinat2D>(17.497,33.0765);
      assert(coordinat);
      coordinats2d.push_back(coordinat);
    }
    std::vector<boost::shared_ptr<Point>> points;
    for (const auto& coordinat2d: coordinats2d)
    {
      const auto point(PointFactory().Create(coordinat2d));
      assert(point);
      point->SetZ(0.0 * boost::units::si::meter);
      points.push_back(point);
    }
    assert(coordinats2d.size() == points.size());
    assert(h.IsConvex(points));
    assert(h.IsConvex(AddConst(points)));
  }
  if (verbose) { TRACE("IsConvex, 3D, from error"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    std::vector<boost::shared_ptr<const Coordinat3D>> coordinats3d;
    {
      const auto coordinat = boost::make_shared<Coordinat3D>(0.0,2.0,1.0);
      assert(coordinat);
      coordinats3d.push_back(coordinat);
    }
    {
      const auto coordinat = boost::make_shared<Coordinat3D>(0.0,2.0,2.0);
      assert(coordinat);
      coordinats3d.push_back(coordinat);
    }
    {
      const auto coordinat = boost::make_shared<Coordinat3D>(1.0,1.0,2.0);
      assert(coordinat);
      coordinats3d.push_back(coordinat);
    }
    std::vector<boost::shared_ptr<Point>> points;
    for (const auto& coordinat: coordinats3d)
    {
      boost::shared_ptr<const Coordinat2D> coordinat2d(
        new Coordinat2D(
          boost::geometry::get<0>(*coordinat),
          boost::geometry::get<1>(*coordinat)
        )
      );
      const auto point(PointFactory().Create(coordinat2d));
      assert(point);
      point->SetZ(boost::geometry::get<2>(*coordinat) * boost::units::si::meter);
      points.push_back(point);
    }
    assert(points.size() == coordinats3d.size());
    assert(h.IsConvex(points));
    assert(h.IsConvex(AddConst(points)));
  }
  if (verbose) { TRACE("IsConvex, 2D, from #228, this is the 2D projection of the next test"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
  /*
    This is not convex

    (-14.335613337899998705,100)
    (-14.335613337899998705,0)
    (-0.302499999969750000,100)
    (-0.302499999969750000,0)
  */

    const Geometry::Coordinats2D points {
      Geometry::Coordinat2D(-14.335613337899998705,100),
      Geometry::Coordinat2D(-14.335613337899998705,0),
      Geometry::Coordinat2D(-0.302499999969750000,100),
      Geometry::Coordinat2D(-0.302499999969750000,0)
    };
    assert(!Geometry().IsConvex(points));
  }
  #ifdef FIX_ISSUE_228
  if (verbose) { TRACE("IsCounterClockwise, 3D, from #228"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /*
    This is not counterclockwise, it is a Z shape on the Y=2 plane

    Observer is behind these points

           Z           Y
           |          /
           |         /
     A---C-|-+      /
    /|  /| |/|     /
   +---+---+ |    /
   | | | | | |   /
   | | | | | |  /
   | | | | | | /
   | | | | | |/
  -|-B-|-D-|-+- - - - - - Y = 2.0
   |/  |/  |/
  -+---+---O------------- X
          /|
         / |
        /  |

    (-3.78624,2,10)'
    (-3.78624,2,0)'
    (-0.55,2,10)'
    (-0.55,2,0)'
    Observer: (-2.1871,3.74169,5)
    */
    boost::shared_ptr<const Coordinat2D> left{new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create( left,10.0 * boost::units::si::meter),
      PointFactory().Create( left, 0.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter),
      PointFactory().Create(right, 0.0 * boost::units::si::meter)
    };

    assert(!h.IsConvex(points));
    assert(!h.IsConvex(AddConst(points)));
    assert(!h.IsPlane(points));

    const Coordinat3D observer{-2.1871,3.74169,5};
    assert(!h.IsCounterClockwise(points,observer));
    assert(!h.IsCounterClockwise(AddConst(points),observer));
    assert(!h.IsClockwise(points,observer));
    assert(!h.IsClockwise(AddConst(points),observer));

    h.MakeCounterClockwise(points,observer);
    assert( h.IsCounterClockwise(points,observer));
    assert( h.IsCounterClockwise(AddConst(points),observer));
    assert(!h.IsClockwise(points,observer));
    assert(!h.IsClockwise(AddConst(points),observer));
  }
  if (verbose) { TRACE("IsCounterClockwise, 3D, from #228, test for positive"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /*
    This is counterclockwise

    Observer is behind these points

           Z           Y
           |          /
           |         /
     C---D-|-+      /
    /|  /| |/|     /
   +---+---+ |    /
   | | | | | |   /
   | | | | | |  /
   | | | | | | /
   | | | | | |/
  -|-B-|-A-|-+-
   |/  |/  |/
  -+---+---O-------- X
          /|
         / |
        /  |

    (-0.55,2,0)
    (-3.78624,2,0)
    (-3.78624,2,10)
    (-0.55,2,10)
    Observer: (-2.1871,3.74169,5)
    */
    boost::shared_ptr<const Coordinat2D> left{new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create(right, 0.0 * boost::units::si::meter),
      PointFactory().Create( left, 0.0 * boost::units::si::meter),
      PointFactory().Create( left,10.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter)
    };

    assert(h.IsConvex(points));
    assert(h.IsConvex(AddConst(points)));

    const Coordinat3D observer{-2.1871,3.74169,5};
    assert(h.IsCounterClockwise(points,observer));
    assert(h.IsCounterClockwise(AddConst(points),observer));
    assert(!h.IsClockwise(points,observer));
    assert(!h.IsClockwise(AddConst(points),observer));

    h.MakeCounterClockwise(points,observer);
    assert( h.IsCounterClockwise(points,observer));
    assert( h.IsCounterClockwise(AddConst(points),observer));
    assert(!h.IsClockwise(points,observer));
    assert(!h.IsClockwise(AddConst(points),observer));
  }
  #endif // FIX_ISSUE_228
  #ifdef BRUTE_FORCE_TEST_MAKECOUNTERCLOCKWISE
  if (verbose) { TRACE("MakeCounterClockwise, make shuffled points counterclockwise, from #228"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /*

    Observer is behind these points

           Z           Y
           |          /
           |         /
     C---D-|-+      /
    /|  /| |/|     /
   +---+---+ |    /
   | | | | | |   /
   | | | | | |  /
   | | | | | | /
   | | | | | |/
  -|-B-|-A-|-+-
   |/  |/  |/
  -+---+---O-------- X
          /|
         / |
        /  |

    (-0.55,2,0)
    (-3.78624,2,0)
    (-3.78624,2,10)
    (-0.55,2,10)
    Observer: (-2.1871,3.74169,5)
    */
    boost::shared_ptr<const Coordinat2D> left{new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create(right, 0.0 * boost::units::si::meter),
      PointFactory().Create( left, 0.0 * boost::units::si::meter),
      PointFactory().Create( left,10.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter)
    };

    assert(h.IsConvex(points));
    assert(h.IsConvex(AddConst(points)));

    const Coordinat3D observer{-2.1871,3.74169,5};

    for (int i{0}; i!=50; ++i)
    {
      std::random_shuffle(std::begin(points),std::end(points));

      h.MakeCounterClockwise(points,observer);
      assert( h.IsCounterClockwise(points,observer));
      assert( h.IsCounterClockwise(AddConst(points),observer));
      assert(!h.IsClockwise(points,observer));
      assert(!h.IsClockwise(AddConst(points),observer));
    }
  }
  #endif // BRUTE_FORCE_TEST_MAKECOUNTERCLOCKWISE
  //OrderByX
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    const auto f = h.OrderByX();
    const auto v = ribi::trim::PointFactory().CreateTestPrism();
    const int sz { static_cast<int>(v.size()) };
    for (int i=0; i!=sz; ++i)
    {
      const auto lhs = v[i];
      for (int j=i; j!=sz; ++j)
      {
        const auto rhs = v[j];
        if (i == j)
        {
          assert(*lhs == *rhs);
          assert(!f(lhs,rhs));
          assert(!f(rhs,lhs));
        }
        if (i != j)
        {
          assert(*lhs != *rhs);
          assert(f(lhs,rhs) != f(rhs,lhs));
        }
      }
    }
  }
  //MakeConvex
  if (verbose) { TRACE("MakeConvex"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    boost::shared_ptr<const Coordinat2D> left {new Coordinat2D(1.17557,2.35781)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(2.23114,3.23607)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create(right,6.0 * boost::units::si::meter),
      PointFactory().Create(right,5.0 * boost::units::si::meter),
      PointFactory().Create( left,6.0 * boost::units::si::meter),
      PointFactory().Create( left,5.0 * boost::units::si::meter)
    };
    h.MakeConvex(points);
    assert(h.IsConvex(points));
  }

  //MakeConvex
  if (verbose) { TRACE("MakeConvex, from #228"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /*
    (-0.55,2,0)
    (-3.78624,2,0)
    (-0.55,2,10)
    (-3.78624,2,10)
    Observer: (-2.1871,3.74169,5)
    */

    boost::shared_ptr<const Coordinat2D> left {new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create(right, 0.0 * boost::units::si::meter),
      PointFactory().Create( left, 0.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter),
      PointFactory().Create( left,10.0 * boost::units::si::meter)
    };
    h.MakeConvex(points);
    assert(h.IsConvex(points));
  }
  #ifdef BRUTE_FORCE_TEST_ISSUE228
  if (verbose) { TRACE("MakeConvex, #228"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /*
    (-3.78624,2,10)
    (-0.55,2,10)
    (-0.55,2,20)
    (-3.78624,2,20)
    */
    boost::shared_ptr<const Coordinat2D> left{new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create( left,10.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter),
      PointFactory().Create(right,20.0 * boost::units::si::meter),
      PointFactory().Create( left,20.0 * boost::units::si::meter)
    };
    for (int i=0; i!=5*4*3*2*1; ++i)
    {
      std::random_shuffle(std::begin(points),std::end(points));
      h.MakeConvex(points);
      #ifndef NDEBUG
      if(!h.IsConvex(points))
      {
        TRACE("ERROR");
      }
      #endif
      assert(h.IsConvex(points));
    }
  }
  if (verbose) { TRACE("Making CounterClockWise (seen from an observer), #228"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /* Make these counterclockwise
    (-3.78624,2,0)
    (-3.78624,2,10)
    (-0.55,2,0)
    (-0.55,2,10)
    Observer: (-2.1871,3.74169,5) and others
    */
    boost::shared_ptr<const Coordinat2D> left{new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create( left, 0.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter),
      PointFactory().Create(right, 0.0 * boost::units::si::meter),
      PointFactory().Create( left,10.0 * boost::units::si::meter)
    };

    for (const Coordinat3D& observer:
      {
        Coordinat3D(-2.1871 ,3.74169 ,5),
        Coordinat3D(-1.50283,0.990808,5),
        Coordinat3D(-2.1871 ,3.74169 ,5)
      }
    )
    {
      std::sort(std::begin(points),std::end(points),Helper().OrderByX());

      for (int i=0; i!=5*4*3*2*1; ++i)
      {
        if (Helper().IsCounterClockwise(points,observer)) break;
        std::next_permutation(std::begin(points),std::end(points),Helper().OrderByX());
      }
      assert(Helper().IsCounterClockwise(points,observer));
    }
  }
  if (verbose) { TRACE("MakCounterClockWise, #228"); }
  {
    //const TestTimer test_timer(boost::lexical_cast<std::string>(__LINE__),__FILE__,1.0);
    /* Make these counterclockwise

    (-0.55,2,0)
    (-3.78624,2,0)
    (-0.55,2,10)
    (-3.78624,2,10)
    Observer: (-2.1871,3.74169,5)

    */
    boost::shared_ptr<const Coordinat2D> left{new Coordinat2D(-3.78624,2)};
    boost::shared_ptr<const Coordinat2D> right{new Coordinat2D(-0.55,2)};
    std::vector<boost::shared_ptr<ribi::trim::Point>> points {
      PointFactory().Create( left, 0.0 * boost::units::si::meter),
      PointFactory().Create(right, 0.0 * boost::units::si::meter),
      PointFactory().Create(right,10.0 * boost::units::si::meter),
      PointFactory().Create( left,10.0 * boost::units::si::meter)
    };

    for (const Coordinat3D& observer:
      {
        Coordinat3D(-2.1871 ,3.74169 ,5),
        Coordinat3D(-1.50283,0.990808,5),
        Coordinat3D(-2.1871 ,3.74169 ,5)
      }
    )
    {
      for (int i=0; i!=4*3*2*1*2; ++i) //Try -on average- each permutation twice
      {
        std::random_shuffle(std::begin(points),std::end(points));
        Helper().MakeCounterClockwise(points,observer);
        assert(Helper().IsCounterClockwise(points,observer));
      }
    }
  }
  #endif // BRUTE_FORCE_TEST_ISSUE228

  //assert(!"Yay, fixed #228");
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshpoint.h

 

#ifndef RIBI_TRIANGLEMESHPOINT_H
#define RIBI_TRIANGLEMESHPOINT_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/checked_delete.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/make_shared.hpp>
#include "trianglemeshfwd.h"

#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///An OpenFOAM point, as in the file 'points'
struct Point
{
  //friend class Cell;
  //friend class CellsCreator;
  //friend class Dialog;
  //friend class Template;
  //friend class Face;
  //friend class Helper;

  typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;
  typedef std::vector<boost::weak_ptr<Face>> Faces;

  const boost::shared_ptr<const Coordinat2D>& GetCoordinat() const noexcept { return m_coordinat; }
  Coordinat3D GetCoordinat3D() const noexcept;

  bool CanGetZ() const noexcept;

  const Faces& GetConnected() const noexcept { return m_connected; }

  int GetIndex() const noexcept { return m_index; }

  boost::units::quantity<boost::units::si::length> GetZ() const noexcept;

  ///Let the Point know its Z coordinat itself
  ///Similar to SetLayer
  ///Can be done exactly once
  void SetZ(const boost::units::quantity<boost::units::si::length> z) const noexcept; //const due to mutable

  std::string ToStr() const noexcept;
  std::string ToXml() const noexcept;

  private:
  Point(const Point& ) = delete;
  Point(      Point&&) = delete;
  Point& operator=(const Point& ) = delete;
  Point& operator=(      Point&&) = delete;
  ~Point() noexcept;
  friend void boost::checked_delete<>(      Point* x);
  friend void boost::checked_delete<>(const Point* x);
  friend class boost::detail::sp_ms_deleter<      Point>;
  friend class boost::detail::sp_ms_deleter<const Point>;

  friend class PointFactory;
  explicit Point(
    const boost::shared_ptr<const Coordinat2D> coordinat,
    const int index,
    const PointFactory& lock
  );

  /// m_connected must be mutable, because of the interdependent creation of
  /// Point and Face: a Point needs to know the Face it is connected to,
  /// a Face consists of Point objects
  Faces m_connected;

  const boost::shared_ptr<const Coordinat2D> m_coordinat;

  ///The index of this Point in an TriangleMeshBuilder vector. It is determined at the end
  mutable int m_index;

  mutable boost::shared_ptr<boost::units::quantity<boost::units::si::length>> m_z;

  friend class FaceFactory;
  ///Points are connected to Faces in the Faces' construction
  void AddConnected(const boost::weak_ptr<Face>& face);

  void OnFaceDestroyed(const ribi::trim::Face * const face) noexcept;

  std::function<
    bool(
      const boost::shared_ptr<const ribi::trim::Face>& lhs,
      const boost::shared_ptr<const ribi::trim::Face>& rhs
    )
  >
  OrderByIndex() const noexcept;

  friend class TriangleMeshBuilderImpl;
  ///Determined in the end
  void SetIndex(const int index) const noexcept { m_index = index; }

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend std::ostream& operator<<(std::ostream& os, const Point& n) noexcept;
  friend bool operator==(const Point& lhs, const Point& rhs) noexcept;
  friend bool operator!=(const Point& lhs, const Point& rhs) noexcept;
};

bool operator==(const Point& lhs, const Point& rhs) noexcept;
bool operator!=(const Point& lhs, const Point& rhs) noexcept;

std::ostream& operator<<(std::ostream& os, const Point& n) noexcept;

bool operator<(const boost::shared_ptr<const Point>& lhs, const boost::shared_ptr<      Point>& rhs) = delete;
bool operator<(const boost::shared_ptr<const Point>& lhs, const boost::shared_ptr<const Point>& rhs) = delete;
bool operator<(const boost::shared_ptr<      Point>& lhs, const boost::shared_ptr<      Point>& rhs) = delete;
bool operator<(const boost::shared_ptr<      Point>& lhs, const boost::shared_ptr<const Point>& rhs) = delete;

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHPOINT_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshpoint.cpp

 

#include "trianglemeshpoint.h"

#include <iostream>
#include <iomanip>
#include <sstream>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include "geometry.h"
#include "testtimer.h"
#include "trianglemeshface.h"
#include "trianglemeshhelper.h"
#include "trace.h"
#include "xml.h"
#pragma GCC diagnostic pop

ribi::trim::Point::Point(
  const boost::shared_ptr<const Coordinat2D> coordinat,
  const int index,
  const PointFactory&
) :
    m_connected{},
    m_coordinat(coordinat),
    m_index{index},
    m_z{}
{
  #ifndef NDEBUG
  Test();
  #endif
  using boost::geometry::get;
  assert(m_coordinat);
  assert(m_coordinat == coordinat && "A shallow copy please");
  assert(!std::isnan(get<0>(*m_coordinat)));
  assert(!std::isnan(get<1>(*m_coordinat)));
}

ribi::trim::Point::~Point() noexcept
{

}

void ribi::trim::Point::AddConnected(const boost::weak_ptr<Face>& face)
{
  assert(face.lock().get() != nullptr);
  m_connected.push_back(face);
}

ribi::trim::Point::Coordinat3D ribi::trim::Point::GetCoordinat3D() const noexcept
{
  assert(!std::isnan(boost::geometry::get<0>(*GetCoordinat())));
  assert(!std::isnan(boost::geometry::get<1>(*GetCoordinat())));
  assert(!CanGetZ() || !std::isnan(GetZ().value()));
  
  return Geometry().CreatePoint(
    boost::geometry::get<0>(*GetCoordinat()),
    boost::geometry::get<1>(*GetCoordinat()),
    CanGetZ() ? GetZ().value() : 0.0
  );
}

bool ribi::trim::Point::CanGetZ() const noexcept
{
  return m_z.get();
}

boost::units::quantity<boost::units::si::length> ribi::trim::Point::GetZ() const noexcept
{
  if (!CanGetZ())
  {
    TRACE("BREAK");
  }
  assert(CanGetZ());
  return *m_z;
}

void ribi::trim::Point::OnFaceDestroyed(const ribi::trim::Face * const face) noexcept
{
  assert(1==2);
  const auto new_end = std::remove_if(m_connected.begin(),m_connected.end(),
    [face](const boost::weak_ptr<Face>& connected) { return connected.lock().get() == face; }
  );
  m_connected.erase(new_end,m_connected.end());
}

std::function<
    bool(
      const boost::shared_ptr<const ribi::trim::Face>& lhs,
      const boost::shared_ptr<const ribi::trim::Face>& rhs
    )
  >
  ribi::trim::Point::OrderByIndex() const noexcept
{
  return [](const boost::shared_ptr<const Face>& lhs, const boost::shared_ptr<const Face>& rhs)
  {
    assert(lhs);
    assert(rhs);
    assert(lhs->GetIndex() != rhs->GetIndex());
    return lhs->GetIndex() < rhs->GetIndex();
  };
}

void ribi::trim::Point::SetZ(const boost::units::quantity<boost::units::si::length> z) const noexcept
{
  if (m_z)
  {
    assert(*m_z == z);
    return;
  }
  //assert(!m_z&& "m_z can be set exactly once");
  boost::shared_ptr<boost::units::quantity<boost::units::si::length>> p {
    new boost::units::quantity<boost::units::si::length>(z)
  };
  m_z = p;
  assert(m_z);

  //Let the Point check for themselves for being horizontal or vertical
  #ifndef NDEBUG
  if (GetConnected().empty()) return;
  for (const auto& face: GetConnected())
  {
    assert(face.lock());
    face.lock()->CheckOrientation();
  }
  #endif
}

#ifndef NDEBUG
void ribi::trim::Point::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

std::string ribi::trim::Point::ToStr() const noexcept
{

  std::stringstream s;
  s << *this;
  return s.str();
}

std::string ribi::trim::Point::ToXml() const noexcept
{
  
  std::stringstream s;
  s
    << ribi::xml::ToXml("point_index",GetIndex())
    << Helper().ToXml(*GetCoordinat())
    << ribi::xml::ToXml("z", CanGetZ()
      ?  boost::lexical_cast<std::string>(GetZ().value())
      : ""
      )
  ;
  return s.str();
}

bool ribi::trim::operator==(const ribi::trim::Point& lhs, const ribi::trim::Point& rhs) noexcept
{
  return lhs.GetCoordinat() == rhs.GetCoordinat()
  ;
}

bool ribi::trim::operator!=(const ribi::trim::Point& lhs, const ribi::trim::Point& rhs) noexcept
{
  return !(lhs == rhs);
}

std::ostream& ribi::trim::operator<<(std::ostream& os, const Point& n) noexcept
{
  using boost::geometry::get;
  const auto c = n.GetCoordinat3D();
  os
    << "("
    << get<0>(c)
    << ","
    << get<1>(c)
    << ","
    << get<2>(c)
    << ") (index: "
    << n.GetIndex()
    << ")"
  ;
  return os;
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshpointfactory.h

 

#ifndef RIBI_TRIANGLEMESHPOINTFACTORY_H
#define RIBI_TRIANGLEMESHPOINTFACTORY_H

#include <iosfwd>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/checked_delete.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si/length.hpp>
#include "trianglemeshfwd.h"
#include "trianglemeshwinding.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

class PointFactory
{
  friend class CellsCreator;
  friend class Face;
  friend class FaceFactory;
  friend class Helper;
  friend class Template;
  typedef boost::geometry::model::d2::point_xy<double> Coordinat2D;
  typedef boost::geometry::model::point<double,3,boost::geometry::cs::cartesian> Coordinat3D;

  PointFactory();

  ///This way is used in mesh creation: every 3D point shares the same
  ///ConstCoordinat2D
  ///Create a Point with an undetermined Z coordinat
  boost::shared_ptr<Point> Create(
    const boost::shared_ptr<const Coordinat2D> coordinat
  ) const noexcept;

  ///This way is used in mesh creation: every 3D point shares the same
  ///ConstCoordinat2D
  boost::shared_ptr<Point> Create(
    const boost::shared_ptr<const Coordinat2D> coordinat,
    const boost::units::quantity<boost::units::si::length> z
  ) const noexcept;

  ///Create the points of a testing cube
  /*
      |
    +-+-+
    | | |
   -+-O-+-  View from above, for Z = -1 and Z = 1
    | | |
    +-+-+
      |

  [0]: -1,-1,-1
  [1]: -1,-1, 1
  [2]: -1, 1,-1
  [3]: -1, 1, 1
  [4]:  1,-1,-1
  [5]:  1,-1, 1
  [6]:  1, 1,-1
  [7]:  1, 1, 1

  */
  std::vector<boost::shared_ptr<Point>> CreateTestCube() const noexcept;

  //Create points that should fail to construct a Face from
  std::vector<boost::shared_ptr<Point>> CreateTestInvalid() const noexcept;

  ///Create the points of a testing prism
  /*
      F
     /|\
    D---E
    | | |
    | C |
    |/ \|
    A---B

  */
  std::vector<boost::shared_ptr<Point>> CreateTestPrism() const noexcept;

  ///Creates a square for a certain winding (when viewed from above)
  /*

    Clockwise:

      Y
  2.5 |
    2 + 1-2
  1.5 | | |
    1 + 0-3 where Z = 1.0 for all points
  0.5 |
    0 +-+-+-X
      0 1 2

    Counter-clockwise:

     Y
2.5 |
   2 + 3-2
1.5 | | |
   1 + 0-1  where Z = 1.0 for all points
0.5 |
   0 +-+-+-X
     0 1 2

    Indeterminate:

      Y
  2.3 |
    2 +  2--1
  1.7 |   \/
  1.3 |   /\
    1 +  0--3 where Z = 1.0 for all points
  0.7 |
  0.3 |
    0 +--+--+-X
      0  1  2

  */
  std::vector<boost::shared_ptr<Point>> CreateTestSquare(const Winding winding) const noexcept;

  ///Creates a triangle for a certain winding (when viewed from above)
  /*

    Clockwise:

      Y
  2.5 |
    2 + 1-2
  1.5 | |/
    1 + 0   where Z = 1.0 for all points
  0.5 |
    0 +-+-+-X
      0 1 2

    Counter-clockwise:

      Y
  2.5 |
    2 + 2-1
  1.5 | |/
    1 + 0   where Z = 1.0 for all points
  0.5 |
    0 +-+-+-X
      0 1 2

  */
  std::vector<boost::shared_ptr<Point>> CreateTestTriangle(const Winding winding) const noexcept;

  private:
  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHPOINTFACTORY_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshpointfactory.cpp

 

#include "trianglemeshpointfactory.h"

#include <cassert>



#include "geometry.h"
#include "testtimer.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "trianglemeshhelper.h"
#include "trianglemeshwindings.h"
#include "trace.h"

ribi::trim::PointFactory::PointFactory()
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::shared_ptr<ribi::trim::Point> ribi::trim::PointFactory::Create(
  const boost::shared_ptr<const Coordinat2D> coordinat
) const noexcept
{
  
  //Give every Point some index at creation
  static int cnt = 1;
  const int n = cnt;
  ++cnt;


  const boost::shared_ptr<Point> point(
    new Point(
      coordinat,
      n,
      *this
    )
  );
  assert(point);
  return point;
}

boost::shared_ptr<ribi::trim::Point> ribi::trim::PointFactory::Create(
  const boost::shared_ptr<const Coordinat2D> coordinat,
  const boost::units::quantity<boost::units::si::length> z
) const noexcept
{
  const auto point = Create(coordinat);
  assert(point);
  point->SetZ(z);
  return point;
}

std::vector<boost::shared_ptr<ribi::trim::Point>> ribi::trim::PointFactory::CreateTestCube() const noexcept
{
  const boost::shared_ptr<Coordinat2D> co_a { new Coordinat2D(-1.0,-1.0) };
  const boost::shared_ptr<Coordinat2D> co_b { new Coordinat2D(-1.0, 1.0) };
  const boost::shared_ptr<Coordinat2D> co_c { new Coordinat2D( 1.0,-1.0) };
  const boost::shared_ptr<Coordinat2D> co_d { new Coordinat2D( 1.0, 1.0) };

  const auto a = PointFactory().Create(co_a,-1.0 * boost::units::si::meter);
  const auto b = PointFactory().Create(co_b,-1.0 * boost::units::si::meter);
  const auto c = PointFactory().Create(co_c,-1.0 * boost::units::si::meter);
  const auto d = PointFactory().Create(co_d,-1.0 * boost::units::si::meter);

  const auto e = PointFactory().Create(co_a,1.0 * boost::units::si::meter);
  const auto f = PointFactory().Create(co_b,1.0 * boost::units::si::meter);
  const auto g = PointFactory().Create(co_c,1.0 * boost::units::si::meter);
  const auto h = PointFactory().Create(co_d,1.0 * boost::units::si::meter);

  const std::vector<boost::shared_ptr<Point>> cube {
    a,b,c,d,e,f,g,h
  };
  return cube;
}

std::vector<boost::shared_ptr<ribi::trim::Point>>
  ribi::trim::PointFactory::CreateTestInvalid() const noexcept
{
  //1: -1  0 0
  //2:  1 -0 1
  //7:  1 -0 0
  //3: -1  0 1
  const boost::shared_ptr<Coordinat2D> co_a { new Coordinat2D(-1.0, 0.0) };
  const boost::shared_ptr<Coordinat2D> co_b { new Coordinat2D( 1.0,-0.0) };
  const boost::shared_ptr<Coordinat2D> co_c { new Coordinat2D( 1.0,-0.0) };
  const boost::shared_ptr<Coordinat2D> co_d { new Coordinat2D(-1.0, 0.0) };
  const auto a(PointFactory().Create(co_a));
  const auto b(PointFactory().Create(co_b));
  const auto c(PointFactory().Create(co_c));
  const auto d(PointFactory().Create(co_d));
  a->SetZ(0.0 * boost::units::si::meter);
  b->SetZ(1.0 * boost::units::si::meter);
  c->SetZ(0.0 * boost::units::si::meter);
  d->SetZ(1.0 * boost::units::si::meter);
  //a->SetIndex(1);
  //b->SetIndex(2);
  //c->SetIndex(3);
  //d->SetIndex(4);
  const std::vector<boost::shared_ptr<Point>> square { a,b,c,d };
  return square;
}

std::vector<boost::shared_ptr<ribi::trim::Point>> ribi::trim::PointFactory::CreateTestPrism() const noexcept
{
  const boost::shared_ptr<Coordinat2D> co_a { new Coordinat2D(0.0,0.0) };
  const boost::shared_ptr<Coordinat2D> co_b { new Coordinat2D(1.0,0.0) };
  const boost::shared_ptr<Coordinat2D> co_c { new Coordinat2D(0.0,1.0) };
  const boost::shared_ptr<Coordinat2D> co_d { new Coordinat2D(0.0,0.0) };
  const boost::shared_ptr<Coordinat2D> co_e { new Coordinat2D(1.0,0.0) };
  const boost::shared_ptr<Coordinat2D> co_f { new Coordinat2D(0.0,1.0) };

  const auto a(PointFactory().Create(co_a));
  const auto b(PointFactory().Create(co_b));
  const auto c(PointFactory().Create(co_c));
  const auto d(PointFactory().Create(co_d));
  const auto e(PointFactory().Create(co_e));
  const auto f(PointFactory().Create(co_f));
  a->SetZ(0.0 * boost::units::si::meter);
  b->SetZ(0.0 * boost::units::si::meter);
  c->SetZ(0.0 * boost::units::si::meter);
  d->SetZ(1.0 * boost::units::si::meter);
  e->SetZ(1.0 * boost::units::si::meter);
  f->SetZ(1.0 * boost::units::si::meter);
  const std::vector<boost::shared_ptr<Point>> prism {
    a,b,c,d,e,f
  };
  return prism;
}

std::vector<boost::shared_ptr<ribi::trim::Point>>
  ribi::trim::PointFactory::CreateTestSquare(
  const ribi::trim::Winding winding
) const noexcept
{
  /*

    Clockwise:

      Y
  2.5 |
    2 + 1-2
  1.5 | | |
    1 + 0-3 where Z = 1.0 for all points
  0.5 |
    0 +-+-+-X
      0 1 2

    Counter-clockwise:

     Y
2.5 |
   2 + 3-2
1.5 | | |
   1 + 0-1  where Z = 1.0 for all points
0.5 |
   0 +-+-+-X
     0 1 2

    Indeterminate:

      Y
  2.3 |
    2 +  2--1
  1.7 |   \/
  1.3 |   /\
    1 +  0--3 where Z = 1.0 for all points
  0.7 |
  0.3 |
    0 +--+--+-X
      0  1  2

  */
  const boost::shared_ptr<Coordinat2D> co_a {
    new Coordinat2D(1.0,1.0)
  };
  boost::shared_ptr<Coordinat2D> co_b {
    new Coordinat2D(1.0,2.0)
  };
  boost::shared_ptr<Coordinat2D> co_c {
    new Coordinat2D(2.0,2.0)
  };
  boost::shared_ptr<Coordinat2D> co_d {
    new Coordinat2D(2.0,1.0)
  };

  if (winding == Winding::counter_clockwise)
  {
    std::swap(co_b,co_d);
  }
  else if (winding == Winding::indeterminate)
  {
    std::swap(co_b,co_c);
    std::swap(co_c,co_d);
  }

  const boost::shared_ptr<Point> a { PointFactory().Create(co_a) };
  const boost::shared_ptr<Point> b { PointFactory().Create(co_b) };
  const boost::shared_ptr<Point> c { PointFactory().Create(co_c) };
  const boost::shared_ptr<Point> d { PointFactory().Create(co_d) };

  a->SetZ(1.0 * boost::units::si::meter);
  b->SetZ(1.0 * boost::units::si::meter);
  c->SetZ(1.0 * boost::units::si::meter);
  d->SetZ(1.0 * boost::units::si::meter);
  const std::vector<boost::shared_ptr<Point>> square { a,b,c,d };
  #ifndef NDEBUG
  
  if(Helper().CalcWindingHorizontal(AddConst(square)) != winding)
  {
    TRACE("ERROR");
    TRACE(*a);
    TRACE(*b);
    TRACE(*c);
    TRACE(Windings().ToStr(winding));
    TRACE(Helper().IsClockwiseHorizontal(square));
    TRACE("BREAK");
  }
  #endif
  assert(Helper().CalcWindingHorizontal(AddConst(square)) == winding);
  return square;
}

std::vector<boost::shared_ptr<ribi::trim::Point>>
  ribi::trim::PointFactory::CreateTestTriangle(
  const ribi::trim::Winding winding
) const noexcept
{
  assert( (winding == Winding::clockwise || winding == Winding::counter_clockwise)
    && "It is impossible to create a triangle with an indeterminate winding");

  /*

    Clockwise:

      Y
  2.5 |
    2 + 1-2
  1.5 | |/
    1 + 0   where Z = 1.0 for all points
  0.5 |
    0 +-+-+-X
      0 1 2

    Counter-clockwise:

      Y
  2.5 |
    2 + 2-1
  1.5 | |/
    1 + 0   where Z = 1.0 for all points
  0.5 |
    0 +-+-+-X
      0 1 2

  */

  const boost::shared_ptr<Coordinat2D> co_a {
    new Coordinat2D(1.0,1.0)
  };
  boost::shared_ptr<Coordinat2D> co_b {
    new Coordinat2D(1.0,2.0)
  };
  boost::shared_ptr<Coordinat2D> co_c {
    new Coordinat2D(2.0,2.0)
  };

  if (winding == Winding::counter_clockwise) { std::swap(co_b,co_c); }

  const boost::shared_ptr<Point> a {
    PointFactory().Create(co_a)
  };
  const boost::shared_ptr<Point> b {
    PointFactory().Create(co_b)
  };
  const boost::shared_ptr<Point> c {
    PointFactory().Create(co_c)
  };

  a->SetZ(1.0 * boost::units::si::meter);
  b->SetZ(1.0 * boost::units::si::meter);
  c->SetZ(1.0 * boost::units::si::meter);
  const std::vector<boost::shared_ptr<Point>> triangle { a,b,c };
  #ifndef NDEBUG
  
  if (!(Helper().IsClockwiseHorizontal(triangle)   == (winding == Winding::clockwise)))
  {
    TRACE("ERROR");
    TRACE(*a);
    TRACE(*b);
    TRACE(*c);
    TRACE(Windings().ToStr(winding));
    TRACE(Helper().IsClockwiseHorizontal(triangle));
    TRACE("BREAK");
  }
  #endif
  assert(Helper().IsClockwiseHorizontal(triangle)   == (winding == Winding::clockwise));
  return triangle;
}

#ifndef NDEBUG
void ribi::trim::PointFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  PointFactory().CreateTestTriangle(Winding::clockwise);
  const TestTimer test_timer(__func__,__FILE__,1.0);
  {
    const std::vector<boost::shared_ptr<Point>> prism {
      PointFactory().CreateTestPrism()
    };
    assert(prism.size() == 6 && "A prism has 6 points");
  }
  for (Winding winding: { Winding::clockwise, Winding::counter_clockwise } )
  {
    
    assert(
      Helper().CalcWindingHorizontal(
        AddConst(PointFactory().CreateTestTriangle(winding))
      ) == winding
    );
  }
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshtemplate.h

 

#ifndef RIBI_TRIANGLEMESHTEMPLATE_H
#define RIBI_TRIANGLEMESHTEMPLATE_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include "trianglemeshfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

///Template is a two-dimensional mesh, consisting of Points, Edges and Faces only
///It constructs itself from a TemplateFile its output (.node and .ele) files
///
///The next step will be to create a multiple layers of Cells by CellsCreator
struct Template
{
  //friend class CellsCreator;
  //friend class CellsCreatorFactory;
  //friend class CellFactory;
  //friend class Dialog;

  typedef boost::geometry::model::d2::point_xy<double> ConstCoordinat2D;

  explicit Template(
    const std::string& filename_node,
    const std::string& filename_ele,
    const bool verbose
  );

  int CountFaces() const noexcept { return static_cast<int>(m_faces.size() ); }
  int CountNodes() const noexcept { return static_cast<int>(m_points.size()); }

  static boost::shared_ptr<Template> CreateTest(const int i) noexcept;

  const std::vector<std::pair<int,int>>& GetEdges() const noexcept { return m_edges; }
  const std::vector<boost::shared_ptr<Face>>& GetFaces() const noexcept { return m_faces; }
  const std::vector<std::vector<int>>& GetFacePointIndices() const noexcept { return m_face_point_indices; }
  const std::vector<boost::shared_ptr<Point>>& GetPoints() const noexcept { return m_points; }

  private:
  Template(
    const std::vector<std::pair<int,int>>& edges,
    const std::vector<boost::shared_ptr<Face>>& faces,
    const std::vector<std::vector<int>>& face_point_indices,
    const std::vector<boost::shared_ptr<Point>>& points
  );
  Template(const Template& ) = delete;
  //Template(      Template&&) = delete;
  Template& operator=(const Template& ) = delete;
  //Template& operator=(      Template&&) = delete;
  ~Template() noexcept {}

  ///ints are m_points indices
  std::vector<std::pair<int,int>> m_edges;
  std::vector<boost::shared_ptr<Face>> m_faces;
  std::vector<std::vector<int>> m_face_point_indices;
  std::vector<boost::shared_ptr<Point>> m_points;

  //No idea why Triangle started using the Dutch numbering system...
  static std::string ConvertNumbersToEnglish(const std::string& s) noexcept;

  static boost::shared_ptr<Template> CreateTest2x3() noexcept;
  static boost::shared_ptr<Template> CreateTest3x3() noexcept;
  static boost::shared_ptr<Template> CreateTestSquare2x2() noexcept;
  static boost::shared_ptr<Template> CreateTestTriangle2x2() noexcept;

  static std::vector<std::string> CleanAndSplitString(
    const std::string& input) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend void boost::checked_delete<>(      Template*);
  friend void boost::checked_delete<>(const Template*);
  friend class boost::detail::sp_ms_deleter<      Template>;
  friend class boost::detail::sp_ms_deleter<const Template>;
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHTEMPLATE_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshtemplate.cpp

 

#include "trianglemeshtemplate.h"

#include <cassert>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/algorithm/string.hpp>
#include <boost/math/constants/constants.hpp>

#include "trianglemeshface.h"
#include "trianglemeshfacefactory.h"
#include "testtimer.h"
#include "fileio.h"
#include "trianglemeshhelper.h"
#include "trianglemeshpoint.h"
#include "trianglemeshpointfactory.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::trim::Template::Template(
  const std::string& filename_node,
  const std::string& filename_ele,
  const bool verbose
)
  : m_edges{},
    m_faces{},
    m_face_point_indices{},
    m_points{}
{
  #ifndef NDEBUG
  Test();
  #endif

  if (verbose) { TRACE("Load the points and faces created by Triangle"); }
  {
    const std::vector<std::string> v {
      ribi::fileio::FileIo().FileToVector(
        filename_node
      )
    };
    const int sz = v.size();
    const int percent = sz / 100 ? sz / 100: 1;
    for(int n=0; n!=sz; ++n)
    {
      if (verbose) { if (n % percent == 0) std::clog << '%'; }
      const std::string line = v[n];
      if(n==0) continue; //No idea why this has to be skipped
      const std::vector<std::string> w { CleanAndSplitString(ConvertNumbersToEnglish(line)) };
      if (w.empty() || w[0].empty() ||  w[0] == "#")
      {
        //The final comment line
        continue;
      }
      assert(w.size() == 4);
      assert(CanLexicalCast<int>(w[0]));
      assert(CanLexicalCast<double>(w[1]));
      #ifndef NDEBUG
      if (!CanLexicalCast<double>(w[2]))
      {
        TRACE("ERROR");
        TRACE(line);
        TRACE(w[0]);
        TRACE(w[1]);
        TRACE(w[2]);
        TRACE(w[3]);
        TRACE("BREAK");
      }
      #endif
      assert(CanLexicalCast<double>(w[2]));
      assert(CanLexicalCast<int>(w[3]));
      const double x = boost::lexical_cast<double>(w[1]);
      const double y = boost::lexical_cast<double>(w[2]);
      const boost::shared_ptr<const ConstCoordinat2D> bottom(
        new ConstCoordinat2D(x,y)
      );

      const boost::shared_ptr<Point> node {
        PointFactory().Create(bottom)
      };
      m_points.push_back(node);
    }
  }

  if (verbose) { TRACE("Load and translate faces"); }
  {
    const std::vector<std::string> v
      = ribi::fileio::FileIo().FileToVector(filename_ele);
    const int sz = v.size();
    const int percent = sz / 100 ? sz / 100: 1;
    for(int n=0; n!=sz; ++n)
    {
      if (verbose)
      {
        if (n % percent == 0)
        {
          std::clog << '%';
        }
      }
      const std::string line = v[n];
      if(n==0) continue;
      const std::vector<std::string> w { CleanAndSplitString(line) };
      if (w.empty() || w[0].empty() ||  w[0] == "#")
      {
        //The final comment line
        continue;
      }
      assert(w.size() == 4);
      assert(CanLexicalCast<int>(w[0]));
      assert(CanLexicalCast<int>(w[1]));
      assert(CanLexicalCast<int>(w[2]));
      assert(CanLexicalCast<int>(w[3]));

      //I hope that I made the Triangle.exe output start at index 0..
      const int point1 = boost::lexical_cast<int>(w[1]);
      const int point2 = boost::lexical_cast<int>(w[2]);
      const int point3 = boost::lexical_cast<int>(w[3]);
      assert(point1 >= 0); //Start at index 0
      assert(point2 >= 0); //Start at index 0
      assert(point3 >= 0); //Start at index 0
      assert(point1 - 0 < static_cast<int>(m_points.size()));
      assert(point2 - 0 < static_cast<int>(m_points.size()));
      assert(point3 - 0 < static_cast<int>(m_points.size()));
      const std::vector<int> face_point_indices {
        point1-0, //Start at index 0
        point2-0, //Start at index 0
        point3-0  //Start at index 0
      };
      m_edges.push_back(std::make_pair(face_point_indices[0],face_point_indices[1]));
      m_edges.push_back(std::make_pair(face_point_indices[0],face_point_indices[2]));
      m_edges.push_back(std::make_pair(face_point_indices[1],face_point_indices[2]));

      std::vector<boost::shared_ptr<Point>> face_points {
        m_points[point1-0], //Start at index 0
        m_points[point2-0], //Start at index 0
        m_points[point3-0]  //Start at index 0
      };
      if (!Helper().IsClockwiseHorizontal(face_points))
      {
        std::reverse(face_points.begin(),face_points.end());
      }
      assert(Helper().IsClockwiseHorizontal(face_points));
      if (!Helper().IsConvex(face_points)) { Helper().MakeConvex(face_points); }
      assert(Helper().IsConvex(face_points) && "FaceFactory only accepts convex ordered points");

      const boost::shared_ptr<Face> face {
        FaceFactory().Create(
          face_points,
          FaceOrientation::horizontal,
          verbose
        )
      };
      m_faces.push_back(face);
      m_face_point_indices.push_back(face_point_indices);

      assert(std::unique(m_face_point_indices.begin(),m_face_point_indices.end())
        == m_face_point_indices.end()
        && "Every face should have unique point indices");
    }
  }

  #ifndef NDEBUG
  if (verbose) { TRACE("Checking the result"); }
  const int n_faces = static_cast<int>(m_faces.size());
  assert(m_faces.size() == m_face_point_indices.size());
  for (int i=0; i!=n_faces; ++i)
  {
    const auto face = m_faces[i];
    const auto indices = m_face_point_indices[i];
    assert(face->GetPoints().size() == indices.size());
  }
  #endif

  for (auto& p: m_edges)
  {
    if (p.first > p.second) std::swap(p.first,p.second);
    assert(p.first < p.second);
  }
  std::sort(m_edges.begin(),m_edges.end());

  auto new_end = std::unique(m_edges.begin(),m_edges.end());
  m_edges.erase(new_end,m_edges.end());

  if (verbose) { TRACE("Done checking the result"); }
}

ribi::trim::Template::Template(
  const std::vector<std::pair<int,int>>& edges,
  const std::vector<boost::shared_ptr<Face>>& faces,
  const std::vector<std::vector<int>>& face_point_indices,
  const std::vector<boost::shared_ptr<Point>>& points
) : m_edges{edges}, m_faces{faces}, m_face_point_indices{face_point_indices}, m_points{points}
{
  #ifndef NDEBUG
  Test();
  #endif
}

std::string ribi::trim::Template::ConvertNumbersToEnglish(const std::string& s) noexcept
{
  #ifndef _WIN32
  return boost::algorithm::replace_all_copy(s,",",".");
  #else
  return s;
  #endif
}

boost::shared_ptr<ribi::trim::Template> ribi::trim::Template::CreateTest(const int i) noexcept
{
  switch (i)
  {
    case 0: return CreateTestTriangle2x2();
    case 1: return CreateTestSquare2x2();
    case 2: return CreateTest2x3();
    case 3: return CreateTest3x3();
  }

  assert(!"Should not get here");
  return boost::shared_ptr<Template>();
}

boost::shared_ptr<ribi::trim::Template> ribi::trim::Template::CreateTestSquare2x2() noexcept
{
  std::vector<boost::shared_ptr<Face>> faces;
  std::vector<std::vector<int>> face_point_indices;
  std::vector<boost::shared_ptr<Point>> points;
  const int width{2};
  const int height{2};
  const int n_points{width * height};
  const bool verbose{false};
  points.reserve(n_points);

  //Create points
  {
    for(int i=0; i!=n_points; ++i)
    {
      const double x = static_cast<double>(i % width);
      const double y = static_cast<double>(i / width);
      const std::string boundary_type = "two_times_two";
      const boost::shared_ptr<const ConstCoordinat2D> bottom {
        new ConstCoordinat2D(x,y)
      };
      const boost::shared_ptr<Point> point {
        PointFactory().Create(bottom)
      };
      points.push_back(point);
    }
  }
  #ifndef NDEBUG
  //Check that there is no coordinat present twice
  {
    for (int i=0; i!=n_points; ++i)
    {
      const boost::shared_ptr<const ConstCoordinat2D> a { points[i]->GetCoordinat() };
      for (int j=0; j!=n_points; ++j)
      {
        const boost::shared_ptr<const ConstCoordinat2D> b { points[j]->GetCoordinat() };
        if (a == b)
        {
          //assert(ribi::Distance(*a,*b) < 0.001);
          assert(boost::geometry::distance(*a,*b) < 0.001);
        }
        else
        {
          //assert(ribi::Distance(*a,*b) > 0.001);
          assert(boost::geometry::distance(*a,*b) > 0.001);
        }
      }
    }
  }
  #endif

  //Load and translate faces
  face_point_indices = {
    { 0,1,2 },
    { 1,2,3 }
  };

  const std::vector<std::pair<int,int>> edges {
    { 0,1 },
    { 0,2 },
    { 1,2 },
    { 1,3 },
    { 2,3 }
  };

  {
    for(const auto v: face_point_indices)
    {
      assert(v.size() == 3);
      //I do not correct for one-base Triangle.exe output
      assert(v[0] >= 0);
      assert(v[1] >= 0);
      assert(v[2] >= 0);
      std::vector<boost::shared_ptr<Point>> face_points {
        points[ v[0] ],
        points[ v[1] ],
        points[ v[2] ]
      };
      
      if (!Helper().IsClockwiseHorizontal(face_points))
      {
        std::reverse(face_points.begin(),face_points.end());
      }
      assert(Helper().IsClockwiseHorizontal(face_points));
      const boost::shared_ptr<Face> face {
        FaceFactory().Create(
          face_points,
          FaceOrientation::horizontal,
          verbose
        )
      };
      faces.push_back(face);
    }
  }

  #ifndef NDEBUG
  const int n_faces = static_cast<int>(faces.size());
  assert(faces.size() == face_point_indices.size());
  for (int i=0; i!=n_faces; ++i)
  {
    const auto face = faces[i];
    const auto indices = face_point_indices[i];
    assert(face->GetPoints().size() == indices.size());
  }
  #endif

  assert(faces.size()  == 2 && "A square consists of 2 triangles");
  assert(edges.size()  == 5 && "A square with a diagonal has 5 edges");
  assert(points.size() == 4 && "A square has 4 nodes");

  const boost::shared_ptr<Template> my_template {
    new Template(
      edges,
      faces,
      face_point_indices,
      points
    )
  };
  assert(my_template);
  return my_template;
}


boost::shared_ptr<ribi::trim::Template> ribi::trim::Template::CreateTestTriangle2x2() noexcept
{
  std::vector<boost::shared_ptr<Face>> faces;
  std::vector<std::vector<int>> face_point_indices;
  std::vector<boost::shared_ptr<Point>> points;
  const int width{2};
  //const int height = 2;
  const int n_points{3}; //Triangle
  const bool verbose{false};
  points.reserve(n_points);

  //Create points
  {
    for(int i=0; i!=n_points; ++i)
    {
      const double x = static_cast<double>(i % width);
      const double y = static_cast<double>(i / width);
      const std::string boundary_type{"two_times_two"};
      const boost::shared_ptr<const ConstCoordinat2D> bottom {
        new ConstCoordinat2D(x,y)
      };
      const boost::shared_ptr<Point> point {
        PointFactory().Create(bottom)
      };
      points.push_back(point);
    }
  }
  #ifndef NDEBUG
  //Check that there is no coordinat present twice
  {
    for (int i=0; i!=n_points; ++i)
    {
      const boost::shared_ptr<const ConstCoordinat2D> a { points[i]->GetCoordinat() };
      for (int j=0; j!=n_points; ++j)
      {
        const boost::shared_ptr<const ConstCoordinat2D> b { points[j]->GetCoordinat() };
        if (a == b)
        {
          assert(boost::geometry::distance(*a,*b) < 0.001);
        }
        else
        {
          assert(boost::geometry::distance(*a,*b) > 0.001);
        }
      }
    }
  }
  #endif

  //Load and translate faces
  face_point_indices = {
    { 0,1,2 }
  };

  const std::vector<std::pair<int,int>> edges {
    { 0,1 },
    { 0,2 },
    { 1,2 }
  };

  {
    for(const auto v: face_point_indices)
    {
      assert(v.size() == 3);
      //I do not correct for one-base Triangle.exe output
      assert(v[0] >= 0);
      assert(v[1] >= 0);
      assert(v[2] >= 0);
      std::vector<boost::shared_ptr<Point>> face_points {
        points[ v[0] ],
        points[ v[1] ],
        points[ v[2] ]
      };
      
      if (!Helper().IsClockwiseHorizontal(face_points))
      {
        std::reverse(face_points.begin(),face_points.end());
      }
      #ifndef NDEBUG
      if (!Helper().IsClockwiseHorizontal(face_points))
      {
        TRACE("ERROR");
        TRACE(*face_points[0]);
        TRACE(*face_points[1]);
        TRACE(*face_points[2]);
        TRACE("BREAK");
      }
      #endif
      assert(Helper().IsClockwiseHorizontal(face_points));
      const boost::shared_ptr<Face> face {
        FaceFactory().Create(
          face_points,
          FaceOrientation::horizontal,
          verbose
        )
      };
      faces.push_back(face);
    }
  }

  #ifndef NDEBUG
  const int n_faces = static_cast<int>(faces.size());
  assert(faces.size() == face_point_indices.size());
  for (int i=0; i!=n_faces; ++i)
  {
    const auto face = faces[i];
    const auto indices = face_point_indices[i];
    assert(face->GetPoints().size() == indices.size());
  }
  #endif

  assert(faces.size()  == 1 && "A triangle is only 1 triangle");
  assert(edges.size()  == 3 && "A triangle has 3 edges");
  assert(points.size() == 3 && "A triangle has 3 nodes");

  const boost::shared_ptr<Template> my_template {
    new Template(
      edges,
      faces,
      face_point_indices,
      points
    )
  };
  assert(my_template);
  return my_template;
}

boost::shared_ptr<ribi::trim::Template> ribi::trim::Template::CreateTest2x3() noexcept
{
  std::vector<boost::shared_ptr<Face>> faces;
  std::vector<std::vector<int>> face_point_indices;
  std::vector<boost::shared_ptr<Point>> points;
  const int width{3};
  const int height{2};
  const int n_points{width * height};
  const bool verbose{false};
  points.reserve(n_points);

  //Create points
  {
    for(int i=0; i!=n_points; ++i)
    {
      const double x{static_cast<double>(i % width)};
      const double y{static_cast<double>(i / width)};
      const boost::shared_ptr<const ConstCoordinat2D> bottom{
        new ConstCoordinat2D(x,y)
      };
      const boost::shared_ptr<Point> point{
        PointFactory().Create(bottom)
      };
      points.push_back(point);
    }
  }
  #ifndef NDEBUG
  //Check that there is no coordinat present twice
  {
    for (int i=0; i!=n_points; ++i)
    {
      const boost::shared_ptr<const ConstCoordinat2D> a { points[i]->GetCoordinat() };
      for (int j=0; j!=n_points; ++j)
      {
        const boost::shared_ptr<const ConstCoordinat2D> b { points[j]->GetCoordinat() };
        if (a == b)
        {
          assert(boost::geometry::distance(*a,*b) < 0.001);
        }
        else
        {
          assert(boost::geometry::distance(*a,*b) > 0.001);
        }
      }
    }
  }
  #endif

  //Load and translate faces
  face_point_indices = {
    { 0,1,3 },
    { 1,3,4 },
    { 1,2,4 },
    { 2,4,5 }
  };

  const std::vector<std::pair<int,int>> edges {
    { 0,1 },
    { 0,3 },
    { 1,2 },
    { 1,3 },
    { 1,4 },
    { 2,4 },
    { 2,5 },
    { 3,4 },
    { 4,5 }
  };

  {
    for(const auto v: face_point_indices)
    {
      assert(v.size() == 3);
      //I do not correct for one-base Triangle.exe output
      const int point1 = v[0];
      const int point2 = v[1];
      const int point3 = v[2];
      assert(point1 >= 0);
      assert(point2 >= 0);
      assert(point3 >= 0);
      const std::vector<int> face_point_indices {
        point1,
        point2,
        point3
      };
      std::vector<boost::shared_ptr<Point>> face_points {
        points[point1],
        points[point2],
        points[point3]
      };
      
      if (!Helper().IsClockwiseHorizontal(face_points))
      {
        std::reverse(face_points.begin(),face_points.end());
      }
      assert(Helper().IsClockwiseHorizontal(face_points));
      const boost::shared_ptr<Face> face {
        FaceFactory().Create(
          face_points,
          FaceOrientation::horizontal,
          verbose
        )
      };
      faces.push_back(face);
    }
  }

  #ifndef NDEBUG
  const int n_faces = static_cast<int>(faces.size());
  assert(faces.size() == face_point_indices.size());
  for (int i=0; i!=n_faces; ++i)
  {
    const auto face = faces[i];
    const auto indices = face_point_indices[i];
    assert(face->GetPoints().size() == indices.size());
    /*
    const int n_points = static_cast<int>(indices.size());
    for (int j=0; j!=n_points; ++j)
    {
      //Only true when points are not reversed
      assert(face->GetPoints()[j] == points[ indices[j] ]);
    }
    */
  }
  #endif

  assert(faces.size()  == 4 && "Two adjacent squares consist of 4 triangles");
  assert(edges.size()  == 9 && "Two adjacent squares (with diagonals) have 9 edges");
  assert(points.size() == 6 && "Two adjacent squares have 6 nodes");

  const boost::shared_ptr<Template> my_template {
    new Template(
      edges,
      faces,
      face_point_indices,
      points
    )
  };
  assert(my_template);
  return my_template;
}

boost::shared_ptr<ribi::trim::Template> ribi::trim::Template::CreateTest3x3() noexcept
{
  std::vector<boost::shared_ptr<Face>> faces;
  std::vector<std::vector<int>> face_point_indices;
  std::vector<boost::shared_ptr<Point>> points;
  const int width{3};
  const int height{3};
  const int n_points{width * height};
  const bool verbose{false};
  points.reserve(n_points);

  //Create points
  {
    for(int i=0; i!=n_points; ++i)
    {
      const double x{static_cast<double>(i % width)};
      const double y{static_cast<double>(i / width)};
      const std::string boundary_type = "three_times_three";
      const boost::shared_ptr<const ConstCoordinat2D> bottom{
        new ConstCoordinat2D(x,y)
      };
      const auto point = PointFactory().Create(bottom);
      points.push_back(point);
    }
  }
  #ifndef NDEBUG
  //Check that there is no coordinat present twice
  {
    for (int i=0; i!=n_points; ++i)
    {
      const boost::shared_ptr<const ConstCoordinat2D> a { points[i]->GetCoordinat() };
      for (int j=0; j!=n_points; ++j)
      {
        const boost::shared_ptr<const ConstCoordinat2D> b { points[j]->GetCoordinat() };
        if (a == b)
        {
          assert(boost::geometry::distance(*a,*b) < 0.001);
        }
        else
        {
          assert(boost::geometry::distance(*a,*b) > 0.001);
        }
      }
    }
  }
  #endif

  //Load and translate faces
  face_point_indices = {
    { 0,1,3 },
    { 1,2,4 },
    { 1,3,4 },
    { 2,4,5 },
    { 3,4,6 },
    { 4,5,7 },
    { 4,6,7 },
    { 5,7,8 }
  };

  const std::vector<std::pair<int,int>> edges {
    { 0,1 },
    { 0,3 },
    { 1,2 },
    { 1,3 },
    { 1,4 },
    { 2,4 },
    { 2,5 },
    { 3,4 },
    { 3,6 },
    { 4,5 },
    { 4,6 },
    { 4,7 },
    { 5,7 },
    { 5,8 },
    { 6,7 },
    { 7,8 }
  };

  {
    for(const auto v: face_point_indices)
    {
      assert(v.size() == 3);
      //I do not correct for one-base Triangle.exe output
      const int point1 = v[0];
      const int point2 = v[1];
      const int point3 = v[2];
      assert(point1 >= 0);
      assert(point2 >= 0);
      assert(point3 >= 0);
      const std::vector<int> face_point_indices {
        point1,
        point2,
        point3
      };
      std::vector<boost::shared_ptr<Point>> face_points {
        points[point1],
        points[point2],
        points[point3]
      };
      
      if (!Helper().IsClockwiseHorizontal(face_points))
      {
        std::reverse(face_points.begin(),face_points.end());
      }
      assert(Helper().IsClockwiseHorizontal(face_points));
      const boost::shared_ptr<Face> face {
        FaceFactory().Create(
          face_points,
          FaceOrientation::horizontal,
          verbose
        )
      };
      faces.push_back(face);
    }
  }

  #ifndef NDEBUG
  const int n_faces = static_cast<int>(faces.size());
  assert(faces.size() == face_point_indices.size());
  for (int i=0; i!=n_faces; ++i)
  {
    const auto face = faces[i];
    const auto indices = face_point_indices[i];
    assert(face->GetPoints().size() == indices.size());
    /*
    const int n_points = static_cast<int>(indices.size());
    for (int j=0; j!=n_points; ++j)
    {
      //Only true when points are not reversed
      assert(face->GetPoints()[j] == points[ indices[j] ]);
    }
    */
  }
  #endif

  assert(faces.size()  ==  8 && "2x2 adjacent squares consist of 8 triangles");
  assert(edges.size()  == 16 && "2x2 adjacent squares (with diagonals) have 16 edges");
  assert(points.size() ==  9 && "2x2 adjacent squares have 9 nodes");

  const boost::shared_ptr<Template> my_template {
    new Template(
      edges,
      faces,
      face_point_indices,
      points
    )
  };
  assert(my_template);
  return my_template;
}



std::vector<std::string> ribi::trim::Template::CleanAndSplitString(
  const std::string& input_original) noexcept
{

  std::string input = boost::algorithm::replace_all_copy(input_original,"\t"," ");
  for (int i=0; i!=8; ++i)
  {
    input = boost::algorithm::replace_all_copy(input,"  "," ");
  }
  input = boost::algorithm::trim_copy(input_original);
  const char seperator  = ' ';
  std::vector<std::string> v;
  boost::algorithm::split(v,input,
    std::bind2nd(std::equal_to<char>(),seperator),
    boost::algorithm::token_compress_on);
  return v;
}

#ifndef NDEBUG
void ribi::trim::Template::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  PointFactory();
  FaceFactory();

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  if (verbose) { TRACE("IsClockWise, confirmation"); }
  {
    /*

    Cartesian plane

          |
          |
          A = (0,1)
         /|\
        / | \
    ---+--+--+----
      /   |   \
     C----+----B
          |
          |


    */
    //12 o'clock
    const boost::shared_ptr<const ConstCoordinat2D> a {
      new ConstCoordinat2D(0.0,1.0)
    };
    //4 o'clock
    const boost::shared_ptr<const ConstCoordinat2D> b {
      new ConstCoordinat2D(0.83,-0.5)
    };
    //8 o'clock
    const boost::shared_ptr<const ConstCoordinat2D> c {
      new ConstCoordinat2D(-0.83,-0.5)
    };
    std::vector<boost::shared_ptr<Point>> points {
      PointFactory().Create(a),
      PointFactory().Create(b),
      PointFactory().Create(c)
    };
    points[0]->SetZ(1.0 * boost::units::si::meter);
    points[1]->SetZ(1.0 * boost::units::si::meter);
    points[2]->SetZ(1.0 * boost::units::si::meter);
    assert( Helper().IsClockwiseHorizontal(points));
    std::reverse(points.begin(),points.end());
    assert(!Helper().IsClockwiseHorizontal(points));
  }
  if (verbose) { TRACE("IsClockWise, rejection"); }
  {
    /*

    Cartesian plane

          |
          |
          A = (0,1)
         /|\
        / | \
    ---+--+--+----
      /   |   \
     B----+----C
          |
          |


    */
    //12 o'clock
    const boost::shared_ptr<const ConstCoordinat2D> a {
      new ConstCoordinat2D(0.0,1.0)
    };
    //8 o'clock
    const boost::shared_ptr<const ConstCoordinat2D> b {
      new ConstCoordinat2D(-0.83,-0.5)
    };
    //4 o'clock
    const boost::shared_ptr<const ConstCoordinat2D> c {
      new ConstCoordinat2D(0.83,-0.5)
    };
    std::vector<boost::shared_ptr<Point>> points {
      PointFactory().Create(a),
      PointFactory().Create(b),
      PointFactory().Create(c)
    };
    points[0]->SetZ(1.0 * boost::units::si::meter);
    points[1]->SetZ(1.0 * boost::units::si::meter);
    points[2]->SetZ(1.0 * boost::units::si::meter);
    assert(!Helper().IsClockwiseHorizontal(points));
    std::reverse(points.begin(),points.end());
    assert(Helper().IsClockwiseHorizontal(points));
  }
  for (int i=0; i!=4; ++i)
  {
    const boost::shared_ptr<Template> my_template {
      CreateTest(i)
    };
    assert(my_template);
    for (const auto& face: my_template->GetFaces())
    {
      if (!Helper().IsClockwiseHorizontal(face->GetPoints()))
      {
        TRACE("BREAK");
      }
      assert(Helper().IsClockwiseHorizontal(face->GetPoints()));
    }
  }
}
#endif

 

 

 

 

 

./CppTriangleMesh/trianglemeshtemplateimpl.h

 

#ifndef TRIANGLEMESHTEMPLATEIMPL_H
#define TRIANGLEMESHTEMPLATEIMPL_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include "trianglemeshfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace trim {

class TemplateImpl
{
  friend class Template;
  TemplateImpl();

  private:
};

} //~namespace trim
} //~namespace ribi

#endif // TRIANGLEMESHTEMPLATEIMPL_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshtemplateimpl.cpp

 

#include "trianglemeshtemplateimpl.h"

ribi::trim::TemplateImpl::TemplateImpl()
{
}

 

 

 

 

 

./CppTriangleMesh/trianglemeshwinding.h

 

#ifndef RIBI_TRIANGLEMESHWINDING_H
#define RIBI_TRIANGLEMESHWINDING_H

namespace ribi {
namespace trim {

///The winding the Points of a Face have, when viewed from the inside of a Cell
enum class Winding
{
  clockwise,
  counter_clockwise,
  indeterminate ,
  n_types //To be used in debugging only
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHWINDING_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshwinding.cpp

 

#include "trianglemeshwinding.h"

 

 

 

 

 

./CppTriangleMesh/trianglemeshwindings.h

 

#ifndef RIBI_TRIANGLEMESHWINDINGS_H
#define RIBI_TRIANGLEMESHWINDINGS_H

#include <string>
#include <vector>
#include "trianglemeshwinding.h"

namespace ribi {
namespace trim {

//No state, no Pimpl
class Windings
{
  friend class Face;
  friend class FaceFactory;
  friend class PointFactory;

  Windings() {}
  std::vector<Winding> GetAll() const noexcept;
  std::string ToStr(const Winding winding) const noexcept;
};

} //~namespace trim
} //~namespace ribi

#endif // RIBI_TRIANGLEMESHWINDINGS_H

 

 

 

 

 

./CppTriangleMesh/trianglemeshwindings.cpp

 

#include "trianglemeshwindings.h"

#include <cassert>
#include <stdexcept>

#include "trace.h"

std::vector<ribi::trim::Winding> ribi::trim::Windings::GetAll() const noexcept
{
  const std::vector<Winding> v {
    Winding::clockwise,
    Winding::counter_clockwise,
    Winding::indeterminate
  };
  assert(static_cast<int>(v.size()) == static_cast<int>(Winding::n_types));
  return v;
}

std::string ribi::trim::Windings::ToStr(const Winding winding) const noexcept
{
  switch (winding)
  {
    case Winding::clockwise: return "clockwise";
    case Winding::counter_clockwise: return "counter_clockwise";
    case Winding::indeterminate: return "indeterminate";
    default:
      assert(!"Should not get here");
      throw std::logic_error("ribi::trim::Windings::ToStr: unknown Winding");
  }
}

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml