mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
462 lines
14 KiB
C++
462 lines
14 KiB
C++
#ifdef _MSC_VER
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <ctime>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include "clipper.hpp"
|
|
|
|
using namespace std;
|
|
using namespace ClipperLib;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SVGBuilder class
|
|
// a very simple class that creates an SVG image file
|
|
//---------------------------------------------------------------------------
|
|
|
|
class SVGBuilder
|
|
{
|
|
static string ColorToHtml(unsigned clr)
|
|
{
|
|
stringstream ss;
|
|
ss << '#' << hex << std::setfill('0') << setw(6) << (clr & 0xFFFFFF);
|
|
return ss.str();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static float GetAlphaAsFrac(unsigned clr)
|
|
{
|
|
return ((float)(clr >> 24) / 255);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
class StyleInfo
|
|
{
|
|
public:
|
|
PolyFillType pft;
|
|
unsigned brushClr;
|
|
unsigned penClr;
|
|
double penWidth;
|
|
bool showCoords;
|
|
|
|
StyleInfo()
|
|
{
|
|
pft = pftNonZero;
|
|
brushClr = 0xFFFFFFCC;
|
|
penClr = 0xFF000000;
|
|
penWidth = 0.8;
|
|
showCoords = false;
|
|
}
|
|
};
|
|
|
|
class PolyInfo
|
|
{
|
|
public:
|
|
Paths paths;
|
|
StyleInfo si;
|
|
|
|
PolyInfo(Paths paths, StyleInfo style)
|
|
{
|
|
this->paths = paths;
|
|
this->si = style;
|
|
}
|
|
};
|
|
|
|
typedef std::vector<PolyInfo> PolyInfoList;
|
|
|
|
private:
|
|
PolyInfoList polyInfos;
|
|
static const std::string svg_xml_start[];
|
|
static const std::string poly_end[];
|
|
|
|
public:
|
|
StyleInfo style;
|
|
|
|
void AddPaths(Paths& poly)
|
|
{
|
|
if (poly.size() == 0) return;
|
|
polyInfos.push_back(PolyInfo(poly, style));
|
|
}
|
|
|
|
bool SaveToFile(const string& filename, double scale = 1.0, int margin = 10)
|
|
{
|
|
//calculate the bounding rect ...
|
|
PolyInfoList::size_type i = 0;
|
|
Paths::size_type j;
|
|
while (i < polyInfos.size())
|
|
{
|
|
j = 0;
|
|
while (j < polyInfos[i].paths.size() &&
|
|
polyInfos[i].paths[j].size() == 0) j++;
|
|
if (j < polyInfos[i].paths.size()) break;
|
|
i++;
|
|
}
|
|
if (i == polyInfos.size()) return false;
|
|
|
|
IntRect rec;
|
|
rec.left = polyInfos[i].paths[j][0].X;
|
|
rec.right = rec.left;
|
|
rec.top = polyInfos[i].paths[j][0].Y;
|
|
rec.bottom = rec.top;
|
|
for ( ; i < polyInfos.size(); ++i)
|
|
for (Paths::size_type j = 0; j < polyInfos[i].paths.size(); ++j)
|
|
for (Path::size_type k = 0; k < polyInfos[i].paths[j].size(); ++k)
|
|
{
|
|
IntPoint ip = polyInfos[i].paths[j][k];
|
|
if (ip.X < rec.left) rec.left = ip.X;
|
|
else if (ip.X > rec.right) rec.right = ip.X;
|
|
if (ip.Y < rec.top) rec.top = ip.Y;
|
|
else if (ip.Y > rec.bottom) rec.bottom = ip.Y;
|
|
}
|
|
|
|
if (scale == 0) scale = 1.0;
|
|
if (margin < 0) margin = 0;
|
|
rec.left = (cInt)((double)rec.left * scale);
|
|
rec.top = (cInt)((double)rec.top * scale);
|
|
rec.right = (cInt)((double)rec.right * scale);
|
|
rec.bottom = (cInt)((double)rec.bottom * scale);
|
|
cInt offsetX = -rec.left + margin;
|
|
cInt offsetY = -rec.top + margin;
|
|
|
|
ofstream file;
|
|
file.open(filename);
|
|
if (!file.is_open()) return false;
|
|
file.setf(ios::fixed);
|
|
file.precision(0);
|
|
file << svg_xml_start[0] <<
|
|
((rec.right - rec.left) + margin*2) << "px" << svg_xml_start[1] <<
|
|
((rec.bottom - rec.top) + margin*2) << "px" << svg_xml_start[2] <<
|
|
((rec.right - rec.left) + margin*2) << " " <<
|
|
((rec.bottom - rec.top) + margin*2) << svg_xml_start[3];
|
|
setlocale(LC_NUMERIC, "C");
|
|
file.precision(2);
|
|
|
|
for (PolyInfoList::size_type i = 0; i < polyInfos.size(); ++i)
|
|
{
|
|
file << " <path d=\"";
|
|
for (Paths::size_type j = 0; j < polyInfos[i].paths.size(); ++j)
|
|
{
|
|
if (polyInfos[i].paths[j].size() < 3) continue;
|
|
file << " M " << ((double)polyInfos[i].paths[j][0].X * scale + offsetX) <<
|
|
" " << ((double)polyInfos[i].paths[j][0].Y * scale + offsetY);
|
|
for (Path::size_type k = 1; k < polyInfos[i].paths[j].size(); ++k)
|
|
{
|
|
IntPoint ip = polyInfos[i].paths[j][k];
|
|
double x = (double)ip.X * scale;
|
|
double y = (double)ip.Y * scale;
|
|
file << " L " << (x + offsetX) << " " << (y + offsetY);
|
|
}
|
|
file << " z";
|
|
}
|
|
file << poly_end[0] << ColorToHtml(polyInfos[i].si.brushClr) <<
|
|
poly_end[1] << GetAlphaAsFrac(polyInfos[i].si.brushClr) <<
|
|
poly_end[2] <<
|
|
(polyInfos[i].si.pft == pftEvenOdd ? "evenodd" : "nonzero") <<
|
|
poly_end[3] << ColorToHtml(polyInfos[i].si.penClr) <<
|
|
poly_end[4] << GetAlphaAsFrac(polyInfos[i].si.penClr) <<
|
|
poly_end[5] << polyInfos[i].si.penWidth << poly_end[6];
|
|
|
|
if (polyInfos[i].si.showCoords)
|
|
{
|
|
file << "<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">\n\n";
|
|
for (Paths::size_type j = 0; j < polyInfos[i].paths.size(); ++j)
|
|
{
|
|
if (polyInfos[i].paths[j].size() < 3) continue;
|
|
for (Path::size_type k = 0; k < polyInfos[i].paths[j].size(); ++k)
|
|
{
|
|
IntPoint ip = polyInfos[i].paths[j][k];
|
|
file << "<text x=\"" << (int)(ip.X * scale + offsetX) <<
|
|
"\" y=\"" << (int)(ip.Y * scale + offsetY) << "\">" <<
|
|
ip.X << "," << ip.Y << "</text>\n";
|
|
file << "\n";
|
|
}
|
|
}
|
|
file << "</g>\n";
|
|
}
|
|
}
|
|
file << "</svg>\n";
|
|
file.close();
|
|
setlocale(LC_NUMERIC, "");
|
|
return true;
|
|
}
|
|
}; //SVGBuilder
|
|
//------------------------------------------------------------------------------
|
|
|
|
const std::string SVGBuilder::svg_xml_start [] =
|
|
{"<?xml version=\"1.0\" standalone=\"no\"?>\n"
|
|
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"
|
|
"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\n"
|
|
"<svg width=\"",
|
|
"\" height=\"",
|
|
"\" viewBox=\"0 0 ",
|
|
"\" version=\"1.0\" xmlns=\"http://www.w3.org/2000/svg\">\n\n"
|
|
};
|
|
const std::string SVGBuilder::poly_end [] =
|
|
{"\"\n style=\"fill:",
|
|
"; fill-opacity:",
|
|
"; fill-rule:",
|
|
"; stroke:",
|
|
"; stroke-opacity:",
|
|
"; stroke-width:",
|
|
";\"/>\n\n"
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Miscellaneous function ...
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool SaveToFile(const string& filename, Paths &ppg, double scale = 1.0, unsigned decimal_places = 0)
|
|
{
|
|
ofstream ofs(filename);
|
|
if (!ofs) return false;
|
|
|
|
if (decimal_places > 8) decimal_places = 8;
|
|
ofs << setprecision(decimal_places) << std::fixed;
|
|
|
|
Path pg;
|
|
for (size_t i = 0; i < ppg.size(); ++i)
|
|
{
|
|
for (size_t j = 0; j < ppg[i].size(); ++j)
|
|
ofs << ppg[i][j].X / scale << ", " << ppg[i][j].Y / scale << "," << std::endl;
|
|
ofs << std::endl;
|
|
}
|
|
ofs.close();
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool LoadFromFile(Paths &ppg, const string& filename, double scale)
|
|
{
|
|
//file format assumes:
|
|
// 1. path coordinates (x,y) are comma separated (+/- spaces) and
|
|
// each coordinate is on a separate line
|
|
// 2. each path is separated by one or more blank lines
|
|
|
|
ppg.clear();
|
|
ifstream ifs(filename);
|
|
if (!ifs) return false;
|
|
string line;
|
|
Path pg;
|
|
while (std::getline(ifs, line))
|
|
{
|
|
stringstream ss(line);
|
|
double X = 0.0, Y = 0.0;
|
|
if (!(ss >> X))
|
|
{
|
|
//ie blank lines => flag start of next polygon
|
|
if (pg.size() > 0) ppg.push_back(pg);
|
|
pg.clear();
|
|
continue;
|
|
}
|
|
char c = ss.peek();
|
|
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma
|
|
if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma
|
|
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma
|
|
if (!(ss >> Y)) break; //oops!
|
|
pg.push_back(IntPoint((cInt)(X * scale),(cInt)(Y * scale)));
|
|
}
|
|
if (pg.size() > 0) ppg.push_back(pg);
|
|
ifs.close();
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
void MakeRandomPoly(int edgeCount, int width, int height, Paths & poly)
|
|
{
|
|
poly.resize(1);
|
|
poly[0].resize(edgeCount);
|
|
for (int i = 0; i < edgeCount; i++){
|
|
poly[0][i].X = rand() % width;
|
|
poly[0][i].Y = rand() % height;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool ASCII_icompare(const char* str1, const char* str2)
|
|
{
|
|
//case insensitive compare for ASCII chars only
|
|
while (*str1)
|
|
{
|
|
if (toupper(*str1) != toupper(*str2)) return false;
|
|
str1++;
|
|
str2++;
|
|
}
|
|
return (!*str2);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Main entry point ...
|
|
//------------------------------------------------------------------------------
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
if (argc > 1 &&
|
|
(strcmp(argv[1], "-b") == 0 || strcmp(argv[1], "--benchmark") == 0))
|
|
{
|
|
//do a benchmark test that creates a subject and a clip polygon both with
|
|
//100 vertices randomly placed in a 400 * 400 space. Then perform an
|
|
//intersection operation based on even-odd filling. Repeat all this X times.
|
|
int loop_cnt = 1000;
|
|
char * dummy;
|
|
if (argc > 2) loop_cnt = strtol(argv[2], &dummy, 10);
|
|
if (loop_cnt == 0) loop_cnt = 1000;
|
|
cout << "\nPerforming " << loop_cnt << " random intersection operations ... ";
|
|
srand((int)time(0));
|
|
int error_cnt = 0;
|
|
Paths subject, clip, solution;
|
|
Clipper clpr;
|
|
|
|
time_t time_start = clock();
|
|
for (int i = 0; i < loop_cnt; i++) {
|
|
MakeRandomPoly(100, 400, 400, subject);
|
|
MakeRandomPoly(100, 400, 400, clip);
|
|
clpr.Clear();
|
|
clpr.AddPaths(subject, ptSubject, true);
|
|
clpr.AddPaths(clip, ptClip, true);
|
|
if (!clpr.Execute(ctIntersection, solution, pftEvenOdd, pftEvenOdd))
|
|
error_cnt++;
|
|
}
|
|
double time_elapsed = double(clock() - time_start)/CLOCKS_PER_SEC;
|
|
|
|
cout << "\nFinished in " << time_elapsed << " secs with ";
|
|
cout << error_cnt << " errors.\n\n";
|
|
//let's save the very last result ...
|
|
SaveToFile("Subject.txt", subject);
|
|
SaveToFile("Clip.txt", clip);
|
|
SaveToFile("Solution.txt", solution);
|
|
|
|
//and see the final clipping op as an image too ...
|
|
SVGBuilder svg;
|
|
svg.style.penWidth = 0.8;
|
|
svg.style.pft = pftEvenOdd;
|
|
svg.style.brushClr = 0x1200009C;
|
|
svg.style.penClr = 0xCCD3D3DA;
|
|
svg.AddPaths(subject);
|
|
svg.style.brushClr = 0x129C0000;
|
|
svg.style.penClr = 0xCCFFA07A;
|
|
svg.AddPaths(clip);
|
|
svg.style.brushClr = 0x6080ff9C;
|
|
svg.style.penClr = 0xFF003300;
|
|
svg.style.pft = pftNonZero;
|
|
svg.AddPaths(solution);
|
|
svg.SaveToFile("solution.svg");
|
|
return 0;
|
|
}
|
|
|
|
if (argc < 3)
|
|
{
|
|
cout << "\nUsage:\n"
|
|
<< " clipper_console_demo S_FILE C_FILE CT [S_FILL C_FILL] [PRECISION] [SVG_SCALE]\n"
|
|
<< "or\n"
|
|
<< " clipper_console_demo --benchmark [LOOP_COUNT]\n\n"
|
|
<< "Legend: [optional parameters in square braces]; {comments in curly braces}\n\n"
|
|
<< "Parameters:\n"
|
|
<< " S_FILE & C_FILE are the subject and clip input files (see format below)\n"
|
|
<< " CT: cliptype, either INTERSECTION or UNION or DIFFERENCE or XOR\n"
|
|
<< " SUBJECT_FILL & CLIP_FILL: either EVENODD or NONZERO. Default: NONZERO\n"
|
|
<< " PRECISION (in decimal places) for input data. Default = 0\n"
|
|
<< " SVG_SCALE: scale of the output svg image. Default = 1.0\n"
|
|
<< " LOOP_COUNT is the number of random clipping operations. Default = 1000\n\n"
|
|
<< "\nFile format for input and output files:\n"
|
|
<< " X, Y[,] {first vertex of first path}\n"
|
|
<< " X, Y[,] {next vertex of first path}\n"
|
|
<< " {etc.}\n"
|
|
<< " X, Y[,] {last vertex of first path}\n"
|
|
<< " {blank line(s) between paths}\n"
|
|
<< " X, Y[,] {first vertex of second path}\n"
|
|
<< " X, Y[,] {next vertex of second path}\n"
|
|
<< " {etc.}\n\n"
|
|
<< "Examples:\n"
|
|
<< " clipper_console_demo \"subj.txt\" \"clip.txt\" INTERSECTION EVENODD EVENODD\n"
|
|
<< " clipper_console_demo --benchmark 1000\n";
|
|
return 1;
|
|
}
|
|
|
|
int scale_log10 = 0;
|
|
char* dummy;
|
|
if (argc > 6) scale_log10 = strtol(argv[6], &dummy, 10);
|
|
double scale = std::pow(double(10), scale_log10);
|
|
|
|
double svg_scale = 1.0;
|
|
if (argc > 7) svg_scale = strtod(argv[7], &dummy);
|
|
svg_scale /= scale;
|
|
|
|
Paths subject, clip;
|
|
|
|
if (!LoadFromFile(subject, argv[1], scale))
|
|
{
|
|
cerr << "\nCan't open the file " << argv[1]
|
|
<< " or the file format is invalid.\n";
|
|
return 1;
|
|
}
|
|
if (!LoadFromFile(clip, argv[2], scale))
|
|
{
|
|
cerr << "\nCan't open the file " << argv[2]
|
|
<< " or the file format is invalid.\n";
|
|
return 1;
|
|
}
|
|
|
|
ClipType clipType = ctIntersection;
|
|
const string sClipType[] = {"INTERSECTION", "UNION", "DIFFERENCE", "XOR"};
|
|
|
|
if (argc > 3)
|
|
{
|
|
if (ASCII_icompare(argv[3], "XOR")) clipType = ctXor;
|
|
else if (ASCII_icompare(argv[3], "UNION")) clipType = ctUnion;
|
|
else if (ASCII_icompare(argv[3], "DIFFERENCE")) clipType = ctDifference;
|
|
else clipType = ctIntersection;
|
|
}
|
|
|
|
PolyFillType subj_pft = pftNonZero, clip_pft = pftNonZero;
|
|
if (argc > 5)
|
|
{
|
|
if (ASCII_icompare(argv[4], "EVENODD")) subj_pft = pftEvenOdd;
|
|
if (ASCII_icompare(argv[5], "EVENODD")) clip_pft = pftEvenOdd;
|
|
}
|
|
|
|
Clipper c;
|
|
c.AddPaths(subject, ptSubject, true);
|
|
c.AddPaths(clip, ptClip, true);
|
|
Paths solution;
|
|
|
|
if (!c.Execute(clipType, solution, subj_pft, clip_pft))
|
|
{
|
|
cout << (sClipType[clipType] + " failed!\n\n");
|
|
return 1;
|
|
}
|
|
|
|
cout << "\nFinished!\n\n";
|
|
SaveToFile("solution.txt", solution, scale);
|
|
|
|
//let's see the result too ...
|
|
SVGBuilder svg;
|
|
svg.style.penWidth = 0.8;
|
|
svg.style.brushClr = 0x1200009C;
|
|
svg.style.penClr = 0xCCD3D3DA;
|
|
svg.style.pft = subj_pft;
|
|
svg.AddPaths(subject);
|
|
svg.style.brushClr = 0x129C0000;
|
|
svg.style.penClr = 0xCCFFA07A;
|
|
svg.style.pft = clip_pft;
|
|
svg.AddPaths(clip);
|
|
svg.style.brushClr = 0x6080ff9C;
|
|
svg.style.penClr = 0xFF003300;
|
|
svg.style.pft = pftNonZero;
|
|
svg.AddPaths(solution);
|
|
svg.SaveToFile("solution.svg", svg_scale);
|
|
|
|
//finally, show the svg image in the default viewing application
|
|
system("solution.svg");
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|