diff --git a/NOTICE b/NOTICE index 799bac34..e548fdff 100644 --- a/NOTICE +++ b/NOTICE @@ -25,6 +25,6 @@ This project distributes the sources of several external software products with their own respective licenses which can be found in their code and attached license files. These software products and their licenses are as follows: -* GL2PS (linalg/gl2ps.{h,c}) -- Custom 1-clause license +* GL2PS (lib/gl2ps.{h,c}) -- Custom 1-clause license * Additional color palettes (share/palettes-crameri.txt) -- MIT license * Additional color palettes (share/palettes-cet.txt) -- CC BY 4.0 license diff --git a/glvis.cpp b/glvis.cpp index 8c5721d3..90d39ff0 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -34,6 +34,8 @@ #include "mfem.hpp" #include "lib/palettes.hpp" #include "lib/visual.hpp" +#include "lib/window.hpp" +#include "lib/script_controller.hpp" #include "lib/stream_reader.hpp" #include "lib/file_reader.hpp" #include "lib/coll_reader.hpp" @@ -44,28 +46,6 @@ using namespace mfem; const char *string_none = "(none)"; const char *string_default = "(default)"; -// Global variables for command line arguments -const char *mesh_file = string_none; -const char *sol_file = string_none; -const char *vec_sol_file = string_none; -const char *gfunc_file = string_none; -const char *qfunc_file = string_none; -string dc_protocol = string_default; -int dc_cycle = 0; -const char *arg_keys = string_none; -int pad_digits = 6; -int gf_component = -1; -int qf_component = -1; -int window_x = 0; // not a command line option -int window_y = 0; // not a command line option -int window_w = 400; -int window_h = 350; -const char *window_title = string_default; -const char *c_plot_caption = string_none; -thread_local string plot_caption; -thread_local string extra_caption; -bool secure = socketstream::secure_default; - // Global variables enum InputOptions { @@ -80,1061 +60,29 @@ enum InputOptions INPUT_PARALLEL = 1 << 8, }; int input = INPUT_SERVER_MODE; -thread_local DataState stream_state; -thread_local VisualizationSceneScalarData *vs = NULL; -extern thread_local GLVisCommand* glvis_command; -thread_local communication_thread *comm_thread = NULL; thread_local GeometryRefiner GLVisGeometryRefiner; -const char *window_titles[] = { "GLVis [mesh]", - "GLVis [scalar data]", - "GLVis [vector data]", - }; -istream *script = NULL; -int scr_running = 0; -int scr_level = 0; -Vector *init_nodes = NULL; -double scr_min_val, scr_max_val; - -extern char **environ; - - void PrintSampleUsage(ostream &out); -// switch representation of the quadrature function -void SwitchQuadSolution(); - -// Visualize the data in the global variables mesh, sol/grid_f, etc -bool GLVisInitVis(StreamCollection input_streams) -{ - DataState::FieldType field_type = stream_state.GetType(); - - if (field_type <= DataState::FieldType::MIN - || field_type >= DataState::FieldType::MAX) - { - return false; - } - - const char *win_title = (window_title == string_default) ? - window_titles[(int)field_type] : window_title; - - if (InitVisualization(win_title, window_x, window_y, window_w, window_h)) - { - cerr << "Initializing the visualization failed." << endl; - return false; - } - - if (input_streams.size() > 0) - { - GetAppWindow()->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); - glvis_command = new GLVisCommand(&vs, stream_state); - comm_thread = new communication_thread(std::move(input_streams), glvis_command); - } - - if (stream_state.quad_f) - { - GetAppWindow()->setOnKeyDown('Q', SwitchQuadSolution); - } - - double mesh_range = -1.0; - if (field_type == DataState::FieldType::SCALAR - || field_type == DataState::FieldType::MESH) - { - if (stream_state.grid_f) - { - stream_state.grid_f->GetNodalValues(stream_state.sol); - } - if (stream_state.mesh->SpaceDimension() == 2) - { - VisualizationSceneSolution * vss; - if (stream_state.normals.Size() > 0) - { - vs = vss = new VisualizationSceneSolution(*stream_state.mesh, stream_state.sol, - stream_state.mesh_quad.get(), &stream_state.normals); - } - else - { - vs = vss = new VisualizationSceneSolution(*stream_state.mesh, stream_state.sol, - stream_state.mesh_quad.get()); - } - if (stream_state.grid_f) - { - vss->SetGridFunction(*stream_state.grid_f); - } - if (field_type == DataState::FieldType::MESH) - { - vs->OrthogonalProjection = 1; - vs->SetLight(false); - vs->Zoom(1.8); - // Use the 'bone' palette when visualizing a 2D mesh only (otherwise - // the 'jet-like' palette is used in 2D, see vssolution.cpp). - vs->palette.SetIndex(4); - } - } - else if (stream_state.mesh->SpaceDimension() == 3) - { - VisualizationSceneSolution3d * vss; - vs = vss = new VisualizationSceneSolution3d(*stream_state.mesh, - stream_state.sol, stream_state.mesh_quad.get()); - if (stream_state.grid_f) - { - vss->SetGridFunction(stream_state.grid_f.get()); - } - if (field_type == DataState::FieldType::MESH) - { - if (stream_state.mesh->Dimension() == 3) - { - // Use the 'white' palette when visualizing a 3D volume mesh only - vss->palette.SetIndex(11); - vss->SetLightMatIdx(4); - } - else - { - // Use the 'bone' palette when visualizing a surface mesh only - vss->palette.SetIndex(4); - } - // Otherwise, the 'vivid' palette is used in 3D see vssolution3d.cpp - vss->ToggleDrawAxes(); - vss->ToggleDrawMesh(); - } - } - if (field_type == DataState::FieldType::MESH) - { - if (stream_state.grid_f) - { - mesh_range = stream_state.grid_f->Max() + 1.0; - } - else - { - mesh_range = stream_state.sol.Max() + 1.0; - } - } - } - else if (field_type == DataState::FieldType::VECTOR) - { - if (stream_state.mesh->SpaceDimension() == 2) - { - if (stream_state.grid_f) - { - vs = new VisualizationSceneVector(*stream_state.grid_f); - } - else - { - vs = new VisualizationSceneVector(*stream_state.mesh, stream_state.solu, - stream_state.solv, stream_state.mesh_quad.get()); - } - } - else if (stream_state.mesh->SpaceDimension() == 3) - { - if (stream_state.grid_f) - { - stream_state.ProjectVectorFEGridFunction(); - vs = new VisualizationSceneVector3d(*stream_state.grid_f, - stream_state.mesh_quad.get()); - } - else - { - vs = new VisualizationSceneVector3d(*stream_state.mesh, stream_state.solu, - stream_state.solv, stream_state.solw, - stream_state.mesh_quad.get()); - } - } - } - - if (vs) - { - // increase the refinement factors if visualizing a GridFunction - if (stream_state.grid_f) - { - vs->AutoRefine(); - vs->SetShading(VisualizationSceneScalarData::Shading::Noncomforming, true); - } - if (mesh_range > 0.0) - { - vs->SetValueRange(-mesh_range, mesh_range); - vs->SetAutoscale(0); - } - if (stream_state.mesh->SpaceDimension() == 2 - && field_type == DataState::FieldType::MESH) - { - SetVisualizationScene(vs, 2, stream_state.keys.c_str()); - } - else - { - SetVisualizationScene(vs, 3, stream_state.keys.c_str()); - } - } - return true; -} - -void GLVisStartVis() -{ - RunVisualization(); // deletes vs - vs = NULL; - if (glvis_command) - { - glvis_command->Terminate(); - delete comm_thread; - delete glvis_command; - glvis_command = NULL; - } - cout << "GLVis window closed." << endl; -} - -int ScriptReadSolution(istream &scr, DataState& state) -{ - int err_read; - string mword,sword; - - cout << "Script: solution: " << flush; - // read the mesh - scr >> ws >> mword; // mesh filename (can't contain spaces) - cout << "mesh: " << mword << "; " << flush; - named_ifgzstream imesh(mword.c_str()); - if (!imesh) - { - cout << "Can not open mesh file: " << mword << endl; - return 1; - } - state.SetMesh(new Mesh(imesh, 1, 0, state.fix_elem_orient)); - - // read the solution (GridFunction) - scr >> ws >> sword; - cout << "solution: " << sword << endl; - - FileReader reader(state); - err_read = reader.ReadSerial(FileReader::FileType::GRID_FUNC, mword.c_str(), - sword.c_str()); - - return err_read; -} - -int ScriptReadQuadrature(istream &scr, DataState& state) -{ - int err_read; - string mword,sword; - - cout << "Script: quadrature: " << flush; - // read the mesh - scr >> ws >> mword; // mesh filename (can't contain spaces) - cout << "mesh: " << mword << "; " << flush; - named_ifgzstream imesh(mword.c_str()); - if (!imesh) - { - cout << "Can not open mesh file: " << mword << endl; - return 1; - } - state.SetMesh(new Mesh(imesh, 1, 0, state.fix_elem_orient)); - - // read the quadrature (QuadratureFunction) - scr >> ws >> sword; - cout << "quadrature: " << sword << endl; - - FileReader reader(state); - err_read = reader.ReadSerial(FileReader::FileType::QUAD_FUNC, mword.c_str(), - sword.c_str()); - - return err_read; -} - -int ScriptReadParSolution(istream &scr, DataState& state) -{ - int np, scr_keep_attr, err_read; - string mesh_prefix, sol_prefix; - - cout << "Script: psolution: " << flush; - // read number of processors - scr >> np; - cout << "# processors: " << np << "; " << flush; - // read the mesh prefix - scr >> ws >> mesh_prefix; // mesh prefix (can't contain spaces) - cout << "mesh prefix: " << mesh_prefix << "; " << flush; - scr >> ws >> scr_keep_attr; - if (scr_keep_attr) - { - cout << "(real attributes); " << flush; - } - else - { - cout << "(processor attributes); " << flush; - } - // read the solution prefix - scr >> ws >> sol_prefix; - cout << "solution prefix: " << sol_prefix << endl; - - FileReader reader(state); - err_read = reader.ReadParallel(np, FileReader::FileType::GRID_FUNC, - mesh_prefix.c_str(), sol_prefix.c_str()); - return err_read; -} - -int ScriptReadParQuadrature(istream &scr, DataState& state) -{ - int np, scr_keep_attr, err_read; - string mesh_prefix, quad_prefix; - - cout << "Script: pquadrature: " << flush; - // read number of processors - scr >> np; - cout << "# processors: " << np << "; " << flush; - // read the mesh prefix - scr >> ws >> mesh_prefix; // mesh prefix (can't contain spaces) - cout << "mesh prefix: " << mesh_prefix << "; " << flush; - scr >> ws >> scr_keep_attr; - if (scr_keep_attr) - { - cout << "(real attributes); " << flush; - } - else - { - cout << "(processor attributes); " << flush; - } - // read the quadrature prefix - scr >> ws >> quad_prefix; - cout << "quadrature prefix: " << quad_prefix << endl; - - FileReader reader(state); - err_read = reader.ReadParallel(np, FileReader::FileType::QUAD_FUNC, - mesh_prefix.c_str(), quad_prefix.c_str()); - return err_read; -} - -int ScriptReadDisplMesh(istream &scr, DataState& state) -{ - DataState meshstate; - string word; - - cout << "Script: mesh: " << flush; - scr >> ws >> word; - { - named_ifgzstream imesh(word.c_str()); - if (!imesh) - { - cout << "Can not open mesh file: " << word << endl; - return 1; - } - cout << word << endl; - meshstate.SetMesh(new Mesh(imesh, 1, 0, state.fix_elem_orient)); - } - meshstate.ExtrudeMeshAndSolution(); - Mesh* const m = meshstate.mesh.get(); - if (init_nodes == NULL) - { - init_nodes = new Vector; - meshstate.mesh->GetNodes(*init_nodes); - state.SetMesh(NULL); - state.SetGridFunction(NULL); - } - else - { - FiniteElementCollection *vfec = NULL; - FiniteElementSpace *vfes; - vfes = (FiniteElementSpace *)m->GetNodalFESpace(); - if (vfes == NULL) - { - vfec = new LinearFECollection; - vfes = new FiniteElementSpace(m, vfec, m->SpaceDimension()); - } - - meshstate.SetGridFunction(new GridFunction(vfes)); - GridFunction * const g = meshstate.grid_f.get(); - if (vfec) - { - g->MakeOwner(vfec); - } - m->GetNodes(*g); - if (g->Size() == init_nodes->Size()) - { - subtract(*init_nodes, *g, *g); - } - else - { - cout << "Script: incompatible meshes!" << endl; - *g = 0.0; - } - - state = std::move(meshstate); - } - - return 0; -} - -int ScriptReadDataColl(istream &scr, DataState &state, bool mesh_only = true, - bool quad = false) -{ - int err_read; - int type; - string cword, fword; - - cout << "Script: data_collection: " << flush; - // read the collection - scr >> ws >> type; // collection type - cout << "type: " << type << "; " << flush; - scr >> ws >> cword; // collection filename (can't contain spaces) - cout << "collection: " << cword << "; " << flush; - - if (!mesh_only) - { - // read the field - scr >> ws >> fword; - cout << "field: " << fword << endl; - } - - DataCollectionReader reader(state); - if (dc_protocol != string_default) - { - reader.SetProtocol(dc_protocol.c_str()); - } - - if (mesh_only) - err_read = reader.ReadSerial((DataCollectionReader::CollType)type, - cword.c_str(), dc_cycle); - else - err_read = reader.ReadSerial((DataCollectionReader::CollType)type, - cword.c_str(), dc_cycle, fword.c_str(), quad); - - return err_read; -} - -void ExecuteScriptCommand() -{ - if (!script) - { - cout << "No script stream defined! (Bug?)" << endl; - return; - } - - istream &scr = *script; - string word; - int done_one_command = 0; - while (!done_one_command) - { - scr >> ws; - if (!scr.good()) - { - cout << "End of script." << endl; - scr_level = 0; - return; - } - if (scr.peek() == '#') - { - getline(scr, word); - continue; - } - scr >> word; - if (word == "{") - { - scr_level++; - } - else if (word == "}") - { - scr_level--; - if (scr_level < 0) - { - scr_level = 0; - } - } - else if (word == "data_coll_cycle") - { - scr >> dc_cycle; - } - else if (word == "data_coll_protocol") - { - scr >> dc_protocol; - } - else if (word == "solution" || word == "mesh" || word == "psolution" - || word == "quadrature" || word == "pquadrature" || word == "data_coll_mesh" - || word == "data_coll_field" || word == "data_coll_quad") - { - DataState new_state; - - if (word == "solution") - { - if (ScriptReadSolution(scr, new_state)) - { - done_one_command = 1; - continue; - } - } - else if (word == "quadrature") - { - if (ScriptReadQuadrature(scr, new_state)) - { - done_one_command = 1; - continue; - } - } - else if (word == "mesh") - { - if (ScriptReadDisplMesh(scr, new_state)) - { - done_one_command = 1; - continue; - } - if (new_state.mesh == NULL) - { - cout << "Script: unexpected 'mesh' command!" << endl; - done_one_command = 1; - continue; - } - } - else if (word == "psolution") - { - if (ScriptReadParSolution(scr, new_state)) - { - done_one_command = 1; - continue; - } - } - else if (word == "pquadrature") - { - if (ScriptReadParQuadrature(scr, new_state)) - { - done_one_command = 1; - continue; - } - } - else if (word == "data_coll_mesh") - { - if (ScriptReadDataColl(scr, new_state)) - { - done_one_command = 1; - continue; - } - } - else if (word == "data_coll_field") - { - if (ScriptReadDataColl(scr, new_state, false)) - { - done_one_command = 1; - continue; - } - } - else if (word == "data_coll_quad") - { - if (ScriptReadDataColl(scr, new_state, false, true)) - { - done_one_command = 1; - continue; - } - } - - if (stream_state.SetNewMeshAndSolution(std::move(new_state), vs)) - { - MyExpose(); - } - else - { - cout << "Different type of mesh / solution." << endl; - } - } - else if (word == "screenshot") - { - scr >> ws >> word; - - cout << "Script: screenshot: " << flush; - - if (Screenshot(word.c_str(), true)) - { - cout << "Screenshot(" << word << ") failed." << endl; - done_one_command = 1; - continue; - } - cout << "-> " << word << endl; - - if (scr_min_val > vs->GetMinV()) - { - scr_min_val = vs->GetMinV(); - } - if (scr_max_val < vs->GetMaxV()) - { - scr_max_val = vs->GetMaxV(); - } - } - else if (word == "viewcenter") - { - scr >> vs->ViewCenterX >> vs->ViewCenterY; - cout << "Script: viewcenter: " - << vs->ViewCenterX << ' ' << vs->ViewCenterY << endl; - MyExpose(); - } - else if (word == "perspective") - { - scr >> ws >> word; - cout << "Script: perspective: " << word; - if (word == "off") - { - vs->OrthogonalProjection = 1; - } - else if (word == "on") - { - vs->OrthogonalProjection = 0; - } - else - { - cout << '?'; - } - cout << endl; - MyExpose(); - } - else if (word == "light") - { - scr >> ws >> word; - cout << "Script: light: " << word; - if (word == "off") - { - vs->SetLight(false); - } - else if (word == "on") - { - vs->SetLight(true); - } - else - { - cout << '?'; - } - cout << endl; - MyExpose(); - } - else if (word == "view") - { - double theta, phi; - scr >> theta >> phi; - cout << "Script: view: " << theta << ' ' << phi << endl; - vs->SetView(theta, phi); - MyExpose(); - } - else if (word == "zoom") - { - double factor; - scr >> factor; - cout << "Script: zoom: " << factor << endl; - vs->Zoom(factor); - MyExpose(); - } - else if (word == "shading") - { - scr >> ws >> word; - cout << "Script: shading: " << flush; - VisualizationSceneScalarData::Shading s = - VisualizationSceneScalarData::Shading::Invalid; - if (word == "flat") - { - s = VisualizationSceneScalarData::Shading::Flat; - } - else if (word == "smooth") - { - s = VisualizationSceneScalarData::Shading::Smooth; - } - else if (word == "cool") - { - s = VisualizationSceneScalarData::Shading::Noncomforming; - } - if (s != VisualizationSceneScalarData::Shading::Invalid) - { - vs->SetShading(s, false); - cout << word << endl; - MyExpose(); - } - else - { - cout << word << " ?" << endl; - } - } - else if (word == "subdivisions") - { - int t, b; - scr >> t >> b; - cout << "Script: subdivisions: " << flush; - vs->SetRefineFactors(t, b); - cout << t << ' ' << b << endl; - MyExpose(); - } - else if (word == "valuerange") - { - double min, max; - scr >> min >> max; - cout << "Script: valuerange: " << flush; - vs->SetValueRange(min, max); - cout << min << ' ' << max << endl; - MyExpose(); - } - else if (word == "autoscale") - { - scr >> ws >> word; - cout << "Script: autoscale: " << word; - if (word == "off") - { - vs->SetAutoscale(0); - } - else if (word == "on") - { - vs->SetAutoscale(1); - } - else if (word == "value") - { - vs->SetAutoscale(2); - } - else if (word == "mesh") - { - vs->SetAutoscale(3); - } - else - { - cout << '?'; - } - cout << endl; - } - else if (word == "levellines") - { - double min, max; - int num; - scr >> min >> max >> num; - cout << "Script: levellines: " << flush; - vs->SetLevelLines(min, max, num); - vs->UpdateLevelLines(); - cout << min << ' ' << max << ' ' << num << endl; - MyExpose(); - } - else if (word == "axis_numberformat") - { - char delim; - string axis_formatting; - scr >> ws >> delim; - getline(scr, axis_formatting, delim); - cout << "Script: axis_numberformat: " << flush; - vs->SetAxisNumberFormat(axis_formatting); - cout << axis_formatting << endl; - MyExpose(); - } - else if (word == "colorbar_numberformat") - { - char delim; - string colorbar_formatting; - scr >> ws >> delim; - getline(scr, colorbar_formatting, delim); - cout << "Script: colorbar_numberformat: " << flush; - vs->SetColorbarNumberFormat(colorbar_formatting); - cout << colorbar_formatting << endl; - MyExpose(); - } - else if (word == "window") - { - scr >> window_x >> window_y >> window_w >> window_h; - cout << "Script: window: " << window_x << ' ' << window_y - << ' ' << window_w << ' ' << window_h << endl; - MoveResizeWindow(window_x, window_y, window_w, window_h); - MyExpose(); - } - else if (word == "keys") - { - scr >> stream_state.keys; - cout << "Script: keys: '" << stream_state.keys << "'" << endl; - // SendKeySequence(keys.c_str()); - CallKeySequence(stream_state.keys.c_str()); - MyExpose(); - } - else if (word == "palette") - { - int pal; - scr >> pal; - cout << "Script: palette: " << pal << endl; - vs->palette.SetIndex(pal-1); - MyExpose(); - } - else if (word == "palette_repeat") - { - int rpt_times; - scr >> rpt_times; - cout << "Script: palette_repeat: " << rpt_times << endl; - vs->palette.SetRepeatTimes(rpt_times); - vs->palette.GenerateTextures(); - MyExpose(); - } - else if (word == "toggle_attributes") - { - Array attr_list; - cout << "Script: toggle_attributes:"; - for (scr >> ws; scr.peek() != ';'; scr >> ws) - { - attr_list.Append(0); - scr >> attr_list.Last(); - if (attr_list.Size() <= 256) - { - cout << ' ' << attr_list.Last(); - } - else if (attr_list.Size() == 257) - { - cout << " ... " << flush; - } - } - scr.get(); // read the end symbol: ';' - cout << endl; - vs->ToggleAttributes(attr_list); - MyExpose(); - } - else if (word == "rotmat") - { - cout << "Script: rotmat:"; - for (int i = 0; i < 16; i++) - { - scr >> vs->rotmat[i/4][i%4]; - cout << ' ' << vs->rotmat[i/4][i%4]; - } - cout << endl; - MyExpose(); - } - else if (word == "camera") - { - double cam[9]; - cout << "Script: camera:"; - for (int i = 0; i < 9; i++) - { - scr >> cam[i]; - cout << ' ' << cam[i]; - } - cout << endl; - vs->cam.Set(cam); - MyExpose(); - } - else if (word == "scale") - { - double scale; - cout << "Script: scale:"; - scr >> scale; - cout << ' ' << scale; - cout << endl; - vs->Scale(scale); - MyExpose(); - } - else if (word == "translate") - { - double x, y, z; - cout << "Script: translate:"; - scr >> x >> y >> z; - cout << ' ' << x << ' ' << y << ' ' << z; - cout << endl; - vs->Translate(x, y, z); - MyExpose(); - } - else if (word == "plot_caption") - { - char delim; - scr >> ws >> delim; - getline(scr, plot_caption, delim); - vs->PrepareCaption(); // turn on or off the caption - MyExpose(); - } - else - { - cout << "Unknown command in script: " << word << endl; - } - - done_one_command = 1; - } -} - -void ScriptControl(); - -void ScriptIdleFunc() -{ - ExecuteScriptCommand(); - if (scr_level == 0) - { - ScriptControl(); - } -} - -void ScriptControl() -{ - if (scr_running) - { - scr_running = 0; - RemoveIdleFunc(ScriptIdleFunc); - } - else - { - scr_running = 1; - AddIdleFunc(ScriptIdleFunc); - } -} - -void PlayScript(istream &scr) -{ - string word; - - scr_min_val = numeric_limits::infinity(); - scr_max_val = -scr_min_val; - - // read initializing commands - while (1) - { - scr >> ws; - if (!scr.good()) - { - cout << "Error in script" << endl; - return; - } - if (scr.peek() == '#') - { - getline(scr, word); - continue; - } - scr >> word; - if (word == "window") - { - scr >> window_x >> window_y >> window_w >> window_h; - } - else if (word == "data_coll_cycle") - { - scr >> dc_cycle; - } - else if (word == "data_coll_protocol") - { - scr >> dc_protocol; - } - else if (word == "solution") - { - if (ScriptReadSolution(scr, stream_state)) - { - return; - } - - // start the visualization - break; - } - else if (word == "quadrature") - { - if (ScriptReadQuadrature(scr, stream_state)) - { - return; - } - - // start the visualization - break; - } - else if (word == "psolution") - { - if (ScriptReadParSolution(scr, stream_state)) - { - return; - } - - // start the visualization - break; - } - else if (word == "pquadrature") - { - if (ScriptReadParQuadrature(scr, stream_state)) - { - return; - } - - // start the visualization - break; - } - else if (word == "mesh") - { - if (ScriptReadDisplMesh(scr, stream_state)) - { - return; - } - if (stream_state.mesh) - { - break; - } - } - else if (word == "data_coll_mesh") - { - if (ScriptReadDataColl(scr, stream_state)) - { - return; - } - - // start the visualization - break; - } - else if (word == "data_coll_field") - { - if (ScriptReadDataColl(scr, stream_state, false)) - { - return; - } - - // start the visualization - break; - } - else if (word == "data_coll_quad") - { - if (ScriptReadDataColl(scr, stream_state, false, true)) - { - return; - } - - // start the visualization - break; - } - else - { - cout << "Unknown command in script: " << word << endl; - } - } - - scr_level = scr_running = 0; - script = &scr; - stream_state.keys.clear(); - - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. - GetMainThread(); - - std::thread worker_thread - { - [&](DataState local_state) - { - // set the thread-local DataState - stream_state = std::move(local_state); - if (c_plot_caption != string_none) - { - plot_caption = c_plot_caption; - } - if (GLVisInitVis({})) - { - GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); - GLVisStartVis(); - } - }, - std::move(stream_state) - }; - - SDLMainLoop(); - worker_thread.join(); - - delete init_nodes; init_nodes = NULL; - - cout << "Script: min_val = " << scr_min_val - << ", max_val = " << scr_max_val << endl; - - script = NULL; -} - class Session { StreamCollection input_streams; - DataState state; + Window win; std::thread handler; public: Session(bool fix_elem_orient, - bool save_coloring) + bool save_coloring, + string plot_caption) { - state.fix_elem_orient = fix_elem_orient; - state.save_coloring = save_coloring; + win.data_state.fix_elem_orient = fix_elem_orient; + win.data_state.save_coloring = save_coloring; + win.plot_caption = plot_caption; } - Session(DataState other_state) - : state(std::move(other_state)) + Session(Window other_win) + : win(std::move(other_win)) { } ~Session() = default; @@ -1142,27 +90,20 @@ class Session Session(Session&& from) = default; Session& operator= (Session&& from) = default; - inline DataState& GetState() { return state; } - inline const DataState& GetState() const { return state; } + inline DataState& GetState() { return win.data_state; } + inline const DataState& GetState() const { return win.data_state; } void StartSession() { - auto funcThread = [](DataState thread_state, StreamCollection is) + auto funcThread = [](Window w, StreamCollection is) { - // Set thread-local stream state - stream_state = std::move(thread_state); - if (c_plot_caption != string_none) - { - plot_caption = c_plot_caption; - } - - if (GLVisInitVis(std::move(is))) + if (w.GLVisInitVis(std::move(is))) { - GLVisStartVis(); + w.GLVisStartVis(); } }; handler = std::thread {funcThread, - std::move(state), std::move(input_streams)}; + std::move(win), std::move(input_streams)}; handler.detach(); } @@ -1176,7 +117,7 @@ class Session } string data_type; *ifs >> data_type >> ws; - StreamReader reader(state); + StreamReader reader(win.data_state); reader.ReadStream(*ifs, data_type); input_streams.emplace_back(std::move(ifs)); @@ -1187,7 +128,7 @@ class Session int StartStreamSession(std::unique_ptr &&stream, const std::string &data_type) { - StreamReader reader(state); + StreamReader reader(win.data_state); int ierr = reader.ReadStream(*stream, data_type); if (ierr) { return ierr; } input_streams.emplace_back(std::move(stream)); @@ -1198,7 +139,7 @@ class Session int StartStreamSession(StreamCollection &&streams) { - StreamReader reader(state); + StreamReader reader(win.data_state); int ierr = reader.ReadStreams(streams); if (ierr) { return ierr; } input_streams = std::move(streams); @@ -1210,7 +151,7 @@ class Session }; void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, - bool save_coloring) + bool save_coloring, string plot_caption) { std::vector current_sessions; string data_type; @@ -1360,7 +301,7 @@ void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, while (1); } - Session new_session(fix_elem_orient, save_coloring); + Session new_session(fix_elem_orient, save_coloring, plot_caption); constexpr int tmp_filename_size = 50; char tmp_file[tmp_filename_size]; @@ -1407,7 +348,23 @@ int main (int argc, char *argv[]) // SDL_main(). SDL_SetMainReady(); #endif + // main Window structure + Window win; + // variables for command line arguments + const char *mesh_file = string_none; + const char *sol_file = string_none; + const char *vec_sol_file = string_none; + const char *gfunc_file = string_none; + const char *qfunc_file = string_none; + string dc_protocol = string_default; + int dc_cycle = 0; + const char *arg_keys = string_none; + int pad_digits = 6; + int gf_component = -1; + int qf_component = -1; + const char *c_plot_caption = string_none; + bool secure = socketstream::secure_default; const char *visit_coll = string_none; const char *sidre_coll = string_none; const char *fms_coll = string_none; @@ -1417,6 +374,7 @@ int main (int argc, char *argv[]) const char *stream_file = string_none; const char *script_file = string_none; const char *palette_file = string_none; + const char *window_title = string_default; const char *font_name = string_default; int portnum = 19916; int multisample = GetMultisample(); @@ -1472,17 +430,17 @@ int main (int argc, char *argv[]) "Palette file."); args.AddOption(&arg_keys, "-k", "--keys", "Execute key shortcut commands in the GLVis window."); - args.AddOption(&stream_state.fix_elem_orient, "-fo", "--fix-orientations", + args.AddOption(&win.data_state.fix_elem_orient, "-fo", "--fix-orientations", "-no-fo", "--dont-fix-orientations", "Attempt to fix the orientations of inverted elements."); - args.AddOption(&stream_state.keep_attr, "-a", "--real-attributes", + args.AddOption(&win.data_state.keep_attr, "-a", "--real-attributes", "-ap", "--processor-attributes", "When opening a parallel mesh, use the real mesh attributes" " or replace them with the processor rank."); args.AddOption(&geom_ref_type, "-grt", "--geometry-refiner-type", "Set of points to use when refining geometry:" " 3 = uniform, 1 = Gauss-Lobatto, (see mfem::Quadrature1D)."); - args.AddOption(&stream_state.save_coloring, "-sc", "--save-coloring", + args.AddOption(&win.data_state.save_coloring, "-sc", "--save-coloring", "-no-sc", "--dont-save-coloring", "Save the mesh coloring generated when opening only a mesh."); args.AddOption(&portnum, "-p", "--listen-port", @@ -1496,9 +454,9 @@ int main (int argc, char *argv[]) " visualization."); args.AddOption(&stream_file, "-saved", "--saved-stream", "Load a GLVis stream saved to a file."); - args.AddOption(&window_w, "-ww", "--window-width", + args.AddOption(&win.window_w, "-ww", "--window-width", "Set the window width."); - args.AddOption(&window_h, "-wh", "--window-height", + args.AddOption(&win.window_h, "-wh", "--window-height", "Set the window height."); args.AddOption(&window_title, "-wt", "--window-title", "Set the window title."); @@ -1593,7 +551,11 @@ int main (int argc, char *argv[]) } if (arg_keys != string_none) { - stream_state.keys = arg_keys; + win.data_state.keys = arg_keys; + } + if (window_title != string_default) + { + win.window_title = window_title; } if (font_name != string_default) { @@ -1613,7 +575,7 @@ int main (int argc, char *argv[]) } if (c_plot_caption != string_none) { - plot_caption = c_plot_caption; + win.plot_caption = c_plot_caption; } if (legacy_gl_ctx == true) { @@ -1638,8 +600,9 @@ int main (int argc, char *argv[]) // initialized from the main thread. GetMainThread(); - Session stream_session(stream_state.fix_elem_orient, - stream_state.save_coloring); + Session stream_session(win.data_state.fix_elem_orient, + win.data_state.save_coloring, + win.plot_caption); if (!stream_session.StartSavedSession(stream_file)) { @@ -1661,7 +624,7 @@ int main (int argc, char *argv[]) } cout << "Running script from file: " << script_file << endl; cout << "You may need to press to execute the script steps." << endl; - PlayScript(scr); + ScriptController::PlayScript(std::move(win), scr); return 0; } @@ -1706,8 +669,9 @@ int main (int argc, char *argv[]) // Run server in new thread std::thread serverThread{GLVisServer, portnum, save_stream, - stream_state.fix_elem_orient, - stream_state.save_coloring}; + win.data_state.fix_elem_orient, + win.data_state.save_coloring, + win.plot_caption}; // Start SDL in main thread SDLMainLoop(true); @@ -1748,7 +712,7 @@ int main (int argc, char *argv[]) return 1; } - FileReader reader(stream_state, pad_digits); + FileReader reader(win.data_state, pad_digits); int ierr; if (input & INPUT_PARALLEL) { @@ -1797,7 +761,7 @@ int main (int argc, char *argv[]) mesh_only = true; } - DataCollectionReader reader(stream_state); + DataCollectionReader reader(win.data_state); reader.SetPadDigits(pad_digits); if (dc_protocol != string_default) { @@ -1821,7 +785,7 @@ int main (int argc, char *argv[]) // initialized from the main thread. GetMainThread(); - Session single_session(std::move(stream_state)); + Session single_session(std::move(win)); single_session.StartSession(); SDLMainLoop(); @@ -1850,11 +814,3 @@ void PrintSampleUsage(ostream &os) " glvis -np <#proc> -m [-q ]\n\n" "All Options:\n"; } - -void SwitchQuadSolution() -{ - int iqs = ((int)stream_state.GetQuadSolution()+1) - % ((int)DataState::QuadSolution::MAX); - stream_state.SwitchQuadSolution((DataState::QuadSolution)iqs, vs); - SendExposeEvent(); -} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a3e65200..4f63a6c0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND SOURCES openglvis.cpp palettes.cpp palettes_base.cpp + script_controller.cpp sdl.cpp sdl_helper.cpp sdl_main.cpp @@ -32,7 +33,8 @@ list(APPEND SOURCES vssolution.cpp vssolution3d.cpp vsvector.cpp - vsvector3d.cpp) + vsvector3d.cpp + window.cpp) list(APPEND HEADERS gl/attr_traits.hpp @@ -53,6 +55,7 @@ list(APPEND HEADERS openglvis.hpp palettes.hpp palettes_base.hpp + script_controller.hpp sdl.hpp sdl_helper.hpp sdl_main.hpp @@ -63,7 +66,8 @@ list(APPEND HEADERS vssolution.hpp vssolution3d.hpp vsvector.hpp - vsvector3d.hpp) + vsvector3d.hpp + window.hpp) if(EMSCRIPTEN) # Emscripten build target diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 7fad3f3b..0433001b 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -13,6 +13,8 @@ #include "palettes.hpp" #include "stream_reader.hpp" #include "visual.hpp" +// #include "window.hpp" +// #include #ifdef GLVIS_USE_LIBPNG #include @@ -24,16 +26,15 @@ #include // used in extern context -thread_local std::string plot_caption; -thread_local std::string extra_caption; thread_local mfem::GeometryRefiner GLVisGeometryRefiner; -static VisualizationSceneScalarData * vs = nullptr; - // either bitmap data or png bytes std::vector * screen_state = nullptr; -DataState stream_state; +static Window win; +// using StreamCollection = std::vector>; +// static StreamCollection input_streams; +// static std::thread handler; int last_stream_nproc = 1; @@ -44,196 +45,50 @@ namespace js using namespace mfem; -/// Switch representation of the quadrature function -void SwitchQuadSolution(); - /// Display a new stream -void display(std::stringstream & commands, const int w, const int h) +void display(StreamCollection streams, const int w, const int h) { // reset antialiasing - GetAppWindow()->getRenderer().setAntialiasing(0); - - std::string word; - double minv = 0.0, maxv = 0.0; - while (commands >> word) - { - if (word == "keys") - { - std::cout << "parsing 'keys'" << std::endl; - commands >> stream_state.keys; - } - else if (word == "valuerange") - { - std::cout << "parsing 'valuerange'" << std::endl; - commands >> minv >> maxv; - } - else - { - std::cout << "unknown command '" << word << "'" << std::endl; - } - } - - DataState::FieldType field_type = stream_state.GetType(); - - if (field_type <= DataState::FieldType::MIN - || field_type >= DataState::FieldType::MAX) - { - return; - } - - if (InitVisualization("glvis", 0, 0, w, h)) - { - return; - } - - delete vs; - vs = nullptr; - - if (stream_state.quad_f) - { - GetAppWindow()->setOnKeyDown('Q', SwitchQuadSolution); - } - - double mesh_range = -1.0; - if (field_type == DataState::FieldType::SCALAR - || field_type == DataState::FieldType::MESH) - { - if (stream_state.grid_f) - { - stream_state.grid_f->GetNodalValues(stream_state.sol); - } - if (stream_state.mesh->SpaceDimension() == 2) - { - VisualizationSceneSolution * vss; - if (stream_state.normals.Size() > 0) - { - vs = vss = new VisualizationSceneSolution(*stream_state.mesh, stream_state.sol, - stream_state.mesh_quad.get(), &stream_state.normals); - } - else - { - vs = vss = new VisualizationSceneSolution(*stream_state.mesh, stream_state.sol, - stream_state.mesh_quad.get()); - } - if (stream_state.grid_f) - { - vss->SetGridFunction(*stream_state.grid_f); - } - if (field_type == DataState::FieldType::MESH) - { - vs->OrthogonalProjection = 1; - vs->SetLight(0); - vs->Zoom(1.8); - // Use the 'bone' palette when visualizing a 2D mesh only (otherwise - // the 'jet-like' palette is used in 2D, see vssolution.cpp). - vs->palette.SetIndex(4); - } - } - else if (stream_state.mesh->SpaceDimension() == 3) - { - VisualizationSceneSolution3d * vss; - vs = vss = new VisualizationSceneSolution3d(*stream_state.mesh, - stream_state.sol, stream_state.mesh_quad.get()); - if (stream_state.grid_f) - { - vss->SetGridFunction(stream_state.grid_f.get()); - } - if (field_type == DataState::FieldType::MESH) - { - if (stream_state.mesh->Dimension() == 3) - { - // Use the 'white' palette when visualizing a 3D volume mesh only - vs->palette.SetIndex(11); - vss->SetLightMatIdx(4); - } - else - { - // Use the 'bone' palette when visualizing a surface mesh only - vs->palette.SetIndex(4); - } - // Otherwise, the 'vivid' palette is used in 3D see vssolution3d.cpp - - vss->ToggleDrawAxes(); - vss->ToggleDrawMesh(); - } - } - if (field_type == DataState::FieldType::MESH) - { - if (stream_state.grid_f) - { - mesh_range = stream_state.grid_f->Max() + 1.0; - } - else - { - mesh_range = stream_state.sol.Max() + 1.0; - } - } - } - else if (field_type == DataState::FieldType::VECTOR) - { - if (stream_state.mesh->SpaceDimension() == 2) - { - if (stream_state.grid_f) - { - vs = new VisualizationSceneVector(*stream_state.grid_f); - } - else - { - vs = new VisualizationSceneVector(*stream_state.mesh, stream_state.solu, - stream_state.solv, stream_state.mesh_quad.get()); - } - } - else if (stream_state.mesh->SpaceDimension() == 3) - { - if (stream_state.grid_f) - { - stream_state.ProjectVectorFEGridFunction(); - vs = new VisualizationSceneVector3d(*stream_state.grid_f, - stream_state.mesh_quad.get()); - } - else - { - vs = new VisualizationSceneVector3d(*stream_state.mesh, stream_state.solu, - stream_state.solv, stream_state.solw, - stream_state.mesh_quad.get()); - } - } - } + win.wnd->getRenderer().setAntialiasing(0); - if (vs) - { - // increase the refinement factors if visualizing a GridFunction - if (stream_state.grid_f) - { - vs->AutoRefine(); - vs->SetShading(VisualizationSceneScalarData::Shading::Noncomforming, true); - } - if (mesh_range > 0.0) - { - vs->SetValueRange(-mesh_range, mesh_range); - vs->SetAutoscale(0); - } - if (stream_state.mesh->SpaceDimension() == 2 && - field_type == DataState::FieldType::MESH) - { - SetVisualizationScene(vs, 2); - } - else - { - SetVisualizationScene(vs, 3); - } - } - - CallKeySequence(stream_state.keys.c_str()); + win.window_title = "glvis"; + win.window_x = 0.; + win.window_y = 0.; + win.window_w = w; + win.window_h = h; - if (minv || maxv) - { - vs->SetValueRange(minv, maxv); - } + win.GLVisInitVis(std::move(streams)); - SendExposeEvent(); + win.comm_thread->process_one(); } +// void display2(const int w, const int h) +// { +// // reset antialiasing +// win.wnd->getRenderer().setAntialiasing(0); + +// win.window_title = "glvis"; +// win.window_x = 0.; +// win.window_y = 0.; +// win.window_w = w; +// win.window_h = h; + +// auto funcThread = [](Window w, StreamCollection is) +// { +// if (w.GLVisInitVis(std::move(is))) +// { +// w.GLVisStartVis(); +// } +// }; +// handler = std::thread {funcThread, +// std::move(win), std::move(input_streams)}; +// handler.detach(); + +// // win.GLVisInitVis({}); +// // CallKeySequence(win.data_state.keys.c_str()); +// // SendExposeEvent(); +// } + // // StreamReader::ReadStream requires a list of unique_ptr to istream and since // we cannot directly pass a list of string we need to repack the strings into @@ -242,63 +97,66 @@ void display(std::stringstream & commands, const int w, const int h) // each string in streams must start with `parallel ' // using StringArray = std::vector; -void processParallelStreams(DataState & state, - const StringArray & streams, - std::stringstream * commands = nullptr) +StreamCollection processParallelStreams(DataState & state, + const StringArray & streams) { // std::cerr << "got " << streams.size() << " streams" << std::endl; - // HACK: match unique_ptr interface for ReadStreams: - std::vector sstreams(streams.size()); StreamCollection istreams(streams.size()); for (int i = 0; i < streams.size(); ++i) { - sstreams[i] = std::stringstream(streams[i]); + istreams[i] = std::unique_ptr(new std::stringstream(streams[i])); // pull off the first list std::string word; int nproc, rank; - sstreams[i] >> word >> nproc >> rank; - // std::cerr << "packing " << rank+1 << "/" << nproc << std::endl; - istreams[i] = std::unique_ptr(&sstreams[i]); + *istreams[i] >> word >> nproc >> rank; } StreamReader reader(state); reader.ReadStreams(istreams); - if (commands) - { - commands->seekg(istreams[0]->tellg()); - } - - // HACK: don't let unique_ptr free the data - for (int i = 0; i < streams.size(); ++i) - { - istreams[i].release(); - } - last_stream_nproc = streams.size(); + + return istreams; } void displayParallelStreams(const StringArray & streams, const int w, const int h) { - std::stringstream commands(streams[0]); - processParallelStreams(stream_state, streams, &commands); + StreamCollection sc = processParallelStreams(win.data_state, streams); - display(commands, w, h); + display(std::move(sc), w, h); } void displayStream(const std::string & stream, const int w, const int h) { - std::stringstream ss(stream); + std::unique_ptr ss(new std::istringstream(stream)); std::string data_type; - ss >> data_type; + *ss >> data_type; - StreamReader reader(stream_state); - reader.ReadStream(ss, data_type); + StreamReader reader(win.data_state); + reader.ReadStream(*ss, data_type); - display(ss, w, h); + StreamCollection sc; + sc.emplace_back(std::move(ss)); + display(std::move(sc), w, h); } +// void displayStream(const std::string & stream, const int w, const int h) +// { +// std::stringstream ss(stream); +// std::string data_type; +// ss >> data_type >> ws; + +// StreamReader reader(win.data_state); +// reader.ReadStream(ss, data_type); +// input_streams.emplace_back(std::move(&ss)); + +// // display(ss, w, h); +// display2(w, h); +// } + + + // // update the existing stream // @@ -306,11 +164,11 @@ int update(DataState & new_state) { double mesh_range = -1.0; - if (stream_state.SetNewMeshAndSolution(std::move(new_state), vs)) + if (win.SetNewMeshAndSolution(std::move(new_state))) { if (mesh_range > 0.0) { - vs->SetValueRange(-mesh_range, mesh_range); + win.vs->SetValueRange(-mesh_range, mesh_range); } SendExposeEvent(); @@ -357,13 +215,13 @@ int updateParallelStreams(const StringArray & streams) // void iterVisualization() { - GetAppWindow()->mainIter(); + win.wnd->mainIter(); } void setCanvasId(const std::string & id) { std::cout << "glvis: setting canvas id to " << id << std::endl; - GetAppWindow()->setCanvasId(id); + win.wnd->setCanvasId(id); } void disableKeyHandling() @@ -394,7 +252,7 @@ void processKey(int sym, bool ctrl=false, bool shift=false, bool alt=false) mod |= ctrl ? KMOD_CTRL : 0; mod |= shift ? KMOD_SHIFT : 0; mod |= alt ? KMOD_ALT : 0; - GetAppWindow()->callKeyDown(sym, mod); + win.wnd->callKeyDown(sym, mod); } void setupResizeEventCallback(const std::string & id) @@ -424,9 +282,8 @@ std::string getHelpString() em::val getScreenBuffer(bool flip_y=false) { MyExpose(); - auto * wnd = GetAppWindow(); int w, h; - wnd->getGLDrawSize(w, h); + win.wnd->getGLDrawSize(w, h); // 4 bytes for rgba const size_t buffer_size = w*h*4; @@ -463,25 +320,16 @@ em::val getScreenBuffer(bool flip_y=false) screen_state->data())); } -void SwitchQuadSolution() -{ - int iqs = ((int)stream_state.GetQuadSolution()+1) - % ((int)DataState::QuadSolution::MAX); - stream_state.SwitchQuadSolution((DataState::QuadSolution)iqs, vs); - SendExposeEvent(); -} - #ifdef GLVIS_USE_LIBPNG em::val getPNGByteArray() { constexpr const char * filename = "im.png"; - auto * wnd = GetAppWindow(); int w, h; - wnd->getGLDrawSize(w, h); + win.wnd->getGLDrawSize(w, h); MyExpose(); // save to in-memory file - int status = SaveAsPNG(filename, w, h, wnd->isHighDpi(), true); + int status = SaveAsPNG(filename, w, h, win.wnd->isHighDpi(), true); if (status != 0) { fprintf(stderr, "unable to generate png\n"); diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index d7e4b282..8f82e971 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -15,12 +15,15 @@ #include #include #include +#include #include "mfem.hpp" #include "sdl.hpp" #include "palettes.hpp" #include "visual.hpp" +#ifndef __EMSCRIPTEN__ #include "gl2ps.h" +#endif #if defined(GLVIS_USE_LIBTIFF) #include "tiffio.h" @@ -35,9 +38,9 @@ using namespace mfem; -thread_local int visualize = 0; -thread_local VisualizationScene * locscene; -thread_local GLVisCommand *glvis_command = NULL; +static thread_local int visualize = 0; +static thread_local VisualizationScene *locscene = NULL; +static thread_local GLVisCommand *glvis_command = NULL; #ifdef GLVIS_MULTISAMPLE static int glvis_multisample = GLVIS_MULTISAMPLE; @@ -45,17 +48,23 @@ static int glvis_multisample = GLVIS_MULTISAMPLE; static int glvis_multisample = -1; #endif -float line_w = 1.f; -float line_w_aa = gl3::LINE_WIDTH_AA; +static float line_w = 1.f; +static float line_w_aa = gl3::LINE_WIDTH_AA; + +static thread_local SdlWindow * wnd = nullptr; +static bool wndLegacyGl = false; +bool wndUseHiDPI = true; // shared with sdl_main.cpp -thread_local SdlWindow * wnd = nullptr; -bool wndLegacyGl = false; -bool wndUseHiDPI = true; void SDLMainLoop(bool server_mode) { SdlWindow::StartSDL(server_mode); } +void SetGLVisCommand(GLVisCommand *cmd) +{ + glvis_command = cmd; +} + SdlWindow * GetAppWindow() { return wnd; @@ -79,7 +88,7 @@ void SetUseHiDPI(bool status) void MyExpose(GLsizei w, GLsizei h); void MyExpose(); -int InitVisualization (const char name[], int x, int y, int w, int h) +SdlWindow* InitVisualization(const char name[], int x, int y, int w, int h) { #ifdef GLVIS_DEBUG @@ -90,7 +99,9 @@ int InitVisualization (const char name[], int x, int y, int w, int h) wnd = new SdlWindow(); if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) { - return 1; + delete wnd; + wnd = nullptr; + return NULL; } } else @@ -193,7 +204,7 @@ int InitVisualization (const char name[], int x, int y, int w, int h) #endif locscene = nullptr; - return 0; + return wnd; } void SendKeySequence(const char *seq) @@ -363,8 +374,7 @@ void RunVisualization() wnd->mainLoop(); #endif InitIdleFuncs(); - delete locscene; - delete wnd; + visualize = 0; wnd = nullptr; } @@ -470,7 +480,7 @@ bool CommunicationIdleFunc() bool MainIdleFunc() { bool sleep = true; -#ifndef __EMSCRIPTEN__ + if (glvis_command && visualize == 1 && !(IdleFuncs.Size() > 0 && use_idle)) { @@ -493,24 +503,8 @@ bool MainIdleFunc() sleep = false; } use_idle = !use_idle; -#else - if (IdleFuncs.Size() > 0) - { - LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size(); - if (IdleFuncs[LastIdleFunc]) - { - (*IdleFuncs[LastIdleFunc])(); - } - // Continue executing idle functions - sleep = false; - } -#endif + return sleep; - LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size(); - if (IdleFuncs[LastIdleFunc]) - { - (*IdleFuncs[LastIdleFunc])(); - } } void AddIdleFunc(void (*Func)(void)) @@ -1169,6 +1163,7 @@ void KeyS() SendExposeEvent(); } +#ifndef __EMSCRIPTEN__ inline GL2PSvertex CreatePrintVtx(gl3::FeedbackVertex v) { return @@ -1211,6 +1206,7 @@ void PrintCaptureBuffer(gl3::CaptureBuffer& cbuf) gl2psText(entry.text.c_str(), "Times", 12); } } +#endif void KeyCtrlP() { diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 9929639b..97286abf 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -23,8 +23,11 @@ void SDLMainLoop(bool server_mode = false); +class GLVisCommand; +void SetGLVisCommand(GLVisCommand *cmd); + /// Initializes the visualization and some keys. -int InitVisualization(const char name[], int x, int y, int w, int h); +SdlWindow* InitVisualization(const char name[], int x, int y, int w, int h); void SetVisualizationScene(VisualizationScene * scene, int view = 3, const char *keys = NULL); diff --git a/lib/coll_reader.hpp b/lib/coll_reader.hpp index a0c6f86c..68a5eb20 100644 --- a/lib/coll_reader.hpp +++ b/lib/coll_reader.hpp @@ -19,7 +19,7 @@ class DataCollectionReader { DataState &data; - int pad_digits; + int pad_digits = 6; std::string protocol; public: diff --git a/lib/data_state.cpp b/lib/data_state.cpp index c678a851..461f233b 100644 --- a/lib/data_state.cpp +++ b/lib/data_state.cpp @@ -10,7 +10,6 @@ // CONTRIBUTING.md for details. #include "data_state.hpp" -#include "visual.hpp" #include @@ -523,8 +522,7 @@ void DataState::SetQuadSolution(QuadSolution quad_type) quad_sol = quad_type; } -void DataState::SwitchQuadSolution(QuadSolution quad_type, - VisualizationScene *vs) +void DataState::SwitchQuadSolution(QuadSolution quad_type) { unique_ptr old_mesh; // we must backup the refined mesh to prevent its deleting @@ -535,7 +533,6 @@ void DataState::SwitchQuadSolution(QuadSolution quad_type, } SetQuadSolution(quad_type); ExtrudeMeshAndSolution(); - ResetMeshAndSolution(*this, vs); } // Replace a given VectorFiniteElement-based grid function (e.g. from a Nedelec @@ -564,67 +561,6 @@ DataState::ProjectVectorFEGridFunction(std::unique_ptr gf) return gf; } -bool DataState::SetNewMeshAndSolution(DataState new_state, - VisualizationScene* vs) -{ - if (new_state.mesh->SpaceDimension() == mesh->SpaceDimension() && - new_state.grid_f->VectorDim() == grid_f->VectorDim()) - { - ResetMeshAndSolution(new_state, vs); - - internal.grid_f = std::move(new_state.internal.grid_f); - internal.mesh = std::move(new_state.internal.mesh); - internal.quad_f = std::move(new_state.internal.quad_f); - internal.mesh_quad = std::move(new_state.internal.mesh_quad); - - return true; - } - else - { - return false; - } -} - -void DataState::ResetMeshAndSolution(DataState &ss, VisualizationScene* vs) -{ - if (ss.mesh->SpaceDimension() == 2) - { - if (ss.grid_f->VectorDim() == 1) - { - VisualizationSceneSolution *vss = - dynamic_cast(vs); - ss.grid_f->GetNodalValues(ss.sol); - vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &ss.sol, - ss.grid_f.get()); - } - else - { - VisualizationSceneVector *vsv = - dynamic_cast(vs); - vsv->NewMeshAndSolution(*ss.grid_f, ss.mesh_quad.get()); - } - } - else - { - if (ss.grid_f->VectorDim() == 1) - { - VisualizationSceneSolution3d *vss = - dynamic_cast(vs); - ss.grid_f->GetNodalValues(ss.sol); - vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &ss.sol, - ss.grid_f.get()); - } - else - { - ss.ProjectVectorFEGridFunction(); - - VisualizationSceneVector3d *vss = - dynamic_cast(vs); - vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), ss.grid_f.get()); - } - } -} - void ::VectorExtrudeCoefficient::Eval(Vector &v, ElementTransformation &T, const IntegrationPoint &ip) { diff --git a/lib/data_state.hpp b/lib/data_state.hpp index 47fdc639..3ee65235 100644 --- a/lib/data_state.hpp +++ b/lib/data_state.hpp @@ -144,8 +144,8 @@ struct DataState /// Set the quadrature function representation producing a proxy grid function void SetQuadSolution(QuadSolution type = QuadSolution::LOR_ClosedGL); - /// Switch the quadrature function representation and update the visualization - void SwitchQuadSolution(QuadSolution type, VisualizationScene* vs); + /// Switch the quadrature function representation + void SwitchQuadSolution(QuadSolution type); /// Get the current representation of quadrature solution inline QuadSolution GetQuadSolution() const { return quad_sol; } @@ -158,22 +158,6 @@ struct DataState void ProjectVectorFEGridFunction() { internal.grid_f = ProjectVectorFEGridFunction(std::move(internal.grid_f)); } - - /// Sets a new mesh and solution from another DataState object, and - /// updates the given VisualizationScene pointer with the new data. - /// - /// Mesh space and grid function dimensions must both match the original - /// dimensions of the current DataState. If there is a mismatch in either - /// value, the function will return false, and the mesh/solution will not be - /// updated. - bool SetNewMeshAndSolution(DataState new_state, - VisualizationScene* vs); - - /// Updates the given VisualizationScene pointer with the new data - /// of the given DataState object. - /// @note: Use with caution when the update is compatible - /// @see SetNewMeshAndSolution() - static void ResetMeshAndSolution(DataState &ss, VisualizationScene* vs); }; #endif // GLVIS_DATA_STATE_HPP diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index c5981a75..6d131896 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -112,8 +112,10 @@ void Camera::Print() << std::endl; } -VisualizationScene::VisualizationScene() +VisualizationScene::VisualizationScene(SdlWindow &wnd_) { + wnd = &wnd_; + translmat = glm::mat4(1.0); rotmat = glm::mat4(1.0); rotmat = glm::rotate(rotmat, glm::radians(-60.f), glm::vec3(1.f, 0.f, 0.f)); @@ -130,7 +132,7 @@ VisualizationScene::VisualizationScene() cut_updated = false; background = BG_WHITE; - GetAppWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); + wnd->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); _use_cust_l0_pos = false; light_mat_idx = 3; use_light = true; @@ -1068,12 +1070,12 @@ void VisualizationScene::ToggleBackground() if (background == BG_BLK) { background = BG_WHITE; - GetAppWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); + wnd->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); } else { background = BG_BLK; - GetAppWindow()->getRenderer().setClearColor(0.f, 0.f, 0.f, 1.f); + wnd->getRenderer().setClearColor(0.f, 0.f, 0.f, 1.f); } } diff --git a/lib/openglvis.hpp b/lib/openglvis.hpp index 1455242d..9cbb3899 100644 --- a/lib/openglvis.hpp +++ b/lib/openglvis.hpp @@ -171,7 +171,7 @@ class VisualizationScene const gl3::GlDrawable &gl_drawable); public: - VisualizationScene(); + VisualizationScene(SdlWindow &wnd); virtual ~VisualizationScene(); int spinning, OrthogonalProjection, print, movie; diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp new file mode 100644 index 00000000..d7097907 --- /dev/null +++ b/lib/script_controller.cpp @@ -0,0 +1,984 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "script_controller.hpp" +#include "file_reader.hpp" +#include "coll_reader.hpp" +#include "stream_reader.hpp" +#include "visual.hpp" + +#include +#include +#include + +using namespace std; +using namespace mfem; + +enum class Command +{ + Mesh, + Solution, + ParSolution, + Quadrature, + ParQuadrature, + DataCollMesh, + DataCollField, + DataCollQuad, + DataCollCycle, + DataCollProto, + Screenshot, + Viewcenter, + Perspective, + Light, + View, + Zoom, + Shading, + Subdivisions, + Valuerange, + Autoscale, + Levellines, + AxisNumberFormat, + ColorbarNumberFormat, + Window, + Keys, + Palette, + PaletteRepeat, + ToggleAttributes, + Rotmat, + Camera, + Scale, + Translate, + PlotCaption, + //---------- + Max +}; + +class ScriptCommands +{ + struct CmdItem + { + const char *keyword; + const char *params; + const char *desc; + + bool operator==(const string &key) const { return key == keyword; } + }; + array commands; + +public: + ScriptCommands(); + + decltype(commands)::const_iterator begin() const { return commands.begin(); } + decltype(commands)::const_iterator end() const { return commands.end(); } + CmdItem& operator[](Command cmd) { return commands[(size_t)cmd]; } + const CmdItem& operator[](Command cmd) const { return commands[(size_t)cmd]; } +}; +static const ScriptCommands commands; + +ScriptCommands::ScriptCommands() +{ + (*this)[Command::Mesh] = {"mesh", "", "Visualize the mesh."}; + (*this)[Command::Solution] = {"solution", " ", "Visualize the solution."}; + (*this)[Command::ParSolution] = {"psolution", " ", "Visualize the distributed solution."}; + (*this)[Command::Quadrature] = {"quadrature", " ", "Visualize the quadrature."}; + (*this)[Command::ParQuadrature] = {"pquadrature", " ", "Visualize the distributed quadrature."}; + (*this)[Command::DataCollMesh] = {"data_coll_mesh", " ", "Visualize the mesh from data collection."}; + (*this)[Command::DataCollField] = {"data_coll_field", " ", "Visualize the field from data collection."}; + (*this)[Command::DataCollQuad] = {"data_coll_quad", " ", "Visualize the Q-field from data collection."}; + (*this)[Command::DataCollCycle] = {"data_coll_cycle", "", "Preset the cycle of the data collection."}; + (*this)[Command::DataCollProto] = {"data_coll_protocol", "", "Preset the protocol of the data collection."}; + (*this)[Command::Screenshot] = {"screenshot", "", "Take a screenshot, saving it to the file."}; + (*this)[Command::Viewcenter] = {"viewcenter", " ", "Change the viewcenter."}; + (*this)[Command::Perspective] = {"perspective", "", "Turn on or off perspective projection."}; + (*this)[Command::Light] = {"light", "", "Turn on or off light."}; + (*this)[Command::View] = {"view", " ", "Change the solid angle of view."}; + (*this)[Command::Zoom] = {"zoom", "", "Change the zoom factor."}; + (*this)[Command::Shading] = {"shading", "", "Change the shading algorithm."}; + (*this)[Command::Subdivisions] = {"subdivisions", " ", "Change the refinement level."}; + (*this)[Command::Valuerange] = {"valuerange", " ", "Change the value range."}; + (*this)[Command::Autoscale] = {"autoscale", "", "Change the autoscale algorithm."}; + (*this)[Command::Levellines] = {"levellines", " ", "Set the level lines."}; + (*this)[Command::AxisNumberFormat] = {"axis_numberformat", "''", "Set the axis number format."}; + (*this)[Command::ColorbarNumberFormat] = {"colorbar_numberformat", "''", "Set the colorbar number format."}; + (*this)[Command::Window] = {"window", " ", "Set the position and size of the window."}; + (*this)[Command::Keys] = {"keys", "", "Send the control key sequence."}; + (*this)[Command::Palette] = {"palette", "", "Set the palette index."}; + (*this)[Command::PaletteRepeat] = {"palette_repeat", "", "Set the repetition of the palette."}; + (*this)[Command::ToggleAttributes] = {"toggle_attributes", "<1/0> [[<1/0>] ...];", "Toggle visibility of the attributes."}; + (*this)[Command::Rotmat] = {"rotmat", "<[0,0]> <[1,0]> ... <[3,3]>", "Set the rotation matrix."}; + (*this)[Command::Camera] = {"camera", " ... ... ... ", "Set the camera position, direction and upward vector."}; + (*this)[Command::Scale] = {"scale", "", "Set the scaling factor."}; + (*this)[Command::Translate] = {"translate", " ", "Set the translation coordinates."}; + (*this)[Command::PlotCaption] = {"plot_caption", "''", "Set the plot caption."}; +} + +int ScriptController::ScriptReadSolution(istream &scr, DataState &state) +{ + int err_read; + string mword,sword; + + cout << "Script: solution: " << flush; + // read the mesh + scr >> ws >> mword; // mesh filename (can't contain spaces) + cout << "mesh: " << mword << "; " << flush; + named_ifgzstream imesh(mword.c_str()); + if (!imesh) + { + cout << "Can not open mesh file: " << mword << endl; + return 1; + } + state.SetMesh(new Mesh(imesh, 1, 0, state.fix_elem_orient)); + + // read the solution (GridFunction) + scr >> ws >> sword; + cout << "solution: " << sword << endl; + + FileReader reader(state); + err_read = reader.ReadSerial(FileReader::FileType::GRID_FUNC, mword.c_str(), + sword.c_str()); + + return err_read; +} + +int ScriptController::ScriptReadQuadrature(istream &scr, DataState &state) +{ + int err_read; + string mword,sword; + + cout << "Script: quadrature: " << flush; + // read the mesh + scr >> ws >> mword; // mesh filename (can't contain spaces) + cout << "mesh: " << mword << "; " << flush; + named_ifgzstream imesh(mword.c_str()); + if (!imesh) + { + cout << "Can not open mesh file: " << mword << endl; + return 1; + } + state.SetMesh(new Mesh(imesh, 1, 0, state.fix_elem_orient)); + + // read the quadrature (QuadratureFunction) + scr >> ws >> sword; + cout << "quadrature: " << sword << endl; + + FileReader reader(state); + err_read = reader.ReadSerial(FileReader::FileType::QUAD_FUNC, mword.c_str(), + sword.c_str()); + + return err_read; +} + +int ScriptController::ScriptReadParSolution(istream &scr, DataState &state) +{ + int np, scr_keep_attr, err_read; + string mesh_prefix, sol_prefix; + + cout << "Script: psolution: " << flush; + // read number of processors + scr >> np; + cout << "# processors: " << np << "; " << flush; + // read the mesh prefix + scr >> ws >> mesh_prefix; // mesh prefix (can't contain spaces) + cout << "mesh prefix: " << mesh_prefix << "; " << flush; + scr >> ws >> scr_keep_attr; + if (scr_keep_attr) + { + cout << "(real attributes); " << flush; + } + else + { + cout << "(processor attributes); " << flush; + } + state.keep_attr = scr_keep_attr; + + // read the solution prefix + scr >> ws >> sol_prefix; + cout << "solution prefix: " << sol_prefix << endl; + + FileReader reader(state); + err_read = reader.ReadParallel(np, FileReader::FileType::GRID_FUNC, + mesh_prefix.c_str(), sol_prefix.c_str()); + return err_read; +} + +int ScriptController::ScriptReadParQuadrature(istream &scr, DataState &state) +{ + int np, scr_keep_attr, err_read; + string mesh_prefix, quad_prefix; + + cout << "Script: pquadrature: " << flush; + // read number of processors + scr >> np; + cout << "# processors: " << np << "; " << flush; + // read the mesh prefix + scr >> ws >> mesh_prefix; // mesh prefix (can't contain spaces) + cout << "mesh prefix: " << mesh_prefix << "; " << flush; + scr >> ws >> scr_keep_attr; + if (scr_keep_attr) + { + cout << "(real attributes); " << flush; + } + else + { + cout << "(processor attributes); " << flush; + } + state.keep_attr = scr_keep_attr; + + // read the quadrature prefix + scr >> ws >> quad_prefix; + cout << "quadrature prefix: " << quad_prefix << endl; + + FileReader reader(state); + err_read = reader.ReadParallel(np, FileReader::FileType::QUAD_FUNC, + mesh_prefix.c_str(), quad_prefix.c_str()); + return err_read; +} + +int ScriptController::ScriptReadDisplMesh(istream &scr, DataState &state) +{ + DataState meshstate; + string word; + + cout << "Script: mesh: " << flush; + scr >> ws >> word; + { + named_ifgzstream imesh(word.c_str()); + if (!imesh) + { + cout << "Can not open mesh file: " << word << endl; + return 1; + } + cout << word << endl; + meshstate.SetMesh(new Mesh(imesh, 1, 0, state.fix_elem_orient)); + } + meshstate.ExtrudeMeshAndSolution(); + Mesh* const m = meshstate.mesh.get(); + if (init_nodes == NULL) + { + init_nodes.reset(new Vector); + meshstate.mesh->GetNodes(*init_nodes); + state.SetMesh(NULL); + state.SetGridFunction(NULL); + } + else + { + FiniteElementCollection *vfec = NULL; + FiniteElementSpace *vfes; + vfes = (FiniteElementSpace *)m->GetNodalFESpace(); + if (vfes == NULL) + { + vfec = new LinearFECollection; + vfes = new FiniteElementSpace(m, vfec, m->SpaceDimension()); + } + + meshstate.SetGridFunction(new GridFunction(vfes)); + GridFunction * const g = meshstate.grid_f.get(); + if (vfec) + { + g->MakeOwner(vfec); + } + m->GetNodes(*g); + if (g->Size() == init_nodes->Size()) + { + subtract(*init_nodes, *g, *g); + } + else + { + cout << "Script: incompatible meshes!" << endl; + *g = 0.0; + } + + state = std::move(meshstate); + } + + return 0; +} + +int ScriptController::ScriptReadDataColl(istream &scr, DataState &state, + bool mesh_only, bool quad) +{ + int err_read; + int type; + string cword, fword; + + cout << "Script: data_collection: " << flush; + // read the collection + scr >> ws >> type; // collection type + cout << "type: " << type << "; " << flush; + scr >> ws >> cword; // collection filename (can't contain spaces) + cout << "collection: " << cword << "; " << flush; + + if (!mesh_only) + { + // read the field + scr >> ws >> fword; + cout << "field: " << fword << endl; + } + + DataCollectionReader reader(state); + if (dc_protocol != string_default) + { + reader.SetProtocol(dc_protocol.c_str()); + } + + if (mesh_only) + err_read = reader.ReadSerial((DataCollectionReader::CollType)type, + cword.c_str(), dc_cycle); + else + err_read = reader.ReadSerial((DataCollectionReader::CollType)type, + cword.c_str(), dc_cycle, fword.c_str(), quad); + + return err_read; +} + +void ScriptController::PrintCommands() +{ + cout << "Available commands are:" << endl; + + for (const auto &ci : commands) + { + cout << "\t" << ci.keyword << " " << ci.params << " - " << ci.desc << endl; + } +} + +void ScriptController::ExecuteScriptCommand() +{ + if (!script) + { + cout << "No script stream defined! (Bug?)" << endl; + return; + } + + istream &scr = *script; + string word; + int done_one_command = 0; + while (!done_one_command) + { + scr >> ws; + if (!scr.good()) + { + cout << "End of script." << endl; + scr_level = 0; + return; + } + if (scr.peek() == '#') + { + getline(scr, word); + continue; + } + scr >> word; + if (word == "{") + { + scr_level++; + continue; + } + else if (word == "}") + { + scr_level--; + if (scr_level < 0) + { + scr_level = 0; + } + continue; + } + + auto it = find(commands.begin(), commands.end(), word); + if (it == commands.end()) + { + cout << "Unknown command in script: " << word << endl; + PrintCommands(); + break; + } + + const Command cmd = (Command)(it - commands.begin()); + switch (cmd) + { + case Command::Mesh: + case Command::Solution: + case Command::ParSolution: + case Command::Quadrature: + case Command::ParQuadrature: + case Command::DataCollMesh: + case Command::DataCollField: + case Command::DataCollQuad: + { + DataState new_state; + + switch (cmd) + { + case Command::Solution: + if (ScriptReadSolution(scr, new_state)) + { + done_one_command = 1; + continue; + } + break; + case Command::Quadrature: + if (ScriptReadQuadrature(scr, new_state)) + { + done_one_command = 1; + continue; + } + break; + case Command::Mesh: + if (ScriptReadDisplMesh(scr, new_state)) + { + done_one_command = 1; + continue; + } + if (new_state.mesh == NULL) + { + cout << "Script: unexpected 'mesh' command!" << endl; + done_one_command = 1; + continue; + } + break; + case Command::ParSolution: + if (ScriptReadParSolution(scr, new_state)) + { + done_one_command = 1; + continue; + } + break; + case Command::ParQuadrature: + if (ScriptReadParQuadrature(scr, new_state)) + { + done_one_command = 1; + continue; + } + break; + case Command::DataCollMesh: + if (ScriptReadDataColl(scr, new_state)) + { + done_one_command = 1; + continue; + } + break; + case Command::DataCollField: + if (ScriptReadDataColl(scr, new_state, false)) + { + done_one_command = 1; + continue; + } + break; + case Command::DataCollQuad: + if (ScriptReadDataColl(scr, new_state, false, true)) + { + done_one_command = 1; + continue; + } + break; + default: + break; + } + + if (win.SetNewMeshAndSolution(std::move(new_state))) + { + MyExpose(); + } + else + { + cout << "Different type of mesh / solution." << endl; + } + } + break; + case Command::DataCollCycle: + scr >> dc_cycle; + break; + case Command::DataCollProto: + scr >> dc_protocol; + break; + case Command::Screenshot: + { + scr >> ws >> word; + + cout << "Script: screenshot: " << flush; + + if (Screenshot(word.c_str(), true)) + { + cout << "Screenshot(" << word << ") failed." << endl; + done_one_command = 1; + continue; + } + cout << "-> " << word << endl; + + if (scr_min_val > win.vs->GetMinV()) + { + scr_min_val = win.vs->GetMinV(); + } + if (scr_max_val < win.vs->GetMaxV()) + { + scr_max_val = win.vs->GetMaxV(); + } + } + break; + case Command::Viewcenter: + { + scr >> win.vs->ViewCenterX >> win.vs->ViewCenterY; + cout << "Script: viewcenter: " + << win.vs->ViewCenterX << ' ' << win.vs->ViewCenterY << endl; + MyExpose(); + } + break; + case Command::Perspective: + { + scr >> ws >> word; + cout << "Script: perspective: " << word; + if (word == "off") + { + win.vs->OrthogonalProjection = 1; + } + else if (word == "on") + { + win.vs->OrthogonalProjection = 0; + } + else + { + cout << '?'; + } + cout << endl; + MyExpose(); + } + break; + case Command::Light: + { + scr >> ws >> word; + cout << "Script: light: " << word; + if (word == "off") + { + win.vs->SetLight(false); + } + else if (word == "on") + { + win.vs->SetLight(true); + } + else + { + cout << '?'; + } + cout << endl; + MyExpose(); + } + break; + case Command::View: + { + double theta, phi; + scr >> theta >> phi; + cout << "Script: view: " << theta << ' ' << phi << endl; + win.vs->SetView(theta, phi); + MyExpose(); + } + break; + case Command::Zoom: + { + double factor; + scr >> factor; + cout << "Script: zoom: " << factor << endl; + win.vs->Zoom(factor); + MyExpose(); + } + break; + case Command::Shading: + { + scr >> ws >> word; + cout << "Script: shading: " << flush; + VisualizationSceneScalarData::Shading s = + VisualizationSceneScalarData::Shading::Invalid; + if (word == "flat") + { + s = VisualizationSceneScalarData::Shading::Flat; + } + else if (word == "smooth") + { + s = VisualizationSceneScalarData::Shading::Smooth; + } + else if (word == "cool") + { + s = VisualizationSceneScalarData::Shading::Noncomforming; + } + if (s != VisualizationSceneScalarData::Shading::Invalid) + { + win.vs->SetShading(s, false); + cout << word << endl; + MyExpose(); + } + else + { + cout << word << " ?" << endl; + } + } + break; + case Command::Subdivisions: + { + int t, b; + scr >> t >> b; + cout << "Script: subdivisions: " << flush; + win.vs->SetRefineFactors(t, b); + cout << t << ' ' << b << endl; + MyExpose(); + } + break; + case Command::Valuerange: + { + double min, max; + scr >> min >> max; + cout << "Script: valuerange: " << flush; + win.vs->SetValueRange(min, max); + cout << min << ' ' << max << endl; + MyExpose(); + } + break; + case Command::Autoscale: + { + scr >> ws >> word; + cout << "Script: autoscale: " << word; + if (word == "off") + { + win.vs->SetAutoscale(0); + } + else if (word == "on") + { + win.vs->SetAutoscale(1); + } + else if (word == "value") + { + win.vs->SetAutoscale(2); + } + else if (word == "mesh") + { + win.vs->SetAutoscale(3); + } + else + { + cout << '?'; + } + cout << endl; + } + break; + case Command::Levellines: + { + double min, max; + int num; + scr >> min >> max >> num; + cout << "Script: levellines: " << flush; + win.vs->SetLevelLines(min, max, num); + win.vs->UpdateLevelLines(); + cout << min << ' ' << max << ' ' << num << endl; + MyExpose(); + } + break; + case Command::AxisNumberFormat: + { + char delim; + string axis_formatting; + scr >> ws >> delim; + getline(scr, axis_formatting, delim); + cout << "Script: axis_numberformat: " << flush; + win.vs->SetAxisNumberFormat(axis_formatting); + cout << axis_formatting << endl; + MyExpose(); + } + break; + case Command::ColorbarNumberFormat: + { + char delim; + string colorbar_formatting; + scr >> ws >> delim; + getline(scr, colorbar_formatting, delim); + cout << "Script: colorbar_numberformat: " << flush; + win.vs->SetColorbarNumberFormat(colorbar_formatting); + cout << colorbar_formatting << endl; + MyExpose(); + } + break; + case Command::Window: + { + scr >> win.window_x >> win.window_y >> win.window_w >> win.window_h; + cout << "Script: window: " << win.window_x << ' ' << win.window_y + << ' ' << win.window_w << ' ' << win.window_h << endl; + MoveResizeWindow(win.window_x, win.window_y, win.window_w, win.window_h); + MyExpose(); + } + break; + case Command::Keys: + { + scr >> win.data_state.keys; + cout << "Script: keys: '" << win.data_state.keys << "'" << endl; + // SendKeySequence(keys.c_str()); + CallKeySequence(win.data_state.keys.c_str()); + MyExpose(); + } + break; + case Command::Palette: + { + int pal; + scr >> pal; + cout << "Script: palette: " << pal << endl; + win.vs->palette.SetIndex(pal-1); + MyExpose(); + } + case Command::PaletteRepeat: + { + int rpt_times; + scr >> rpt_times; + cout << "Script: palette_repeat: " << rpt_times << endl; + win.vs->palette.SetRepeatTimes(rpt_times); + win.vs->palette.GenerateTextures(); + MyExpose(); + } + break; + case Command::ToggleAttributes: + { + Array attr_list; + cout << "Script: toggle_attributes:"; + for (scr >> ws; scr.peek() != ';'; scr >> ws) + { + attr_list.Append(0); + scr >> attr_list.Last(); + if (attr_list.Size() <= 256) + { + cout << ' ' << attr_list.Last(); + } + else if (attr_list.Size() == 257) + { + cout << " ... " << flush; + } + } + scr.get(); // read the end symbol: ';' + cout << endl; + win.vs->ToggleAttributes(attr_list); + MyExpose(); + } + break; + case Command::Rotmat: + { + cout << "Script: rotmat:"; + for (int i = 0; i < 16; i++) + { + scr >> win.vs->rotmat[i/4][i%4]; + cout << ' ' << win.vs->rotmat[i/4][i%4]; + } + cout << endl; + MyExpose(); + } + break; + case Command::Camera: + { + double cam[9]; + cout << "Script: camera:"; + for (int i = 0; i < 9; i++) + { + scr >> cam[i]; + cout << ' ' << cam[i]; + } + cout << endl; + win.vs->cam.Set(cam); + MyExpose(); + } + break; + case Command::Scale: + { + double scale; + cout << "Script: scale:"; + scr >> scale; + cout << ' ' << scale; + cout << endl; + win.vs->Scale(scale); + MyExpose(); + } + break; + case Command::Translate: + { + double x, y, z; + cout << "Script: translate:"; + scr >> x >> y >> z; + cout << ' ' << x << ' ' << y << ' ' << z; + cout << endl; + win.vs->Translate(x, y, z); + MyExpose(); + } + break; + case Command::PlotCaption: + { + char delim; + scr >> ws >> delim; + getline(scr, win.plot_caption, delim); + win.vs->PrepareCaption(); // turn on or off the caption + MyExpose(); + } + break; + case Command::Max: //dummy + break; + } + + done_one_command = 1; + } +} + +thread_local ScriptController *ScriptController::script_ctrl = NULL; + +void ScriptController::ScriptIdleFunc() +{ + script_ctrl->ExecuteScriptCommand(); + if (script_ctrl->scr_level == 0) + { + ScriptControl(); + } +} + +void ScriptController::ScriptControl() +{ + if (script_ctrl->scr_running) + { + script_ctrl->scr_running = 0; + RemoveIdleFunc(ScriptIdleFunc); + } + else + { + script_ctrl->scr_running = 1; + AddIdleFunc(ScriptIdleFunc); + } +} + +void ScriptController::PlayScript(Window win, istream &scr) +{ + string word; + bool done = false; + + ScriptController script(std::move(win)); + + script.scr_min_val = numeric_limits::infinity(); + script.scr_max_val = -script.scr_min_val; + + // read initializing commands + while (!done) + { + scr >> ws; + if (!scr.good()) + { + cout << "Error in script" << endl; + return; + } + if (scr.peek() == '#') + { + getline(scr, word); + continue; + } + scr >> word; + + auto it = find(commands.begin(), commands.end(), word); + if (it == commands.end()) + { + cout << "Unknown command in script: " << word << endl; + PrintCommands(); + return; + } + + const Command cmd = (Command)(it - commands.begin()); + switch (cmd) + { + case Command::Window: + scr >> script.win.window_x >> script.win.window_y >> script.win.window_w >> + script.win.window_h; + break; + case Command::DataCollCycle: + scr >> script.dc_cycle; + break; + case Command::DataCollProto: + scr >> script.dc_protocol; + break; + case Command::Solution: + if (ScriptReadSolution(scr, script.win.data_state)) + { + return; + } + done = true; // start the visualization + break; + case Command::Quadrature: + if (ScriptReadQuadrature(scr, script.win.data_state)) + { + return; + } + done = true; // start the visualization + break; + case Command::ParSolution: + if (ScriptReadParSolution(scr, script.win.data_state)) + { + return; + } + done = true; // start the visualization + break; + case Command::ParQuadrature: + if (ScriptReadParQuadrature(scr, script.win.data_state)) + { + return; + } + done = true; // start the visualization + break; + case Command::Mesh: + if (script.ScriptReadDisplMesh(scr, script.win.data_state)) + { + return; + } + done = script.win.data_state.mesh != nullptr; + break; + case Command::DataCollMesh: + if (script.ScriptReadDataColl(scr, script.win.data_state)) + { + return; + } + done = true; // start the visualization + break; + case Command::DataCollField: + if (script.ScriptReadDataColl(scr, script.win.data_state, false)) + { + return; + } + done = true; // start the visualization + break; + case Command::DataCollQuad: + if (script.ScriptReadDataColl(scr, script.win.data_state, false, true)) + { + return; + } + done = true; // start the visualization + break; + default: + cout << "Command not supported at this level: " << word << endl; + break; + } + } + + script.scr_level = script.scr_running = 0; + script.script = &scr; + script.win.data_state.keys.clear(); + + // Make sure the singleton object returned by GetMainThread() is + // initialized from the main thread. + GetMainThread(); + + std::thread worker_thread + { + [&](ScriptController local_script) + { + script_ctrl = &local_script; + if (local_script.win.GLVisInitVis({})) + { + local_script.win.wnd->setOnKeyDown(SDLK_SPACE, ScriptControl); + local_script.win.GLVisStartVis(); + } + }, + std::move(script) + }; + + SDLMainLoop(); + worker_thread.join(); +} diff --git a/lib/script_controller.hpp b/lib/script_controller.hpp new file mode 100644 index 00000000..99b173a5 --- /dev/null +++ b/lib/script_controller.hpp @@ -0,0 +1,58 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_SCRIPT_CONTROLLER_HPP +#define GLVIS_SCRIPT_CONTROLLER_HPP + +#include +#include + +#include "window.hpp" + +extern const char *string_none; +extern const char *string_default; + +class ScriptController +{ + Window win; + + string dc_protocol = string_default; + int dc_cycle = 0; + + istream *script = NULL; + int scr_running = 0; + int scr_level = 0; + std::unique_ptr init_nodes; + double scr_min_val, scr_max_val; + + static int ScriptReadSolution(istream &scr, DataState &state); + static int ScriptReadQuadrature(istream &scr, DataState &state); + static int ScriptReadParSolution(istream &scr, DataState &state); + static int ScriptReadParQuadrature(istream &scr, DataState &state); + int ScriptReadDisplMesh(istream &scr, DataState &state); + int ScriptReadDataColl(istream &scr, DataState &state, bool mesh_only = true, + bool quad = false); + + //key handlers using thread-local singleton + static thread_local ScriptController *script_ctrl; + static void ScriptIdleFunc(); + static void ScriptControl(); + + static void PrintCommands(); + void ExecuteScriptCommand(); + +public: + ScriptController(Window win_) : win(std::move(win_)) { } + + static void PlayScript(Window win, std::istream &scr); +}; + +#endif // GLVIS_SCRIPT_CONTROLLER_HPP diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 3131d757..7636dac1 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -539,7 +539,10 @@ void SdlWindow::signalLoop() lock_guard evt_guard{event_mutex}; call_idle_func = true; } - events_available.notify_all(); + if (is_multithreaded) + { + events_available.notify_all(); + } } void SdlWindow::getWindowSize(int& w, int& h) diff --git a/lib/stream_reader.cpp b/lib/stream_reader.cpp index 9fa45538..24b40d85 100644 --- a/lib/stream_reader.cpp +++ b/lib/stream_reader.cpp @@ -11,222 +11,282 @@ #include "stream_reader.hpp" +#include +#include + using namespace std; using namespace mfem; +enum class Command +{ + Mesh, + Solution, + Quadrature, + Fem2D, + VFem2D, + VFem2D_keys, + Fem3D, + VFem3D, + VFem3D_keys, + Fem2D_GF, + Fem2D_GF_keys, + VFem2D_GF, + VFem2D_GF_keys, + Fem3D_GF, + Fem3D_GF_keys, + VFem3D_GF, + VFem3D_GF_keys, + RawScalar2D, + //---------- + Max +}; + +class StreamCommands +{ + struct CmdItem + { + const char *keyword; + bool keys; + const char *params; + const char *desc; + + bool operator==(const string &key) const { return key == keyword; } + }; + array commands; + +public: + StreamCommands(); + + decltype(commands)::const_iterator begin() const { return commands.begin(); } + decltype(commands)::const_iterator end() const { return commands.end(); } + CmdItem& operator[](Command cmd) { return commands[(size_t)cmd]; } + const CmdItem& operator[](Command cmd) const { return commands[(size_t)cmd]; } +}; +static const StreamCommands commands; + +StreamCommands::StreamCommands() +{ + (*this)[Command::Mesh] = {"mesh", false, "", "Visualize the mesh."}; + (*this)[Command::Solution] = {"solution", false, " ", "Visualize the solution."}; + (*this)[Command::Quadrature] = {"quadrature", false, " ", "Visualize the quadrature."}; + (*this)[Command::Fem2D] = {"fem2d_data", false, " ", "Visualize the 2D scalar data."}; + (*this)[Command::VFem2D] = {"vfem2d_data", false, " ", "Visualize the 2D vector data."}; + (*this)[Command::VFem2D_keys] = {"vfem2d_data_keys", true, " ", "Visualize the 2D vector data and apply control keys."}; + (*this)[Command::Fem3D] = {"fem3d_data", false, " ", "Visualize the 3D scalar data."}; + (*this)[Command::VFem3D] = {"vfem3d_data", false, " ", "Visualize the 3D vector data."}; + (*this)[Command::VFem3D_keys] = {"vfem3d_data_keys", true, " ", "Visualize the 3D vector data and apply control keys."}; + (*this)[Command::Fem2D_GF] = {"fem2d_gf_data", false, " ", "Visualize the 2D scalar grid function."}; + (*this)[Command::Fem2D_GF_keys] = {"fem2d_gf_data_keys", true, " ", "Visualize the 2D scalar grid function and apply control keys."}; + (*this)[Command::VFem2D_GF] = {"vfem2d_gf_data", false, " ", "Visualize the 2D vector grid function."}; + (*this)[Command::VFem2D_GF_keys] = {"vfem2d_gf_data_keys", true, " ", "Visualize the 2D vector grid function and apply control keys."}; + (*this)[Command::Fem3D_GF] = {"fem3d_gf_data", false, " ", "Visualize the 3D scalar grid function."}; + (*this)[Command::Fem3D_GF_keys] = {"fem3d_gf_data_keys", true, " ", "Visualize the 3D scalar grid function and apply control keys."}; + (*this)[Command::VFem3D_GF] = {"vfem3d_gf_data", false, " ", "Visualize the 3D vector grid function."}; + (*this)[Command::VFem3D_GF_keys] = {"vfem3d_gf_data_keys", true, " ", "Visualize the 3D vector grid function and apply control keys."}; + (*this)[Command::RawScalar2D] = {"raw_scalar_2d", false, "", "Visualize the 2D scalar data (see stream_reader.cpp)."}; +} + +void StreamReader::PrintCommands() +{ + cout << "Available commands are:" << endl; + + for (const auto &ci : commands) + { + cout << "\t" << ci.keyword << " " << ci.params << " - " << ci.desc << endl; + } +} + +bool StreamReader::SupportsDataType(const string &data_type) +{ + auto it = find(commands.begin(), commands.end(), data_type); + return it != commands.end(); +} + int StreamReader::ReadStream( istream &is, const string &data_type) { data.SetMesh(NULL); data.keys.clear(); - if (data_type == "fem2d_data") - { - data.type = DataState::FieldType::SCALAR; - data.SetMesh(new Mesh(is, 0, 0, data.save_coloring)); - data.sol.Load(is, data.mesh->GetNV()); - } - else if (data_type == "vfem2d_data" || data_type == "vfem2d_data_keys") - { - data.type = DataState::FieldType::VECTOR; - data.SetMesh(new Mesh(is, 0, 0, data.save_coloring)); - data.solu.Load(is, data.mesh->GetNV()); - data.solv.Load(is, data.mesh->GetNV()); - if (data_type == "vfem2d_data_keys") - { - is >> data.keys; - } - } - else if (data_type == "fem3d_data") - { - data.type = DataState::FieldType::SCALAR; - data.SetMesh(new Mesh(is, 0, 0, data.save_coloring)); - data.sol.Load(is, data.mesh->GetNV()); - } - else if (data_type == "vfem3d_data" || data_type == "vfem3d_data_keys") - { - data.type = DataState::FieldType::VECTOR; - data.SetMesh(new Mesh(is, 0, 0, data.save_coloring)); - data.solu.Load(is, data.mesh->GetNV()); - data.solv.Load(is, data.mesh->GetNV()); - data.solw.Load(is, data.mesh->GetNV()); - if (data_type == "vfem3d_data_keys") - { - is >> data.keys; - } - } - else if (data_type == "fem2d_gf_data" || data_type == "fem2d_gf_data_keys") - { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetGridFunction(new GridFunction(data.mesh.get(), is)); - if (data_type == "fem2d_gf_data_keys") - { - is >> data.keys; - } - } - else if (data_type == "vfem2d_gf_data" || data_type == "vfem2d_gf_data_keys") - { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetGridFunction(new GridFunction(data.mesh.get(), is)); - if (data_type == "vfem2d_gf_data_keys") - { - is >> data.keys; - } - } - else if (data_type == "fem3d_gf_data" || data_type == "fem3d_gf_data_keys") - { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetGridFunction(new GridFunction(data.mesh.get(), is)); - if (data_type == "fem3d_gf_data_keys") - { - is >> data.keys; - } - } - else if (data_type == "vfem3d_gf_data" || data_type == "vfem3d_gf_data_keys") - { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetGridFunction(new GridFunction(data.mesh.get(), is)); - if (data_type == "vfem3d_gf_data_keys") - { - is >> data.keys; - } - } - else if (data_type == "solution") + auto it = find(commands.begin(), commands.end(), data_type); + if (it == commands.end()) { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetGridFunction(new GridFunction(data.mesh.get(), is)); - } - else if (data_type == "quadrature") - { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetQuadFunction(new QuadratureFunction(data.mesh.get(), is)); - data.SetQuadSolution(); - } - else if (data_type == "mesh") - { - data.SetMesh(new Mesh(is, 1, 0, data.save_coloring)); - data.SetMeshSolution(); + cerr << "Unknown data format " << data_type << endl; + PrintCommands(); + return 1; } - else if (data_type == "raw_scalar_2d") + + Command cmd = (Command)(it - commands.begin()); + switch (cmd) { - Array *> vertices; - Array *> elements; - Array elem_types; - string ident; - int num_patches, num_vert, num_elem, n; - is >> ws >> ident; // 'patches' - is >> num_patches; - // cout << ident << ' ' << num_patches << endl; - vertices.SetSize(num_patches); - vertices = NULL; - elements.SetSize(num_patches); - elements = NULL; - elem_types.SetSize(num_patches); - elem_types = 0; - int tot_num_vert = 0; - int tot_num_elem = 0; - int mesh_type = 0; - for (int i = 0; i < num_patches; i++) + case Command::Fem2D: + data.type = DataState::FieldType::SCALAR; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + data.sol.Load(is, data.mesh->GetNV()); + break; + case Command::VFem2D: + case Command::VFem2D_keys: + data.type = DataState::FieldType::VECTOR; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + data.solu.Load(is, data.mesh->GetNV()); + data.solv.Load(is, data.mesh->GetNV()); + break; + case Command::Fem3D: + data.type = DataState::FieldType::SCALAR; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + data.sol.Load(is, data.mesh->GetNV()); + break; + case Command::VFem3D: + case Command::VFem3D_keys: + data.type = DataState::FieldType::VECTOR; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + data.solu.Load(is, data.mesh->GetNV()); + data.solv.Load(is, data.mesh->GetNV()); + data.solw.Load(is, data.mesh->GetNV()); + break; + case Command::Fem2D_GF: + case Command::Fem2D_GF_keys: + case Command::VFem2D_GF: + case Command::VFem2D_GF_keys: + case Command::Fem3D_GF: + case Command::Fem3D_GF_keys: + case Command::VFem3D_GF: + case Command::VFem3D_GF_keys: + case Command::Solution: + data.SetMesh(new Mesh(is, 1, 0, data.fix_elem_orient)); + data.SetGridFunction(new GridFunction(data.mesh.get(), is)); + break; + case Command::Quadrature: + data.SetMesh(new Mesh(is, 1, 0, data.fix_elem_orient)); + data.SetQuadFunction(new QuadratureFunction(data.mesh.get(), is)); + break; + case Command::Mesh: + data.SetMesh(new Mesh(is, 1, 0, data.fix_elem_orient)); + data.SetMeshSolution(); + break; + case Command::RawScalar2D: { - is >> ws >> ident; // 'vertices' - is >> num_vert; - // cout << '\n' << ident << ' ' << num_vert << endl; - // read vertices in the format: x y z nx ny nz - vertices[i] = new Array(6*num_vert); - Array &verts = *vertices[i]; - for (int j = 0; j < verts.Size(); j++) + Array *> vertices; + Array *> elements; + Array elem_types; + string ident; + int num_patches, num_vert, num_elem, n; + is >> ws >> ident; // 'patches' + is >> num_patches; + // cout << ident << ' ' << num_patches << endl; + vertices.SetSize(num_patches); + vertices = NULL; + elements.SetSize(num_patches); + elements = NULL; + elem_types.SetSize(num_patches); + elem_types = 0; + int tot_num_vert = 0; + int tot_num_elem = 0; + int mesh_type = 0; + for (int i = 0; i < num_patches; i++) { - is >> verts[j]; + is >> ws >> ident; // 'vertices' + is >> num_vert; + // cout << '\n' << ident << ' ' << num_vert << endl; + // read vertices in the format: x y z nx ny nz + vertices[i] = new Array(6*num_vert); + Array &verts = *vertices[i]; + for (int j = 0; j < verts.Size(); j++) + { + is >> verts[j]; + } + + is >> ws >> ident; // 'triangles' or 'quads' + if (ident == "triangles") + { + n = 3, mesh_type |= 1; + } + else + { + n = 4, mesh_type |= 2; + } + elem_types[i] = n; + is >> num_elem; + // cout << ident << ' ' << num_elem << endl; + elements[i] = new Array(n*num_elem); + Array &elems = *elements[i]; + for (int j = 0; j < elems.Size(); j++) + { + is >> elems[j]; + elems[j] += tot_num_vert; + } + tot_num_vert += num_vert; + tot_num_elem += num_elem; } - is >> ws >> ident; // 'triangles' or 'quads' - if (ident == "triangles") + data.SetMesh(new Mesh(2, tot_num_vert, tot_num_elem, 0)); + data.sol.SetSize(tot_num_vert); + data.normals.SetSize(3*tot_num_vert); + + int v_off = 0; + for (int i = 0; i < num_patches; i++) { - n = 3, mesh_type |= 1; + Array &verts = *vertices[i]; + num_vert = verts.Size()/6; + for (int j = 0; j < num_vert; j++) + { + data.mesh->AddVertex(&verts[6*j]); + data.sol(v_off) = verts[6*j+2]; + data.normals(3*v_off+0) = verts[6*j+3]; + data.normals(3*v_off+1) = verts[6*j+4]; + data.normals(3*v_off+2) = verts[6*j+5]; + v_off++; + } + + n = elem_types[i]; + Array &elems = *elements[i]; + num_elem = elems.Size()/n; + // int attr = 1; + int attr = i + 1; + if (n == 3) + for (int j = 0; j < num_elem; j++) + { + data.mesh->AddTriangle(&elems[3*j], attr); + } + else + for (int j = 0; j < num_elem; j++) + { + data.mesh->AddQuad(&elems[4*j], attr); + } } - else + + if (mesh_type == 1) { - n = 4, mesh_type |= 2; + data.mesh->FinalizeTriMesh(1, 0, data.fix_elem_orient); } - elem_types[i] = n; - is >> num_elem; - // cout << ident << ' ' << num_elem << endl; - elements[i] = new Array(n*num_elem); - Array &elems = *elements[i]; - for (int j = 0; j < elems.Size(); j++) + else if (mesh_type == 2) { - is >> elems[j]; - elems[j] += tot_num_vert; + data.mesh->FinalizeQuadMesh(1, 0, data.fix_elem_orient); } - tot_num_vert += num_vert; - tot_num_elem += num_elem; - } - - data.SetMesh(new Mesh(2, tot_num_vert, tot_num_elem, 0)); - data.sol.SetSize(tot_num_vert); - data.normals.SetSize(3*tot_num_vert); - - int v_off = 0; - for (int i = 0; i < num_patches; i++) - { - Array &verts = *vertices[i]; - num_vert = verts.Size()/6; - for (int j = 0; j < num_vert; j++) + else { - data.mesh->AddVertex(&verts[6*j]); - data.sol(v_off) = verts[6*j+2]; - data.normals(3*v_off+0) = verts[6*j+3]; - data.normals(3*v_off+1) = verts[6*j+4]; - data.normals(3*v_off+2) = verts[6*j+5]; - v_off++; + mfem_error("Input data contains mixture of triangles and quads!"); } - n = elem_types[i]; - Array &elems = *elements[i]; - num_elem = elems.Size()/n; - // int attr = 1; - int attr = i + 1; - if (n == 3) - for (int j = 0; j < num_elem; j++) - { - data.mesh->AddTriangle(&elems[3*j], attr); - } - else - for (int j = 0; j < num_elem; j++) - { - data.mesh->AddQuad(&elems[4*j], attr); - } - } - - if (mesh_type == 1) - { - data.mesh->FinalizeTriMesh(1, 0, data.save_coloring); - } - else if (mesh_type == 2) - { - data.mesh->FinalizeQuadMesh(1, 0, data.save_coloring); - } - else - { - mfem_error("Input data contains mixture of triangles and quads!"); - } + data.mesh->GenerateBoundaryElements(); - data.mesh->GenerateBoundaryElements(); + for (int i = num_patches; i > 0; ) + { + i--; + delete elements[i]; + delete vertices[i]; + } - for (int i = num_patches; i > 0; ) - { - i--; - delete elements[i]; - delete vertices[i]; + data.type = DataState::FieldType::SCALAR; } - - data.type = DataState::FieldType::SCALAR; + break; + case Command::Max: //dummy + break; } - else + + if (commands[cmd].keys) { - cerr << "Unknown data format" << endl; - cerr << data_type << endl; - return 1; + is >> data.keys; } data.ExtrudeMeshAndSolution(); @@ -248,7 +308,7 @@ int StreamReader::ReadStreams(const StreamCollection &input_streams) for (int p = 0; p < nproc; p++) { #ifdef GLVIS_DEBUG - cout << "connection[" << p << "]: reading initial data ... " << flush; + cout << "connection[" << p << "]: reading data ... " << flush; #endif istream &isock = *input_streams[p]; // assuming the "parallel nproc p" part of the stream has been read @@ -256,7 +316,7 @@ int StreamReader::ReadStreams(const StreamCollection &input_streams) #ifdef GLVIS_DEBUG cout << " type " << data_type << " ... " << flush; #endif - mesh_array[p] = new Mesh(isock, 1, 0, data.save_coloring); + mesh_array[p] = new Mesh(isock, 1, 0, data.fix_elem_orient); if (!data.keep_attr) { // set element and boundary attributes to proc+1 diff --git a/lib/stream_reader.hpp b/lib/stream_reader.hpp index eb9606f0..5801d362 100644 --- a/lib/stream_reader.hpp +++ b/lib/stream_reader.hpp @@ -26,7 +26,14 @@ class StreamReader DataState &data; public: - StreamReader(DataState &data_) : data(data_) { } + + StreamReader(DataState &data_): data(data_) { } + + /// Prints available commands + static void PrintCommands(); + + /// Tests if the data type is supported + static bool SupportsDataType(const std::string &data_type); /// Read the content of an input stream (e.g. from socket/file) int ReadStream(std::istream &is, const std::string &data_type); diff --git a/lib/threads.cpp b/lib/threads.cpp index 5c2cb678..fbc3a71b 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -11,20 +11,20 @@ #include "visual.hpp" #include "palettes.hpp" -#include + +#include +#include using namespace std; extern const char *strings_off_on[]; // defined in vsdata.cpp -GLVisCommand::GLVisCommand( - VisualizationSceneScalarData **_vs, DataState& state) - : curr_state(state) +GLVisCommand::GLVisCommand(Window &win_) + : win(win_) { - vs = _vs; // should be set in this thread by a call to InitVisualization() - thread_wnd = GetAppWindow(); + thread_wnd = win.wnd.get(); num_waiting = 0; terminating = false; @@ -463,7 +463,7 @@ int GLVisCommand::Execute() } else { - auto qs = curr_state.GetQuadSolution(); + auto qs = win.data_state.GetQuadSolution(); if (qs != DataState::QuadSolution::NONE) { new_state.SetQuadSolution(qs); @@ -475,11 +475,11 @@ int GLVisCommand::Execute() new_state.ExtrudeMeshAndSolution(); } } - if (curr_state.SetNewMeshAndSolution(std::move(new_state), *vs)) + if (win.SetNewMeshAndSolution(std::move(new_state))) { if (mesh_range > 0.0) { - (*vs)->SetValueRange(-mesh_range, mesh_range); + win.vs->SetValueRange(-mesh_range, mesh_range); } MyExpose(); } @@ -500,7 +500,7 @@ int GLVisCommand::Execute() cout << "Command: screenshot -> " << screenshot_filename << endl; // Allow SdlWindow to handle the expose and screenshot action, in case // any actions need to be taken before MyExpose(). - GetAppWindow()->screenshot(screenshot_filename, true); + thread_wnd->screenshot(screenshot_filename, true); break; } @@ -539,8 +539,8 @@ int GLVisCommand::Execute() case PLOT_CAPTION: { cout << "Command: plot_caption: " << plot_caption << endl; - ::plot_caption = plot_caption; - (*vs)->PrepareCaption(); // turn on or off the caption + win.plot_caption = plot_caption; + win.vs->PrepareCaption(); // turn on or off the caption MyExpose(); break; } @@ -549,8 +549,8 @@ int GLVisCommand::Execute() { cout << "Command: axis_labels: '" << axis_label_x << "' '" << axis_label_y << "' '" << axis_label_z << "'" << endl; - (*vs)->SetAxisLabels(axis_label_x.c_str(), axis_label_y.c_str(), - axis_label_z.c_str()); + win.vs->SetAxisLabels(axis_label_x.c_str(), axis_label_y.c_str(), + axis_label_z.c_str()); MyExpose(); break; } @@ -559,7 +559,7 @@ int GLVisCommand::Execute() { cout << "Command: axis_numberformat: '" << axis_formatting << "'" << endl; - (*vs)->SetAxisNumberFormat(axis_formatting); + win.vs->SetAxisNumberFormat(axis_formatting); MyExpose(); break; } @@ -568,7 +568,7 @@ int GLVisCommand::Execute() { cout << "Command: colorbar_numberformat: '" << colorbar_formatting << "'" << endl; - (*vs)->SetColorbarNumberFormat(colorbar_formatting); + win.vs->SetColorbarNumberFormat(colorbar_formatting); MyExpose(); break; } @@ -584,7 +584,7 @@ int GLVisCommand::Execute() { cout << "Command: view: " << view_ang_theta << ' ' << view_ang_phi << endl; - (*vs)->SetView(view_ang_theta, view_ang_phi); + win.vs->SetView(view_ang_theta, view_ang_phi); MyExpose(); break; } @@ -592,7 +592,7 @@ int GLVisCommand::Execute() case ZOOM: { cout << "Command: zoom: " << zoom_factor << endl; - (*vs)->Zoom(zoom_factor); + win.vs->Zoom(zoom_factor); MyExpose(); break; } @@ -600,7 +600,7 @@ int GLVisCommand::Execute() case SUBDIVISIONS: { cout << "Command: subdivisions: " << flush; - (*vs)->SetRefineFactors(subdiv_tot, subdiv_bdr); + win.vs->SetRefineFactors(subdiv_tot, subdiv_bdr); cout << subdiv_tot << ' ' << subdiv_bdr << endl; MyExpose(); break; @@ -609,7 +609,7 @@ int GLVisCommand::Execute() case VALUE_RANGE: { cout << "Command: valuerange: " << flush; - (*vs)->SetValueRange(val_min, val_max); + win.vs->SetValueRange(val_min, val_max); cout << val_min << ' ' << val_max << endl; MyExpose(); break; @@ -618,8 +618,8 @@ int GLVisCommand::Execute() case LEVELLINES: { cout << "Command: levellines: " << flush; - (*vs)->SetLevelLines(lvl_min, lvl_max, lvl_num); - (*vs)->UpdateLevelLines(); + win.vs->SetLevelLines(lvl_min, lvl_max, lvl_num); + win.vs->UpdateLevelLines(); cout << lvl_min << ' ' << lvl_max << ' ' << lvl_num << endl; MyExpose(); break; @@ -644,7 +644,7 @@ int GLVisCommand::Execute() } if (s != VisualizationSceneScalarData::Shading::Invalid) { - (*vs)->SetShading(s, false); + win.vs->SetShading(s, false); cout << shading << endl; MyExpose(); } @@ -659,8 +659,8 @@ int GLVisCommand::Execute() { cout << "Command: viewcenter: " << view_center_x << ' ' << view_center_y << endl; - (*vs)->ViewCenterX = view_center_x; - (*vs)->ViewCenterY = view_center_y; + win.vs->ViewCenterX = view_center_x; + win.vs->ViewCenterY = view_center_y; MyExpose(); break; } @@ -670,19 +670,19 @@ int GLVisCommand::Execute() cout << "Command: autoscale: " << autoscale_mode; if (autoscale_mode == "off") { - (*vs)->SetAutoscale(0); + win.vs->SetAutoscale(0); } else if (autoscale_mode == "on") { - (*vs)->SetAutoscale(1); + win.vs->SetAutoscale(1); } else if (autoscale_mode == "value") { - (*vs)->SetAutoscale(2); + win.vs->SetAutoscale(2); } else if (autoscale_mode == "mesh") { - (*vs)->SetAutoscale(3); + win.vs->SetAutoscale(3); } else { @@ -695,10 +695,10 @@ int GLVisCommand::Execute() case PALETTE: { cout << "Command: palette: " << palette << endl; - (*vs)->palette.SetIndex(palette-1); + win.vs->palette.SetIndex(palette-1); if (!GetUseTexture()) { - (*vs)->EventUpdateColors(); + win.vs->EventUpdateColors(); } MyExpose(); break; @@ -707,12 +707,12 @@ int GLVisCommand::Execute() case PALETTE_REPEAT: { cout << "Command: palette_repeat: " << palette_repeat << endl; - (*vs)->palette.SetRepeatTimes(palette_repeat); - (*vs)->palette.GenerateTextures(); + win.vs->palette.SetRepeatTimes(palette_repeat); + win.vs->palette.GenerateTextures(); if (!GetUseTexture()) { - (*vs)->EventUpdateColors(); + win.vs->EventUpdateColors(); } MyExpose(); break; @@ -726,7 +726,7 @@ int GLVisCommand::Execute() cout << ' ' << camera[i]; } cout << endl; - (*vs)->cam.Set(camera); + win.vs->cam.Set(camera); MyExpose(); break; } @@ -798,172 +798,211 @@ GLVisCommand::~GLVisCommand() } } -communication_thread::communication_thread(StreamCollection _is, - GLVisCommand* cmd) - : is(std::move(_is)), glvis_command(cmd) +enum class Command { - new_m = NULL; - new_g = NULL; + Parallel, + Screenshot, + Viewcenter, + View, + Zoom, + Shading, + Subdivisions, + Valuerange, + Autoscale, + Levellines, + AxisNumberFormat, + ColorbarNumberFormat, + WindowSize, + WindowGeometry, + WindowTitle, + Keys, + Palette, + PaletteRepeat, + Camera, + PlotCaption, + AxisLabels, + Pause, + Autopause, + //---------- + Max +}; + +class ThreadCommands +{ + struct CmdItem + { + const char *keyword; + const char *params; + const char *desc; + + bool operator==(const string &key) const { return key == keyword; } + }; + array commands; + +public: + ThreadCommands(); + + decltype(commands)::const_iterator begin() const { return commands.begin(); } + decltype(commands)::const_iterator end() const { return commands.end(); } + CmdItem& operator[](Command cmd) { return commands[(size_t)cmd]; } + const CmdItem& operator[](Command cmd) const { return commands[(size_t)cmd]; } +}; +static const ThreadCommands commands; - if (is.size() > 0) +ThreadCommands::ThreadCommands() +{ + (*this)[Command::Parallel] = {"parallel", " ", "Prefix for distributed mesh/solution/quadrature."}; + (*this)[Command::Screenshot] = {"screenshot", "", "Take a screenshot, saving it to the file."}; + (*this)[Command::Viewcenter] = {"viewcenter", " ", "Change the viewcenter."}; + (*this)[Command::View] = {"view", " ", "Change the solid angle of view."}; + (*this)[Command::Zoom] = {"zoom", "", "Change the zoom factor."}; + (*this)[Command::Shading] = {"shading", "", "Change the shading algorithm."}; + (*this)[Command::Subdivisions] = {"subdivisions", " ", "Change the refinement level."}; + (*this)[Command::Valuerange] = {"valuerange", " ", "Change the value range."}; + (*this)[Command::Autoscale] = {"autoscale", "", "Change the autoscale algorithm."}; + (*this)[Command::Levellines] = {"levellines", " ", "Set the level lines."}; + (*this)[Command::AxisNumberFormat] = {"axis_numberformat", "''", "Set the axis number format."}; + (*this)[Command::ColorbarNumberFormat] = {"colorbar_numberformat", "''", "Set the colorbar number format."}; + (*this)[Command::WindowSize] = {"window_size", " ", "Set the size of the window."}; + (*this)[Command::WindowGeometry] = {"window_geometry", " ", "Set the position and size of the window."}; + (*this)[Command::WindowTitle] = {"window_title", "''", "Set title of the window."}; + (*this)[Command::Keys] = {"keys", "<keys>", "Send the control key sequence."}; + (*this)[Command::Palette] = {"palette", "<index>", "Set the palette index."}; + (*this)[Command::PaletteRepeat] = {"palette_repeat", "<times>", "Set the repetition of the palette."}; + (*this)[Command::Camera] = {"camera", "<cam[0]> ... <cam[2]> <dir[0]> ... <dir[2]> <up[0]> ... <up[2]>", "Set the camera position, direction and upward vector."}; + (*this)[Command::PlotCaption] = {"plot_caption", "'<caption>'", "Set the plot caption."}; + (*this)[Command::AxisLabels] = {"axis_labels", "'<x label>' '<y label>' '<z label>'", "Set labels of the axes."}; + (*this)[Command::Pause] = {"pause", "", "Stop the stream until space is pressed."}; + (*this)[Command::Autopause] = {"autopause", "<0/off/1/on>", "Turns off or on autopause."}; +} + +communication_thread::communication_thread(StreamCollection _is, + GLVisCommand* cmd, bool multithread) + : is(std::move(_is)), glvis_command(cmd), is_multithread(multithread) +{ + if (is_multithread && is.size() > 0) { tid = std::thread(&communication_thread::execute, this); } } +bool communication_thread::process_one() +{ + std::string word; + *is[0] >> ws; + *is[0] >> word; + return execute_one(word); +} + communication_thread::~communication_thread() { - if (is.size() > 0) + if (is_multithread && is.size() > 0) { terminate_thread = true; tid.join(); } } -void communication_thread::execute() +void communication_thread::print_commands() { - while (1) + StreamReader::PrintCommands(); + + for (const auto &ci : commands) { - *is[0] >> ws; - // thread cancellation point - if (terminate_thread) { break; } + cout << "\t" << ci.keyword << " " << ci.params << " - " << ci.desc << endl; + } +} - *is[0] >> ident; - if (!(*is[0])) +bool communication_thread::execute_one(std::string ident) +{ + // new solution handled by StreamReader + if (StreamReader::SupportsDataType(ident)) + { + DataState tmp; + tmp.fix_elem_orient = glvis_command->FixElementOrientations(); + StreamReader reader(tmp); + reader.ReadStream(*is[0], ident); + + // cout << "Stream: new solution" << endl; + + if (glvis_command->NewMeshAndSolution(std::move(tmp))) { - break; + return false; } - - if (ident == "mesh" || ident == "solution" || - ident == "quadrature" || ident == "parallel") + if (!tmp.keys.empty()) { - bool fix_elem_orient = glvis_command->FixElementOrientations(); - DataState tmp; - if (ident == "mesh") + if (glvis_command->KeyCommands(tmp.keys.c_str())) { - tmp.SetMesh(new Mesh(*is[0], 1, 0, fix_elem_orient)); - if (!(*is[0])) - { - break; - } - tmp.SetGridFunction(NULL); + return false; } - else if (ident == "solution") + } + return true; + } + + auto it = find(commands.begin(), commands.end(), ident); + if (it == commands.end()) + { + cout << "Stream: unknown command: " << ident << endl; + print_commands(); + return false; + } + + const Command cmd = (Command)(it - commands.begin()); + switch (cmd) + { + case Command::Parallel: + { + unsigned int proc, nproc, np = 0; + do { - tmp.SetMesh(new Mesh(*is[0], 1, 0, fix_elem_orient)); - if (!(*is[0])) + istream &isock = *is[np]; + isock >> nproc >> proc >> ws; +#ifdef GLVIS_DEBUG + cout << "connection[" << np << "]: parallel " << nproc << ' ' + << proc << endl; +#endif + if (nproc != is.size()) { - break; + cout << "Unexpected number of processors: " << nproc + << ", expected: " << is.size() << endl; + mfem_error(); } - tmp.SetGridFunction(new GridFunction(tmp.mesh.get(), *is[0])); - if (!(*is[0])) + if (proc >= nproc) { - break; + cout << "Invalid processor rank: " << proc + << ", number of processors: " << nproc << endl; + mfem_error(); } - } - else if (ident == "quadrature") - { - tmp.SetMesh(new Mesh(*is[0], 1, 0, fix_elem_orient)); - if (!(*is[0])) + np++; + if (np == nproc) { break; } - tmp.SetQuadFunction(new QuadratureFunction(tmp.mesh.get(), *is[0])); - if (!(*is[0])) + *is[np] >> ident >> ws; // "parallel" + if (ident != "parallel") { - break; + cout << "Expected keyword \"parallel\", got \"" << ident + << '"' << endl; + mfem_error(); } } - else if (ident == "parallel") - { - std::vector<Mesh*> mesh_array; - std::vector<GridFunction*> gf_array; - std::vector<QuadratureFunction*> qf_array; - int proc, nproc, np = 0; - bool keep_attr = glvis_command->KeepAttrib(); - do - { - istream &isock = *is[np]; - isock >> nproc >> proc >> ws; -#ifdef GLVIS_DEBUG - cout << "connection[" << np << "]: parallel " << nproc << ' ' - << proc << endl; -#endif - isock >> ident >> ws; - mesh_array.resize(nproc); - mesh_array[proc] = new Mesh(isock, 1, 0, fix_elem_orient); - if (!keep_attr) - { - // set element and boundary attributes to proc+1 - for (int i = 0; i < mesh_array[proc]->GetNE(); i++) - { - mesh_array[proc]->GetElement(i)->SetAttribute(proc+1); - } - for (int i = 0; i < mesh_array[proc]->GetNBE(); i++) - { - mesh_array[proc]->GetBdrElement(i)->SetAttribute(proc+1); - } - } - if (ident == "solution") - { - gf_array.resize(nproc); - gf_array[proc] = new GridFunction(mesh_array[proc], isock); - } - else if (ident == "quadrature") - { - qf_array.resize(nproc); - qf_array[proc] = new QuadratureFunction(mesh_array[proc], isock); - } - else - { - cout << "Stream: unknown command: " << ident << endl; - } - np++; - if (np == nproc) - { - break; - } - *is[np] >> ident >> ws; // "parallel" - } - while (1); - - tmp.SetMesh(new Mesh(mesh_array.data(), nproc)); - if (gf_array.size() > 0) - { - tmp.SetGridFunction(new GridFunction(tmp.mesh.get(), gf_array.data(), nproc)); - } - else if (qf_array.size() > 0) - { - tmp.SetQuadFunction(qf_array); - } + while (1); - for (int p = 0; p < nproc; p++) - { - if (gf_array.size() > 0) - { - delete gf_array[nproc-1-p]; - } - if (qf_array.size() > 0) - { - delete qf_array[nproc-1-p]; - } - delete mesh_array[nproc-1-p]; - } - gf_array.clear(); - qf_array.clear(); - mesh_array.clear(); - } + DataState tmp; + tmp.fix_elem_orient = glvis_command->FixElementOrientations(); + tmp.keep_attr = glvis_command->KeepAttrib(); + StreamReader reader(tmp); + reader.ReadStreams(is); // cout << "Stream: new solution" << endl; - tmp.ExtrudeMeshAndSolution(); - if (glvis_command->NewMeshAndSolution(std::move(tmp))) { - goto comm_terminate; + return false; } } - else if (ident == "screenshot") + break; + case Command::Screenshot: { string filename; @@ -978,10 +1017,11 @@ void communication_thread::execute() if (glvis_command->Screenshot(filename.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "keys") + break; + case Command::Keys: { string keys; @@ -996,10 +1036,11 @@ void communication_thread::execute() if (glvis_command->KeyCommands(keys.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "window_size") + break; + case Command::WindowSize: { int w, h, t; @@ -1014,10 +1055,11 @@ void communication_thread::execute() if (glvis_command->WindowSize(w, h)) { - goto comm_terminate; + return false; } } - else if (ident == "window_geometry") + break; + case Command::WindowGeometry: { int x, y, w, h, t; @@ -1032,10 +1074,11 @@ void communication_thread::execute() if (glvis_command->WindowGeometry(x, y, w, h)) { - goto comm_terminate; + return false; } } - else if (ident == "window_title") + break; + case Command::WindowTitle: { char c; string title; @@ -1055,10 +1098,11 @@ void communication_thread::execute() if (glvis_command->WindowTitle(title.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "plot_caption") + break; + case Command::PlotCaption: { char c; string caption; @@ -1078,10 +1122,11 @@ void communication_thread::execute() if (glvis_command->PlotCaption(caption.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "axis_labels") + break; + case Command::AxisLabels: { char c; string label_x, label_y, label_z; @@ -1111,10 +1156,11 @@ void communication_thread::execute() label_y.c_str(), label_z.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "pause") + break; + case Command::Pause: { // all processors sent the command for (size_t i = 1; i < is.size(); i++) @@ -1124,10 +1170,11 @@ void communication_thread::execute() if (glvis_command->Pause()) { - goto comm_terminate; + return false; } } - else if (ident == "view") + break; + case Command::View: { double theta, phi, a; @@ -1142,10 +1189,11 @@ void communication_thread::execute() if (glvis_command->ViewAngles(theta, phi)) { - goto comm_terminate; + return false; } } - else if (ident == "zoom") + break; + case Command::Zoom: { double factor, a; @@ -1160,10 +1208,11 @@ void communication_thread::execute() if (glvis_command->Zoom(factor)) { - goto comm_terminate; + return false; } } - else if (ident == "subdivisions") + break; + case Command::Subdivisions: { int tot, bdr, a; @@ -1178,10 +1227,11 @@ void communication_thread::execute() if (glvis_command->Subdivisions(tot, bdr)) { - goto comm_terminate; + return false; } } - else if (ident == "valuerange") + break; + case Command::Valuerange: { double minv, maxv, a; @@ -1196,10 +1246,11 @@ void communication_thread::execute() if (glvis_command->ValueRange(minv, maxv)) { - goto comm_terminate; + return false; } } - else if (ident == "levellines") + break; + case Command::Levellines: { double minv, maxv, a; int num, b; @@ -1215,10 +1266,11 @@ void communication_thread::execute() if (glvis_command->Levellines(minv, maxv, num)) { - goto comm_terminate; + return false; } } - else if (ident == "axis_numberformat") + break; + case Command::AxisNumberFormat: { char c; string formatting; @@ -1238,10 +1290,11 @@ void communication_thread::execute() if (glvis_command->AxisNumberFormat(formatting)) { - goto comm_terminate; + return false; } } - else if (ident == "colorbar_numberformat") + break; + case Command::ColorbarNumberFormat: { char c; string formatting; @@ -1261,10 +1314,11 @@ void communication_thread::execute() if (glvis_command->ColorbarNumberFormat(formatting)) { - goto comm_terminate; + return false; } } - else if (ident == "shading") + break; + case Command::Shading: { string shd; @@ -1279,10 +1333,11 @@ void communication_thread::execute() if (glvis_command->SetShading(shd.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "viewcenter") + break; + case Command::Viewcenter: { double x, y, a; @@ -1297,10 +1352,11 @@ void communication_thread::execute() if (glvis_command->ViewCenter(x, y)) { - goto comm_terminate; + return false; } } - else if (ident == "autoscale") + break; + case Command::Autoscale: { string mode; @@ -1315,10 +1371,11 @@ void communication_thread::execute() if (glvis_command->Autoscale(mode.c_str())) { - goto comm_terminate; + return false; } } - else if (ident == "palette") + break; + case Command::Palette: { int pal, a; @@ -1333,10 +1390,11 @@ void communication_thread::execute() if (glvis_command->Palette(pal)) { - goto comm_terminate; + return false; } } - else if (ident == "palette_repeat") + break; + case Command::PaletteRepeat: { int n, a; @@ -1351,10 +1409,11 @@ void communication_thread::execute() if (glvis_command->PaletteRepeat(n)) { - goto comm_terminate; + return false; } } - else if (ident == "camera") + break; + case Command::Camera: { double cam[9], a; @@ -1375,10 +1434,11 @@ void communication_thread::execute() if (glvis_command->Camera(cam)) { - goto comm_terminate; + return false; } } - else if (ident == "autopause") + break; + case Command::Autopause: { string mode; @@ -1393,18 +1453,42 @@ void communication_thread::execute() if (glvis_command->Autopause(mode.c_str())) { - goto comm_terminate; + return false; } } - else + break; + case Command::Max: //dummy + break; + } + + return true; +} + +void communication_thread::execute() +{ + std::string ident; + bool status = true; + + while (status) + { + *is[0] >> ws; + // thread cancellation point + if (terminate_thread) { break; } + + *is[0] >> ident; + if (!(*is[0])) { - cout << "Stream: unknown command: " << ident << endl; + break; } + + status = execute_one(ident); } - cout << "Stream: end of input." << endl; + if (status) + { + cout << "Stream: end of input." << endl; + } -comm_terminate: for (size_t i = 0; i < is.size(); i++) { socketstream *isock = dynamic_cast<socketstream *>(is[i].get()); diff --git a/lib/threads.hpp b/lib/threads.hpp index 1a48aa95..3ec65d81 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -12,8 +12,7 @@ #ifndef GLVIS_THREADS_HPP #define GLVIS_THREADS_HPP -#include "vsdata.hpp" -#include "data_state.hpp" +#include "window.hpp" #include <mfem.hpp> #include <thread> #include <atomic> @@ -23,9 +22,8 @@ class GLVisCommand { private: // Pointers to global GLVis data - VisualizationSceneScalarData **vs; - DataState& curr_state; - SdlWindow *thread_wnd; + Window &win; + SdlWindow *thread_wnd; std::mutex glvis_mutex; std::condition_variable glvis_cond; @@ -101,12 +99,11 @@ class GLVisCommand public: // called by the main execution thread - GLVisCommand(VisualizationSceneScalarData **_vs, - DataState& thread_state); + GLVisCommand(Window &win); // to be used by worker threads - bool KeepAttrib() { return curr_state.keep_attr; } // may need to sync this - bool FixElementOrientations() { return curr_state.fix_elem_orient; } + bool KeepAttrib() { return win.data_state.keep_attr; } // may need to sync this + bool FixElementOrientations() { return win.data_state.fix_elem_orient; } // called by worker threads int NewMeshAndSolution(DataState &&ss); @@ -155,21 +152,22 @@ class communication_thread StreamCollection is; GLVisCommand* glvis_command; - - // data that may be dynamically allocated by the thread - std::unique_ptr<Mesh> new_m; - std::unique_ptr<GridFunction> new_g; - std::string ident; + bool is_multithread; // thread object std::thread tid; // signal for thread cancellation std::atomic<bool> terminate_thread {false}; + static void print_commands(); + bool execute_one(std::string word); void execute(); public: - communication_thread(StreamCollection _is, GLVisCommand* cmd); + communication_thread(StreamCollection _is, GLVisCommand* cmd, + bool mulithread = true); + + bool process_one(); ~communication_thread(); }; diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index dd19091b..8c255dac 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -23,8 +23,7 @@ using namespace std; #include "aux_vis.hpp" #include "material.hpp" #include "palettes.hpp" - -#include "gl2ps.h" +#include "threads.hpp" const char *strings_off_on[] = { "off", "on" }; @@ -596,13 +595,13 @@ void VisualizationSceneScalarData::PrepareColorBar (double minval, // Draw a centered caption at the top (visible with the colorbar) void VisualizationSceneScalarData::PrepareCaption() { - bool empty = plot_caption.empty(); + bool empty = win.plot_caption.empty(); colorbar = (colorbar ? empty+1 : !empty); - string caption(plot_caption); - if (!extra_caption.empty()) + string caption(win.plot_caption); + if (!win.extra_caption.empty()) { - caption += " (" + extra_caption + ")"; + caption += " (" + win.extra_caption + ")"; } caption_buf.clear(); @@ -611,8 +610,8 @@ void VisualizationSceneScalarData::PrepareCaption() GetFont()->getObjectSize(caption, caption_w, caption_h); } -thread_local VisualizationSceneScalarData * vsdata; -extern thread_local VisualizationScene * locscene; +static thread_local VisualizationSceneScalarData *vsdata; +static thread_local Window *window; void KeycPressed(GLenum state) { @@ -643,7 +642,7 @@ void KeycPressed(GLenum state) void KeyCPressed() { cout << "Enter new caption: " << flush; - std::getline(cin, plot_caption); + std::getline(cin, window->plot_caption); vsdata->PrepareCaption(); // turn on or off the caption SendExposeEvent(); } @@ -715,22 +714,22 @@ void KeyLPressed() void KeyrPressed() { - locscene -> spinning = 0; + window->vs -> spinning = 0; RemoveIdleFunc(MainLoop); vsdata -> CenterObject(); - locscene -> ViewAngle = 45.0; - locscene -> ViewScale = 1.0; - locscene -> ViewCenterX = 0.0; - locscene -> ViewCenterY = 0.0; - locscene->cam.Reset(); + window->vs -> ViewAngle = 45.0; + window->vs -> ViewScale = 1.0; + window->vs -> ViewCenterX = 0.0; + window->vs -> ViewCenterY = 0.0; + window->vs->cam.Reset(); vsdata -> key_r_state = 0; SendExposeEvent(); } void KeyRPressed() { - locscene->spinning = 0; + window->vs->spinning = 0; RemoveIdleFunc(MainLoop); vsdata->Toggle2DView(); SendExposeEvent(); @@ -744,14 +743,14 @@ void KeypPressed(GLenum state) } else { - locscene->palette.NextIndex(); + window->vs->palette.NextIndex(); SendExposeEvent(); } } void KeyPPressed() { - locscene->palette.PrevIndex(); + window->vs->palette.PrevIndex(); SendExposeEvent(); } @@ -910,30 +909,30 @@ void KeyF2Pressed() void KeykPressed() { - locscene->matAlpha -= 0.05; - if (locscene->matAlpha < 0.0) + window->vs->matAlpha -= 0.05; + if (window->vs->matAlpha < 0.0) { - locscene->matAlpha = 0.0; + window->vs->matAlpha = 0.0; } - locscene->GenerateAlphaTexture(); + window->vs->GenerateAlphaTexture(); SendExposeEvent(); } void KeyKPressed() { - locscene->matAlpha += 0.05; - if (locscene->matAlpha > 1.0) + window->vs->matAlpha += 0.05; + if (window->vs->matAlpha > 1.0) { - locscene->matAlpha = 1.0; + window->vs->matAlpha = 1.0; } - locscene->GenerateAlphaTexture(); + window->vs->GenerateAlphaTexture(); SendExposeEvent(); } void KeyAPressed() { - bool curr_aa = GetAppWindow()->getRenderer().getAntialiasing(); - GetAppWindow()->getRenderer().setAntialiasing(!curr_aa); + bool curr_aa = window->wnd->getRenderer().getAntialiasing(); + window->wnd->getRenderer().setAntialiasing(!curr_aa); cout << "Multisampling/Antialiasing: " << strings_off_on[!curr_aa ? 1 : 0] << endl; @@ -944,23 +943,23 @@ void KeyAPressed() void KeyCommaPressed() { - locscene->matAlphaCenter -= 0.25; + window->vs->matAlphaCenter -= 0.25; // vsdata -> EventUpdateColors(); - locscene->GenerateAlphaTexture(); + window->vs->GenerateAlphaTexture(); SendExposeEvent(); #ifdef GLVIS_DEBUG - cout << "MatAlphaCenter = " << locscene->matAlphaCenter << endl; + cout << "MatAlphaCenter = " << window->vs->matAlphaCenter << endl; #endif } void KeyLessPressed() { - locscene->matAlphaCenter += 0.25; + window->vs->matAlphaCenter += 0.25; // vsdata -> EventUpdateColors(); - locscene->GenerateAlphaTexture(); + window->vs->GenerateAlphaTexture(); SendExposeEvent(); #ifdef GLVIS_DEBUG - cout << "MatAlphaCenter = " << locscene->matAlphaCenter << endl; + cout << "MatAlphaCenter = " << window->vs->matAlphaCenter << endl; #endif } @@ -1160,7 +1159,7 @@ void VisualizationSceneScalarData::Toggle2DView() break; } - // if (locscene -> view != 2) // make 'R' work the same in 2D and 3D + // if (window->vs -> view != 2) // make 'R' work the same in 2D and 3D key_r_state = (key_r_state+1)%6; rotmat = newrot.mtx; @@ -1354,20 +1353,22 @@ void VisualizationSceneScalarData::SetAutoscale(int _autoscale) } VisualizationSceneScalarData::VisualizationSceneScalarData( - Mesh & m, Vector & s, Mesh *mc) - : a_label_x("x"), a_label_y("y"), a_label_z("z") + Window &win_, bool init) : VisualizationScene(*win_.wnd), win(win_) { - mesh = &m; - mesh_coarse = mc; - sol = &s; + mesh = win.data_state.mesh.get(); + mesh_coarse = win.data_state.mesh_quad.get(); + sol = &win.data_state.sol; - Init(); + if (init) + { + Init(); + } } void VisualizationSceneScalarData::Init() { vsdata = this; - wnd = GetAppWindow(); + window = &win; arrow_type = arrow_scaling_type = 0; scaling = 0; @@ -1749,7 +1750,7 @@ void VisualizationSceneScalarData::SetLevelLines ( void VisualizationSceneScalarData::PrintState() { - cout << "\nkeys: " << GetAppWindow()->getSavedKeys() << "\n" + cout << "\nkeys: " << wnd->getSavedKeys() << "\n" << "\nlight " << strings_off_on[use_light ? 1 : 0] << "\nperspective " << strings_off_on[OrthogonalProjection ? 0 : 1] << "\nviewcenter " << ViewCenterX << ' ' << ViewCenterY diff --git a/lib/vsdata.hpp b/lib/vsdata.hpp index ed09eca6..0020abc1 100644 --- a/lib/vsdata.hpp +++ b/lib/vsdata.hpp @@ -17,12 +17,10 @@ #include "mfem.hpp" #include "openglvis.hpp" #include "aux_vis.hpp" +#include "window.hpp" using namespace mfem; -extern thread_local std::string plot_caption; // defined in glvis.cpp -extern thread_local std::string extra_caption; // defined in glvis.cpp - class Plane { private: @@ -72,9 +70,11 @@ class VisualizationSceneScalarData : public VisualizationScene Mesh *mesh{}, *mesh_coarse{}; Vector *sol{}; + Window &win; + double minv, maxv; - std::string a_label_x, a_label_y, a_label_z; + std::string a_label_x{"x"}, a_label_y{"y"}, a_label_z{"z"}; int scaling, colorbar, drawaxes; Shading shading; @@ -158,9 +158,7 @@ class VisualizationSceneScalarData : public VisualizationScene /// Shrink factor with respect to the element (material) attributes centers double shrinkmat; - VisualizationSceneScalarData() - : a_label_x("x"), a_label_y("y"), a_label_z("z") {} - VisualizationSceneScalarData (Mesh & m, Vector & s, Mesh *mc = NULL); + VisualizationSceneScalarData(Window &win, bool init = true); virtual ~VisualizationSceneScalarData(); @@ -272,7 +270,7 @@ class VisualizationSceneScalarData : public VisualizationScene // colorbar states are: 0) no colorbar, no caption; 1) colorbar with // caption; 2) colorbar without caption. static const int next[2][3] = { { 1, 2, 0 }, { 2, 0, 0 } }; - colorbar = next[plot_caption.empty()][colorbar]; + colorbar = next[win.plot_caption.empty()][colorbar]; } // Turn on or off the caption diff --git a/lib/vssolution.cpp b/lib/vssolution.cpp index 246f88c0..44638f00 100644 --- a/lib/vssolution.cpp +++ b/lib/vssolution.cpp @@ -26,7 +26,6 @@ using namespace std; thread_local VisualizationSceneSolution *vssol; -extern thread_local VisualizationScene *locscene; extern thread_local GeometryRefiner GLVisGeometryRefiner; #ifdef GLVIS_ISFINITE @@ -419,20 +418,22 @@ static void KeyF12Pressed() } } -VisualizationSceneSolution::VisualizationSceneSolution() -{ - v_normals = NULL; -} - VisualizationSceneSolution::VisualizationSceneSolution( - Mesh &m, Vector &s, Mesh *mc, Vector *normals) + Window &win_, bool init) : VisualizationSceneScalarData(win_, false) { - mesh = &m; - mesh_coarse = mc; - sol = &s; - v_normals = normals; + if (win.data_state.normals.Size() > 0) + { + v_normals = &win.data_state.normals; + } - Init(); + if (init) + { + Init(); + if (win.data_state.grid_f) + { + SetGridFunction(*win.data_state.grid_f); + } + } } void VisualizationSceneSolution::Init() @@ -549,11 +550,11 @@ void VisualizationSceneSolution::ToggleDrawElems() if (drawelems < 2) { - extra_caption.clear(); + win.extra_caption.clear(); } else { - extra_caption = modes[drawelems]; + win.extra_caption = modes[drawelems]; } if (drawelems == 0) diff --git a/lib/vssolution.hpp b/lib/vssolution.hpp index 86b848ca..b41be912 100644 --- a/lib/vssolution.hpp +++ b/lib/vssolution.hpp @@ -26,7 +26,7 @@ using namespace mfem; class VisualizationSceneSolution : public VisualizationSceneScalarData { protected: - Vector *v_normals; + Vector *v_normals{}; GridFunction *rsol; int drawmesh, drawelems, drawnums, draworder; @@ -86,9 +86,7 @@ class VisualizationSceneSolution : public VisualizationSceneScalarData int attr_to_show, bdr_attr_to_show; Array<int> el_attr_to_show, bdr_el_attr_to_show; - VisualizationSceneSolution(); - VisualizationSceneSolution(Mesh &m, Vector &s, Mesh *mc = NULL, - Vector *normals = NULL); + VisualizationSceneSolution(Window &win, bool init = true); virtual ~VisualizationSceneSolution(); diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp index 4d927da8..ada41aec 100644 --- a/lib/vssolution3d.cpp +++ b/lib/vssolution3d.cpp @@ -21,7 +21,7 @@ using namespace mfem; using namespace std; -thread_local VisualizationSceneSolution3d *vssol3d; +static thread_local VisualizationSceneSolution3d *vssol3d; extern thread_local GeometryRefiner GLVisGeometryRefiner; // Reference geometries with a cut in the middle, based on subdivision of @@ -687,18 +687,18 @@ static void KeyF10Pressed() SendExposeEvent(); } -VisualizationSceneSolution3d::VisualizationSceneSolution3d() -{} - -VisualizationSceneSolution3d::VisualizationSceneSolution3d(Mesh &m, Vector &s, - Mesh *mc) +VisualizationSceneSolution3d::VisualizationSceneSolution3d(Window &win_, + bool init) + : VisualizationSceneScalarData(win_, false) { - mesh = &m; - mesh_coarse = mc; - sol = &s; - GridF = NULL; - - Init(); + if (init) + { + Init(); + if (win.data_state.grid_f) + { + SetGridFunction(win.data_state.grid_f.get()); + } + } } diff --git a/lib/vssolution3d.hpp b/lib/vssolution3d.hpp index 0448e128..30ad530e 100644 --- a/lib/vssolution3d.hpp +++ b/lib/vssolution3d.hpp @@ -43,7 +43,7 @@ class VisualizationSceneSolution3d : public VisualizationSceneScalarData int nlevels; Array<double> levels; - GridFunction *GridF; + GridFunction *GridF{}; void Init(); @@ -119,8 +119,7 @@ class VisualizationSceneSolution3d : public VisualizationSceneScalarData Array<int> bdr_attr_to_show; - VisualizationSceneSolution3d(); - VisualizationSceneSolution3d(Mesh & m, Vector & s, Mesh *mc); + VisualizationSceneSolution3d(Window &win, bool init = true); void SetGridFunction (GridFunction *gf) { GridF = gf; } diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp index 2b5efda4..206f47fd 100644 --- a/lib/vsvector.cpp +++ b/lib/vsvector.cpp @@ -102,9 +102,7 @@ std::string VisualizationSceneVector::GetHelpString() const return os.str(); } -thread_local VisualizationSceneVector * vsvector; -extern thread_local VisualizationScene * locscene; -extern thread_local VisualizationSceneSolution * vssol; +static thread_local VisualizationSceneVector * vsvector; extern thread_local GeometryRefiner GLVisGeometryRefiner; thread_local int ianim = 0; @@ -256,56 +254,50 @@ const char *Vec2ScalarNames[7] = "curl", "anisotropy" }; -VisualizationSceneVector::VisualizationSceneVector(Mesh & m, - Vector & sx, Vector & sy, Mesh *mc) +VisualizationSceneVector::VisualizationSceneVector(Window &win_) + : VisualizationSceneSolution(win_, false) { - mesh = &m; - mesh_coarse = mc; - solx = &sx; - soly = &sy; - - sol = new Vector(mesh -> GetNV()); + if (win.data_state.grid_f) + { + FiniteElementSpace *fes = win.data_state.grid_f->FESpace(); + if (fes == NULL || win.data_state.grid_f->VectorDim() != 2) + { + cout << "VisualizationSceneVector::VisualizationSceneVector" << endl; + exit(1); + } - VecGridF = NULL; + VecGridF = win.data_state.grid_f.get(); - Init(); -} + solx = new Vector(mesh -> GetNV()); + soly = new Vector(mesh -> GetNV()); -VisualizationSceneVector::VisualizationSceneVector(GridFunction &vgf) -{ - FiniteElementSpace *fes = vgf.FESpace(); - if (fes == NULL || vgf.VectorDim() != 2) + VecGridF->GetNodalValues(*solx, 1); + VecGridF->GetNodalValues(*soly, 2); + } + else { - cout << "VisualizationSceneVector::VisualizationSceneVector" << endl; - exit(1); + solx = &win.data_state.solu; + soly = &win.data_state.solv; } - VecGridF = &vgf; - - mesh = fes->GetMesh(); - - solx = new Vector(mesh -> GetNV()); - soly = new Vector(mesh -> GetNV()); - - vgf.GetNodalValues (*solx, 1); - vgf.GetNodalValues (*soly, 2); + sol = new Vector(mesh -> GetNV()); - sol = new Vector(mesh -> GetNV()); + Init(); - // VisualizationSceneSolution::Init() sets rsol = NULL ! + if (VecGridF) { - Init(); - SetGridFunction(vgf); - } + // VisualizationSceneSolution::Init() sets rsol = NULL ! + SetGridFunction(*VecGridF); - mesh->GetNodes(vc0); - if (vc0.Size() != vgf.Size()) - { - vc0.Destroy(); - } - else - { - vc0 += vgf; + mesh->GetNodes(vc0); + if (vc0.Size() != VecGridF->Size()) + { + vc0.Destroy(); + } + else + { + vc0 += *VecGridF; + } } } @@ -381,7 +373,7 @@ void VisualizationSceneVector::CycleVec2Scalar(int print) } Vec2Scalar = Vec2ScalarFunctions[i]; - extra_caption = Vec2ScalarNames[i]; + win.extra_caption = Vec2ScalarNames[i]; for (i = 0; i < mesh->GetNV(); i++) { @@ -488,7 +480,7 @@ void VisualizationSceneVector::Init() ArrowScale = 1.0; RefineFactor = 1; Vec2Scalar = VecLength; - extra_caption = Vec2ScalarNames[0]; + win.extra_caption = Vec2ScalarNames[0]; for (int i = 0; i < mesh->GetNV(); i++) { diff --git a/lib/vsvector.hpp b/lib/vsvector.hpp index c7453bd2..b8c53601 100644 --- a/lib/vsvector.hpp +++ b/lib/vsvector.hpp @@ -26,7 +26,7 @@ class VisualizationSceneVector : public VisualizationSceneSolution gl3::GlDrawable vector_buf; gl3::GlDrawable displine_buf; - GridFunction *VecGridF; + GridFunction *VecGridF{}; void Init(); @@ -48,8 +48,7 @@ class VisualizationSceneVector : public VisualizationSceneSolution int GetFunctionAutoRefineFactor() override; public: - VisualizationSceneVector(Mesh &m, Vector &sx, Vector &sy, Mesh *mc = NULL); - VisualizationSceneVector(GridFunction &vgf); + VisualizationSceneVector(Window &win_); void NewMeshAndSolution(GridFunction &vgf, Mesh *mc = NULL); diff --git a/lib/vsvector3d.cpp b/lib/vsvector3d.cpp index d66059f2..c9f99f90 100644 --- a/lib/vsvector3d.cpp +++ b/lib/vsvector3d.cpp @@ -114,8 +114,8 @@ std::string VisualizationSceneVector3d::GetHelpString() const return os.str(); } -thread_local VisualizationSceneVector3d *vsvector3d; -extern thread_local VisualizationScene *locscene; +static thread_local VisualizationSceneVector3d *vsvector3d; +static thread_local Window *window; extern thread_local GeometryRefiner GLVisGeometryRefiner; static void KeyDPressed() @@ -150,13 +150,13 @@ static void KeyBPressed() static void KeyrPressed() { - locscene -> spinning = 0; + window->vs -> spinning = 0; RemoveIdleFunc(MainLoop); vsvector3d -> CenterObject(); - locscene -> ViewAngle = 45.0; - locscene -> ViewScale = 1.0; - locscene -> ViewCenterX = 0.0; - locscene -> ViewCenterY = 0.0; + window->vs -> ViewAngle = 45.0; + window->vs -> ViewScale = 1.0; + window->vs -> ViewCenterX = 0.0; + window->vs -> ViewCenterY = 0.0; vsvector3d -> ianim = vsvector3d -> ianimd = 0; vsvector3d -> Prepare(); vsvector3d -> PrepareLines(); @@ -167,7 +167,7 @@ static void KeyrPressed() static void KeyRPressed() { - locscene->spinning = 0; + window->vs->spinning = 0; RemoveIdleFunc(MainLoop); vsvector3d -> ianim = vsvector3d -> ianimd = 0; vsvector3d -> Prepare(); @@ -330,7 +330,7 @@ void VisualizationSceneVector3d::SetScalarFunction() } break; } - extra_caption = scal_func_name[scal_func]; + win.extra_caption = scal_func_name[scal_func]; } void VisualizationSceneVector3d::ToggleScalarFunction() @@ -341,49 +341,38 @@ void VisualizationSceneVector3d::ToggleScalarFunction() FindNewValueRange(true); } -VisualizationSceneVector3d::VisualizationSceneVector3d(Mesh &m, Vector &sx, - Vector &sy, Vector &sz, Mesh *mc) +VisualizationSceneVector3d::VisualizationSceneVector3d(Window &win_) + : VisualizationSceneSolution3d(win_, false) { - mesh = &m; - mesh_coarse = mc; - solx = &sx; - soly = &sy; - solz = &sz; + if (win.data_state.grid_f) + { + FiniteElementSpace *fes = win.data_state.grid_f->FESpace(); + if (fes == NULL || fes->GetVDim() != 3) + { + cout << "VisualizationSceneVector3d::VisualizationSceneVector3d" << endl; + exit(1); + } - sol = new Vector(mesh->GetNV()); + VecGridF = win.data_state.grid_f.get(); - sfes = NULL; - VecGridF = NULL; + sfes = new FiniteElementSpace(mesh, fes->FEColl(), 1, fes->GetOrdering()); + GridF = new GridFunction(sfes); - Init(); -} + solx = new Vector(mesh->GetNV()); + soly = new Vector(mesh->GetNV()); + solz = new Vector(mesh->GetNV()); -VisualizationSceneVector3d::VisualizationSceneVector3d(GridFunction &vgf, - Mesh *mc) -{ - FiniteElementSpace *fes = vgf.FESpace(); - if (fes == NULL || fes->GetVDim() != 3) + VecGridF->GetNodalValues(*solx, 1); + VecGridF->GetNodalValues(*soly, 2); + VecGridF->GetNodalValues(*solz, 3); + } + else { - cout << "VisualizationSceneVector3d::VisualizationSceneVector3d" << endl; - exit(1); + solx = &win.data_state.solu; + soly = &win.data_state.solv; + solz = &win.data_state.solw; } - VecGridF = &vgf; - - mesh = fes->GetMesh(); - mesh_coarse = mc; - - sfes = new FiniteElementSpace(mesh, fes->FEColl(), 1, fes->GetOrdering()); - GridF = new GridFunction(sfes); - - solx = new Vector(mesh->GetNV()); - soly = new Vector(mesh->GetNV()); - solz = new Vector(mesh->GetNV()); - - vgf.GetNodalValues(*solx, 1); - vgf.GetNodalValues(*soly, 2); - vgf.GetNodalValues(*solz, 3); - sol = new Vector(mesh->GetNV()); Init(); @@ -391,6 +380,8 @@ VisualizationSceneVector3d::VisualizationSceneVector3d(GridFunction &vgf, void VisualizationSceneVector3d::Init() { + window = &win; + key_r_state = 0; drawdisp = 0; diff --git a/lib/vsvector3d.hpp b/lib/vsvector3d.hpp index b32fe3ea..e15a89b9 100644 --- a/lib/vsvector3d.hpp +++ b/lib/vsvector3d.hpp @@ -26,8 +26,8 @@ class VisualizationSceneVector3d : public VisualizationSceneSolution3d gl3::GlDrawable vector_buf; gl3::GlDrawable displine_buf; - GridFunction *VecGridF; - FiniteElementSpace *sfes; + GridFunction *VecGridF{}; + FiniteElementSpace *sfes{}; void Init(); @@ -39,9 +39,7 @@ class VisualizationSceneVector3d : public VisualizationSceneSolution3d public: int ianim, ianimd, ianimmax, drawdisp; - VisualizationSceneVector3d(Mesh & m, Vector & sx, Vector & sy, Vector & sz, - Mesh *mc = NULL); - VisualizationSceneVector3d (GridFunction &vgf, Mesh *mc = NULL); + VisualizationSceneVector3d(Window &win); void NewMeshAndSolution(Mesh *new_m, Mesh *new_mc, GridFunction *new_v); diff --git a/lib/window.cpp b/lib/window.cpp new file mode 100644 index 00000000..6ae30459 --- /dev/null +++ b/lib/window.cpp @@ -0,0 +1,269 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "window.hpp" +#include "visual.hpp" + +Window &Window::operator=(Window &&w) +{ + internal = std::move(w.internal); + + data_state = std::move(w.data_state); + + window_x = w.window_x; + window_y = w.window_y; + window_w = w.window_w; + window_h = w.window_h; + window_title = w.window_title; + plot_caption = std::move(w.plot_caption); + extra_caption = std::move(w.extra_caption); + + return *this; +} + +// Visualize the data in the global variables mesh, sol/grid_f, etc +bool Window::GLVisInitVis(StreamCollection input_streams) +{ + DataState::FieldType field_type = data_state.GetType(); + + if (field_type <= DataState::FieldType::MIN + || field_type >= DataState::FieldType::MAX) + { + return false; + } + + static const char *window_titles[] = { "GLVis [mesh]", + "GLVis [scalar data]", + "GLVis [vector data]", + }; + + const char *win_title = (window_title == nullptr) ? + window_titles[(int)field_type] : window_title; + + internal.wnd.reset(InitVisualization(win_title, window_x, window_y, window_w, + window_h)); + if (!wnd) + { + cerr << "Initializing the visualization failed." << endl; + return false; + } + + if (input_streams.size() > 0) + { +#ifndef __EMSCRIPTEN__ + wnd->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); +#endif + internal.glvis_command.reset(new GLVisCommand(*this)); + SetGLVisCommand(glvis_command.get()); +#ifndef __EMSCRIPTEN__ + constexpr bool multithreaded = true; +#else + constexpr bool multithreaded = false; +#endif + internal.comm_thread.reset(new communication_thread(std::move(input_streams), + glvis_command.get(), + multithreaded)); + } + + locwin = this; + + if (data_state.quad_f) + { + wnd->setOnKeyDown('Q', SwitchQuadSolution); + } + + double mesh_range = -1.0; + if (field_type == DataState::FieldType::SCALAR + || field_type == DataState::FieldType::MESH) + { + if (data_state.grid_f) + { + data_state.grid_f->GetNodalValues(data_state.sol); + } + if (data_state.mesh->SpaceDimension() == 2) + { + internal.vs.reset(new VisualizationSceneSolution(*this)); + + if (field_type == DataState::FieldType::MESH) + { + vs->OrthogonalProjection = 1; + vs->SetLight(false); + vs->Zoom(1.8); + // Use the 'bone' palette when visualizing a 2D mesh only (otherwise + // the 'jet-like' palette is used in 2D, see vssolution.cpp). + vs->palette.SetIndex(4); + } + } + else if (data_state.mesh->SpaceDimension() == 3) + { + VisualizationSceneSolution3d *vss; + vss = new VisualizationSceneSolution3d(*this); + internal.vs.reset(vss); + + if (field_type == DataState::FieldType::MESH) + { + if (data_state.mesh->Dimension() == 3) + { + // Use the 'white' palette when visualizing a 3D volume mesh only + vss->palette.SetIndex(11); + vss->SetLightMatIdx(4); + } + else + { + // Use the 'bone' palette when visualizing a surface mesh only + vss->palette.SetIndex(4); + } + // Otherwise, the 'vivid' palette is used in 3D see vssolution3d.cpp + vss->ToggleDrawAxes(); + vss->ToggleDrawMesh(); + } + } + if (field_type == DataState::FieldType::MESH) + { + if (data_state.grid_f) + { + mesh_range = data_state.grid_f->Max() + 1.0; + } + else + { + mesh_range = data_state.sol.Max() + 1.0; + } + } + } + else if (field_type == DataState::FieldType::VECTOR) + { + if (data_state.mesh->SpaceDimension() == 2) + { + internal.vs.reset(new VisualizationSceneVector(*this)); + } + else if (data_state.mesh->SpaceDimension() == 3) + { + if (data_state.grid_f) + { + data_state.ProjectVectorFEGridFunction(); + } + internal.vs.reset(new VisualizationSceneVector3d(*this)); + } + } + + if (vs) + { + // increase the refinement factors if visualizing a GridFunction + if (data_state.grid_f) + { + vs->AutoRefine(); + vs->SetShading(VisualizationSceneScalarData::Shading::Noncomforming, true); + } + if (mesh_range > 0.0) + { + vs->SetValueRange(-mesh_range, mesh_range); + vs->SetAutoscale(0); + } + if (data_state.mesh->SpaceDimension() == 2 + && field_type == DataState::FieldType::MESH) + { + SetVisualizationScene(vs.get(), 2, data_state.keys.c_str()); + } + else + { + SetVisualizationScene(vs.get(), 3, data_state.keys.c_str()); + } + } + return true; +} + +void Window::GLVisStartVis() +{ + RunVisualization(); + internal.vs.reset(); + internal.wnd.reset(); + if (glvis_command) + { + glvis_command->Terminate(); + internal.comm_thread.reset(); + internal.glvis_command.reset(); + } + cout << "GLVis window closed." << endl; +} + +void Window::SwitchQuadSolution(DataState::QuadSolution quad_type) +{ + data_state.SwitchQuadSolution(quad_type); + ResetMeshAndSolution(data_state); +} + +bool Window::SetNewMeshAndSolution(DataState new_state) +{ + if (new_state.mesh->SpaceDimension() == data_state.mesh->SpaceDimension() && + new_state.grid_f->VectorDim() == data_state.grid_f->VectorDim()) + { + ResetMeshAndSolution(new_state); + + data_state = std::move(new_state); + + return true; + } + else + { + return false; + } +} + +void Window::ResetMeshAndSolution(DataState &ss) +{ + if (ss.mesh->SpaceDimension() == 2) + { + if (ss.grid_f->VectorDim() == 1) + { + VisualizationSceneSolution *vss = + dynamic_cast<VisualizationSceneSolution *>(internal.vs.get()); + ss.grid_f->GetNodalValues(ss.sol); + vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &ss.sol, + ss.grid_f.get()); + } + else + { + VisualizationSceneVector *vsv = + dynamic_cast<VisualizationSceneVector *>(internal.vs.get()); + vsv->NewMeshAndSolution(*ss.grid_f, ss.mesh_quad.get()); + } + } + else + { + if (ss.grid_f->VectorDim() == 1) + { + VisualizationSceneSolution3d *vss = + dynamic_cast<VisualizationSceneSolution3d *>(internal.vs.get()); + ss.grid_f->GetNodalValues(ss.sol); + vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &ss.sol, + ss.grid_f.get()); + } + else + { + ss.ProjectVectorFEGridFunction(); + + VisualizationSceneVector3d *vss = + dynamic_cast<VisualizationSceneVector3d *>(internal.vs.get()); + vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), ss.grid_f.get()); + } + } +} + + +thread_local Window *Window::locwin = NULL; + +void Window::SwitchQuadSolution() +{ + int iqs = ((int)locwin->data_state.GetQuadSolution()+1) + % ((int)DataState::QuadSolution::MAX); + locwin->SwitchQuadSolution((DataState::QuadSolution)iqs); + SendExposeEvent(); +} diff --git a/lib/window.hpp b/lib/window.hpp new file mode 100644 index 00000000..a0746a98 --- /dev/null +++ b/lib/window.hpp @@ -0,0 +1,84 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_WINDOW_HPP +#define GLVIS_WINDOW_HPP + +#include <string> + +#include "data_state.hpp" +#include "stream_reader.hpp" + +class SdlWindow; +class VisualizationSceneScalarData; +class communication_thread; +class GLVisCommand; + +struct Window +{ +private: + struct + { + std::unique_ptr<SdlWindow> wnd; + std::unique_ptr<VisualizationSceneScalarData> vs; + std::unique_ptr<communication_thread> comm_thread; + std::unique_ptr<GLVisCommand> glvis_command; + } internal; + +public: + DataState data_state; + const std::unique_ptr<SdlWindow> &wnd{internal.wnd}; + const std::unique_ptr<VisualizationSceneScalarData> &vs{internal.vs}; + const std::unique_ptr<communication_thread> &comm_thread {internal.comm_thread}; + const std::unique_ptr<GLVisCommand> &glvis_command{internal.glvis_command}; + + int window_x = 0; // not a command line option + int window_y = 0; // not a command line option + int window_w = 400; + int window_h = 350; + const char *window_title = nullptr; + std::string plot_caption; + std::string extra_caption; + + Window() = default; + Window(Window &&w) { *this = std::move(w); } + Window& operator=(Window &&w); + + /// Visualize the data in the global variables mesh, sol/grid_f, etc + bool GLVisInitVis(StreamCollection input_streams); + void GLVisStartVis(); + + /// Switch the quadrature function representation and update the visualization + void SwitchQuadSolution(DataState::QuadSolution type); + + /// Sets a new mesh and solution from another DataState object, and + /// updates the VisualizationScene with the new data. + /// + /// Mesh space and grid function dimensions must both match the original + /// dimensions of the current DataState. If there is a mismatch in either + /// value, the function will return false, and the mesh/solution will not be + /// updated. + bool SetNewMeshAndSolution(DataState new_state); + + /// Updates the VisualizationScene with the new data of the given DataState object. + /// @note: Use with caution when the update is compatible + /// @see SetNewMeshAndSolution() + void ResetMeshAndSolution(DataState &ss); + +private: + /// Thread-local singleton for key handlers + static thread_local Window *locwin; + + /// Switch representation of the quadrature function + static void SwitchQuadSolution(); +}; + +#endif // GLVIS_WINDOW_HPP diff --git a/makefile b/makefile index 17548919..50d6f5b4 100644 --- a/makefile +++ b/makefile @@ -254,14 +254,15 @@ ALL_SOURCE_FILES = \ lib/gl/shader.cpp lib/gl/types.cpp lib/aux_js.cpp lib/aux_vis.cpp \ lib/coll_reader.cpp lib/data_state.cpp lib/file_reader.cpp \ lib/font.cpp lib/gl2ps.c lib/gltf.cpp lib/material.cpp \ - lib/openglvis.cpp lib/palettes.cpp lib/palettes_base.cpp lib/sdl.cpp \ - lib/sdl_helper.cpp lib/sdl_main.cpp lib/sdl_windows.cpp \ - lib/sdl_x11.cpp lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ - lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp \ - lib/vsvector3d.cpp + lib/openglvis.cpp lib/palettes_base.cpp lib/palettes.cpp lib/sdl.cpp \ + lib/script_controller.cpp lib/sdl_helper.cpp lib/sdl_main.cpp \ + lib/sdl_windows.cpp lib/sdl_x11.cpp lib/stream_reader.cpp \ + lib/threads.cpp lib/vsdata.cpp lib/vssolution.cpp lib/vssolution3d.cpp \ + lib/vsvector.cpp lib/vsvector3d.cpp lib/window.cpp OBJC_SOURCE_FILES = $(if $(NOTMAC),,lib/sdl_mac.mm) DESKTOP_ONLY_SOURCE_FILES = \ - lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl_x11.cpp + lib/gl/renderer_ff.cpp lib/gl2ps.c lib/script_controller.cpp \ + lib/sdl_x11.cpp WEB_ONLY_SOURCE_FILES = lib/aux_js.cpp LOGO_FILE = share/logo.rgba LOGO_FILE_CPP = $(LOGO_FILE).bin.cpp @@ -275,12 +276,12 @@ HEADER_FILES = \ lib/gl/types.hpp lib/aux_vis.hpp lib/coll_reader.hpp \ lib/data_state.hpp lib/file_reader.hpp lib/font.hpp \ lib/geom_utils.hpp lib/gl2ps.h lib/gltf.hpp lib/logo.hpp \ - lib/material.hpp lib/openglvis.hpp lib/palettes.hpp \ - lib/palettes_base.hpp lib/sdl.hpp lib/sdl_helper.hpp lib/sdl_mac.hpp \ - lib/sdl_main.hpp lib/sdl_windows.hpp lib/sdl_x11.hpp \ - lib/stream_reader.hpp lib/threads.hpp lib/visual.hpp lib/vsdata.hpp \ - lib/vssolution.hpp lib/vssolution3d.hpp lib/vsvector.hpp \ - lib/vsvector3d.hpp + lib/material.hpp lib/openglvis.hpp lib/palettes_base.hpp \ + lib/palettes.hpp lib/script_controller.hpp lib/sdl_helper.hpp \ + lib/sdl.hpp lib/sdl_mac.hpp lib/sdl_main.hpp lib/sdl_windows.hpp \ + lib/sdl_x11.hpp lib/stream_reader.hpp lib/threads.hpp lib/visual.hpp \ + lib/vsdata.hpp lib/vssolution.hpp lib/vssolution3d.hpp \ + lib/vsvector.hpp lib/vsvector3d.hpp lib/window.hpp DESKTOP_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(DESKTOP_ONLY_SOURCE_FILES) $(LOGO_FILE_CPP) WEB_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(WEB_ONLY_SOURCE_FILES)