|
| 1 | +// Scene-script commands that drive the Geometry Trace panel the way a user |
| 2 | +// and a running job do together (#217): every step, playback started and |
| 3 | +// stopped, the trace changed under a running animation, the context |
| 4 | +// switched or closed, the panels unfocused as on quit. Used only through |
| 5 | +// ECCE_VIEWER_SCENE (tests/apps/geomtrace_stress.py). |
| 6 | + |
| 7 | +#include <cmath> |
| 8 | +#include <cstdio> |
| 9 | +#include <sstream> |
| 10 | + |
| 11 | +#include <wx/aui/aui.h> |
| 12 | +#include <wx/app.h> |
| 13 | +#include <wx/utils.h> |
| 14 | + |
| 15 | +#include "util/EventDispatcher.H" |
| 16 | +#include "util/Event.H" |
| 17 | + using namespace ecce; |
| 18 | + |
| 19 | +#include "tdat/PropTSVecTable.H" |
| 20 | +#include "tdat/TAtm.H" |
| 21 | +#include "dsm/IPropCalculation.H" |
| 22 | + |
| 23 | +#include "viz/SGContainer.H" |
| 24 | +#include "viz/SGFragment.H" |
| 25 | + |
| 26 | +#include "wxgui/ewxPlotCtrl.H" |
| 27 | +#include "wxgui/PlaybackControl.H" |
| 28 | +#include "wxgui/ThingToggle.H" |
| 29 | + |
| 30 | +#include "wxviz/SceneScript.H" |
| 31 | + |
| 32 | +#include "Builder.H" |
| 33 | +#include "GeomTracePropertyPanel.H" |
| 34 | +#include "PropertyPanel.H" |
| 35 | + |
| 36 | + |
| 37 | +namespace { |
| 38 | + |
| 39 | +GeomTracePropertyPanel *tracePanel(IPropCalculation *calc) |
| 40 | +{ |
| 41 | + if (!calc) return 0; |
| 42 | + set<PropertyPanel*> panels = |
| 43 | + PropertyPanel::getPanels(calc->getURL().toString()); |
| 44 | + for (PropertyPanel *p : panels) { |
| 45 | + GeomTracePropertyPanel *gt = dynamic_cast<GeomTracePropertyPanel*>(p); |
| 46 | + if (gt) return gt; |
| 47 | + } |
| 48 | + return 0; |
| 49 | +} |
| 50 | + |
| 51 | + |
| 52 | +PlaybackControl *playback(wxWindow *w) |
| 53 | +{ |
| 54 | + if (!w) return 0; |
| 55 | + PlaybackControl *pb = dynamic_cast<PlaybackControl*>(w); |
| 56 | + if (pb) return pb; |
| 57 | + for (wxWindow *c : w->GetChildren()) |
| 58 | + if ((pb = playback(c)) != 0) return pb; |
| 59 | + return 0; |
| 60 | +} |
| 61 | + |
| 62 | + |
| 63 | +void spin(int ms) |
| 64 | +{ |
| 65 | + wxLongLong end = wxGetLocalTimeMillis() + ms; |
| 66 | + do { |
| 67 | + wxTheApp->Yield(true); |
| 68 | + wxMilliSleep(2); |
| 69 | + } while (wxGetLocalTimeMillis() < end); |
| 70 | +} |
| 71 | + |
| 72 | + |
| 73 | +// One GEOMTRACE step as eccejobstore publishes it. |
| 74 | +string stepMessage(int step, int atoms, int values) |
| 75 | +{ |
| 76 | + std::ostringstream os; |
| 77 | + os << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n" |
| 78 | + << "<tsvectable columnLabel=\"Atom\" columnLabels=\"X Y Z\" columns=\"3\"" |
| 79 | + << " name=\"GEOMTRACE\" rowLabel=\"Geometry Step\" rows=\"" << atoms |
| 80 | + << "\" units=\"Angstrom\" vectorLabel=\"Coordinate\" vectors=\"1\">" |
| 81 | + << "<step number=\"" << step << "\">"; |
| 82 | + for (int i = 0; i < values; i++) os << " " << 0.1 * (i % 7) + 0.01 * step; |
| 83 | + os << "</step></tsvectable>\n"; |
| 84 | + return os.str(); |
| 85 | +} |
| 86 | + |
| 87 | +} // namespace |
| 88 | + |
| 89 | + |
| 90 | +bool Builder::traceStressCommand(SceneScript& s, const vector<string>& w) |
| 91 | +{ |
| 92 | + const string& c = w[0]; |
| 93 | + GeomTracePropertyPanel *gt = tracePanel(p_calculation); |
| 94 | + PlaybackControl *pb = playback(gt); |
| 95 | + |
| 96 | + // "gtfocus": give the panel the viewer, as a click into it does. |
| 97 | + if (c == "gtfocus") { |
| 98 | + if (!gt) { |
| 99 | + string have; |
| 100 | + for (PropertyPanel *p : |
| 101 | + PropertyPanel::getPanels(p_calculation->getURL().toString())) |
| 102 | + have += " [" + p->getName() + "]"; |
| 103 | + return s.fail("gtfocus: no Geometry Trace panel in " + |
| 104 | + p_calculation->getURL().toString() + "; panels:" + have); |
| 105 | + } |
| 106 | + gt->setFocus(true); |
| 107 | + spin(50); |
| 108 | + return true; |
| 109 | + } |
| 110 | + |
| 111 | + // "gtsweep": a playback tick for every step and a few past either end. |
| 112 | + if (c == "gtsweep") { |
| 113 | + if (!gt || !pb) return s.fail("gtsweep: no Geometry Trace panel"); |
| 114 | + PropTSVecTable *trace = |
| 115 | + dynamic_cast<PropTSVecTable*>(p_calculation->getProperty("GEOMTRACE")); |
| 116 | + int n = trace ? trace->tables() : 0; |
| 117 | + for (int i = -2; i < n + 3; i++) { |
| 118 | + wxCommandEvent ev(wxEVT_PLAYBACK_TICK_EVENT, pb->GetId()); |
| 119 | + ev.SetEventObject(pb); |
| 120 | + ev.SetExtraLong(i); |
| 121 | + gt->GetEventHandler()->ProcessEvent(ev); |
| 122 | + spin(5); |
| 123 | + } |
| 124 | + return true; |
| 125 | + } |
| 126 | + |
| 127 | + // "gtplay <ms> [delay ms]": press Play and let it run. |
| 128 | + if (c == "gtplay" && w.size() >= 2) { |
| 129 | + if (!pb) return s.fail("gtplay: no playback control"); |
| 130 | + if (w.size() >= 3) pb->SetDelay(atoi(w[2].c_str())); |
| 131 | + wxCustomButton *b = |
| 132 | + dynamic_cast<wxCustomButton*>(pb->FindWindow(PlaybackControl::ID_PLAY)); |
| 133 | + if (!b) return s.fail("gtplay: no Play button"); |
| 134 | + b->SetValue(true); |
| 135 | + wxCommandEvent ev(wxEVT_TOGGLEBUTTON, PlaybackControl::ID_PLAY); |
| 136 | + ev.SetEventObject(b); |
| 137 | + ev.SetInt(1); |
| 138 | + pb->GetEventHandler()->ProcessEvent(ev); |
| 139 | + spin(atoi(w[1].c_str())); |
| 140 | + return true; |
| 141 | + } |
| 142 | + |
| 143 | + // "gtstop": press Stop. |
| 144 | + if (c == "gtstop") { |
| 145 | + if (!pb) return s.fail("gtstop: no playback control"); |
| 146 | + wxCommandEvent ev(wxEVT_BUTTON, PlaybackControl::ID_STOP); |
| 147 | + ev.SetEventObject(pb->FindWindow(PlaybackControl::ID_STOP)); |
| 148 | + pb->GetEventHandler()->ProcessEvent(ev); |
| 149 | + spin(20); |
| 150 | + return true; |
| 151 | + } |
| 152 | + |
| 153 | + // "gtupdate grow|gap|badsize|junk": a GEOMTRACE message from a running |
| 154 | + // job, delivered as propertyChangeMCB does: the next step, a step that |
| 155 | + // skips ahead (forces a reload), a step whose size disagrees with its |
| 156 | + // header, and a link to a step the data server cannot deliver. |
| 157 | + if (c == "gtupdate" && w.size() == 2) { |
| 158 | + PropTSVecTable *trace = |
| 159 | + dynamic_cast<PropTSVecTable*>(p_calculation->getProperty("GEOMTRACE")); |
| 160 | + if (!trace) return s.fail("gtupdate: no GEOMTRACE"); |
| 161 | + int n = trace->tables(); |
| 162 | + int atoms = trace->rows(); |
| 163 | + string value; |
| 164 | + if (w[1] == "grow") value = stepMessage(n + 1, atoms, 3 * atoms); |
| 165 | + else if (w[1] == "gap") value = stepMessage(n + 3, atoms, 3 * atoms); |
| 166 | + else if (w[1] == "badsize") value = stepMessage(n + 1, atoms, 3 * atoms + 3); |
| 167 | + else if (w[1] == "junk") |
| 168 | + value = p_calculation->getURL().toString() + "/Props/NOSUCHSTEP"; |
| 169 | + else return s.fail("gtupdate: grow, gap, badsize or junk"); |
| 170 | + try { |
| 171 | + p_calculation->updateProperty("GEOMTRACE", value); |
| 172 | + } catch (...) { |
| 173 | + return s.fail("gtupdate " + w[1] + ": updateProperty threw"); |
| 174 | + } |
| 175 | + set<PropertyPanel*> panels = PropertyPanel::getPanelsForPropertyName( |
| 176 | + p_calculation->getURL().toString(), "GEOMTRACE"); |
| 177 | + for (PropertyPanel *p : panels) p->propertyUpdate("GEOMTRACE"); |
| 178 | + spin(20); |
| 179 | + return true; |
| 180 | + } |
| 181 | + |
| 182 | + // "gtexpect <steps>": the cached trace still holds <steps> steps and the |
| 183 | + // molecule on screen has finite coordinates. |
| 184 | + if (c == "gtexpect" && w.size() == 2) { |
| 185 | + TProperty *prop = p_calculation->getProperty("GEOMTRACE"); |
| 186 | + PropTSVecTable *trace = dynamic_cast<PropTSVecTable*>(prop); |
| 187 | + int want = atoi(w[1].c_str()); |
| 188 | + if (!trace || trace->tables() != want) { |
| 189 | + char buf[96]; |
| 190 | + snprintf(buf, sizeof buf, "gtexpect: GEOMTRACE has %d steps, want %d", |
| 191 | + trace ? trace->tables() : -1, want); |
| 192 | + return s.fail(buf); |
| 193 | + } |
| 194 | + SGFragment *frag = getSG()->getFragment(); |
| 195 | + for (size_t i = 0; i < frag->numAtoms(); i++) { |
| 196 | + const double *xyz = frag->atomRef(i)->coordinates(); |
| 197 | + if (!std::isfinite(xyz[0]) || !std::isfinite(xyz[1]) || |
| 198 | + !std::isfinite(xyz[2])) |
| 199 | + return s.fail("gtexpect: atom coordinates are not finite"); |
| 200 | + } |
| 201 | + fprintf(stderr, "GTSTRESS: %d steps, %lu atoms\n", want, |
| 202 | + (unsigned long)frag->numAtoms()); |
| 203 | + return true; |
| 204 | + } |
| 205 | + |
| 206 | + // "gtcontext <url>": switch calculation, playing or not. |
| 207 | + if (c == "gtcontext" && w.size() == 2) { |
| 208 | + setContext(w[1]); |
| 209 | + spin(200); |
| 210 | + return true; |
| 211 | + } |
| 212 | + |
| 213 | + // "gtfloat": float the trace pane, then Tools > Dock Floating Panels. |
| 214 | + if (c == "gtfloat") { |
| 215 | + if (!gt) return s.fail("gtfloat: no Geometry Trace panel"); |
| 216 | + wxAuiPaneInfo &pane = p_mgr.GetPane(gt); |
| 217 | + if (!pane.IsOk()) return s.fail("gtfloat: the panel has no pane"); |
| 218 | + pane.Show().Float(); |
| 219 | + p_mgr.Update(); |
| 220 | + spin(200); |
| 221 | + wxCommandEvent none; |
| 222 | + OnDockFloatingPanels(none); |
| 223 | + spin(200); |
| 224 | + return true; |
| 225 | + } |
| 226 | + |
| 227 | + // "gtunfocusall": what quit() does to the panels before it exits. |
| 228 | + if (c == "gtunfocusall") { |
| 229 | + set<VizPropertyPanel*> panels = VizPropertyPanel::getPanels(); |
| 230 | + for (VizPropertyPanel *p : panels) p->setFocus(false); |
| 231 | + spin(50); |
| 232 | + return true; |
| 233 | + } |
| 234 | + |
| 235 | + // "gtremovepanels": what a state change back to "created" does to the |
| 236 | + // panels (urlStateMCB), with the animation still running. |
| 237 | + if (c == "gtremovepanels") { |
| 238 | + removePropertyPanels(p_calculation->getURL().toString()); |
| 239 | + spin(500); |
| 240 | + return true; |
| 241 | + } |
| 242 | + |
| 243 | + // "gtclose": File > Close on the current calculation. |
| 244 | + if (c == "gtclose") { |
| 245 | + doClose("", true); |
| 246 | + spin(300); |
| 247 | + return true; |
| 248 | + } |
| 249 | + |
| 250 | + return s.fail("unknown command: " + c); |
| 251 | +} |
0 commit comments