Add a C++ version of the check.tcl/check.sh logic. Idea is to allow generating overlap files independent of MGED and without requiring unix shells/commands.

svn:revision:75656
svn:branch:trunk
svn:account:starseeker
This commit is contained in:
Clifford Yapp committed 2020-05-01 13:33:08 +00:00
1 parent 83f47e3804
commit 8106d89cbd
2 files changed
+388

No files matched your search

+1
View File
@@ -16,6 +16,7 @@ BRLCAD_ADDDATA(gtransfer.c sample_applications)
BRLCAD_ADDEXEC(gex "gex.cpp" "libbu;librt")
BRLCAD_ADDEXEC(glint glint.cpp "librt;${M_LIBRARY}")
BRLCAD_ADDEXEC(gqa gqa.c "libged;librt")
BRLCAD_ADDEXEC(gchecker gchecker.cpp "libged;librt;libbu")
set(GSH_SRCS
gsh.cpp
+387
View File
@@ -0,0 +1,387 @@
/* G C H E C K E R . C P P
* BRL-CAD
*
* Copyright (c) 2020 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; see the file named COPYING for more
* information.
*/
/** @file gchecker.cpp
*
* C++ version of the check.tcl logic generating overlap files used
* by the MGED Overlap Tool
*
*/
#include "common.h"
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h> /* for mkdir */
#endif
#include <fstream>
#include <iostream>
#include <iomanip>
#include <regex>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "bu/app.h"
#include "bu/file.h"
#include "bu/opt.h"
#include "bu/path.h"
#include "ged.h"
static void
_cmd_help(const char *usage, struct bu_opt_desc *d)
{
struct bu_vls str = BU_VLS_INIT_ZERO;
char *option_help;
bu_vls_sprintf(&str, "%s", usage);
if ((option_help = bu_opt_describe(d, NULL))) {
bu_vls_printf(&str, "Options:\n%s\n", option_help);
bu_free(option_help, "help str");
}
bu_log("%s", bu_vls_cstr(&str));
bu_vls_free(&str);
}
int
main(int argc, const char **argv)
{
int print_help = 0;
int dry_run = 0;
int verbose = 0;
const char *usage = "Usage: gchecker [options] file.g [objects ...]\n\n";
struct bu_opt_desc d[4];
BU_OPT(d[0], "h", "help", "", NULL, &print_help, "Print help and exit");
BU_OPT(d[1], "d", "dry-run", "", NULL, &dry_run, "Step through the checker stages, but don't raytrace");
BU_OPT(d[2], "v", "verbose", "", NULL, &verbose, "Print verbose information about result processing");
BU_OPT_NULL(d[3]);
std::set<std::pair<std::string, std::string>> unique_pairs;
std::multimap<std::pair<std::string, std::string>, double> pair_sizes;
std::map<std::pair<std::string, std::string>, double> pair_avg_sizes;
bu_setprogname(argv[0]);
argc-=(argc>0); argv+=(argc>0);
/* must be wanting help */
if (argc < 1) {
_cmd_help(usage, d);
return 0;
}
/* parse standard options */
int opt_ret = bu_opt_parse(NULL, argc, argv, d);
if (print_help) {
_cmd_help(usage, d);
return 0;
}
/* adjust argc to match the leftovers of the options parsing */
argc = opt_ret;
if (argc < 1) {
_cmd_help(usage, d);
return 1;
}
char gqa_exe[MAXPATHLEN] = {0};
char mged_exe[MAXPATHLEN] = {0};
char rtcheck_exe[MAXPATHLEN] = {0};
bu_dir(gqa_exe, MAXPATHLEN, BU_DIR_BIN, "gqa", BU_DIR_EXT, NULL);
bu_dir(mged_exe, MAXPATHLEN, BU_DIR_BIN, "mged", BU_DIR_EXT, NULL);
bu_dir(rtcheck_exe, MAXPATHLEN, BU_DIR_BIN, "rtcheck", BU_DIR_EXT, NULL);
if (!bu_file_exists(gqa_exe, NULL)) {
bu_exit(1, "could not locate gqa executable");
}
if (!bu_file_exists(mged_exe, NULL)) {
bu_exit(1, "could not locate mged executable");
}
if (!bu_file_exists(rtcheck_exe, NULL)) {
bu_exit(1, "could not locate rtcheck executable");
}
// Have programs - see if we have the .g file
struct bu_vls gfile = BU_VLS_INIT_ZERO;
bu_vls_sprintf(&gfile, "%s", argv[0]);
if (!bu_file_exists(bu_vls_cstr(&gfile), NULL)) {
char fpgfile[MAXPATHLEN] = {0};
bu_dir(fpgfile, MAXPATHLEN, BU_DIR_CURR, bu_vls_cstr(&gfile), NULL);
bu_vls_sprintf(&gfile, "%s", fpgfile);
if (!bu_file_exists(bu_vls_cstr(&gfile), NULL)) {
bu_vls_free(&gfile);
bu_exit(1, "file %s does not exist", argv[0]);
}
}
struct bu_vls gbasename = BU_VLS_INIT_ZERO;
if (!bu_path_component(&gbasename, bu_vls_cstr(&gfile), BU_PATH_BASENAME)) {
bu_vls_free(&gfile);
bu_exit(1, "Could not identify basename in geometry file path \"%s\"", argv[0]);
}
struct bu_vls wdir = BU_VLS_INIT_ZERO;
bu_vls_printf(&wdir, "%s.ck", bu_vls_cstr(&gbasename));
if (bu_file_exists(bu_vls_cstr(&wdir), NULL)) {
bu_vls_free(&gfile);
bu_vls_free(&gbasename);
bu_exit(1, "Working directory\"%s\" already exists - remove to continue", bu_vls_cstr(&wdir));
}
if (dry_run) {
bu_log("(Note: dry run - skipping rtcheck)\n");
}
// Make the working directory
#ifdef HAVE_WINDOWS_H
CreateDirectory(bu_vls_cstr(&wdir), NULL);
#else
/* mode: 775 */
mkdir(bu_vls_cstr(&wdir), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
// Put a copy of the .g file in the working directory
std::ifstream sgfile(bu_vls_cstr(&gfile), std::ios::binary);
std::ofstream dgfile;
dgfile.open(bu_dir(NULL, 0, bu_vls_cstr(&wdir), bu_vls_cstr(&gbasename), NULL), std::ios::binary);
dgfile << sgfile.rdbuf();
dgfile.close();
sgfile.close();
// Change working directory
chdir(bu_vls_cstr(&wdir));
if (verbose) {
bu_log("Working on a copy in %s\n", bu_vls_cstr(&wdir));
}
// All set - open up the .g file and go to work.
struct ged *gedp = ged_open("db", bu_vls_cstr(&gbasename), 1);
if (gedp == GED_NULL) {
bu_exit(1, "Failed to open \"%s\" ", bu_vls_cstr(&gbasename));
}
// Make sure our reference counts are up to date, so we can tell
// which objects are top level
db_update_nref(gedp->ged_wdbp->dbip, &rt_uniresource);
std::vector<struct directory *> objs;
if (argc > 1) {
for (int i = 1; i < argc; i++) {
struct directory *dp = db_lookup(gedp->ged_wdbp->dbip, argv[i], LOOKUP_QUIET);
if (dp == RT_DIR_NULL) {
bu_exit(1, "Failed to open object \"%s\" in \"%s\" ", argv[i], bu_vls_cstr(&gbasename));
}
objs.push_back(dp);
}
} else {
// Get all top level objects
struct directory **all_paths;
int obj_cnt = db_ls(gedp->ged_wdbp->dbip, DB_LS_TOPS, NULL, &all_paths);
for (int i = 0; i < obj_cnt; i++) {
objs.push_back(all_paths[i]);
}
bu_free(all_paths, "free db_tops output");
}
if (objs.size() == 1) {
bu_log("Processing tops object: %s\n", objs[0]->d_namep);
} else {
bu_log("Processing tops objects:\n");
for (size_t i = 0; i < objs.size(); i++) {
bu_log(" %s\n", objs[i]->d_namep);
}
}
int total_views = 0;
for (size_t i = 0; i < objs.size(); i++) {
for (int az = 0; az < 180; az+=45) {
for (int el = 0; el < 180; el+=45) {
total_views++;
}
}
}
if (total_views != (int)(16 * objs.size())) {
bu_exit(1, "view incrementing error\n");
}
// Run rtcheck equiv.
if (!dry_run) {
std::regex oregex("<(.*),.(.*)>: ([0-9]*).* (.*).mm");
for (size_t i = 0; i < objs.size(); i++) {
for (int az = 0; az < 180; az+=45) {
for (int el = 0; el < 180; el+=45) {
struct bu_vls str = BU_VLS_INIT_ZERO;
const char **av = (const char **)bu_calloc(8, sizeof(char *), "cmd array");
av[0] = bu_strdup("check");
av[1] = bu_strdup("overlaps");
av[2] = bu_strdup("-G1024");
bu_vls_sprintf(&str, "-a%d", az);
av[3] = bu_strdup(bu_vls_cstr(&str));
bu_vls_sprintf(&str, "-e%d", el);
av[4] = bu_strdup(bu_vls_cstr(&str));
av[5] = bu_strdup("-q");
av[6] = bu_strdup(objs[i]->d_namep);
bu_vls_trunc(gedp->ged_result_str, 0);
if (ged_check(gedp, 7, av) != GED_OK) {
bu_exit(1, "error running ged_check\n");
}
for (int j = 0; j < 7; j++) bu_free((void *)av[j], "str");
bu_free(av, "av array");
bu_vls_free(&str);
// Split up results into something we can process with regex
std::istringstream sres(bu_vls_cstr(gedp->ged_result_str));
std::string line;
while (std::getline(sres, line)) {
std::smatch nvar;
if (!std::regex_search(line, nvar, oregex) || nvar.size() != 5) {
continue;
}
if (verbose) {
bu_log("%zd: %s\n", nvar.size(), line.c_str());
for (size_t m = 0; m < nvar.size(); m++) {
bu_log(" %zd: %s\n", m, nvar.str(m).c_str());
}
}
std::pair<std::string, std::string> key;
// sort left and right strings lexicographically to produce unique pairing keys
key = (nvar.str(1) < nvar.str(2)) ? std::make_pair(nvar.str(1), nvar.str(2)) : std::make_pair(nvar.str(2), nvar.str(1));
// size = count * depth
double val = std::stod(nvar.str(3)) * std::stod(nvar.str(4));
unique_pairs.insert(key);
pair_sizes.insert(std::make_pair(key, val));
if (verbose) {
bu_log("Inserting: %s,%s -> %f\n", key.first.c_str(), key.second.c_str(), val);
}
}
}
}
}
// Run gqa equiv.
for (size_t i = 0; i < objs.size(); i++) {
for (int az = 0; az < 180; az+=45) {
for (int el = 0; el < 180; el+=45) {
const char **av = (const char **)bu_calloc(6, sizeof(char *), "cmd array");
av[0] = bu_strdup("check");
av[1] = bu_strdup("overlaps");
av[2] = bu_strdup("-g1mm,1mm");
av[3] = bu_strdup("-q");
av[4] = bu_strdup(objs[i]->d_namep);
bu_vls_trunc(gedp->ged_result_str, 0);
if (ged_check(gedp, 5, av) != GED_OK) {
bu_exit(1, "error running ged_check\n");
}
for (int j = 0; j < 5; j++) bu_free((void *)av[j], "str");
bu_free(av, "av array");
// Split up results into something we can process with regex
std::istringstream sres(bu_vls_cstr(gedp->ged_result_str));
std::string line;
while (std::getline(sres, line)) {
std::smatch nvar;
if (!std::regex_search(line, nvar, oregex) || nvar.size() != 5) {
continue;
}
if (verbose) {
bu_log("%zd: %s\n", nvar.size(), line.c_str());
for (size_t m = 0; m < nvar.size(); m++) {
bu_log(" %zd: %s\n", m, nvar.str(m).c_str());
}
}
std::pair<std::string, std::string> key;
// sort left and right strings lexicographically to produce unique pairing keys
key = (nvar.str(1) < nvar.str(2)) ? std::make_pair(nvar.str(1), nvar.str(2)) : std::make_pair(nvar.str(2), nvar.str(1));
// size = count * depth
double val = std::stod(nvar.str(3)) * std::stod(nvar.str(4));
unique_pairs.insert(key);
pair_sizes.insert(std::make_pair(key, val));
if (verbose) {
bu_log("Inserting: %s,%s -> %f\n", key.first.c_str(), key.second.c_str(), val);
}
}
}
}
}
}
if (verbose) {
bu_log("Found %zd unique pairings: \n", unique_pairs.size());
}
std::set<std::pair<std::string, std::string>>::iterator p_it;
for (p_it = unique_pairs.begin(); p_it != unique_pairs.end(); p_it++) {
if (verbose) {
bu_log(" %s + %s: \n", p_it->first.c_str(), p_it->second.c_str());
}
// For each pairing, get the average size
size_t scnt = pair_sizes.count(*p_it);
double ssum = 0.0;
if (verbose) {
bu_log(" Have %zd sizes: \n", scnt);
}
std::multimap<std::pair<std::string, std::string>, double>::iterator s_it;
for (s_it = pair_sizes.equal_range(*p_it).first; s_it != pair_sizes.equal_range(*p_it).second; s_it++) {
double s = (*s_it).second;
ssum += s;
if (verbose) {
bu_log(" %f \n", s);
}
}
if (verbose) {
bu_log(" Avg: %f\n", ssum/(double)scnt);
}
pair_avg_sizes[*p_it] = ssum/(double)scnt;
}
std::string ofile = std::string("ck.") + std::string(bu_vls_cstr(&gbasename)) + std::string(".overlaps");
std::ofstream of(ofile);
std::map<std::pair<std::string, std::string>, double>::iterator a_it;
for (a_it = pair_avg_sizes.begin(); a_it != pair_avg_sizes.end(); a_it++) {
bu_log("%s + %s: %f\n", a_it->first.first.c_str(), a_it->first.second.c_str(), a_it->second);
of << a_it->first.first.c_str() << " " << a_it->first.second.c_str() << " " << std::fixed << std::setprecision(5) << a_it->second << "\n";
}
of.close();
// Remove the copy of the .g file
std::remove(bu_vls_cstr(&gbasename));
// Clean up
bu_vls_free(&gfile);
bu_vls_free(&gbasename);
bu_vls_free(&wdir);
}
// Local Variables:
// tab-width: 8
// mode: C++
// c-basic-offset: 4
// indent-tabs-mode: t
// c-file-style: "stroustrup"
// End:
// ex: shiftwidth=4 tabstop=8