mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-15 16:18:22 +01:00
32fbc5ab Merge commit '050663209be623e2af35e3f93a41ac6f5ac1f18f' 05066320 Squashed 'phreeqcpp/' changes from 93ab5c9..82515f7 git-subtree-dir: src git-subtree-split: 32fbc5ab56eb19fff92831e32cbac7f01e1cdf56
1383 lines
32 KiB
C++
1383 lines
32 KiB
C++
// ChartObject.cpp: implementation of the ChartObject class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
#ifdef MULTICHART
|
|
#include "Phreeqc.h"
|
|
#ifdef _DEBUG
|
|
#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger)
|
|
#endif
|
|
#include "Utils.h"
|
|
#include <iostream>
|
|
#include "ChartObject.h"
|
|
#include "Parser.h"
|
|
#include <fstream>
|
|
#include <cmath>
|
|
#include <iomanip>
|
|
#include "phqalloc.h"
|
|
|
|
#include "Form1.h"
|
|
using namespace zdg_ui2;
|
|
|
|
#if defined(PHREEQCI_GUI)
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
ChartObject::ChartObject(PHRQ_io *io)
|
|
//
|
|
// default constructor for cxxExchComp
|
|
//
|
|
:
|
|
cxxNumKeyword(io)
|
|
{
|
|
new_ug = false;
|
|
FirstCallToUSER_GRAPH = true;
|
|
|
|
update_time_chart = 150;
|
|
PanelHeight = 510;
|
|
PanelWidth = 640;
|
|
|
|
Symbol_map["Square"] = 0;
|
|
Symbol_map["Diamond"] = 1;
|
|
Symbol_map["Triangle"] = 2;
|
|
Symbol_map["Circle"] = 3;
|
|
Symbol_map["XCross"] = 4;
|
|
Symbol_map["Plus"] = 5;
|
|
Symbol_map["Star"] = 6;
|
|
Symbol_map["TriangleDown"] = 7;
|
|
Symbol_map["HDash"] = 8;
|
|
Symbol_map["VDash"] = 9;
|
|
Symbol_map["None"] = 10;
|
|
|
|
Color_vector.push_back("Red");
|
|
Color_vector.push_back("Green");
|
|
Color_vector.push_back("Blue");
|
|
Color_vector.push_back("Orange");
|
|
Color_vector.push_back("Magenta");
|
|
// Color_vector.push_back("Yellow");
|
|
Color_vector.push_back("Black");
|
|
Color_vector.push_back("Cyan");
|
|
Color_vector.push_back("Brown");
|
|
Color_vector.push_back("Lime");
|
|
Color_vector.push_back("Gray");
|
|
|
|
chart_title.clear();
|
|
axis_titles.clear();
|
|
|
|
int i;
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
axis_scale_x[i] = NA;
|
|
axis_scale_y[i] = NA;
|
|
axis_scale_y2[i] = NA;
|
|
}
|
|
|
|
chart_type = 0;
|
|
graph_initial_solutions = false;
|
|
shifts_as_points = false;
|
|
connect_simulations = true;
|
|
colnr = 0;
|
|
ColumnOffset = 0;
|
|
prev_advection_step = 0;
|
|
prev_transport_step = 0;
|
|
prev_sim_no = 0;
|
|
end_timer = false;
|
|
done = false;
|
|
|
|
curve_added = false;
|
|
point_added = false;
|
|
|
|
user_graph = new rate;
|
|
user_graph->commands.clear();
|
|
user_graph->name = NULL;
|
|
user_graph->new_def = 0;
|
|
user_graph->linebase = user_graph->loopbase = user_graph->varbase = NULL;
|
|
|
|
default_symbol = 0;
|
|
default_symbol_csv = 0;
|
|
default_color = 0;
|
|
default_color_csv = 0;
|
|
|
|
graph_x = NA;
|
|
graph_y.clear();
|
|
secondary_y.clear();
|
|
|
|
usingResource = 0;
|
|
form_started = false;
|
|
active = true;
|
|
detach = false;
|
|
|
|
batch_background = true;
|
|
batch_grid = true;
|
|
batch = ChO_NO_BATCH;
|
|
}
|
|
|
|
ChartObject::~ChartObject()
|
|
{
|
|
while (0 != System::Threading::Interlocked::CompareExchange(this->usingResource, 7, 0))
|
|
{
|
|
System::Threading::Thread::Sleep(5);
|
|
}
|
|
this->Rate_free();
|
|
delete this->user_graph;
|
|
|
|
std::vector<CurveObject *>::iterator it;
|
|
for (it = this->CurvesCSV.begin(); it != CurvesCSV.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
|
|
for (it = this->Curves.begin(); it != Curves.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
|
|
bool
|
|
ChartObject::Set_axis_scale(CParser & parser)
|
|
{
|
|
std::istream::pos_type ptr;
|
|
std::istream::pos_type next_char;
|
|
std::string token;
|
|
LDBLE *scale_ptr = NULL;
|
|
std::vector<std::string> string_vector;
|
|
size_t j = 0;
|
|
|
|
// rereads option
|
|
parser.copy_token(token, next_char);
|
|
|
|
// reads parameters
|
|
while ((j < 6) && (parser.copy_token(token, next_char)) != CParser::TT_EMPTY)
|
|
{
|
|
string_vector.push_back(token);
|
|
j++;
|
|
}
|
|
if (string_vector.size() == 0)
|
|
{
|
|
error_msg("No axis defined for scales", PHRQ_io::OT_CONTINUE);
|
|
return false;
|
|
}
|
|
std::string str = string_vector[0];
|
|
std::string type;
|
|
Utilities::str_tolower(str);
|
|
if (str.substr(0,2) == "y2" || str.substr(0,1) == "s")
|
|
{
|
|
type = "sy";
|
|
scale_ptr = this->axis_scale_y2;
|
|
}
|
|
else if (str.substr(0,1) == "y")
|
|
{
|
|
type = "y";
|
|
scale_ptr = this->axis_scale_y;
|
|
}
|
|
else if (str.substr(0,1) == "x")
|
|
{
|
|
type = "x";
|
|
scale_ptr = this->axis_scale_x;
|
|
}
|
|
else
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Found " << str;
|
|
estream << ", but expect axis type \'x\', \'y\', \'y2\'or \'sy\'.";
|
|
estream << "\n";
|
|
error_msg(estream.str().c_str(), PHRQ_io::OT_CONTINUE);
|
|
return false;
|
|
}
|
|
|
|
for (j = 1; j < string_vector.size() && j < 5; j++)
|
|
{
|
|
std::string s = string_vector[j];
|
|
if (s[0] == 'a' || s[0] == 'A')
|
|
{
|
|
scale_ptr[j - 1] = NA;
|
|
}
|
|
else if (CParser::token_type(s) == CParser::TT_DIGIT)
|
|
{
|
|
scale_ptr[j - 1] = atof(s.c_str());
|
|
}
|
|
else
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Found " << s;
|
|
estream << ", but expect number or 'a(uto)'.";
|
|
estream << "\n";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
return false;
|
|
}
|
|
}
|
|
if (string_vector.size() == 6)
|
|
{
|
|
std::string s = string_vector[5];
|
|
if (s[0] == 't' || s[0] == 'T' || s[0] == 'l' || s[0] == 'L')
|
|
{
|
|
scale_ptr[4] = 10.0;
|
|
if (((fabs(scale_ptr[0] - NA) > 1e-3) && scale_ptr[0] <=0) ||
|
|
((fabs(scale_ptr[1] - NA) > 1e-3) && scale_ptr[1] <=0))
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "MIN and MAX must be > 0 for log " << type << "-scale.";
|
|
estream << "\n";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
}
|
|
if ((fabs(scale_ptr[0] - NA) > 1e-3) && (fabs(scale_ptr[1] - NA) > 1e-3))
|
|
{
|
|
if (scale_ptr[0] > scale_ptr[1])
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Maximum must be larger than minimum of axis_scale " << type << "-scale." << "\n";
|
|
estream << "Switching values for MIN and MAX. " << "\n";
|
|
warning_msg(estream.str().c_str());
|
|
LDBLE t;
|
|
t = scale_ptr[0];
|
|
scale_ptr[0] = scale_ptr[1];
|
|
scale_ptr[1] = scale_ptr[0];
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ChartObject::Read(CParser & parser)
|
|
{
|
|
|
|
std::istream::pos_type ptr;
|
|
std::istream::pos_type next_char;
|
|
std::string token;
|
|
int opt_save;
|
|
bool useLastLine(false);
|
|
bool new_command_lines(false);
|
|
// if no definitions in USER_GRAPH data block, deactivate plot
|
|
bool no_def(true);
|
|
if (this->FirstCallToUSER_GRAPH)
|
|
{
|
|
this->new_ug = true;
|
|
}
|
|
else
|
|
{
|
|
this->ColumnOffset = (int) this->Curves.size();
|
|
this->new_ug = true;
|
|
}
|
|
this->new_plotxy_curves.clear();
|
|
|
|
// Read number and description
|
|
{
|
|
this->read_number_description(parser);
|
|
}
|
|
|
|
opt_save = CParser::OPT_DEFAULT;
|
|
|
|
for (;;)
|
|
{
|
|
int opt;
|
|
if (useLastLine == false)
|
|
{
|
|
opt = parser.get_option(vopts, next_char);
|
|
}
|
|
else
|
|
{
|
|
opt = parser.getOptionFromLastLine(vopts, next_char, true);
|
|
}
|
|
if (opt == CParser::OPT_DEFAULT)
|
|
{
|
|
opt = opt_save;
|
|
}
|
|
switch (opt)
|
|
{
|
|
case CParser::OPT_EOF:
|
|
break;
|
|
case CParser::OPT_KEYWORD:
|
|
break;
|
|
case CParser::OPT_ERROR:
|
|
opt = CParser::OPT_EOF;
|
|
error_msg("Unknown input in USER_GRAPH keyword.", CONTINUE);
|
|
error_msg(parser.line().c_str(), CONTINUE);
|
|
useLastLine = false;
|
|
break;
|
|
case 0: /* start */
|
|
opt_save = CParser::OPT_DEFAULT;
|
|
break;
|
|
case 1: /* end */
|
|
opt_save = CParser::OPT_DEFAULT;
|
|
break;
|
|
case 2: /* headings */
|
|
case 3: /* heading */
|
|
this->new_headings.clear();
|
|
while (parser.copy_token(token, next_char) != CParser::TT_EMPTY)
|
|
{
|
|
this->new_headings.push_back(token);
|
|
}
|
|
this->headings_original = this->new_headings;
|
|
break;
|
|
case 4: /* chart title */
|
|
{
|
|
std::string tok;
|
|
parser.get_rest_of_line(tok);
|
|
std::string::iterator b = tok.begin();
|
|
std::string::iterator e = tok.end();
|
|
CParser::copy_title(this->chart_title, b, e);
|
|
}
|
|
break;
|
|
case 5: /* axis titles */
|
|
{
|
|
this->axis_titles.clear();
|
|
std::string l;
|
|
parser.get_rest_of_line(l);
|
|
std::string tok;
|
|
std::string::iterator b = l.begin();
|
|
std::string::iterator e = l.end();
|
|
this->axis_titles.clear();
|
|
while(parser.copy_title(tok, b, e) != CParser::TT_EMPTY)
|
|
{
|
|
this->axis_titles.push_back(tok);
|
|
}
|
|
}
|
|
while (this->axis_titles.size() < 3) this->axis_titles.push_back("");
|
|
break;
|
|
case 6: /* axis scales */
|
|
{
|
|
this->Set_axis_scale(parser);
|
|
}
|
|
break;
|
|
case 7: /* initial_solutions */
|
|
this->graph_initial_solutions = parser.get_true_false(next_char, FALSE);
|
|
break;
|
|
case 8: /* plot_concentration_vs */
|
|
parser.copy_token(token, next_char);
|
|
Utilities::str_tolower(token);
|
|
if (token[0] == 'x' || token[0] == 'd')
|
|
chart_type = 0;
|
|
else if (token[0] == 't')
|
|
chart_type = 1;
|
|
else
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Found " << token << ", but expect plot type: (\'x\' or \'dist\') for distance, (\'t\') for time.";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
}
|
|
break;
|
|
case 9: /* shifts_as_points */
|
|
this->shifts_as_points = parser.get_true_false(next_char, true);
|
|
if (this->shifts_as_points)
|
|
this->chart_type = 0;
|
|
else
|
|
this->chart_type = 1;
|
|
break;
|
|
case 10: /* grid_offset */
|
|
break;
|
|
case 11: /* connect_simulations */
|
|
this->connect_simulations = parser.get_true_false(next_char, true);
|
|
break;
|
|
case 12: /* plot_csv_file */
|
|
case 17: /* plot_tsv_file */
|
|
{
|
|
std::string file_name;
|
|
parser.get_rest_of_line(file_name);
|
|
file_name = trim(file_name);
|
|
this->OpenCSVFile(file_name);
|
|
this->csv_file_names.push_back(file_name);
|
|
}
|
|
break;
|
|
case 13: /* clear */
|
|
case 14: /* detach */
|
|
this->detach = true;
|
|
break;
|
|
/* End of modifications */
|
|
case 15: /* active */
|
|
this->active = parser.get_true_false(next_char, true);
|
|
if (this->active && this->Get_connect_simulations())
|
|
{
|
|
this->ColumnOffset = 0;//this->Curves.size();
|
|
this->new_ug = false;
|
|
}
|
|
break;
|
|
/* End of modifications */
|
|
case 16: /* batch */
|
|
{
|
|
this->batch = ChartObject::ChO_BATCH_ONLY;
|
|
std::string rest_of_line, lc_rest_of_line;
|
|
parser.get_rest_of_line(rest_of_line);
|
|
lc_rest_of_line = rest_of_line;
|
|
Utilities::str_tolower(lc_rest_of_line);
|
|
|
|
std::vector<std::string> file_types;
|
|
file_types.push_back(".emf");
|
|
file_types.push_back(".png");
|
|
file_types.push_back(".jpg");
|
|
file_types.push_back(".gif");
|
|
file_types.push_back(".tiff");
|
|
file_types.push_back(".bmp");
|
|
file_types.push_back(".jpeg");
|
|
|
|
size_t first, last;
|
|
size_t i;
|
|
for (i = 0; i < file_types.size(); i++)
|
|
{
|
|
first = lc_rest_of_line.rfind(file_types[i].c_str());
|
|
if (first != std::string::npos)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i >= file_types.size())
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Batch file name must have suffix emf, png, jpg, jpeg, gif, tiff, or bmp.";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
break;
|
|
}
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
this->batch = ChartObject::ChO_EMF;
|
|
last = first + 4;
|
|
break;
|
|
case 1:
|
|
this->batch = ChartObject::ChO_PNG;
|
|
last = first + 4;
|
|
break;
|
|
case 2:
|
|
this->batch = ChartObject::ChO_JPG;
|
|
last = first + 4;
|
|
break;
|
|
case 3:
|
|
this->batch = ChartObject::ChO_GIF;
|
|
last = first + 4;
|
|
break;
|
|
case 4:
|
|
this->batch = ChartObject::ChO_TIFF;
|
|
last = first + 5;
|
|
break;
|
|
case 5:
|
|
this->batch = ChartObject::ChO_BMP;
|
|
last = first + 4;
|
|
break;
|
|
case 6:
|
|
this->batch = ChartObject::ChO_JPG;
|
|
last = first + 5;
|
|
break;
|
|
}
|
|
|
|
this->batch_fn = rest_of_line.substr(0, last);
|
|
if (last+1 < rest_of_line.size())
|
|
{
|
|
token = rest_of_line.substr(last);
|
|
token = trim(token);
|
|
std::string tf;
|
|
std::string::iterator b = token.begin();
|
|
std::string::iterator e = token.end();
|
|
|
|
CParser::copy_token(tf, b, e);
|
|
if (tf.size() > 0)
|
|
{
|
|
Utilities::str_tolower(tf);
|
|
if (tf[0] == 'f')
|
|
{
|
|
this->batch_background = false;
|
|
}
|
|
}
|
|
CParser::copy_token(tf, b, e);
|
|
if (tf.size() > 0)
|
|
{
|
|
Utilities::str_tolower(tf);
|
|
if (tf[0] == 'f')
|
|
{
|
|
this->batch_grid = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CParser::OPT_DEFAULT: // Read Basic commands
|
|
{
|
|
if (!new_command_lines)
|
|
{
|
|
this->rate_command_list.clear();
|
|
this->rate_command_list_original.clear();
|
|
new_command_lines = true;
|
|
}
|
|
this->rate_new_def = true;
|
|
/* read command */
|
|
std::string cmd(parser.line());
|
|
this->rate_command_list_original.push_back(cmd);
|
|
std::string cmd_lower = cmd;
|
|
Utilities::str_tolower(cmd_lower);
|
|
if ((cmd_lower.find("graph_y") != std::string::npos) ||
|
|
(cmd_lower.find("graph_sy") != std::string::npos))
|
|
{
|
|
//Number of curves not known here
|
|
//Curves are created in Basic cmds
|
|
}
|
|
if (cmd_lower.find("plot_xy") != std::string::npos)
|
|
{
|
|
//Curves are created in Basic cmds
|
|
CurveObject new_curve = ExtractCurveInfo(cmd); // truncates cmd
|
|
// Add to list of new plotxy curves
|
|
this->new_plotxy_curves.push_back(new_curve);
|
|
}
|
|
this->rate_command_list.push_back(cmd);
|
|
}
|
|
opt_save = CParser::OPT_DEFAULT;
|
|
break;
|
|
}
|
|
if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD)
|
|
break;
|
|
no_def = false;
|
|
}
|
|
|
|
// install new plotxy commands
|
|
// disable this user_graph if USER_GRAPH block is empty
|
|
//if (new_command_lines || no_def) this->Set_rate_struct();
|
|
if (new_command_lines) this->Set_rate_struct();
|
|
if (no_def) this->Set_active(false);
|
|
return true;
|
|
}
|
|
bool
|
|
ChartObject::OpenCSVFile(std::string file_name)
|
|
{
|
|
std::string token;
|
|
|
|
std::ifstream f_csv(file_name.c_str(), std::ifstream::in);
|
|
|
|
if (!f_csv.is_open())
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Could not open tsv file for USER_GRAPH " << file_name << "\n Please, give the full path + filename.";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
return false;
|
|
}
|
|
|
|
CParser parser(f_csv, this->Get_io());
|
|
parser.set_echo_file(CParser::EO_NONE);
|
|
|
|
/* Get lines */
|
|
int linenr = 0;
|
|
|
|
// temporary storage for new CSV curves
|
|
std::vector<std::string> headings;
|
|
std::vector<CurveObject *> csv_curves;
|
|
|
|
// Read file line by line into temporary curves
|
|
while (parser.check_line("cvs file", false, true, true, false) != PHRQ_io::LT_EOF)
|
|
{
|
|
// Headings line
|
|
if (linenr == 0)
|
|
{
|
|
// Skip x in 1st column
|
|
parser.get_iss() >> token;
|
|
|
|
// Read rest of headings
|
|
while (parser.get_iss() >> token)
|
|
{
|
|
headings.push_back(token);
|
|
}
|
|
// add curves to temporary csv_curves
|
|
size_t i;
|
|
for (i = 0; i < headings.size(); i++)
|
|
{
|
|
CurveObject *c = new CurveObject;
|
|
c->Set_id(headings[i]);
|
|
c->Set_line_w(0);
|
|
std::string sym = "";
|
|
this->Get_legal_symbol_csv(sym);
|
|
c->Set_symbol(sym);
|
|
csv_curves.push_back(c);
|
|
}
|
|
|
|
linenr++;
|
|
continue;
|
|
}
|
|
|
|
token = parser.line();
|
|
std::string tok1;
|
|
CParser::TOKEN_TYPE tt = CParser::parse_delimited(token, tok1, "\t");
|
|
|
|
// Curve properties lines
|
|
if (linenr < 6 && tt != CParser::TT_DIGIT)
|
|
{
|
|
Utilities::str_tolower(tok1);
|
|
std::string tok2;
|
|
size_t i=0;
|
|
while (token.size() != 0 && i < csv_curves.size())
|
|
{
|
|
CParser::parse_delimited(token, tok2, "\t");
|
|
tok2 = trim(tok2);
|
|
if (!strncmp(tok1.c_str(), "color", 5))
|
|
{
|
|
csv_curves[i]->Set_color(tok2);
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "symbol", 5) && (strstr(tok1.c_str(), "_si") == NULL)
|
|
&& (strstr(tok1.c_str(), "-si") == NULL))
|
|
{
|
|
csv_curves[i]->Set_symbol(tok2);
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "symbol_size", 8) || !strncmp(tok1.c_str(), "symbol-size", 8))
|
|
{
|
|
if (tok2.size() > 0)
|
|
{
|
|
csv_curves[i]->Set_symbol_size(atof(tok2.c_str()));
|
|
}
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "line_w", 5) || !strncmp(tok1.c_str(), "line-w", 5))
|
|
{
|
|
csv_curves[i]->Set_line_w(atof(tok2.c_str()));
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "y_axis", 5) || !strncmp(tok1.c_str(), "y-axis", 5))
|
|
{
|
|
if (tok2.size() > 0)
|
|
{
|
|
csv_curves[i]->Set_y_axis(atoi(tok2.c_str()));
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
linenr++;
|
|
continue;
|
|
}
|
|
|
|
// Curve data
|
|
if (linenr < 6) linenr = 6;
|
|
if (tt != CParser::TT_DIGIT)
|
|
{
|
|
linenr++;
|
|
continue;
|
|
}
|
|
|
|
// x value for all curves
|
|
LDBLE x_value = atof(tok1.c_str());
|
|
|
|
// y values for curves
|
|
std::string tok2;
|
|
size_t i=0;
|
|
while (token.size() != 0 && i < csv_curves.size())
|
|
{
|
|
CParser::parse_delimited(token, tok2, "\t");
|
|
Utilities::squeeze_white(tok2);
|
|
if (tok2.size() == 0)
|
|
{
|
|
//csv_curves[i].Get_x().push_back(NA);
|
|
//csv_curves[i].Get_y().push_back(NA);
|
|
}
|
|
else
|
|
{
|
|
csv_curves[i]->Get_x().push_back(x_value);
|
|
csv_curves[i]->Get_y().push_back(atof(tok2.c_str()));
|
|
}
|
|
i++;
|
|
}
|
|
linenr++;
|
|
}
|
|
|
|
// Append new curves
|
|
|
|
std::vector<CurveObject *>::iterator it = csv_curves.begin();
|
|
for (; it != csv_curves.end(); it++)
|
|
{
|
|
if ((*it)->Get_x().size() > 0)
|
|
{
|
|
std::string col = (*it)->Get_color();
|
|
this->Get_color_string_csv(col);
|
|
(*it)->Set_color(col);
|
|
this->CurvesCSV.push_back(*it);
|
|
this->Set_curve_added(true);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CurveObject
|
|
ChartObject::ExtractCurveInfo(std::string & cmd_line)
|
|
{
|
|
/* plot_xy x, tot("Cl"), color = Red, symbol = Circle, symbol_size = 0.0, line_w = 1.0, y_axis = 2 */
|
|
|
|
// Make copy of cmd_line
|
|
//int curvenr = (int) this->Curves.size();
|
|
std::string str_line = cmd_line;
|
|
|
|
// Massage line
|
|
while (Utilities::replace(" ,",",",str_line));
|
|
while (Utilities::replace("\t,",",",str_line));
|
|
while (Utilities::replace(",","#",str_line));
|
|
while (Utilities::replace("#",", ", str_line));
|
|
|
|
int sel;
|
|
std::string token, tok1, tok2;
|
|
std::string revised_line;
|
|
std::string::iterator b = str_line.begin();
|
|
std::string::iterator e = str_line.end();
|
|
// new curve
|
|
CurveObject new_curve;
|
|
|
|
while (CParser::copy_token(token, b, e) != CParser::TT_EMPTY)
|
|
{
|
|
sel = -1;
|
|
std::string token_save = token;
|
|
Utilities::str_tolower(token);
|
|
tok1 = token;
|
|
if (!strncmp(tok1.c_str(), "color", 5))
|
|
{
|
|
sel = 0;
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "symbol", 5) && (strstr(tok1.c_str(), "_si") == NULL)
|
|
&& (strstr(tok1.c_str(), "-si") == NULL))
|
|
{
|
|
sel = 1;
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "symbol_size", 8) || !strncmp(tok1.c_str(), "symbol-size", 8))
|
|
{
|
|
sel = 2;
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "line_w", 5) || !strncmp(tok1.c_str(), "line-w", 5))
|
|
{
|
|
sel = 3;
|
|
}
|
|
else if (!strncmp(tok1.c_str(), "y_axis", 5) || !strncmp(tok1.c_str(), "y-axis", 5))
|
|
{
|
|
sel = 4;
|
|
}
|
|
|
|
// Append to command line
|
|
if (sel < 0)
|
|
{
|
|
revised_line.append(token_save);
|
|
revised_line.append(" ");
|
|
}
|
|
// Parse plot_xy pair
|
|
else
|
|
{
|
|
// remove preceding comma
|
|
std::string comma = revised_line.substr(revised_line.size() - 2);
|
|
if (comma == ", ")
|
|
{
|
|
revised_line = revised_line.substr(0, revised_line.size() - 2);
|
|
revised_line.append(" ");
|
|
}
|
|
|
|
token = token_save;
|
|
size_t p1 = token.find("=");
|
|
std::string tok2;
|
|
// "=" found
|
|
if(p1 != std::string::npos)
|
|
{
|
|
if (p1 != token.size() - 1)
|
|
{
|
|
// color=Red
|
|
tok2 = token.substr(p1 + 1);
|
|
}
|
|
else
|
|
{
|
|
// color= Red
|
|
CParser::copy_token(tok2, b, e);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no "=" found
|
|
CParser::copy_token(tok2, b, e);
|
|
p1 = tok2.find("=");
|
|
if (tok2 == "=")
|
|
{
|
|
// color = Red
|
|
CParser::copy_token(tok2, b, e);
|
|
}
|
|
else if (p1 != std::string::npos)
|
|
{
|
|
// color =Red
|
|
tok2 = tok2.substr(p1 + 1);
|
|
}
|
|
else
|
|
{
|
|
// color Red
|
|
tok2 = tok2;
|
|
}
|
|
}
|
|
// remove any commas
|
|
while(Utilities::replace(",","",tok1));
|
|
while(Utilities::replace(",","",tok2));
|
|
|
|
// tok1 is name, tok2 is value
|
|
|
|
switch (sel)
|
|
{
|
|
case 0:
|
|
new_curve.Set_color(tok2);
|
|
break;
|
|
case 1:
|
|
new_curve.Set_symbol(tok2);
|
|
break;
|
|
case 2:
|
|
new_curve.Set_symbol_size(atof(tok2.c_str()));
|
|
break;
|
|
case 3:
|
|
new_curve.Set_line_w(atof(tok2.c_str()));
|
|
break;
|
|
case 4:
|
|
new_curve.Set_y_axis(atoi(tok2.c_str()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
cmd_line = revised_line;
|
|
return new_curve;
|
|
}
|
|
void
|
|
ChartObject::Set_rate_struct(void)
|
|
{
|
|
|
|
if (rate_command_list.size() == 0) return;
|
|
std::list<std::string>::iterator it = rate_command_list.begin();
|
|
std::ostringstream oss;
|
|
for (; it != rate_command_list.end(); it++)
|
|
{
|
|
oss << *it << "\n";
|
|
}
|
|
this->Rate_free();
|
|
this->user_graph->commands = oss.str().c_str();
|
|
this->user_graph->new_def = this->rate_new_def;
|
|
this->user_graph->loopbase = NULL;
|
|
this->user_graph->varbase = NULL;
|
|
this->user_graph->linebase = NULL;
|
|
this->user_graph->name = NULL;
|
|
}
|
|
void
|
|
ChartObject::Get_legal_symbol(std::string &sym)
|
|
{
|
|
std::map<std::string, int>::iterator it;
|
|
if ((it = this->Symbol_map.find(sym)) == this->Symbol_map.end())
|
|
{
|
|
sym = "Default";
|
|
for (it = this->Symbol_map.begin(); it != this->Symbol_map.end(); it++)
|
|
{
|
|
if (default_symbol == it->second)
|
|
{
|
|
sym = it->first;
|
|
break;
|
|
}
|
|
}
|
|
default_symbol++;
|
|
default_symbol = default_symbol % this->Symbol_map.size();
|
|
}
|
|
return;
|
|
}
|
|
void
|
|
ChartObject::Get_legal_symbol_csv(std::string &sym)
|
|
{
|
|
std::map<std::string, int>::iterator it;
|
|
if ((it = this->Symbol_map.find(sym)) == this->Symbol_map.end())
|
|
{
|
|
sym = "Default";
|
|
for (it = this->Symbol_map.begin(); it != this->Symbol_map.end(); it++)
|
|
{
|
|
if (this->default_symbol_csv == it->second)
|
|
{
|
|
sym = it->first;
|
|
break;
|
|
}
|
|
}
|
|
default_symbol_csv++;
|
|
default_symbol_csv = default_symbol_csv % this->Symbol_map.size();
|
|
}
|
|
return;
|
|
}
|
|
void
|
|
ChartObject::Get_color_string(std::string &color)
|
|
{
|
|
if (color.size() == 0)
|
|
{
|
|
color = Color_vector[this->default_color];
|
|
default_color++;
|
|
default_color = default_color % this->Color_vector.size();
|
|
}
|
|
return;
|
|
}
|
|
void
|
|
ChartObject::Get_color_string_csv(std::string &color)
|
|
{
|
|
if (color.size() == 0)
|
|
{
|
|
color = Color_vector[this->default_color_csv];
|
|
default_color_csv++;
|
|
default_color_csv = default_color_csv % this->Color_vector.size();
|
|
}
|
|
return;
|
|
}
|
|
ZedGraph::SymbolType
|
|
ChartObject::Return_SymbolType(const std::string sym)
|
|
{
|
|
int i;
|
|
std::map<std::string, int>::iterator it;
|
|
if ((it = this->Symbol_map.find(sym)) != this->Symbol_map.end())
|
|
{
|
|
i = it->second;
|
|
}
|
|
else
|
|
{
|
|
i = default_symbol++;
|
|
default_symbol = default_symbol % this->Symbol_map.size();
|
|
}
|
|
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
return SymbolType::Square;
|
|
break;
|
|
case 1:
|
|
return SymbolType::Diamond;
|
|
break;
|
|
case 2:
|
|
return SymbolType::Triangle;
|
|
break;
|
|
case 3:
|
|
return SymbolType::Circle;
|
|
break;
|
|
case 4:
|
|
return SymbolType::XCross;
|
|
break;
|
|
case 5:
|
|
return SymbolType::Plus;
|
|
break;
|
|
case 6:
|
|
return SymbolType::Star;
|
|
break;
|
|
case 7:
|
|
return SymbolType::TriangleDown;
|
|
break;
|
|
case 8:
|
|
return SymbolType::HDash;
|
|
break;
|
|
case 9:
|
|
return SymbolType::VDash;
|
|
break;
|
|
case 10:
|
|
return SymbolType::None;
|
|
break;
|
|
default:
|
|
return SymbolType::Default;
|
|
break;
|
|
}
|
|
}
|
|
void
|
|
ChartObject::SaveCurvesToFile(std::string &file_name)
|
|
{
|
|
// reimplemented in Form
|
|
// This version not currently used
|
|
std::ofstream f_out(file_name.c_str(), std::ifstream::out);
|
|
|
|
if (!f_out.is_open())
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Could not open csv file for USER_GRAPH " << file_name;
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
return;
|
|
}
|
|
|
|
// list of curves
|
|
std::vector<CurveObject *> all_curves;
|
|
size_t i;
|
|
for (i = 0; i < this->CurvesCSV.size(); i++)
|
|
{
|
|
all_curves.push_back(this->CurvesCSV[i]);
|
|
}
|
|
for (i = 0; i < this->Curves.size(); i++)
|
|
{
|
|
all_curves.push_back(Curves[i]);
|
|
}
|
|
// write headings
|
|
size_t max_points = 0;
|
|
std::vector<CurveObject *>::iterator it = all_curves.begin();
|
|
f_out.precision(4);
|
|
i = 0;
|
|
for ( ; it != all_curves.end(); it++)
|
|
{
|
|
f_out.width(12);
|
|
f_out << "x" << "\t";
|
|
f_out.width(12);
|
|
if ((*it)->Get_id().size() > 0)
|
|
{
|
|
f_out << (*it)->Get_id() << "\t";
|
|
}
|
|
else
|
|
{
|
|
f_out << "y" << "\t";;
|
|
}
|
|
if ((*it)->Get_x().size() > max_points)
|
|
max_points = (*it)->Get_x().size();
|
|
}
|
|
f_out << "\n";
|
|
|
|
// write data
|
|
size_t i2 = 0;
|
|
f_out << std::scientific;
|
|
f_out.precision(4);
|
|
while (i2 < max_points)
|
|
{
|
|
for (it = all_curves.begin(); it != all_curves.end(); it++)
|
|
{
|
|
if (i2 < (*it)->Get_x().size())
|
|
{
|
|
f_out.width(12);
|
|
f_out << (*it)->Get_x()[i2] << "\t";
|
|
f_out.width(12);
|
|
f_out << (*it)->Get_y()[i2] << "\t";
|
|
}
|
|
else if (i2 < max_points)
|
|
{
|
|
f_out.width(13);
|
|
f_out << "\t";
|
|
f_out.width(13);
|
|
f_out << "\t";
|
|
}
|
|
}
|
|
f_out << "\n";
|
|
i2++;
|
|
}
|
|
f_out.close();
|
|
return;
|
|
}
|
|
// file only used with MULTICHART
|
|
bool
|
|
ChartObject::start_chart(void)
|
|
{
|
|
Application::EnableVisualStyles();
|
|
Application::SetCompatibleTextRenderingDefault(true);
|
|
|
|
// needed to send ChartObject pointer to thread
|
|
Thread ^t = gcnew Thread(
|
|
gcnew ParameterizedThreadStart(Form1::ThreadForm));
|
|
|
|
t->SetApartmentState(ApartmentState::STA);
|
|
t->IsBackground = false;
|
|
t->Priority = ThreadPriority::Normal;
|
|
|
|
ChartObj ^p = gcnew ChartObj(this);
|
|
t->Start(p);
|
|
this->form_started = true;
|
|
|
|
//Thread::Sleep( 1 ); /* this when debugging... */
|
|
//_beginthread(void (Form1::ThreadForm), 0, NULL);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ChartObject::Rate_free(void)
|
|
{
|
|
|
|
|
|
if (this->phreeqc_ptr)
|
|
{
|
|
user_graph->commands.clear();
|
|
}
|
|
if (user_graph->linebase != NULL)
|
|
{
|
|
char cmd[] = "new; quit";
|
|
if (this->phreeqc_ptr)
|
|
{
|
|
phreeqc_ptr-> basic_run(cmd, user_graph->linebase, user_graph->varbase, user_graph->loopbase);
|
|
}
|
|
user_graph->linebase = NULL;
|
|
user_graph->varbase = NULL;
|
|
user_graph->loopbase = NULL;
|
|
}
|
|
}
|
|
void ChartObject::Initialize_graph_pts(void)
|
|
{
|
|
graph_x = NA;
|
|
graph_y.clear();
|
|
secondary_y.clear();
|
|
}
|
|
|
|
void
|
|
ChartObject::Finalize_graph_pts(void)
|
|
{
|
|
if (graph_x != NA)
|
|
{
|
|
std::map<int, LDBLE>::iterator it;
|
|
for (it = graph_y.begin(); it != graph_y.end(); it++)
|
|
{
|
|
Curves[it->first]->Get_x().push_back(graph_x);
|
|
Curves[it->first]->Get_y().push_back(it->second);
|
|
// Mark added curve for first point, might have been invisible in DefineCurves
|
|
if (Curves[it->first]->Get_x().size() == 1)
|
|
this->Set_curve_added(true);
|
|
}
|
|
}
|
|
if (graph_x != NA)
|
|
{
|
|
std::map<int, bool>::iterator it;
|
|
for (it = secondary_y.begin(); it != secondary_y.end(); it++)
|
|
{
|
|
Curves[it->first]->Set_y_axis(2);
|
|
}
|
|
}
|
|
}
|
|
void
|
|
ChartObject::Add_new_series(void)
|
|
{
|
|
std::vector<CurveObject *> Curves;
|
|
size_t i;
|
|
size_t e = this->Curves.size();
|
|
for (i = this->ColumnOffset; i < e; i++)
|
|
{
|
|
this->Add_curve(false,
|
|
this->Curves[i]->Get_id(),
|
|
this->Curves[i]->Get_line_w(),
|
|
"",
|
|
this->Curves[i]->Get_symbol_size(),
|
|
this->Curves[i]->Get_y_axis(),
|
|
"");
|
|
}
|
|
this->ColumnOffset = (int) e;
|
|
this->curve_added = true;
|
|
}
|
|
void
|
|
ChartObject::Add_curve(bool plotxy, std::string id,
|
|
LDBLE line_width,
|
|
std::string symbol,
|
|
LDBLE symbol_size,
|
|
int y_axis,
|
|
std::string color)
|
|
{
|
|
CurveObject *c = new CurveObject;
|
|
c->Set_id(id);
|
|
c->Set_line_w(line_width);
|
|
this->Get_legal_symbol(symbol);
|
|
c->Set_symbol(symbol);
|
|
c->Set_symbol_size(symbol_size);
|
|
if (this->CurvesCSV.size() > this->Curves.size() && !plotxy)
|
|
{
|
|
c->Set_symbol_size(0.0);
|
|
}
|
|
c->Set_y_axis(y_axis);
|
|
this->Get_color_string(color);
|
|
c->Set_color(color);
|
|
|
|
this->Curves.push_back(c);
|
|
}
|
|
void
|
|
ChartObject::Set_rate_new_def(bool tf)
|
|
{
|
|
this->rate_new_def = tf;
|
|
if (this->user_graph != NULL)
|
|
{
|
|
if (tf)
|
|
{
|
|
this->user_graph->new_def = 1;
|
|
}
|
|
else
|
|
{
|
|
this->user_graph->new_def = 0;
|
|
}
|
|
}
|
|
}
|
|
void
|
|
ChartObject::dump(std::ostream & oss, unsigned int indent)
|
|
{
|
|
size_t i;
|
|
oss.precision(DBL_DIG - 1);
|
|
std::string indent0(""), indent1("");
|
|
for (i = 0; i < indent; ++i)
|
|
indent0.append(Utilities::INDENT);
|
|
for (i = 0; i < indent + 1; ++i)
|
|
indent1.append(Utilities::INDENT);
|
|
oss << indent0 << "USER_GRAPH " << this->n_user << " " << this->description << "\n";
|
|
|
|
// chart title
|
|
oss << indent1 << "-chart_title \"" << this->chart_title << "\"" << "\n";
|
|
|
|
// axis titles
|
|
if (this->axis_titles.size() > 0)
|
|
{
|
|
oss << indent1 << "-axis_titles ";
|
|
|
|
for (i = 0; i < this->axis_titles.size(); i++)
|
|
{
|
|
oss << "\"" << axis_titles[i] << "\" ";
|
|
}
|
|
oss << "\n";
|
|
}
|
|
|
|
// axis_scale_x
|
|
LDBLE *scale_ptr = this->axis_scale_x;
|
|
{
|
|
oss << indent1 << "-axis_scale x_axis ";
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (scale_ptr[i] == NA)
|
|
{
|
|
oss << " auto";
|
|
}
|
|
else
|
|
{
|
|
oss << " " << scale_ptr[i];
|
|
}
|
|
}
|
|
if (scale_ptr[4] == 10.0)
|
|
{
|
|
oss << " log";
|
|
}
|
|
oss << "\n";
|
|
}
|
|
// axis_scale_y
|
|
scale_ptr = this->axis_scale_y;
|
|
{
|
|
oss << indent1 << "-axis_scale y_axis ";
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (scale_ptr[i] == NA)
|
|
{
|
|
oss << " auto";
|
|
}
|
|
else
|
|
{
|
|
oss << " " << scale_ptr[i];
|
|
}
|
|
}
|
|
if (scale_ptr[4] == 10.0)
|
|
{
|
|
oss << " log";
|
|
}
|
|
oss << "\n";
|
|
}
|
|
// axis_scale_sy
|
|
scale_ptr = this->axis_scale_y2;
|
|
{
|
|
oss << indent1 << "-axis_scale sy_axis ";
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (scale_ptr[i] == NA)
|
|
{
|
|
oss << " auto";
|
|
}
|
|
else
|
|
{
|
|
oss << " " << scale_ptr[i];
|
|
}
|
|
}
|
|
if (scale_ptr[4] == 10.0)
|
|
{
|
|
oss << " log";
|
|
}
|
|
oss << "\n";
|
|
}
|
|
// chart type
|
|
if (this->chart_type == 0)
|
|
{
|
|
oss << indent1 << "-plot_concentration_vs x" << "\n";
|
|
}
|
|
else
|
|
{
|
|
oss << indent1 << "-plot_concentration_vs t" << "\n";
|
|
}
|
|
// graph_initial_solutions
|
|
if (this->graph_initial_solutions)
|
|
{
|
|
oss << indent1 << "-initial_solutions true" << "\n";
|
|
}
|
|
else
|
|
{
|
|
oss << indent1 << "-initial_solutions false" << "\n";
|
|
}
|
|
// connect_simulations
|
|
if (this->connect_simulations)
|
|
{
|
|
oss << indent1 << "-connect_simulations true" << "\n";
|
|
}
|
|
else
|
|
{
|
|
oss << indent1 << "-connect_simulations false" << "\n";
|
|
}
|
|
// csv files
|
|
for (i = 0; i < this->csv_file_names.size(); i++)
|
|
{
|
|
oss << indent1 << "-plot_tsv_file " << this->csv_file_names[i] << "\n";
|
|
}
|
|
|
|
// headings
|
|
if (this->headings_original.size() > 0)
|
|
{
|
|
oss << indent1 << "-headings ";
|
|
for (i = 0; i < this->headings_original.size(); i++)
|
|
{
|
|
oss << this->headings_original[i] << " ";
|
|
}
|
|
oss << "\n";
|
|
}
|
|
|
|
// commands
|
|
oss << indent1 << "-start" << "\n";
|
|
std::list<std::string>::iterator it = rate_command_list_original.begin();
|
|
for (; it != rate_command_list_original.end(); it++)
|
|
{
|
|
oss << *it << "\n";
|
|
}
|
|
oss << indent1 << "-end" << "\n";
|
|
|
|
/*
|
|
class rate *user_graph;
|
|
// C++ for rate struct
|
|
std::string rate_name;
|
|
std::list<std::string> rate_command_list;
|
|
bool rate_new_def;
|
|
|
|
int default_symbol;
|
|
int default_symbol_csv;
|
|
int default_color;
|
|
int default_color_csv;
|
|
|
|
// temporary storage before stored graph_x/y/sy data are stored in curves
|
|
// Initialize_graph_pts and Finalize_graph_pts use this storage.
|
|
LDBLE graph_x;
|
|
std::map<int, LDBLE> graph_y;
|
|
std::map<int, bool> secondary_y;
|
|
|
|
// temporary plotxy curve definitions before stored in curves
|
|
// a plotxy curve is copied to Curves when cmdplotxy is encountered
|
|
// this keeps order correct between plotxy and graph_x/y/sy
|
|
std::vector<CurveObject> new_plotxy_curves;
|
|
|
|
// temporary headings until stored during basic_run
|
|
std::vector<std::string> new_headings;
|
|
bool active;
|
|
bool detach;
|
|
bool form_started;
|
|
*/
|
|
}
|
|
|
|
const std::vector< std::string >::value_type temp_vopts[] = {
|
|
std::vector< std::string >::value_type("start"), // 0
|
|
std::vector< std::string >::value_type("end"), // 1
|
|
std::vector< std::string >::value_type("heading"), // 2
|
|
std::vector< std::string >::value_type("headings"), // 3
|
|
std::vector< std::string >::value_type("chart_title"), // 4
|
|
std::vector< std::string >::value_type("axis_titles"), // 5
|
|
std::vector< std::string >::value_type("axis_scale"), // 6
|
|
std::vector< std::string >::value_type("initial_solutions"), // 7
|
|
std::vector< std::string >::value_type("plot_concentration_vs"), // 8
|
|
std::vector< std::string >::value_type("shifts_as_points"), // 9
|
|
std::vector< std::string >::value_type("grid_offset"), // 10
|
|
std::vector< std::string >::value_type("connect_simulations"), // 11
|
|
std::vector< std::string >::value_type("plot_csv_file"), // 12
|
|
std::vector< std::string >::value_type("clear"), // 13
|
|
std::vector< std::string >::value_type("detach"), // 14
|
|
std::vector< std::string >::value_type("active"), // 15
|
|
std::vector< std::string >::value_type("batch"), // 16
|
|
std::vector< std::string >::value_type("plot_tsv_file") // 17
|
|
};
|
|
const std::vector< std::string > ChartObject::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]);
|
|
|
|
|
|
#endif // MULTICHART
|