mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-16 16:44:49 +01:00
HEAP[phreeqcpp.exe]: HEAP: Free Heap block XXXXXXX modified at XXXXXXX after it was freed Windows has triggered a breakpoint in phreeqcpp.exe. This may be due to a corruption of the heap, which indicates a bug in phreeqcpp.exe or any of the DLLs it has loaded. This may also be due to the user pressing F12 while phreeqcpp.exe has focus. Contains temporary code before cleanup git-svn-id: svn://136.177.114.72/svn_GW/phreeqc3/trunk@6585 1feff8c3-07ed-0310-ac33-dd36852eb9cd
1332 lines
30 KiB
C++
1332 lines
30 KiB
C++
// ChartObject.cpp: implementation of the ChartObject class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
#ifdef MULTICHART
|
|
#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 <math.h>
|
|
#include <iomanip>
|
|
#include "Phreeqc.h"
|
|
#include "phqalloc.h"
|
|
|
|
#include "Form1.h"
|
|
using namespace zdg_ui2;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// 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 = NULL;
|
|
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 = ChO_NO_BATCH;
|
|
}
|
|
|
|
ChartObject::~ChartObject()
|
|
{
|
|
while (0 != System::Threading::Interlocked::Exchange(this->usingResource, 1))
|
|
{
|
|
System::Threading::Thread::Sleep(1);
|
|
}
|
|
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)
|
|
{
|
|
|
|
static std::vector < std::string > vopts;
|
|
if (vopts.empty())
|
|
{
|
|
vopts.reserve(20);
|
|
vopts.push_back("start"); // 0
|
|
vopts.push_back("end"); // 1
|
|
vopts.push_back("heading"); // 2
|
|
vopts.push_back("headings"); // 3
|
|
vopts.push_back("chart_title"); // 4
|
|
vopts.push_back("axis_titles"); // 5
|
|
vopts.push_back("axis_scale"); // 6
|
|
vopts.push_back("initial_solutions"); // 7
|
|
vopts.push_back("plot_concentration_vs"); // 8
|
|
vopts.push_back("shifts_as_points"); // 9
|
|
vopts.push_back("grid_offset"); // 10
|
|
vopts.push_back("connect_simulations"); // 11
|
|
vopts.push_back("plot_csv_file"); // 12
|
|
vopts.push_back("clear"); // 13
|
|
vopts.push_back("detach"); // 14
|
|
vopts.push_back("active"); // 15
|
|
vopts.push_back("batch"); // 16
|
|
|
|
}
|
|
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 = 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);
|
|
CParser::copy_title(this->chart_title, tok.begin(), tok.end());
|
|
}
|
|
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 */
|
|
#ifdef PHREEQ98
|
|
/*
|
|
i = copy_token(token, &next_char, &l);
|
|
str_tolower(token);
|
|
if (i == DIGIT)
|
|
sscanf(token, "%d", &RowOffset);
|
|
i = copy_token(token, &next_char, &l);
|
|
str_tolower(token);
|
|
if (i == DIGIT)
|
|
sscanf(token, "%d", &ColumnOffset);
|
|
*/
|
|
#endif
|
|
break;
|
|
case 11: /* connect_simulations */
|
|
this->connect_simulations = parser.get_true_false(next_char, true);
|
|
break;
|
|
case 12: /* plot_csv_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);
|
|
break;
|
|
/* End of modifications */
|
|
case 16: /* batch */
|
|
{
|
|
this->batch = ChartObject::ChO_BATCH_ONLY;
|
|
|
|
std::string file_name;
|
|
if (parser.copy_token(file_name, next_char) != CParser::TT_EMPTY)
|
|
{
|
|
//Get suffix from file_name
|
|
size_t ssi = file_name.rfind('.');
|
|
if (ssi == std::string::npos)
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Batch file name must have suffix emf, phg, jpg, gif, tiff, or bmp.";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
break;
|
|
}
|
|
|
|
std::string suffix = file_name.substr(ssi + 1);
|
|
Utilities::str_tolower(suffix);
|
|
if (suffix == "emf")
|
|
this->batch = ChartObject::ChO_EMF;
|
|
else if (suffix == "png")
|
|
this->batch = ChartObject::ChO_PNG;
|
|
else if (suffix == "jpg")
|
|
this->batch = ChartObject::ChO_JPG;
|
|
else if (suffix == "gif")
|
|
this->batch = ChartObject::ChO_GIF;
|
|
else if (suffix == "tiff")
|
|
this->batch = ChartObject::ChO_TIFF;
|
|
else if (suffix == "bmp")
|
|
this->batch = ChartObject::ChO_BMP;
|
|
else if (suffix == "jpeg")
|
|
this->batch = ChartObject::ChO_JPG;
|
|
else
|
|
{
|
|
std::ostringstream estream;
|
|
estream << "Batch file name must have suffix emf, phg, jpg, jpeg, gif, tiff, or bmp.";
|
|
error_msg(estream.str().c_str(), CONTINUE);
|
|
break;
|
|
}
|
|
this->batch_fn = file_name;
|
|
|
|
// Get background bool
|
|
parser.copy_token(token, next_char);
|
|
if (token.size() > 0)
|
|
{
|
|
Utilities::str_tolower(token);
|
|
if (token[0] == 'f')
|
|
{
|
|
this->batch_background = 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 csv 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 = (char *) phreeqc_ptr-> PHRQ_malloc((oss.str().size()) + 100 * sizeof(char));
|
|
::strcpy(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)
|
|
{
|
|
|
|
|
|
user_graph->commands = (char *) phreeqc_ptr-> free_check_null(user_graph->commands);
|
|
|
|
if (user_graph->linebase != NULL)
|
|
{
|
|
char cmd[] = "new; quit";
|
|
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_csv_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";
|
|
|
|
/*
|
|
struct 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;
|
|
*/
|
|
}
|
|
#endif // MULTICHART
|