diff --git a/CHANGELOG b/CHANGELOG index ca2af50f..c9072437 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,16 @@ Unlike previous GLVis releases, this version requires a C++17 compiler. default 'local' to each rank; 'global' vs. 'local' numbering can be toggled with 'Alt+n'. +- Refactored code base enabling automatic generation of the script and stream + command lists (shown as help when an unrecognized option is entered). Other + improvements include: + * Unified window initialization between the app and GLVis-js. + * Unified handling of streams and improved data storage, enabling animation + of all data types. + * Fixed the flag for real attributes of distributed meshes. + * Fixed setting of padding digits for data collections. + * Fixed cycling of the vector-to-scalar function in 2D with raw data. + Version 4.4 released on May 1, 2025 =================================== 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 13915d4d..4b6f7f4d 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -37,6 +37,8 @@ #include #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" @@ -47,28 +49,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 { @@ -83,1065 +63,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); - } - // Window structure with DataState could allow to remove this call - vs->SetDataOffsets(stream_state.offsets.get()); - } - 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()); - } - // Window structure with DataState could allow to remove this call - vs->SetDataOffsets(stream_state.offsets.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; @@ -1149,27 +93,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(); } @@ -1183,7 +120,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)); @@ -1194,7 +131,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)); @@ -1205,7 +142,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); @@ -1217,7 +154,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; @@ -1367,7 +304,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]; @@ -1414,7 +351,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; @@ -1424,6 +377,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(); @@ -1479,17 +433,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", @@ -1503,9 +457,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."); @@ -1600,7 +554,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) { @@ -1620,7 +578,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) { @@ -1645,8 +603,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)) { @@ -1668,7 +627,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; } @@ -1713,8 +672,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); @@ -1755,7 +715,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) { @@ -1804,7 +764,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) { @@ -1828,7 +788,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(); @@ -1857,11 +817,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 13b3db7c..e3e5bc86 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -25,6 +25,7 @@ list(APPEND SOURCES palettes.cpp palettes_default.cpp palettes_base.cpp + script_controller.cpp sdl.cpp sdl_helper.cpp sdl_main.cpp @@ -33,7 +34,8 @@ list(APPEND SOURCES vssolution.cpp vssolution3d.cpp vsvector.cpp - vsvector3d.cpp) + vsvector3d.cpp + window.cpp) list(APPEND HEADERS gl/attr_traits.hpp @@ -54,6 +56,7 @@ list(APPEND HEADERS openglvis.hpp palettes.hpp palettes_base.hpp + script_controller.hpp sdl.hpp sdl_helper.hpp sdl_main.hpp @@ -64,7 +67,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..401af562 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -24,16 +24,12 @@ #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; int last_stream_nproc = 1; @@ -44,14 +40,11 @@ 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) { // reset antialiasing - GetAppWindow()->getRenderer().setAntialiasing(0); + win.wnd->getRenderer().setAntialiasing(0); std::string word; double minv = 0.0, maxv = 0.0; @@ -60,7 +53,7 @@ void display(std::stringstream & commands, const int w, const int h) if (word == "keys") { std::cout << "parsing 'keys'" << std::endl; - commands >> stream_state.keys; + commands >> win.data_state.keys; } else if (word == "valuerange") { @@ -73,162 +66,19 @@ void display(std::stringstream & commands, const int w, const int h) } } - 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.window_title = "glvis"; + win.window_x = 0.; + win.window_y = 0.; + win.window_w = w; + win.window_h = h; - 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); - } - } + win.GLVisInitVis({}); - CallKeySequence(stream_state.keys.c_str()); + CallKeySequence(win.data_state.keys.c_str()); if (minv || maxv) { - vs->SetValueRange(minv, maxv); + win.vs->SetValueRange(minv, maxv); } SendExposeEvent(); @@ -282,7 +132,7 @@ void displayParallelStreams(const StringArray & streams, const int w, const int h) { std::stringstream commands(streams[0]); - processParallelStreams(stream_state, streams, &commands); + processParallelStreams(win.data_state, streams, &commands); display(commands, w, h); } @@ -293,7 +143,7 @@ void displayStream(const std::string & stream, const int w, const int h) std::string data_type; ss >> data_type; - StreamReader reader(stream_state); + StreamReader reader(win.data_state); reader.ReadStream(ss, data_type); display(ss, w, h); @@ -306,11 +156,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 +207,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 +244,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 +274,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 +312,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 8655691f..c76278a8 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -14,12 +14,15 @@ #include #include #include +#include #include "gl/types.hpp" -#include "gl2ps.h" #include "palettes.hpp" #include "sdl.hpp" #include "threads.hpp" +#ifndef __EMSCRIPTEN__ +#include "gl2ps.h" +#endif #include @@ -38,7 +41,7 @@ using namespace mfem; using namespace std; thread_local int visualize = 0; -thread_local VisualizationScene * locscene; +thread_local VisualizationScene * locscene = nullptr; thread_local GLVisCommand *glvis_command = nullptr; #ifdef GLVIS_MULTISAMPLE @@ -47,17 +50,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; @@ -81,7 +90,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 @@ -92,7 +101,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 @@ -195,7 +206,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) @@ -365,8 +376,7 @@ void RunVisualization() wnd->mainLoop(); #endif InitIdleFuncs(); - delete locscene; - delete wnd; + visualize = 0; wnd = nullptr; } @@ -453,6 +463,7 @@ void InitIdleFuncs() } } +#ifndef __EMSCRIPTEN__ bool CommunicationIdleFunc() { int status = glvis_command->Execute(); @@ -468,6 +479,7 @@ bool CommunicationIdleFunc() } return false; } +#endif bool MainIdleFunc() { @@ -1171,6 +1183,7 @@ void KeyS() SendExposeEvent(); } +#ifndef __EMSCRIPTEN__ inline GL2PSvertex CreatePrintVtx(gl3::FeedbackVertex v) { return @@ -1213,6 +1226,7 @@ void PrintCaptureBuffer(gl3::CaptureBuffer& cbuf) gl2psText(entry.text.c_str(), "Times", 12); } } +#endif void KeyCtrlP() { @@ -1275,7 +1289,9 @@ void ThreadsPauseFunc(GLenum state) { if (state & KMOD_CTRL) { +#ifndef __EMSCRIPTEN__ glvis_command->ToggleAutopause(); +#endif } else { diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index f20471de..c29c255f 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -22,8 +22,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 bba8b9fa..49d62fb9 100644 --- a/lib/coll_reader.hpp +++ b/lib/coll_reader.hpp @@ -17,7 +17,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 9b319a39..6fbf1af8 100644 --- a/lib/data_state.cpp +++ b/lib/data_state.cpp @@ -12,8 +12,7 @@ #include #include "data_state.hpp" -#include "vsvector.hpp" -#include "vsvector3d.hpp" +#include "visual.hpp" using namespace std; using namespace mfem; @@ -43,12 +42,6 @@ DataState &DataState::operator=(DataState &&ss) type = ss.type; quad_sol = ss.quad_sol; - - sol = std::move(ss.sol); - solu = std::move(ss.solu); - solv = std::move(ss.solv); - solw = std::move(ss.solw); - normals = std::move(ss.normals); keys = std::move(ss.keys); fix_elem_orient = ss.fix_elem_orient; @@ -72,6 +65,77 @@ void DataState::SetMesh(std::unique_ptr &&pmesh) if (quad_f && quad_f->GetSpace()->GetMesh() != mesh.get()) { SetQuadFunction(NULL); } } +void DataState::SetScalarData(Vector new_sol) +{ + internal.grid_f.reset(); + internal.quad_f.reset(); + + if (!sol) + { + internal.sol.reset(new Vector(std::move(new_sol))); + } + else + { + *sol = std::move(new_sol); + } + type = DataState::FieldType::SCALAR; +} + +void DataState::SetNormals(Vector new_normals) +{ + if (!normals) + { + internal.sol.reset(new Vector(std::move(new_normals))); + } + else + { + *sol = std::move(new_normals); + } +} + +void DataState::SetVectorData(Vector new_solx, Vector new_soly) +{ + MFEM_VERIFY(mesh->SpaceDimension() == 2, "Incompatible space dimension"); + + internal.grid_f.reset(); + internal.quad_f.reset(); + + if (!solx || !soly) + { + internal.solx.reset(new Vector(std::move(new_solx))); + internal.soly.reset(new Vector(std::move(new_soly))); + } + else + { + *solx = std::move(new_solx); + *soly = std::move(new_soly); + } + type = DataState::FieldType::VECTOR; +} + +void DataState::SetVectorData(Vector new_solx, Vector new_soly, + Vector new_solz) +{ + MFEM_VERIFY(mesh->SpaceDimension() == 3, "Incompatible space dimension"); + + internal.grid_f.reset(); + internal.quad_f.reset(); + + if (!solx || !soly || !solz) + { + internal.solx.reset(new Vector(std::move(new_solx))); + internal.soly.reset(new Vector(std::move(new_soly))); + internal.solz.reset(new Vector(std::move(new_solz))); + } + else + { + *solx = std::move(new_solx); + *soly = std::move(new_soly); + *solz = std::move(new_solz); + } + type = DataState::FieldType::VECTOR; +} + void DataState::SetGridFunction(GridFunction *gf, int component) { internal.grid_f.reset(gf); @@ -87,7 +151,7 @@ void DataState::SetGridFunction(std::unique_ptr &&pgf, SetGridFunctionSolution(component); } -void DataState::SetGridFunction(std::vector &gf_array, +void DataState::SetGridFunction(std::vector &gf_array, int num_pieces, int component) { SetGridFunction(new GridFunction(mesh.get(), gf_array.data(), num_pieces), @@ -239,14 +303,14 @@ void DataState::Extrude1DMeshAndSolution() internal.grid_f.reset(grid_f_2d); } - else if (sol.Size() == mesh->GetNV()) + else if (sol) { Vector sol2d(mesh2d->GetNV()); for (int i = 0; i < mesh->GetNV(); i++) { - sol2d(2*i+0) = sol2d(2*i+1) = sol(i); + sol2d(2*i+0) = sol2d(2*i+1) = (*sol)(i); } - sol = sol2d; + *sol = std::move(sol2d); } if (!mesh_quad) { internal.mesh.swap(internal.mesh_quad); } @@ -321,7 +385,6 @@ void DataState::SetMeshSolution() } cout << "Number of colors: " << grid_f->Max() + 1 << endl; } - grid_f->GetNodalValues(sol); if (save_coloring) { const char col_fname[] = "GLVis_coloring.gf"; @@ -333,8 +396,8 @@ void DataState::SetMeshSolution() } else // zero solution { - sol.SetSize (mesh -> GetNV()); - sol = 0.0; + internal.sol.reset(new Vector(mesh->GetNV())); + *sol = 0.0; } type = FieldType::MESH; } @@ -368,15 +431,7 @@ void DataState::SetGridFunctionSolution(int gf_component) return; } - if (grid_f->VectorDim() == 1) - { - grid_f->GetNodalValues(sol); - type = FieldType::SCALAR; - } - else - { - type = FieldType::VECTOR; - } + type = (grid_f->VectorDim() == 1) ? FieldType::SCALAR : FieldType::VECTOR; } void DataState::SetQuadFunctionSolution(int qf_component) @@ -532,8 +587,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 @@ -544,7 +598,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 @@ -573,67 +626,7 @@ 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); - - // do not move 'sol' vector as it is updated directly - internal = std::move(new_state.internal); - - return true; - } - else - { - return false; - } -} - -void DataState::ResetMeshAndSolution(DataState &ss, VisualizationScene* vs) -{ - if (ss.mesh->SpaceDimension() == 2) - { - if (ss.grid_f->VectorDim() == 1) - { - auto *vss = dynamic_cast(vs); - // use the local vector as pointer is invalid after the move - ss.grid_f->GetNodalValues(sol); - // update the offsets before the mesh and solution - vss->SetDataOffsets(ss.offsets.get()); - vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &sol, - ss.grid_f.get()); - } - else - { - auto *vsv = dynamic_cast(vs); - vsv->SetDataOffsets(ss.offsets.get()); - vsv->NewMeshAndSolution(*ss.grid_f, ss.mesh_quad.get()); - } - } - else - { - if (ss.grid_f->VectorDim() == 1) - { - auto *vss = dynamic_cast(vs); - // use the local vector as pointer is invalid after the move - ss.grid_f->GetNodalValues(sol); - vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &sol, - ss.grid_f.get()); - } - else - { - ss.ProjectVectorFEGridFunction(); - - auto *vss = dynamic_cast(vs); - vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), ss.grid_f.get()); - } - } -} - -void DataState::ComputeDofsOffsets(std::vector &gf_array) +void DataState::ComputeDofsOffsets(std::vector &gf_array) { const int nprocs = static_cast(gf_array.size()); MFEM_VERIFY(!gf_array.empty(), "No grid functions provided for offsets"); diff --git a/lib/data_state.hpp b/lib/data_state.hpp index 47e9e0a0..a5d192c8 100644 --- a/lib/data_state.hpp +++ b/lib/data_state.hpp @@ -22,7 +22,6 @@ #include "openglvis.hpp" - struct DataState { enum class FieldType @@ -70,10 +69,10 @@ struct DataState using Offsets = std::vector; private: - friend class StreamReader; - friend class FileReader; struct { + std::unique_ptr sol, solx, soly, solz; + std::unique_ptr normals; std::unique_ptr mesh; std::unique_ptr mesh_quad; std::unique_ptr grid_f; @@ -88,17 +87,15 @@ struct DataState void SetGridFunctionSolution(int component = -1); void SetQuadFunctionSolution(int component = -1); - /// 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() - void ResetMeshAndSolution(DataState &ss, VisualizationScene* vs); - /// Compute the dofs offsets from the grid function vector void ComputeDofsOffsets(std::vector &gf_array); public: - mfem::Vector sol, solu, solv, solw, normals; + const std::unique_ptr &sol{internal.sol}; + const std::unique_ptr &solx{internal.solx}; + const std::unique_ptr &soly{internal.soly}; + const std::unique_ptr &solz{internal.solz}; + const std::unique_ptr &normals{internal.normals}; const std::unique_ptr &mesh{internal.mesh}; const std::unique_ptr &mesh_quad{internal.mesh_quad}; const std::unique_ptr &grid_f{internal.grid_f}; @@ -128,6 +125,18 @@ struct DataState the same one. */ void SetMesh(std::unique_ptr &&pmesh); + /// Set scalar data + void SetScalarData(mfem::Vector sol); + + /// Set normals + void SetNormals(mfem::Vector normals); + + /// Set 2D vector data + void SetVectorData(mfem::Vector solx, mfem::Vector soly); + + /// Set 3D vector data + void SetVectorData(mfem::Vector solx, mfem::Vector soly, mfem::Vector solz); + /// Set a grid function (plain pointer version) /** Note that ownership is passed from the caller. @see SetGridFunction(std::unique_ptr &&, int ) */ @@ -186,8 +195,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; } @@ -200,16 +209,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); }; #endif // GLVIS_DATA_STATE_HPP diff --git a/lib/file_reader.cpp b/lib/file_reader.cpp index 8897c7f1..7e8b3a04 100644 --- a/lib/file_reader.cpp +++ b/lib/file_reader.cpp @@ -65,19 +65,27 @@ int FileReader::ReadSerial(FileReader::FileType ft, const char *mesh_file, // get rid of NetGen's info line char buff[128]; solin->getline(buff,128); - data.sol.Load(*solin, data.mesh->GetNV()); - data.type = DataState::FieldType::SCALAR; + Vector sol; + sol.Load(*solin, data.mesh->GetNV()); + data.SetScalarData(std::move(sol)); } break; case FileType::VECTOR_SOL: - data.solu.Load(*solin, data.mesh->GetNV()); - data.solv.Load(*solin, data.mesh->GetNV()); + { + Vector solx, soly, solz; + solx.Load(*solin, data.mesh->GetNV()); + soly.Load(*solin, data.mesh->GetNV()); if (data.mesh->SpaceDimension() == 3) { - data.solw.Load(*solin, data.mesh->GetNV()); + solz.Load(*solin, data.mesh->GetNV()); + data.SetVectorData(std::move(solx), std::move(soly), std::move(solz)); } - data.type = DataState::FieldType::VECTOR; - break; + else + { + data.SetVectorData(std::move(solx), std::move(soly)); + } + } + break; default: cerr << "Unknown file type. Exit" << endl; exit(1); diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index bd0e1f0c..9b066a38 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -113,8 +113,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)); @@ -131,7 +133,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; @@ -1069,12 +1071,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 6bc59aa1..63c33624 100644 --- a/lib/openglvis.hpp +++ b/lib/openglvis.hpp @@ -169,7 +169,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..eb3e3d62 --- /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; + + std::string dc_protocol = string_default; + int dc_cycle = 0; + + std::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(std::istream &scr, DataState &state); + static int ScriptReadQuadrature(std::istream &scr, DataState &state); + static int ScriptReadParSolution(std::istream &scr, DataState &state); + static int ScriptReadParQuadrature(std::istream &scr, DataState &state); + int ScriptReadDisplMesh(std::istream &scr, DataState &state); + int ScriptReadDataColl(std::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/stream_reader.cpp b/lib/stream_reader.cpp index 3723835b..c9207b92 100644 --- a/lib/stream_reader.cpp +++ b/lib/stream_reader.cpp @@ -11,222 +11,295 @@ #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") + auto it = find(commands.begin(), commands.end(), data_type); + if (it == commands.end()) { - 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()); + cerr << "Unknown data format " << data_type << endl; + PrintCommands(); + return 1; } - else if (data_type == "vfem3d_data" || data_type == "vfem3d_data_keys") + + Command cmd = (Command)(it - commands.begin()); + switch (cmd) { - 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") + case Command::Fem2D: { - is >> data.keys; + Vector sol; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + sol.Load(is, data.mesh->GetNV()); + data.SetScalarData(std::move(sol)); } - } - 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") + break; + case Command::VFem2D: + case Command::VFem2D_keys: { - is >> data.keys; + Vector solx, soly; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + solx.Load(is, data.mesh->GetNV()); + soly.Load(is, data.mesh->GetNV()); + data.SetVectorData(std::move(solx), std::move(soly)); } - } - 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") + break; + case Command::Fem3D: { - is >> data.keys; + Vector sol; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + sol.Load(is, data.mesh->GetNV()); + data.SetScalarData(std::move(sol)); } - } - 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") + break; + case Command::VFem3D: + case Command::VFem3D_keys: { - is >> data.keys; + Vector solx, soly, solz; + data.SetMesh(new Mesh(is, 0, 0, data.fix_elem_orient)); + solx.Load(is, data.mesh->GetNV()); + soly.Load(is, data.mesh->GetNV()); + solz.Load(is, data.mesh->GetNV()); + data.SetVectorData(std::move(solx), std::move(soly), std::move(solz)); } - } - 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") + 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 >> data.keys; - } - } - else if (data_type == "solution") - { - 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(); - } - else if (data_type == "raw_scalar_2d") - { - 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 >> 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)); + Vector sol(tot_num_vert); + Vector normals(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]); + sol(v_off) = verts[6*j+2]; + normals(3*v_off+0) = verts[6*j+3]; + normals(3*v_off+1) = verts[6*j+4]; + 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.SetScalarData(std::move(sol)); + data.SetNormals(std::move(normals)); } - - 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 +321,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 +329,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 720b878d..b62211af 100644 --- a/lib/stream_reader.hpp +++ b/lib/stream_reader.hpp @@ -25,7 +25,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 7c2a9e3d..dd35dcae 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -9,6 +9,8 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. +#include +#include #include #include @@ -24,13 +26,11 @@ using namespace mfem; 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; @@ -459,33 +459,36 @@ int GLVisCommand::Execute() case NEW_MESH_AND_SOLUTION: { double mesh_range = -1.0; - if (!new_state.grid_f) + switch (new_state.GetType()) { - if (!new_state.quad_f) - { - new_state.save_coloring = false; - new_state.SetMeshSolution(); + case DataState::FieldType::MESH: mesh_range = new_state.grid_f->Max() + 1.0; - } - else - { - auto qs = curr_state.GetQuadSolution(); - if (qs != DataState::QuadSolution::NONE) - { - new_state.SetQuadSolution(qs); - } - else + break; + case DataState::FieldType::SCALAR: + case DataState::FieldType::VECTOR: + if (new_state.quad_f) { - new_state.SetQuadSolution(); + auto qs = win.data_state.GetQuadSolution(); + if (qs != DataState::QuadSolution::NONE) + { + new_state.SetQuadSolution(qs); + } + else + { + new_state.SetQuadSolution(); + } + new_state.ExtrudeMeshAndSolution(); } - new_state.ExtrudeMeshAndSolution(); - } + break; + default: + cerr << "Unknown field type" << endl; + break; } - 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(); } @@ -506,7 +509,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; } @@ -545,8 +548,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; } @@ -555,8 +558,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; } @@ -565,7 +568,7 @@ int GLVisCommand::Execute() { cout << "Command: axis_numberformat: '" << axis_formatting << "'" << endl; - (*vs)->SetAxisNumberFormat(axis_formatting); + win.vs->SetAxisNumberFormat(axis_formatting); MyExpose(); break; } @@ -574,7 +577,7 @@ int GLVisCommand::Execute() { cout << "Command: colorbar_numberformat: '" << colorbar_formatting << "'" << endl; - (*vs)->SetColorbarNumberFormat(colorbar_formatting); + win.vs->SetColorbarNumberFormat(colorbar_formatting); MyExpose(); break; } @@ -590,7 +593,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; } @@ -598,7 +601,7 @@ int GLVisCommand::Execute() case ZOOM: { cout << "Command: zoom: " << zoom_factor << endl; - (*vs)->Zoom(zoom_factor); + win.vs->Zoom(zoom_factor); MyExpose(); break; } @@ -606,7 +609,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; @@ -615,7 +618,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; @@ -624,8 +627,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; @@ -650,7 +653,7 @@ int GLVisCommand::Execute() } if (s != VisualizationSceneScalarData::Shading::Invalid) { - (*vs)->SetShading(s, false); + win.vs->SetShading(s, false); cout << shading << endl; MyExpose(); } @@ -665,8 +668,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; } @@ -676,19 +679,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 { @@ -701,10 +704,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; @@ -713,12 +716,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; @@ -732,7 +735,7 @@ int GLVisCommand::Execute() cout << ' ' << camera[i]; } cout << endl; - (*vs)->cam.Set(camera); + win.vs->cam.Set(camera); MyExpose(); break; } @@ -804,6 +807,84 @@ GLVisCommand::~GLVisCommand() } } +enum class Command +{ + 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; + +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) : is(std::move(_is)), glvis_command(cmd) @@ -826,6 +907,16 @@ communication_thread::~communication_thread() } } +void communication_thread::print_commands() +{ + StreamReader::PrintCommands(); + + for (const auto &ci : commands) + { + cout << "\t" << ci.keyword << " " << ci.params << " - " << ci.desc << endl; + } +} + void communication_thread::execute() { while (1) @@ -840,53 +931,48 @@ void communication_thread::execute() break; } - if (ident == "mesh" || ident == "solution" || - ident == "quadrature" || ident == "parallel") + // new solution handled by StreamReader + if (StreamReader::SupportsDataType(ident)) { - bool fix_elem_orient = glvis_command->FixElementOrientations(); DataState tmp; - if (ident == "mesh") + tmp.fix_elem_orient = glvis_command->FixElementOrientations(); + StreamReader reader(tmp); + reader.ReadStream(*is[0], ident); + if (!(*is[0])) { - tmp.SetMesh(new Mesh(*is[0], 1, 0, fix_elem_orient)); - if (!(*is[0])) - { - break; - } - tmp.SetGridFunction(NULL); + break; } - else if (ident == "solution") + + // cout << "Stream: new solution" << endl; + + if (glvis_command->NewMeshAndSolution(std::move(tmp))) { - tmp.SetMesh(new Mesh(*is[0], 1, 0, fix_elem_orient)); - if (!(*is[0])) - { - break; - } - tmp.SetGridFunction(new GridFunction(tmp.mesh.get(), *is[0])); - if (!(*is[0])) - { - break; - } + goto comm_terminate; } - else if (ident == "quadrature") + if (!tmp.keys.empty()) { - tmp.SetMesh(new Mesh(*is[0], 1, 0, fix_elem_orient)); - if (!(*is[0])) + if (glvis_command->KeyCommands(tmp.keys.c_str())) { - break; - } - tmp.SetQuadFunction(new QuadratureFunction(tmp.mesh.get(), *is[0])); - if (!(*is[0])) - { - break; + goto comm_terminate; } } - else if (ident == "parallel") + continue; + } + + auto it = find(commands.begin(), commands.end(), ident); + if (it == commands.end()) + { + cout << "Stream: unknown command: " << ident << endl; + print_commands(); + goto comm_terminate; + } + + const Command cmd = (Command)(it - commands.begin()); + switch (cmd) + { + case Command::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(); + unsigned int proc, nproc, np = 0; do { istream &isock = *is[np]; @@ -895,34 +981,17 @@ void communication_thread::execute() 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") + if (nproc != is.size()) { - gf_array.resize(nproc); - gf_array[proc] = new GridFunction(mesh_array[proc], isock); + cout << "Unexpected number of processors: " << nproc + << ", expected: " << is.size() << endl; + mfem_error(); } - else if (ident == "quadrature") + if (proc >= nproc) { - qf_array.resize(nproc); - qf_array[proc] = new QuadratureFunction(mesh_array[proc], isock); - } - else - { - cout << "Stream: unknown command: " << ident << endl; + cout << "Invalid processor rank: " << proc + << ", number of processors: " << nproc << endl; + mfem_error(); } np++; if (np == nproc) @@ -930,482 +999,486 @@ void communication_thread::execute() break; } *is[np] >> ident >> ws; // "parallel" + if (ident != "parallel") + { + cout << "Expected keyword \"parallel\", got \"" << ident + << '"' << endl; + mfem_error(); + } } while (1); - tmp.SetMesh(new Mesh(mesh_array.data(), nproc)); - tmp.keep_attr = keep_attr; - if (gf_array.size() > 0) + 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; + + if (glvis_command->NewMeshAndSolution(std::move(tmp))) { - tmp.SetGridFunction(gf_array, nproc); + goto comm_terminate; } - else if (qf_array.size() > 0) + } + break; + case Command::Screenshot: + { + string filename; + + *is[0] >> ws >> filename; + + // all processors sent the screenshot command + for (size_t i = 1; i < is.size(); i++) { - tmp.SetQuadFunction(qf_array); + *is[i] >> ws >> ident; // 'screenshot' + *is[i] >> ws >> ident; // filename } - for (int p = 0; p < nproc; p++) + if (glvis_command->Screenshot(filename.c_str())) { - 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]; + goto comm_terminate; } - gf_array.clear(); - qf_array.clear(); - mesh_array.clear(); } + break; + case Command::Keys: + { + string keys; - // cout << "Stream: new solution" << endl; + *is[0] >> ws >> keys; - tmp.ExtrudeMeshAndSolution(); + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'keys' + *is[i] >> ws >> ident; // keys + } - if (glvis_command->NewMeshAndSolution(std::move(tmp))) - { - goto comm_terminate; + if (glvis_command->KeyCommands(keys.c_str())) + { + goto comm_terminate; + } } - } - else if (ident == "screenshot") - { - string filename; - - *is[0] >> ws >> filename; - - // all processors sent the screenshot command - for (size_t i = 1; i < is.size(); i++) + break; + case Command::WindowSize: { - *is[i] >> ws >> ident; // 'screenshot' - *is[i] >> ws >> ident; // filename - } + int w, h, t; - if (glvis_command->Screenshot(filename.c_str())) - { - goto comm_terminate; - } - } - else if (ident == "keys") - { - string keys; + *is[0] >> w >> h; - *is[0] >> ws >> keys; + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'window_size' + *is[i] >> t >> t; + } - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'keys' - *is[i] >> ws >> ident; // keys + if (glvis_command->WindowSize(w, h)) + { + goto comm_terminate; + } } - - if (glvis_command->KeyCommands(keys.c_str())) + break; + case Command::WindowGeometry: { - goto comm_terminate; - } - } - else if (ident == "window_size") - { - int w, h, t; + int x, y, w, h, t; - *is[0] >> w >> h; + *is[0] >> x >> y >> w >> h; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'window_size' - *is[i] >> t >> t; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'window_geometry' + *is[i] >> t >> t >> t >> t; + } - if (glvis_command->WindowSize(w, h)) - { - goto comm_terminate; + if (glvis_command->WindowGeometry(x, y, w, h)) + { + goto comm_terminate; + } } - } - else if (ident == "window_geometry") - { - int x, y, w, h, t; - - *is[0] >> x >> y >> w >> h; - - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) + break; + case Command::WindowTitle: { - *is[i] >> ws >> ident; // 'window_geometry' - *is[i] >> t >> t >> t >> t; - } + char c; + string title; - if (glvis_command->WindowGeometry(x, y, w, h)) - { - goto comm_terminate; - } - } - else if (ident == "window_title") - { - char c; - string title; + // read the opening char + *is[0] >> ws >> c; + // use the opening char as termination as well + getline(*is[0], title, c); - // read the opening char - *is[0] >> ws >> c; - // use the opening char as termination as well - getline(*is[0], title, c); + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'window_title' + *is[i] >> ws >> c; + getline(*is[i], ident, c); + } - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'window_title' - *is[i] >> ws >> c; - getline(*is[i], ident, c); + if (glvis_command->WindowTitle(title.c_str())) + { + goto comm_terminate; + } } - - if (glvis_command->WindowTitle(title.c_str())) + break; + case Command::PlotCaption: { - goto comm_terminate; - } - } - else if (ident == "plot_caption") - { - char c; - string caption; + char c; + string caption; - // read the opening char - *is[0] >> ws >> c; - // use the opening char as termination as well - getline(*is[0], caption, c); + // read the opening char + *is[0] >> ws >> c; + // use the opening char as termination as well + getline(*is[0], caption, c); - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'plot_caption' - *is[i] >> ws >> c; - getline(*is[i], ident, c); + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'plot_caption' + *is[i] >> ws >> c; + getline(*is[i], ident, c); + } + + if (glvis_command->PlotCaption(caption.c_str())) + { + goto comm_terminate; + } } + break; + case Command::AxisLabels: + { + char c; + string label_x, label_y, label_z; + + // read the opening char + *is[0] >> ws >> c; + // use the opening char as termination as well + getline(*is[0], label_x, c); + *is[0] >> ws >> c; + getline(*is[0], label_y, c); + *is[0] >> ws >> c; + getline(*is[0], label_z, c); + + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'axis_label' + *is[i] >> ws >> c; + getline(*is[i], ident, c); + *is[i] >> ws >> c; + getline(*is[i], ident, c); + *is[i] >> ws >> c; + getline(*is[i], ident, c); + } - if (glvis_command->PlotCaption(caption.c_str())) - { - goto comm_terminate; + if (glvis_command->AxisLabels(label_x.c_str(), + label_y.c_str(), + label_z.c_str())) + { + goto comm_terminate; + } } - } - else if (ident == "axis_labels") - { - char c; - string label_x, label_y, label_z; - - // read the opening char - *is[0] >> ws >> c; - // use the opening char as termination as well - getline(*is[0], label_x, c); - *is[0] >> ws >> c; - getline(*is[0], label_y, c); - *is[0] >> ws >> c; - getline(*is[0], label_z, c); - - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) + break; + case Command::Pause: { - *is[i] >> ws >> ident; // 'axis_label' - *is[i] >> ws >> c; - getline(*is[i], ident, c); - *is[i] >> ws >> c; - getline(*is[i], ident, c); - *is[i] >> ws >> c; - getline(*is[i], ident, c); - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'pause' + } - if (glvis_command->AxisLabels(label_x.c_str(), - label_y.c_str(), - label_z.c_str())) - { - goto comm_terminate; + if (glvis_command->Pause()) + { + goto comm_terminate; + } } - } - else if (ident == "pause") - { - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) + break; + case Command::View: { - *is[i] >> ws >> ident; // 'pause' - } + double theta, phi, a; - if (glvis_command->Pause()) - { - goto comm_terminate; - } - } - else if (ident == "view") - { - double theta, phi, a; + *is[0] >> theta >> phi; - *is[0] >> theta >> phi; + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'view' + *is[i] >> a >> a; + } - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'view' - *is[i] >> a >> a; + if (glvis_command->ViewAngles(theta, phi)) + { + goto comm_terminate; + } } - - if (glvis_command->ViewAngles(theta, phi)) + break; + case Command::Zoom: { - goto comm_terminate; - } - } - else if (ident == "zoom") - { - double factor, a; + double factor, a; - *is[0] >> factor; + *is[0] >> factor; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'zoom' - *is[i] >> a; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'zoom' + *is[i] >> a; + } - if (glvis_command->Zoom(factor)) - { - goto comm_terminate; + if (glvis_command->Zoom(factor)) + { + goto comm_terminate; + } } - } - else if (ident == "subdivisions") - { - int tot, bdr, a; - - *is[0] >> tot >> bdr; - - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) + break; + case Command::Subdivisions: { - *is[i] >> ws >> ident; // 'subdivisions' - *is[i] >> a >> a; - } + int tot, bdr, a; - if (glvis_command->Subdivisions(tot, bdr)) - { - goto comm_terminate; - } - } - else if (ident == "valuerange") - { - double minv, maxv, a; + *is[0] >> tot >> bdr; - *is[0] >> minv >> maxv; + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'subdivisions' + *is[i] >> a >> a; + } - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'valuerange' - *is[i] >> a >> a; + if (glvis_command->Subdivisions(tot, bdr)) + { + goto comm_terminate; + } } - - if (glvis_command->ValueRange(minv, maxv)) + break; + case Command::Valuerange: { - goto comm_terminate; - } - } - else if (ident == "levellines") - { - double minv, maxv, a; - int num, b; + double minv, maxv, a; - *is[0] >> minv >> maxv >> num; + *is[0] >> minv >> maxv; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'levellines' - *is[i] >> a >> a >> b; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'valuerange' + *is[i] >> a >> a; + } - if (glvis_command->Levellines(minv, maxv, num)) - { - goto comm_terminate; + if (glvis_command->ValueRange(minv, maxv)) + { + goto comm_terminate; + } } - } - else if (ident == "axis_numberformat") - { - char c; - string formatting; + break; + case Command::Levellines: + { + double minv, maxv, a; + int num, b; - // read the opening char - *is[0] >> ws >> c; - // read formatting string & use c for termination - getline(*is[0], formatting, c); + *is[0] >> minv >> maxv >> num; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'axis_numberformat' - *is[i] >> ws >> c; - getline(*is[i], ident, c); - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'levellines' + *is[i] >> a >> a >> b; + } - if (glvis_command->AxisNumberFormat(formatting)) - { - goto comm_terminate; + if (glvis_command->Levellines(minv, maxv, num)) + { + goto comm_terminate; + } } - } - else if (ident == "colorbar_numberformat") - { - char c; - string formatting; + break; + case Command::AxisNumberFormat: + { + char c; + string formatting; - // read the opening char - *is[0] >> ws >> c; - // read formatting string & use c for termination - getline(*is[0], formatting, c); + // read the opening char + *is[0] >> ws >> c; + // read formatting string & use c for termination + getline(*is[0], formatting, c); - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'colorbar_numberformat' - *is[i] >> ws >> c; - getline(*is[i], ident, c); - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'axis_numberformat' + *is[i] >> ws >> c; + getline(*is[i], ident, c); + } - if (glvis_command->ColorbarNumberFormat(formatting)) - { - goto comm_terminate; + if (glvis_command->AxisNumberFormat(formatting)) + { + goto comm_terminate; + } } - } - else if (ident == "shading") - { - string shd; + break; + case Command::ColorbarNumberFormat: + { + char c; + string formatting; - *is[0] >> ws >> shd; + // read the opening char + *is[0] >> ws >> c; + // read formatting string & use c for termination + getline(*is[0], formatting, c); - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'shading' - *is[i] >> ws >> ident; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'colorbar_numberformat' + *is[i] >> ws >> c; + getline(*is[i], ident, c); + } - if (glvis_command->SetShading(shd.c_str())) - { - goto comm_terminate; + if (glvis_command->ColorbarNumberFormat(formatting)) + { + goto comm_terminate; + } } - } - else if (ident == "viewcenter") - { - double x, y, a; + break; + case Command::Shading: + { + string shd; - *is[0] >> x >> y; + *is[0] >> ws >> shd; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'viewcenter' - *is[i] >> a >> a; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'shading' + *is[i] >> ws >> ident; + } - if (glvis_command->ViewCenter(x, y)) - { - goto comm_terminate; + if (glvis_command->SetShading(shd.c_str())) + { + goto comm_terminate; + } } - } - else if (ident == "autoscale") - { - string mode; + break; + case Command::Viewcenter: + { + double x, y, a; - *is[0] >> ws >> mode; + *is[0] >> x >> y; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'autoscale' - *is[i] >> ws >> ident; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'viewcenter' + *is[i] >> a >> a; + } - if (glvis_command->Autoscale(mode.c_str())) - { - goto comm_terminate; + if (glvis_command->ViewCenter(x, y)) + { + goto comm_terminate; + } } - } - else if (ident == "palette") - { - int pal, a; + break; + case Command::Autoscale: + { + string mode; - *is[0] >> pal; + *is[0] >> ws >> mode; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'palette' - *is[i] >> a; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'autoscale' + *is[i] >> ws >> ident; + } - if (glvis_command->Palette(pal)) - { - goto comm_terminate; + if (glvis_command->Autoscale(mode.c_str())) + { + goto comm_terminate; + } } - } - else if (ident == "palette_repeat") - { - int n, a; + break; + case Command::Palette: + { + int pal, a; - *is[0] >> n; + *is[0] >> pal; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'palette_repeat' - *is[i] >> a; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'palette' + *is[i] >> a; + } - if (glvis_command->PaletteRepeat(n)) - { - goto comm_terminate; + if (glvis_command->Palette(pal)) + { + goto comm_terminate; + } } - } - else if (ident == "camera") - { - double cam[9], a; - - for (int i = 0; i < 9; i++) + break; + case Command::PaletteRepeat: { - *is[0] >> cam[i]; - } + int n, a; - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'camera' - for (int j = 0; j < 9; j++) + *is[0] >> n; + + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) { + *is[i] >> ws >> ident; // 'palette_repeat' *is[i] >> a; } - } - if (glvis_command->Camera(cam)) - { - goto comm_terminate; + if (glvis_command->PaletteRepeat(n)) + { + goto comm_terminate; + } } - } - else if (ident == "autopause") - { - string mode; + break; + case Command::Camera: + { + double cam[9], a; - *is[0] >> ws >> mode; + for (int i = 0; i < 9; i++) + { + *is[0] >> cam[i]; + } - // all processors sent the command - for (size_t i = 1; i < is.size(); i++) - { - *is[i] >> ws >> ident; // 'autopause' - *is[i] >> ws >> ident; - } + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'camera' + for (int j = 0; j < 9; j++) + { + *is[i] >> a; + } + } - if (glvis_command->Autopause(mode.c_str())) + if (glvis_command->Camera(cam)) + { + goto comm_terminate; + } + } + break; + case Command::Autopause: { - goto comm_terminate; + string mode; + + *is[0] >> ws >> mode; + + // all processors sent the command + for (size_t i = 1; i < is.size(); i++) + { + *is[i] >> ws >> ident; // 'autopause' + *is[i] >> ws >> ident; + } + + if (glvis_command->Autopause(mode.c_str())) + { + goto comm_terminate; + } } - } - else - { - cout << "Stream: unknown command: " << ident << endl; + break; + case Command::Max: //dummy + break; } } diff --git a/lib/threads.hpp b/lib/threads.hpp index 863ed2b7..d738ffce 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -17,6 +17,7 @@ #include <atomic> #include <condition_variable> +#include "window.hpp" #include "vsdata.hpp" #include "data_state.hpp" @@ -24,9 +25,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; @@ -102,12 +102,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); @@ -167,6 +166,7 @@ class communication_thread // signal for thread cancellation std::atomic<bool> terminate_thread {false}; + static void print_commands(); void execute(); public: diff --git a/lib/visual.hpp b/lib/visual.hpp index 3e7319f9..a7796ce5 100644 --- a/lib/visual.hpp +++ b/lib/visual.hpp @@ -22,7 +22,9 @@ #include "vssolution3d.hpp" #include "vsvector.hpp" #include "vsvector3d.hpp" +#ifndef __EMSCRIPTEN__ #include "threads.hpp" +#endif #include "gl/types.hpp" #endif // GLVIS_VISUAL_HPP diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index aa3fad58..c811e5ac 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -19,10 +19,12 @@ #include "aux_vis.hpp" #include "material.hpp" #include "palettes.hpp" - -using namespace mfem; +#ifndef __EMSCRIPTEN__ +#include "threads.hpp" +#endif using namespace std; +using namespace mfem; const char *strings_off_on[] = { "off", "on" }; @@ -594,13 +596,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(); @@ -609,8 +611,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) { @@ -641,7 +643,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(); } @@ -713,22 +715,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(); @@ -742,14 +744,14 @@ void KeypPressed(GLenum state) } else { - locscene->palette.NextIndex(); + window->vs->palette.NextIndex(); SendExposeEvent(); } } void KeyPPressed() { - locscene->palette.PrevIndex(); + window->vs->palette.PrevIndex(); SendExposeEvent(); } @@ -908,30 +910,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; @@ -942,23 +944,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 } @@ -1158,7 +1160,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; @@ -1352,20 +1354,23 @@ 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.get(); + offsets = win.data_state.offsets.get(); - Init(); + if (init) + { + Init(); + } } void VisualizationSceneScalarData::Init() { vsdata = this; - wnd = GetAppWindow(); + window = &win; arrow_type = arrow_scaling_type = 0; scaling = 0; @@ -1747,7 +1752,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 5db58930..f7ad8481 100644 --- a/lib/vsdata.hpp +++ b/lib/vsdata.hpp @@ -15,10 +15,7 @@ #include <mfem.hpp> #include "openglvis.hpp" #include "aux_vis.hpp" -#include "data_state.hpp" - -extern thread_local std::string plot_caption; // defined in glvis.cpp -extern thread_local std::string extra_caption; // defined in glvis.cpp +#include "window.hpp" class Plane { @@ -70,9 +67,11 @@ class VisualizationSceneScalarData : public VisualizationScene mfem::Vector *sol{}; const DataState::Offsets *offsets{}; + 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; @@ -159,13 +158,13 @@ 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 (mfem::Mesh & m, mfem::Vector & s, - mfem::Mesh *mc = nullptr); + VisualizationSceneScalarData(Window &win, bool init = true); virtual ~VisualizationSceneScalarData(); + /// Set a new mesh and solution from the given data state + virtual void NewMeshAndSolution(const DataState &s) = 0; + virtual std::string GetHelpString() const { return ""; } // Determine 'xscale', 'yscale', and 'zscale' using the current bounding @@ -223,11 +222,6 @@ class VisualizationSceneScalarData : public VisualizationScene mfem::Mesh *GetMesh() { return mesh; } - void SetDataOffsets(const DataState::Offsets *data_offsets) - { - offsets = data_offsets; - } - gl3::SceneInfo GetSceneObjs() override; void ProcessUpdatedBufs(gl3::SceneInfo& scene); @@ -279,7 +273,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 5c237398..1dd31287 100644 --- a/lib/vssolution.cpp +++ b/lib/vssolution.cpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#include "vssolution.hpp" +#include "visual.hpp" #include "palettes.hpp" #include "gltf.hpp" @@ -17,7 +17,6 @@ using namespace std; using namespace mfem; thread_local VisualizationSceneSolution *vssol; -extern thread_local VisualizationScene *locscene; extern thread_local GeometryRefiner GLVisGeometryRefiner; #ifdef GLVIS_ISFINITE @@ -423,25 +422,29 @@ 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; + v_normals = win.data_state.normals.get(); + + if (win.data_state.grid_f) + { + rsol = win.data_state.grid_f.get(); + sol = new Vector(mesh->GetNV()); + } - Init(); + if (init) + { + if (rsol) + { + rsol->GetNodalValues(*sol); + } + Init(); + } } void VisualizationSceneSolution::Init() { - rsol = NULL; vssol = this; drawelems = 1; @@ -527,6 +530,10 @@ void VisualizationSceneSolution::Init() VisualizationSceneSolution::~VisualizationSceneSolution() { + if (rsol) + { + delete sol; + } } void VisualizationSceneSolution::ToggleDrawElems() @@ -553,11 +560,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) @@ -590,6 +597,19 @@ void VisualizationSceneSolution::ToggleDrawElems() } } +void VisualizationSceneSolution::NewMeshAndSolution(const DataState &s) +{ + if (rsol && s.grid_f) + { + s.grid_f->GetNodalValues(*sol); + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), sol, s.grid_f.get()); + } + else + { + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), s.sol.get()); + } +} + void VisualizationSceneSolution::NewMeshAndSolution( Mesh *new_m, Mesh *new_mc, Vector *new_sol, GridFunction *new_u) { diff --git a/lib/vssolution.hpp b/lib/vssolution.hpp index 6ea110d7..cf6de730 100644 --- a/lib/vssolution.hpp +++ b/lib/vssolution.hpp @@ -22,8 +22,8 @@ class VisualizationSceneSolution : public VisualizationSceneScalarData { protected: - mfem::Vector *v_normals; - mfem::GridFunction *rsol; + mfem::Vector *v_normals{}; + mfem::GridFunction *rsol{}; int drawmesh, drawelems, draworder; enum class Numbering { NONE, ELEMENTS, EDGES, VERTICES, DOFS, MAX }; @@ -55,6 +55,10 @@ class VisualizationSceneSolution : public VisualizationSceneScalarData void Init(); + void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, + mfem::Vector *new_sol, + mfem::GridFunction *new_u = nullptr); + void FindNewBox(double rx[], double ry[], double rval[]); void DrawCPLine(gl3::GlBuilder& bld, @@ -85,20 +89,13 @@ class VisualizationSceneSolution : public VisualizationSceneScalarData int attr_to_show, bdr_attr_to_show; mfem::Array<int> el_attr_to_show, bdr_el_attr_to_show; - VisualizationSceneSolution(); - VisualizationSceneSolution(mfem::Mesh &m, mfem::Vector &s, - mfem::Mesh *mc = nullptr, - mfem::Vector *normals = nullptr); + VisualizationSceneSolution(Window &win, bool init = true); virtual ~VisualizationSceneSolution(); std::string GetHelpString() const override; - void SetGridFunction(mfem::GridFunction & u) { rsol = &u; } - - void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, - mfem::Vector *new_sol, - mfem::GridFunction *new_u = nullptr); + void NewMeshAndSolution(const DataState &s) override; void SetNewScalingFromBox() override; void FindNewBox(bool prepare) override; diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp index 72fb13b4..8d81247a 100644 --- a/lib/vssolution3d.cpp +++ b/lib/vssolution3d.cpp @@ -17,12 +17,12 @@ #include <mfem.hpp> #include "palettes.hpp" -#include "vssolution3d.hpp" +#include "visual.hpp" using namespace std; using namespace mfem; -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 @@ -688,18 +688,24 @@ 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; + if (win.data_state.grid_f) + { + GridF = win.data_state.grid_f.get(); + sol = new Vector(mesh->GetNV()); + } - Init(); + if (init) + { + if (GridF) + { + GridF->GetNodalValues(*sol); + } + Init(); + } } @@ -819,9 +825,26 @@ void VisualizationSceneSolution3d::Init() VisualizationSceneSolution3d::~VisualizationSceneSolution3d() { + if (GridF) + { + delete sol; + } delete [] node_pos; } +void VisualizationSceneSolution3d::NewMeshAndSolution(const DataState &s) +{ + if (GridF && s.grid_f) + { + s.grid_f->GetNodalValues(*sol); + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), sol, s.grid_f.get()); + } + else + { + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), s.sol.get()); + } +} + void VisualizationSceneSolution3d::NewMeshAndSolution( Mesh *new_m, Mesh *new_mc, Vector *new_sol, GridFunction *new_u) { diff --git a/lib/vssolution3d.hpp b/lib/vssolution3d.hpp index b36d88f6..caf7ddab 100644 --- a/lib/vssolution3d.hpp +++ b/lib/vssolution3d.hpp @@ -41,10 +41,14 @@ class VisualizationSceneSolution3d : public VisualizationSceneScalarData int nlevels; mfem::Array<double> levels; - mfem::GridFunction *GridF; + mfem::GridFunction *GridF{}; void Init(); + void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, + mfem::Vector *new_sol, + mfem::GridFunction *new_u = nullptr); + void GetFaceNormals(const int FaceNo, const int side, const mfem::IntegrationRule &ir, mfem::DenseMatrix &normals); @@ -118,14 +122,9 @@ class VisualizationSceneSolution3d : public VisualizationSceneScalarData mfem::Array<int> bdr_attr_to_show; - VisualizationSceneSolution3d(); - VisualizationSceneSolution3d(mfem::Mesh & m, mfem::Vector & s, mfem::Mesh *mc); + VisualizationSceneSolution3d(Window &win, bool init = true); - void SetGridFunction(mfem::GridFunction *gf) { GridF = gf; } - - void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, - mfem::Vector *new_sol, - mfem::GridFunction *new_u = nullptr); + void NewMeshAndSolution(const DataState &s) override; virtual ~VisualizationSceneSolution3d(); diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp index 9a55109d..acc7bc45 100644 --- a/lib/vsvector.cpp +++ b/lib/vsvector.cpp @@ -15,7 +15,7 @@ #include <cmath> #include <mfem.hpp> -#include "vsvector.hpp" +#include "visual.hpp" using namespace mfem; using namespace std; @@ -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,46 @@ 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()); - - VecGridF = NULL; - - Init(); -} - -VisualizationSceneVector::VisualizationSceneVector(GridFunction &vgf) -{ - FiniteElementSpace *fes = vgf.FESpace(); - if (fes == NULL || vgf.VectorDim() != 2) + if (win.data_state.grid_f) { - cout << "VisualizationSceneVector::VisualizationSceneVector" << endl; - exit(1); - } - - VecGridF = &vgf; - - mesh = fes->GetMesh(); - - solx = new Vector(mesh -> GetNV()); - soly = new Vector(mesh -> GetNV()); + FiniteElementSpace *fes = win.data_state.grid_f->FESpace(); + if (fes == NULL || win.data_state.grid_f->VectorDim() != 2) + { + cout << "VisualizationSceneVector::VisualizationSceneVector" << endl; + exit(1); + } - vgf.GetNodalValues (*solx, 1); - vgf.GetNodalValues (*soly, 2); + VecGridF = win.data_state.grid_f.get(); - sol = new Vector(mesh -> GetNV()); + solx = new Vector(mesh -> GetNV()); + soly = new Vector(mesh -> GetNV()); - // VisualizationSceneSolution::Init() sets rsol = NULL ! - { - Init(); - SetGridFunction(vgf); + VecGridF->GetNodalValues(*solx, 1); + VecGridF->GetNodalValues(*soly, 2); } - - mesh->GetNodes(vc0); - if (vc0.Size() != vgf.Size()) + else { - vc0.Destroy(); + sol = new Vector(mesh -> GetNV()); + solx = win.data_state.solx.get(); + soly = win.data_state.soly.get(); } - else + + Init(); + + if (VecGridF) { - vc0 += vgf; + mesh->GetNodes(vc0); + if (vc0.Size() != VecGridF->Size()) + { + vc0.Destroy(); + } + else + { + vc0 += *VecGridF; + } } } @@ -357,7 +345,7 @@ void VisualizationSceneVector::CycleVec2Scalar(int print) for (i = 0; Vec2Scalar != Vec2ScalarFunctions[i]; i++) ; - if (VecGridF->FESpace()->GetVDim() == 1) + if (VecGridF && VecGridF->FESpace()->GetVDim() == 1) { if (dynamic_cast<const ND_FECollection*>(VecGridF->FESpace()->FEColl())) { @@ -370,10 +358,14 @@ void VisualizationSceneVector::CycleVec2Scalar(int print) i = (i + 1) % 5; } } - else + else if (VecGridF) { i = (i + 1) % 7; } + else + { + i = (i + 1) % 4; + } if (print) { @@ -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++) { @@ -408,7 +400,21 @@ void VisualizationSceneVector::CycleVec2Scalar(int print) } } -void VisualizationSceneVector::NewMeshAndSolution(GridFunction &vgf, Mesh *mc) +void VisualizationSceneVector::NewMeshAndSolution(const DataState &s) +{ + if (VecGridF && s.grid_f) + { + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), solx, soly, s.grid_f.get()); + } + else + { + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), s.solx.get(), s.soly.get()); + } +} + +void VisualizationSceneVector::NewMeshAndSolution( + Mesh *new_m, Mesh *new_mc, Vector *new_sol_x, Vector *new_sol_y, + GridFunction *vgf) { delete sol; @@ -419,10 +425,9 @@ void VisualizationSceneVector::NewMeshAndSolution(GridFunction &vgf, Mesh *mc) } Mesh *old_m = mesh; - Mesh *new_mesh = vgf.FESpace()->GetMesh(); - mesh = new_mesh; - mesh_coarse = mc; - VecGridF = &vgf; + mesh = new_m; + mesh_coarse = new_mc; + VecGridF = vgf; // If the number of elements changes, recompute the refinement factor if (mesh->GetNE() != old_m->GetNE()) @@ -441,20 +446,28 @@ void VisualizationSceneVector::NewMeshAndSolution(GridFunction &vgf, Mesh *mc) } } - solx = new Vector(mesh->GetNV()); - soly = new Vector(mesh->GetNV()); + if (vgf) + { + solx = new Vector(mesh->GetNV()); + soly = new Vector(mesh->GetNV()); - vgf.GetNodalValues(*solx, 1); - vgf.GetNodalValues(*soly, 2); + vgf->GetNodalValues(*solx, 1); + vgf->GetNodalValues(*soly, 2); - mesh->GetNodes(vc0); - if (vc0.Size() != vgf.Size()) - { - vc0.Destroy(); + mesh->GetNodes(vc0); + if (vc0.Size() != vgf->Size()) + { + vc0.Destroy(); + } + else + { + vc0 += *vgf; + } } else { - vc0 += vgf; + solx = new_sol_x; + soly = new_sol_y; } sol = new Vector(mesh->GetNV()); @@ -463,7 +476,7 @@ void VisualizationSceneVector::NewMeshAndSolution(GridFunction &vgf, Mesh *mc) (*sol)(i) = Vec2Scalar((*solx)(i), (*soly)(i)); } - VisualizationSceneSolution::NewMeshAndSolution(mesh, mesh_coarse, sol, &vgf); + VisualizationSceneSolution::NewMeshAndSolution(mesh, mesh_coarse, sol, vgf); if (autoscale) { @@ -488,7 +501,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++) { @@ -523,13 +536,15 @@ void VisualizationSceneVector::Init() VisualizationSceneVector::~VisualizationSceneVector() { - delete sol; - if (VecGridF) { delete soly; delete solx; } + else + { + delete sol; + } } void VisualizationSceneVector::GetRefinedValues(const int i, diff --git a/lib/vsvector.hpp b/lib/vsvector.hpp index 870db3ad..e019fa24 100644 --- a/lib/vsvector.hpp +++ b/lib/vsvector.hpp @@ -25,10 +25,14 @@ class VisualizationSceneVector : public VisualizationSceneSolution gl3::GlDrawable vector_buf; gl3::GlDrawable displine_buf; - mfem::GridFunction *VecGridF; + mfem::GridFunction *VecGridF{}; void Init(); + void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, + mfem::Vector *new_sol_x, mfem::Vector *new_sol_y, + mfem::GridFunction *new_u = nullptr); + void GetRefinedValues(const int i, const mfem::IntegrationRule &ir, mfem::Vector &vals, mfem::DenseMatrix &tr, const bool do_shrink = true) override; @@ -48,11 +52,9 @@ class VisualizationSceneVector : public VisualizationSceneSolution int GetFunctionAutoRefineFactor() override; public: - VisualizationSceneVector(mfem::Mesh &m, mfem::Vector &sx, mfem::Vector &sy, - mfem::Mesh *mc = nullptr); - VisualizationSceneVector(mfem::GridFunction &vgf); + VisualizationSceneVector(Window &win_); - void NewMeshAndSolution(mfem::GridFunction &vgf, mfem::Mesh *mc = nullptr); + void NewMeshAndSolution(const DataState &s) override; virtual ~VisualizationSceneVector(); diff --git a/lib/vsvector3d.cpp b/lib/vsvector3d.cpp index ae322bbb..57cd4315 100644 --- a/lib/vsvector3d.cpp +++ b/lib/vsvector3d.cpp @@ -14,7 +14,7 @@ #include <cmath> #include <limits> -#include "vsvector3d.hpp" +#include "visual.hpp" using namespace mfem; using namespace std; @@ -113,8 +113,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() @@ -149,13 +149,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(); @@ -166,7 +166,7 @@ static void KeyrPressed() static void KeyRPressed() { - locscene->spinning = 0; + window->vs->spinning = 0; RemoveIdleFunc(MainLoop); vsvector3d -> ianim = vsvector3d -> ianimd = 0; vsvector3d -> Prepare(); @@ -329,7 +329,7 @@ void VisualizationSceneVector3d::SetScalarFunction() } break; } - extra_caption = scal_func_name[scal_func]; + win.extra_caption = scal_func_name[scal_func]; } void VisualizationSceneVector3d::ToggleScalarFunction() @@ -340,56 +340,46 @@ 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); + sol = new Vector(mesh->GetNV()); + solx = win.data_state.solx.get(); + soly = win.data_state.soly.get(); + solz = win.data_state.solz.get(); } - 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(); } void VisualizationSceneVector3d::Init() { + window = &win; + key_r_state = 0; drawdisp = 0; @@ -459,8 +449,6 @@ int VisualizationSceneVector3d::GetFunctionAutoRefineFactor() VisualizationSceneVector3d::~VisualizationSceneVector3d() { - delete sol; - if (VecGridF) { delete solz; @@ -469,10 +457,29 @@ VisualizationSceneVector3d::~VisualizationSceneVector3d() delete GridF; delete sfes; } + else + { + delete sol; + } +} + +void VisualizationSceneVector3d::NewMeshAndSolution(const DataState &s) +{ + if (VecGridF && s.grid_f) + { + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), solx, soly, solz, + s.grid_f.get()); + } + else + { + NewMeshAndSolution(s.mesh.get(), s.mesh_quad.get(), s.solx.get(), s.soly.get(), + s.solz.get()); + } } void VisualizationSceneVector3d::NewMeshAndSolution( - Mesh *new_m, Mesh *new_mc, GridFunction *new_v) + Mesh *new_m, Mesh *new_mc, Vector *new_sol_x, Vector *new_sol_y, + Vector *new_sol_z, GridFunction *new_v) { delete sol; if (VecGridF) @@ -507,21 +514,30 @@ void VisualizationSceneVector3d::NewMeshAndSolution( } } - FiniteElementSpace *new_fes = new_v->FESpace(); FindNodePos(); - sfes = new FiniteElementSpace(mesh, new_fes->FEColl(), 1, - new_fes->GetOrdering()); - GridF = new GridFunction(sfes); - - solx = new Vector(mesh->GetNV()); - soly = new Vector(mesh->GetNV()); - solz = new Vector(mesh->GetNV()); - - VecGridF->GetNodalValues(*solx, 1); - VecGridF->GetNodalValues(*soly, 2); - VecGridF->GetNodalValues(*solz, 3); + if (new_v) + { + FiniteElementSpace *new_fes = new_v->FESpace(); + sfes = new FiniteElementSpace(mesh, new_fes->FEColl(), 1, + new_fes->GetOrdering()); + GridF = new GridFunction(sfes); + + solx = new Vector(mesh->GetNV()); + soly = new Vector(mesh->GetNV()); + solz = new Vector(mesh->GetNV()); + + VecGridF->GetNodalValues(*solx, 1); + VecGridF->GetNodalValues(*soly, 2); + VecGridF->GetNodalValues(*solz, 3); + } + else + { + solx = new_sol_x; + soly = new_sol_y; + solz = new_sol_z; + } sol = new Vector(mesh->GetNV()); diff --git a/lib/vsvector3d.hpp b/lib/vsvector3d.hpp index 7c1b9c56..9e2e83a2 100644 --- a/lib/vsvector3d.hpp +++ b/lib/vsvector3d.hpp @@ -26,11 +26,15 @@ class VisualizationSceneVector3d : public VisualizationSceneSolution3d gl3::GlDrawable vector_buf; gl3::GlDrawable displine_buf; - mfem::GridFunction *VecGridF; - mfem::FiniteElementSpace *sfes; + mfem::GridFunction *VecGridF{}; + mfem::FiniteElementSpace *sfes{}; void Init(); + void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, + mfem::Vector *new_sol_x, mfem::Vector *new_sol_y, mfem::Vector *new_sol_z, + mfem::GridFunction *new_u = nullptr); + mfem::Array<int> vflevel; mfem::Array<double> dvflevel; @@ -39,13 +43,9 @@ class VisualizationSceneVector3d : public VisualizationSceneSolution3d public: int ianim, ianimd, ianimmax, drawdisp; - VisualizationSceneVector3d(mfem::Mesh & m, mfem::Vector & sx, mfem::Vector & sy, - mfem::Vector & sz, - mfem::Mesh *mc = nullptr); - VisualizationSceneVector3d(mfem::GridFunction &vgf, mfem::Mesh *mc = nullptr); + VisualizationSceneVector3d(Window &win); - void NewMeshAndSolution(mfem::Mesh *new_m, mfem::Mesh *new_mc, - mfem::GridFunction *new_v); + void NewMeshAndSolution(const DataState &s) override; virtual ~VisualizationSceneVector3d(); diff --git a/lib/window.cpp b/lib/window.cpp new file mode 100644 index 00000000..7b515469 --- /dev/null +++ b/lib/window.cpp @@ -0,0 +1,235 @@ +// 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) + { + std::cerr << "Initializing the visualization failed." << std::endl; + return false; + } + +#ifndef __EMSCRIPTEN__ + if (input_streams.size() > 0) + { + wnd->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); + internal.glvis_command.reset(new GLVisCommand(*this)); + SetGLVisCommand(glvis_command.get()); + internal.comm_thread.reset(new communication_thread(std::move(input_streams), + glvis_command.get())); + } +#endif + + 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.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(); +#ifndef __EMSCRIPTEN__ + if (glvis_command) + { + glvis_command->Terminate(); + internal.comm_thread.reset(); + internal.glvis_command.reset(); + } +#endif + std::cout << "GLVis window closed." << std::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.GetType() == data_state.GetType() && + (((new_state.grid_f && data_state.grid_f) && + (new_state.grid_f->VectorDim() == data_state.grid_f->VectorDim())) + ||(!new_state.grid_f && !data_state.grid_f))) + { + ResetMeshAndSolution(new_state); + + data_state = std::move(new_state); + + return true; + } + else + { + return false; + } +} + +void Window::ResetMeshAndSolution(DataState &ss) +{ + if (ss.mesh->SpaceDimension() == 3 && + ss.GetType() == DataState::FieldType::VECTOR) + { + ss.ProjectVectorFEGridFunction(); + } + + vs->NewMeshAndSolution(ss); +} + + +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..6793ffeb --- /dev/null +++ b/lib/window.hpp @@ -0,0 +1,88 @@ +// 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; +#ifndef __EMSCRIPTEN__ + std::unique_ptr<communication_thread> comm_thread; + std::unique_ptr<GLVisCommand> glvis_command; +#endif + } internal; + +public: + DataState data_state; + const std::unique_ptr<SdlWindow> &wnd{internal.wnd}; + const std::unique_ptr<VisualizationSceneScalarData> &vs{internal.vs}; +#ifndef __EMSCRIPTEN__ + const std::unique_ptr<communication_thread> &comm_thread {internal.comm_thread}; + const std::unique_ptr<GLVisCommand> &glvis_command{internal.glvis_command}; +#endif + + 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); + +private: + /// Thread-local singleton for key handlers + static thread_local Window *locwin; + + /// Switch representation of the quadrature function + static void SwitchQuadSolution(); + + /// 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); +}; + +#endif // GLVIS_WINDOW_HPP diff --git a/makefile b/makefile index f1d825c8..1b4d5ad0 100644 --- a/makefile +++ b/makefile @@ -254,15 +254,16 @@ 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/palettes_default.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/openglvis.cpp lib/palettes_base.cpp lib/palettes.cpp \ + lib/palettes_default.cpp lib/script_controller.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/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/threads.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 @@ -276,12 +277,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)