Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) TriangleFile

 

Technical facts

 

 

 

 

 

 

./CppTriangleFile/CppTriangleFile.pri

 

INCLUDEPATH += \
    ../../Classes/CppTriangleFile

SOURCES += \
    ../../Classes/CppTriangleFile/trianglefile.cpp

HEADERS  += \
    ../../Classes/CppTriangleFile/trianglefile.h

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

RESOURCES += \
    ../../Classes/CppTriangleFile/CppTriangleFile.qrc

 

 

 

 

 

./CppTriangleFile/trianglefile.h

 

#ifndef RIBI_TRIANGLEFILE_H
#define RIBI_TRIANGLEFILE_H

#include <string>
#include <vector>

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

namespace ribi {

struct PolyFileFromPolygons;

///TriangleFile allows for conversion between multiple polygons
///  and Triangle output files
///The following steps are taken:
/// - Convert the shapes to WKT
///
///TriangleFile can then call ExecuteTriangle on itself
///to create a triangulated 2D mesh in the form of a .ele, .node and poly file
///
///Triangle.exe is written by Jonathan Shewchuk, http://people.sc.fsu.edu/~jburkardt/c_src/triangle/triangle.html
struct TriangleFile
{
  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::linestring<Coordinat> Linestring;
  typedef boost::geometry::model::polygon<Coordinat> Polygon;
  typedef std::vector<Linestring> Linestrings;
  typedef std::vector<Polygon> Polygons;
  typedef std::pair<Polygons,Linestrings> Shapes;

  ///Start from polygons
  explicit TriangleFile(const Polygons& polygons, const Linestrings& linestrings);
  explicit TriangleFile(const Shapes& shapes) : TriangleFile(shapes.first,shapes.second) {}

  ///Call Triangle
  //#define TODO_ISSUE_207
  #ifdef  TODO_ISSUE_207
  void ExecuteTriangle(
    std::string& node_filename,
    std::string& ele_filename,
    std::string& poly_filename,
    const Angle triangle_min_angle,
    const Area triangle_max_area,
    const bool verbose
  ) const;

  ///Call Triangle its C++ equivalent
  void ExecuteTriangleCpp(
    std::string& node_filename,
    std::string& ele_filename,
    std::string& poly_filename,
    const Angle triangle_min_angle,
    const Area triangle_max_area,
    const bool verbose
  ) const;
  #endif
  ///Executes Triangle.exe from a Windows command line
  ///Creates three output files:
  ///- ele_filename: the .ele file
  ///- node_filename: the .node file
  ///- poly_filename: the .poly file
  void ExecuteTriangleExe(
    std::string& node_filename,
    std::string& ele_filename,
    std::string& poly_filename,
    const Angle triangle_min_angle,
    const Area triangle_max_area,
    const bool verbose
  ) const;

  ///Executes TriangleCpp.exe from command line
  ///Creates three output files:
  ///- ele_filename: the .ele file
  ///- node_filename: the .node file
  ///- poly_filename: the .poly file
  void ExecuteTriangleCppExe(
    std::string& node_filename,
    std::string& ele_filename,
    std::string& poly_filename,
    const Angle triangle_min_angle,
    const Area triangle_max_area,
    const bool verbose
  ) const;

  ///Count the number of input edges
  int GetTriangleInputNedges() const noexcept;

  ///Count the number of input vertices
  int GetTriangleInputNvertices() const noexcept;

  const Polygons& GetShapes() const noexcept;

  ///Obtain the Triangle.exe input .poly file
  std::string GetTriangleInputPoly() const noexcept { return ToStr(); }

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

  ///Obtain the Triangle.exe input .poly file
  std::string ToStr() const noexcept;

  private:
  boost::shared_ptr<PolyFileFromPolygons> m_polyfile;

  static std::pair<int,char **> CreateArgv(const std::vector<std::string>& v) noexcept;
  static void DeleteArgv(const std::pair<int,char **>& p) noexcept;

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

  friend std::ostream& operator<<(std::ostream& os, const TriangleFile& f) noexcept;
};

std::ostream& operator<<(std::ostream& os, const TriangleFile& f) noexcept;

} //~namespace ribi

#endif // RIBI_TRIANGLEFILE_H

 

 

 

 

 

./CppTriangleFile/trianglefile.cpp

 

#include "trianglefile.h"

#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/make_shared.hpp>
#include <boost/units/io.hpp>

#include <QFile>

#include "fileio.h"
#include "geometry.h"
#include "polyfile.h"
#include "polyfilefrompolygons.h"
#include "testtimer.h"
#include "trace.h"

//#define TODO_ISSUE_207
#ifdef  TODO_ISSUE_207
#include "trianglecppmain.h"
#endif

#pragma GCC diagnostic pop

ribi::TriangleFile::TriangleFile(
  const Polygons& polygons,
  const Linestrings& linestrings
) : m_polyfile(boost::make_shared<PolyFileFromPolygons>(polygons,linestrings))
{
  #ifndef NDEBUG
  Test();
  #endif
}

std::pair<int,char **> ribi::TriangleFile::CreateArgv(const std::vector<std::string>& v) noexcept
{
  typedef char* String;
  typedef String* Strings;
  const int argc = static_cast<int>(v.size());
  const int buffer_sz = 256; //Will leak
  Strings argv = new String[256];
  for (int i=0; i!=argc; ++i)
  {
    argv[i] = new char[v[i].size() + 1];
    std::strcpy(argv[i],&v[i][0]);
  }
  for (int i=argc; i!=buffer_sz; ++i)
  {
    argv[i] = nullptr;
  }
  std::pair<int,char **> p = std::make_pair(argc,argv);
  return p;
}

void ribi::TriangleFile::DeleteArgv(const std::pair<int,char **>& p) noexcept
{
  const int argc = p.first;
  for(int i = 0; i != argc; ++i)
  {
    delete[] p.second[i];
  }
  delete[] p.second;
}

//#define TODO_ISSUE_207
#ifdef  TODO_ISSUE_207
void ribi::TriangleFile::ExecuteTriangle(
  std::string& node_filename,
  std::string& ele_filename,
  std::string& poly_filename,
  const double quality,
  const double area,
  const bool verbose) const
{
  const std::string filename { fileio::FileIo().GetTempFileName(".poly") };
  node_filename = "";
  ele_filename = "";
  poly_filename = "";

  {
    std::ofstream f(filename.c_str());
    m_polyfile << this->ToStr();
  }

  const std::string exe_filename { "triangle.exe" };


  //Specific
  const std::string quality_str = boost::lexical_cast<std::string>(quality);
  const std::string area_str = boost::lexical_cast<std::string>(area);
  assert(quality_str.find(',') == std::string::npos && "No Dutch please");
  assert(area_str.find(',')    == std::string::npos && "No Dutch please");
  std::vector<std::string> cmd {
    exe_filename,
    "-j",
    "-z",
    "-q",
    quality_str,
    "-a",
    area_str,
    filename
  };
  if (!verbose)
  {
    cmd.push_back("-Q");
    std::swap(cmd[cmd.size() - 1], cmd[cmd.size() - 2]);
  }
  const std::pair<int,char **> p = CreateArgv(cmd);
  triangle_main(p.first,p.second);
  DeleteArgv(p);
  //End of specific part

  const std::string filename_base(fileio::FileIo().GetFileBasename(filename));
  node_filename = filename_base + ".1.node";
  ele_filename = filename_base + ".1.ele";
  poly_filename = filename_base + ".1.poly";
  assert(fileio::FileIo().IsRegularFile(node_filename));
  assert(fileio::FileIo().IsRegularFile(ele_filename));
  assert(fileio::FileIo().IsRegularFile(poly_filename));
  fileio::FileIo().DeleteFile(filename);
}

void ribi::TriangleFile::ExecuteTriangleCpp(
  std::string& node_filename,
  std::string& ele_filename,
  std::string& poly_filename,
  const double quality,
  const double area,
  const bool verbose) const
{
  const std::string filename { fileio::FileIo().GetTempFileName(".poly") };
  node_filename = "";
  ele_filename = "";
  poly_filename = "";

  {
    std::ofstream f(filename.c_str());
    m_polyfile << this->ToStr();
  }

  assert(fileio::FileIo().IsRegularFile(filename));
  const std::string exe_filename { "triangle.exe" };

  //Specific
  const std::string quality_str = boost::lexical_cast<std::string>(quality);
  const std::string area_str = boost::lexical_cast<std::string>(area);
  assert(quality_str.find(',') == std::string::npos && "No Dutch please");
  assert(area_str.find(',')    == std::string::npos && "No Dutch please");
  std::vector<std::string> cmd {
    exe_filename,
    "-j",
    "-z",
    "-q",
    quality_str,
    "-a",
    area_str,
    filename
  };
  if (!verbose)
  {
    cmd.push_back("-Q");
    std::swap(cmd[cmd.size() - 1], cmd[cmd.size() - 2]);
  }
  tricpp::triangle_cpp_main(cmd);
  //End of specific part

  const std::string filename_base(fileio::FileIo().GetFileBasename(filename));
  node_filename = filename_base + ".1.node";
  ele_filename = filename_base + ".1.ele";
  poly_filename = filename_base + ".1.poly";
  assert(fileio::FileIo().IsRegularFile(node_filename));
  assert(fileio::FileIo().IsRegularFile(ele_filename));
  assert(fileio::FileIo().IsRegularFile(poly_filename));
  fileio::FileIo().DeleteFile(filename);
}
#endif // TODO_ISSUE_207

void ribi::TriangleFile::ExecuteTriangleExe(
  std::string& node_filename,
  std::string& ele_filename,
  std::string& poly_filename,
  const Angle triangle_min_angle, //Triangle calls this the 'quality' parameter
  const Area triangle_max_area,   //Triangle calls this the 'area' parameter
  const bool verbose) const
{
  const std::string filename { fileio::FileIo().GetTempFileNameSimple(".poly") };
  const bool delete_poly_file{false}; //True does not leave files scattered around
  node_filename = "";
  ele_filename = "";
  poly_filename = "";

  if (verbose)
  {
    std::stringstream s;
    s << "Writing .poly file to '" << filename << "'";
    std::clog << s.str() << std::endl;
  }
  {
    std::ofstream f(filename.c_str());
    f << (*m_polyfile);
    //f << std::fixed << std::setprecision(11) << (*m_polyfile);
  }

  assert(fileio::FileIo().IsRegularFile(filename));
  #ifdef _WIN32
  const std::string exe_filename { "ToolTriangleConsole.exe" };
  #else
  const std::string exe_filename { "ToolTriangleConsole" };
  #endif

  //Specific
  const std::string quality_str
    = boost::lexical_cast<std::string>(
        triangle_min_angle.value()
      * 360.0 / boost::math::constants::two_pi<double>() //Triangle uses degrees
    );

  //Prevent a '1.0e-5' notation as Triangle cannot handle it
  //Convert it to 0.00001 instead
  std::string area_str = "";
  {
    std::stringstream s;
    s << std::fixed
      //<< std::setprecision(20) //Works
      << std::setprecision(99) //Works
      << triangle_max_area.value()
    ;
    area_str = s.str();
  }

  assert(quality_str.find(',') == std::string::npos && "No Dutch please");
  assert(area_str.find(',')    == std::string::npos && "No Dutch please");
  if (!fileio::FileIo().IsRegularFile(exe_filename))
  {
    QFile file( (":/trianglefile/files/" + exe_filename).c_str() );
    file.copy(exe_filename.c_str());
  }
  assert(fileio::FileIo().IsRegularFile(exe_filename));

  #ifndef _WIN32
  {
    //Do chmod +x in Linux
    std::stringstream chmod;
    chmod
      << "chmod +x "
      << exe_filename;
    if (verbose) { std::cout << "Starting command '" << chmod.str() << "'" << std::endl; }
    const bool error = std::system(chmod.str().c_str());
    if (error)
    {
      if (verbose)
      {
        std::cout << "Finished command with an error (" << __FILE__ << "," << __LINE__ << ")" << std::endl
          << *m_polyfile << std::endl;
      }
      std::stringstream s;
      s << "Error: '" << chmod.str() << "' failed (" << __FILE__ << "," << __LINE__ << "), with error code " << error;
      throw std::runtime_error(s.str());
    }

  }
  #endif

  std::stringstream command;
  command
  #ifndef _WIN32
    << "./"
  #endif
    << exe_filename
    << " -j -z -q"
    << quality_str
    << " -a"
    << area_str
    << (verbose ? "" : " -Q")
    << " "
    << filename
  ;
  if (verbose) { std::cout << "Starting command '" << command.str() << "'" << std::endl; }
  const bool error = std::system(command.str().c_str());

  //Delete input file directly after running the program,
  //so that independent of a possible error this behavior occurs
  {
    if (delete_poly_file)
    {
      if (verbose) { std::cout << "Deleted file " << filename << std::endl; }
      fileio::FileIo().DeleteFile(filename);
    }
    else
    {
      if (verbose) { std::cout << "Kept file " << filename << " for inspection" << std::endl; }
    }
  }

  //Respond to errors
  if (error)
  {
    if (verbose)
    {
      std::cout << "Finished command with an error (" << __FILE__ << "," << __LINE__ << ")" << std::endl
        << *m_polyfile << std::endl;
    }
    std::stringstream s;
    s << "Error: '" << command.str() << "' failed (" << __FILE__ << "," << __LINE__ << "), with error code " << error;
    throw std::runtime_error(s.str());
  }
  if (verbose) { std::cout << "Finished command without errors" << std::endl; }
  //End of specific

  const std::string filename_base(fileio::FileIo().GetFileBasename(filename));
  node_filename = filename_base + ".1.node";
  ele_filename = filename_base + ".1.ele";
  poly_filename = filename_base + ".1.poly";
  assert(fileio::FileIo().IsRegularFile(node_filename));
  assert(fileio::FileIo().IsRegularFile(ele_filename));
  assert(fileio::FileIo().IsRegularFile(poly_filename));
}

int ribi::TriangleFile::GetTriangleInputNedges() const noexcept
{
  return m_polyfile ? m_polyfile->GetNedges() : 0;
}

int ribi::TriangleFile::GetTriangleInputNvertices() const noexcept
{
  return m_polyfile ? m_polyfile->GetNvertices() : 0;
}


const ribi::TriangleFile::Polygons& ribi::TriangleFile::GetShapes() const noexcept
{
  assert(m_polyfile);
  return m_polyfile->GetPolygons();
}


std::string ribi::TriangleFile::GetVersion() noexcept
{
  return "1.8";
}

std::vector<std::string> ribi::TriangleFile::GetVersionHistory() noexcept
{
  return {
    "2014-02-07: Version 1.0: initial version, use of Windows executable only",
    "2014-04-04: Version 1.1: allow to call Triangle its code directly",
    "2014-05-18: Version 1.2: allow use of a Linux executable"
    "2014-05-19: Version 1.3: use of non-freezing Windows executable",
    "2014-05-26: Version 1.4: use of units in Triangle 'area' (the maximum area of a triangle) and 'quality' (the minimum angle of a triangle's corner) parameters"
    "2014-05-27: Version 1.5: split of sections to PolyFile",
    "2014-06-02: Version 1.6: split of sections to PolyFileFromPolygons",
    "2014-06-25: Version 1.7: use fixed notation instead of scientific, as Triangle converts it incorrectly it",
    "2014-06-26: Version 1.8: added GetNvertices and GetNedges"
  };
}

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

  #ifdef FIX_ISSUE_231
  const bool verbose{false};
  //Call Triangle with simple shapes
  {
    const std::vector<Coordinat> points {
      {0.5, 2.0}, //0
      {1.0, 1.0}, //1
      {1.0, 0.0}, //2
      {0.0, 0.0}, //3
      {0.0, 1.0}  //4
    };
    Polygon v;
    boost::geometry::append(v, points);
    const Polygons polygons = { v };
    const Linestrings linestrings = {};
    const Shapes shapes = std::make_pair(polygons,linestrings);
    try
    {
      TriangleFile f(shapes);
      std::string filename_node;
      std::string filename_ele;
      std::string filename_poly;
      const Angle min_angle
        = 20.0 //Default used by Triangle, in degrees
        * (boost::math::constants::two_pi<double>() / 360.0)
        * boost::units::si::radian
      ;
      const Area max_area = 1.0 * boost::units::si::square_meter;
      //#define TODO_ISSUE_207
      #ifdef  TODO_ISSUE_207
      f.ExecuteTriangle(filename_node,filename_ele,filename_poly,min_angle,max_area,verbose);
      #else
      f.ExecuteTriangleExe(filename_node,filename_ele,filename_poly,min_angle,max_area,verbose);
      #endif // TODO_ISSUE_207
      assert(fileio::FileIo().IsRegularFile(filename_node));
      assert(fileio::FileIo().IsRegularFile(filename_ele));
      assert(fileio::FileIo().IsRegularFile(filename_poly));
    }
    catch (std::exception& e)
    {
      std::stringstream s;
      s
        << "TriangleFile::Test: "
        << "Triangle.exe failed: " << e.what()
      ;
      TRACE(s.str());
      std::exit(0); //TEMP for profiling
      assert(!"Should not get here");
    }
  }
  #endif // FIX_ISSUE_231

}
#endif

std::string ribi::TriangleFile::ToStr() const noexcept
{
  std::stringstream s;
  s << (*this);
  return s.str();
  //assert(m_polyfile);
  //return m_polyfile->ToStr();
}

std::ostream& ribi::operator<<(std::ostream& os, const TriangleFile& f) noexcept
{
  assert(f.m_polyfile);
  os << (*f.m_polyfile);
  return os;
}

 

 

 

 

 

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