You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
13 KiB
C

1 month ago
/*------------------------------------------------------------------------------
* Copyright (c) 2023 by Bai Bing (seread@163.com)
* See COPYING file for copying and redistribution conditions.
*
* Alians IT Studio.
*----------------------------------------------------------------------------*/
#pragma once
#include <filesystem>
#include <fstream>
#include <map>
#include <regex>
#include <string>
#include <sstream>
#include <utility>
#include <vector>
#include "_Define.h"
#include "core/UUID.h"
#include "utils/Misc.h"
#include "ASPoint.h"
namespace ais
{
template <typename PT = ais::Point>
class AIS_EXPORT Discontinuity
{
public:
Discontinuity() = default;
Discontinuity(const char *name_, std::vector<PT> points_) : name(name_), points(points_)
{
build_polygon_box();
};
Discontinuity(const char *contents)
{
std::istringstream ss(contents);
std::vector<std::string> lines;
for (std::string line; std::getline(ss, line);)
{
if (!line.empty())
lines.emplace_back(line);
}
if (!lines.empty())
{
auto header = lines[0];
auto tokens = ais::str_split_all(header, ",");
size_t pointCount = std::atoi(tokens[0].c_str());
bool nodataFlags = bool(std::atoi(tokens[1].c_str()));
if (pointCount == lines.size() - 1)
{
// ensure the header information is correct
name = tokens.size() > 2 ? tokens[2] : ais::uuid();
for (int i = 0; i < pointCount; i++)
{
points.emplace_back(PT(lines[i + 1].c_str()));
}
}
}
build_polygon_box();
}
~Discontinuity() = default;
inline virtual std::string type() const { return "Discontinuity"; }
std::string name;
std::vector<PT> points;
std::vector<PT> polygon; // points which can contain an enclosed polygon
std::vector<ais::Point> box; // outside box which to help quickly test for grid iteration
void build_polygon_box()
{
// build the polygon points if exists
auto i = points.begin();
auto di = points.end();
for (; i < di; ++i)
{
for (auto j = i + 1; j != di; ++j)
{
if (*i == *j)
{
di = j;
break;
}
}
if (di != points.end())
{
// the polygon begin and end point are same for an enclosed area
polygon = std::vector<PT>(i, di + 1);
break;
}
}
// build box for quick test
if (!points.empty())
{
auto xMinMax = getMinMax(getAxisValues(points, Axis3DType::X));
auto yMinMax = getMinMax(getAxisValues(points, Axis3DType::Y));
box = std::vector<ais::Point>{
{xMinMax.first, yMinMax.first},
{xMinMax.first, yMinMax.second},
{xMinMax.second, yMinMax.second},
{xMinMax.second, yMinMax.first},
{xMinMax.first, yMinMax.first},
};
}
}
const std::string str() const
{
std::stringstream ss;
ss << type() << ": " << name << std::endl;
ss << "-----------------------------------------------" << std::endl;
for (auto &p : points)
{
ss << p;
if (std::find(points.begin(), points.end(), p) != points.end())
ss << " P";
ss << std::endl;
}
return ss.str();
}
};
template <typename PT = ais::Point>
class AIS_EXPORT DiscontinuityFile
{
public:
DiscontinuityFile() = default;
DiscontinuityFile(const char *filename)
{
try
{
load(filename);
}
catch (const std::exception &e)
{
std::cerr << "Error loading " << filename << ", " << e.what() << std::endl;
}
}
//============================================================================
///
/// @param filename
///
/// @return count of loaded breaklines
///
virtual size_t load(const char *filename)
{
size_t lineCount = 0;
std::filesystem::path path(filename);
if (!std::filesystem::exists(filename))
{
std::string msg(filename);
msg += " is not existed!";
THROW_INVALID_ARGUMENT(msg.c_str());
}
std::string ext = path.extension().string();
ext = ais::tolower(ext);
if (ext == ".bln")
{
lineCount = load_bln_file(filename);
}
else if (ext == ".dfd")
{
lineCount = load_dfd_file(filename);
}
else
{
lineCount = load_dat_file(filename);
}
return lineCount;
}
inline virtual void clear() { discontinuities.clear(); };
ais::Discontinuity<PT> &operator[](const char *name) const { return discontinuities[name]; };
std::map<std::string, Discontinuity<PT>> discontinuities;
private:
virtual size_t load_bln_file(const char *filename)
{
std::ifstream file(filename);
if (!file.good())
{
std::string msg("Can't open file ");
msg += filename;
THROW_INVALID_ARGUMENT(msg.c_str());
}
bool inLine = false;
std::smatch m;
std::regex regHeader("^([\\d]+),([\\d]?)(.*?)$");
std::regex regexLine("([\\d\\-\\.]+)");
std::stringstream ss;
std::string line = "";
while (getline(file, line))
{
if (line[0] == '#' || line.empty())
{
// close current line information
inLine = false;
// transfer line informaiton
auto s = ss.str();
if (s.empty())
continue;
ais::Discontinuity<PT> discontinuity(s.c_str());
if (!discontinuity.points.empty())
{
discontinuities[discontinuity.name] = discontinuity;
}
ss.str("");
// skip comments and empty lines
continue;
}
if (!inLine && std::regex_match(line, m, regHeader))
{
// capture header line
inLine = true;
ss << line << std::endl;
continue;
}
if (inLine && std::regex_search(line, m, regexLine))
{
ss << line << std::endl;
continue;
}
}
// for finally line
auto s = ss.str();
if (!s.empty())
{
ais::Discontinuity<PT> discontinuity(s.c_str());
if (!discontinuity.points.empty())
{
discontinuities[discontinuity.name] = discontinuity;
}
}
return discontinuities.size();
}
virtual size_t load_dfd_file(const char *filename)
{
std::ifstream file(filename);
if (!file.good())
{
std::string msg("Can't open file ");
msg += filename;
THROW_INVALID_ARGUMENT(msg.c_str());
}
std::vector<std::string> skipRegexStrings = {"version", "HowToViewCurve", "Property", "Solid", "NoDraw", "#", "NameIn", "Times_New_Roman"};
bool inLine = false;
std::stringstream ss;
ss << std::fixed << std::setprecision(4);
std::string line = "";
size_t linePointCount = 0;
std::string lineName;
while (getline(file, line))
{
std::smatch m;
for (auto &rex : skipRegexStrings)
{
std::regex r(rex, std::regex_constants::icase);
if (std::regex_search(line, m, r))
{
// skip comments and empty lines
continue;
}
}
std::regex regexHeader("^pline(.*)", std::regex_constants::icase);
if (std::regex_search(line, m, regexHeader))
{
// line start
inLine = true;
lineName = m[1].str().substr(1);
linePointCount = 0;
ss.str(std::string());
continue;
}
std::regex regexLine("([\\d\\-\\.]+)");
if (inLine && std::regex_search(line, m, regexLine))
{
// line point information
ss << line << std::endl;
linePointCount++;
continue;
}
if (inLine && line.empty())
{
// line end and build discontinuity informaiton
std::string s = std::to_string(linePointCount) + ",1," + lineName + "\n";
s += ss.str();
// reset line information
inLine = false;
linePointCount = 0;
lineName = "";
ss.str(std::string());
// covert discontinuity to object
ais::Discontinuity<PT> discontinuity(s.c_str());
if (!discontinuity.points.empty())
{
discontinuities.emplace(discontinuity.name, discontinuity);
}
}
}
return discontinuities.size();
}
virtual size_t load_dat_file(const char *filename)
{
std::ifstream file(filename);
if (!file.good())
{
std::string msg("Can't open file ");
msg += filename;
THROW_INVALID_ARGUMENT(msg.c_str());
}
bool inLine = false;
std::stringstream ss;
ss << std::fixed << std::setprecision(4);
std::string line = "";
size_t linePointCount = 0;
std::string lineName;
while (getline(file, line))
{
if (line[0] == '#' || line.empty())
{
// skip comments and empty lines
continue;
}
auto tokens = ais::str_split_all(line, " ");
double x = 0, y = 0;
std::string flag;
std::string name;
if (tokens.size() != 4)
{
// skip line which may incorrectly
continue;
}
// line infomation
x = std::stod(tokens[0]);
y = std::stod(tokens[1]);
flag = tokens[2];
name = tokens[3];
if (!inLine && flag == "6")
{
// line start
inLine = true;
lineName = name;
ss.str(std::string());
// put point to line content
ss << x << "," << y << std::endl;
linePointCount = 1;
}
else if (inLine && flag == "7" && name == lineName)
{
// in line, put point to line content
ss << x << "," << y << std::endl;
linePointCount++;
}
else if (inLine && flag == "8" && name == lineName)
{
// line stop
ss << x << "," << y << std::endl;
linePointCount++;
inLine = false;
// build discontinuity informaiton
std::string s = std::to_string(linePointCount) + ",1," + lineName + "\n";
s += ss.str();
// reset line information
linePointCount = 0;
lineName = "";
ss.str(std::string());
// covert discontinuity to object
ais::Discontinuity<PT> discontinuity(s.c_str());
if (!discontinuity.points.empty())
{
discontinuities.emplace(discontinuity.name, discontinuity);
}
}
}
return discontinuities.size();
}
};
}