|
21 | 21 | #include "inv/misc/SoChildList.H" |
22 | 22 | #include "inv/nodes/SoSeparator.H" |
23 | 23 | #include "inv/ChemKit/ChemIso.H" |
| 24 | +#include "inv/ChemKit/ChemDetail.H" |
| 25 | +#include "inv/actions/SoCallbackAction.H" |
| 26 | +#include "inv/actions/SoRayPickAction.H" |
| 27 | +#include "inv/SoPickedPoint.H" |
| 28 | +#include "inv/SoPrimitiveVertex.H" |
| 29 | +#include "inv/nodes/SoShape.H" |
| 30 | +#include "viz/VRVector.H" |
24 | 31 | #include "inv/misc/SoChildList.H" |
25 | 32 |
|
26 | 33 | #include "dsm/ICalculation.H" |
@@ -524,6 +531,10 @@ bool SceneScript::exec(const vector<string>& w, const string& rest) |
524 | 531 | return snapshot(w[1], 480, 0.2f, 0.3f, 0.4f); |
525 | 532 | } else if (c == "thumb" && w.size() == 2) { |
526 | 533 | return snapshot(w[1], 64, 0.0f, 0.0f, 0.0f); |
| 534 | + } else if (c == "nmcheck" && w.size() == 2) { |
| 535 | + return nmCheck(w[1]); |
| 536 | + } else if (p_ext) { |
| 537 | + return p_ext(*this, w); |
527 | 538 | } else { |
528 | 539 | return fail("unknown command: " + c); |
529 | 540 | } |
@@ -867,3 +878,137 @@ bool SceneScript::snapshot(const string& name, int size, float r, float g, |
867 | 878 | writePpm(p_outdir + "/" + name + ".ppm", size, size, rend->getBuffer()); |
868 | 879 | return true; |
869 | 880 | } |
| 881 | + |
| 882 | + |
| 883 | +namespace { |
| 884 | + |
| 885 | +struct TipData { SbVec3f centre; float maxDist; }; |
| 886 | + |
| 887 | +void arrowTriangle(void *d, SoCallbackAction *a, const SoPrimitiveVertex *v1, |
| 888 | + const SoPrimitiveVertex *v2, const SoPrimitiveVertex *v3) |
| 889 | +{ |
| 890 | + TipData *t = (TipData *)d; |
| 891 | + const SoPrimitiveVertex *v[3] = {v1, v2, v3}; |
| 892 | + for (int i = 0; i < 3; i++) { |
| 893 | + SbVec3f p; |
| 894 | + a->getModelMatrix().multVecMatrix(v[i]->getPoint(), p); |
| 895 | + float dist = (p - t->centre).length(); |
| 896 | + if (dist > t->maxDist) t->maxDist = dist; |
| 897 | + } |
| 898 | +} |
| 899 | + |
| 900 | +} // namespace |
| 901 | + |
| 902 | +// Two measurements from the scene as drawn, neither from NModeVectCmd's |
| 903 | +// arithmetic: how far the arrow's geometry reaches from the atom centre, |
| 904 | +// and where a ray along the arrow meets that atom's sphere. |
| 905 | +bool SceneScript::nmCheck(const string& name) |
| 906 | +{ |
| 907 | + SGFragment *frag = p_sg->getFragment(); |
| 908 | + SoWxRenderArea *area = findRenderArea(p_viewer); |
| 909 | + if (!frag || !area) return fail("nmcheck: no fragment or render area"); |
| 910 | + SbViewportRegion vpr(SbVec2s(480, 480)); |
| 911 | + SoSearchAction sa; |
| 912 | + sa.setType(VRVector::getClassTypeId(), FALSE); |
| 913 | + sa.setInterest(SoSearchAction::ALL); |
| 914 | + sa.apply(p_sg->getNMVecRoot()); |
| 915 | + FILE *o = fopen((p_outdir + "/" + name + ".txt").c_str(), "w"); |
| 916 | + if (!o) return fail("nmcheck: cannot write"); |
| 917 | + fprintf(o, "# atom elem |d|_A tip_A sphere_A tip_minus_sphere_A verdict\n"); |
| 918 | + for (int k = 0; k < sa.getPaths().getLength(); k++) { |
| 919 | + VRVector *vec = (VRVector *)sa.getPaths()[k]->getTail(); |
| 920 | + const double *pos = frag->atomRef(k)->coordinates(), *dir = vec->getDirection(); |
| 921 | + SbVec3f c((float)pos[0], (float)pos[1], (float)pos[2]); |
| 922 | + SbVec3f u((float)dir[0], (float)dir[1], (float)dir[2]); |
| 923 | + float dlen = u.length(); |
| 924 | + TipData td = {c, 0.0f}; |
| 925 | + SoCallbackAction ca; |
| 926 | + ca.addTriangleCallback(SoShape::getClassTypeId(), arrowTriangle, &td); |
| 927 | + ca.apply(vec); |
| 928 | + float sphere = -1.0f; |
| 929 | + if (dlen > 0.0f) { |
| 930 | + u /= dlen; |
| 931 | + const float L = 6.0f; |
| 932 | + SoRayPickAction rp(vpr); |
| 933 | + rp.setRay(c + u * L, -u, 0.0f, L); |
| 934 | + rp.setPickAll(TRUE); |
| 935 | + rp.apply(p_viewer->getTopNode()); |
| 936 | + const SoPickedPointList& pl = rp.getPickedPointList(); |
| 937 | + for (int i = 0; i < pl.getLength(); i++) { |
| 938 | + const ChemDetail *cd = dynamic_cast<const ChemDetail *>(pl[i]->getDetail()); |
| 939 | + if (!cd) continue; |
| 940 | + int32_t ai = -1, bi = -1; |
| 941 | + cd->getAtomBondIndex(ai, bi); |
| 942 | + if (ai != k || bi >= 0) continue; |
| 943 | + float r = (pl[i]->getPoint() - c).length(); |
| 944 | + if (r > sphere) sphere = r; |
| 945 | + } |
| 946 | + } |
| 947 | + const char *verdict = sphere < 0.0f ? "no-sphere-hit" |
| 948 | + : (td.maxDist <= sphere ? "INSIDE" : "outside"); |
| 949 | + fprintf(o, "%d %s %.4f %.4f %.4f %.4f %s\n", k + 1, |
| 950 | + frag->atomRef(k)->atomicSymbol().c_str(), dlen, td.maxDist, sphere, |
| 951 | + td.maxDist - sphere, verdict); |
| 952 | + } |
| 953 | + fclose(o); |
| 954 | + return true; |
| 955 | +} |
| 956 | + |
| 957 | +namespace { |
| 958 | + |
| 959 | +struct FrameLog { |
| 960 | + SGContainer *sg; |
| 961 | + string prefix; |
| 962 | + FILE *log; |
| 963 | + int n; |
| 964 | + wxStopWatch clock; |
| 965 | +}; |
| 966 | + |
| 967 | +void logFrame(void *d) |
| 968 | +{ |
| 969 | + FrameLog *f = (FrameLog *)d; |
| 970 | + GLint vp[4]; |
| 971 | + glGetIntegerv(GL_VIEWPORT, vp); |
| 972 | + vector<unsigned char> px((size_t)vp[2] * vp[3] * 3); |
| 973 | + glFinish(); |
| 974 | + glPixelStorei(GL_PACK_ALIGNMENT, 1); |
| 975 | + glReadPixels(vp[0], vp[1], vp[2], vp[3], GL_RGB, GL_UNSIGNED_BYTE, &px[0]); |
| 976 | + char buf[32]; |
| 977 | + snprintf(buf, sizeof buf, "-%02d.ppm", f->n); |
| 978 | + writePpm(f->prefix + buf, vp[2], vp[3], &px[0]); |
| 979 | + // The thresholds the drawn isosurfaces carry at this frame, read from |
| 980 | + // the scene graph, not from whoever set them. |
| 981 | + SoSearchAction sa; |
| 982 | + sa.setType(ChemIso::getClassTypeId(), FALSE); |
| 983 | + sa.setInterest(SoSearchAction::ALL); |
| 984 | + sa.apply(f->sg->getMORoot()); |
| 985 | + fprintf(f->log, "frame %02d t=%ldms %dx%d isosurfaces %d thresholds", f->n, |
| 986 | + f->clock.Time(), vp[2], vp[3], sa.getPaths().getLength()); |
| 987 | + for (int k = 0; k < sa.getPaths().getLength(); k++) |
| 988 | + fprintf(f->log, " %.6g", ((ChemIso *)sa.getPaths()[k]->getTail())->threshold.getValue()); |
| 989 | + fprintf(f->log, "\n"); |
| 990 | + fflush(f->log); |
| 991 | + f->n++; |
| 992 | +} |
| 993 | + |
| 994 | +} // namespace |
| 995 | + |
| 996 | +bool SceneScript::recordFrames(wxWindow *canvasOwner, const string& name, |
| 997 | + std::function<void()> act) |
| 998 | +{ |
| 999 | + SoWxRenderArea *area = findRenderArea(canvasOwner); |
| 1000 | + if (!area) return fail("recordframes: no render area"); |
| 1001 | + FrameLog f; |
| 1002 | + f.sg = p_sg; |
| 1003 | + f.prefix = p_outdir + "/" + name; |
| 1004 | + f.log = fopen((f.prefix + "-frames.txt").c_str(), "w"); |
| 1005 | + if (!f.log) return fail("recordframes: cannot write"); |
| 1006 | + f.n = 0; |
| 1007 | + area->setFrameCallback(logFrame, &f); |
| 1008 | + act(); |
| 1009 | + // Let any deferred redraw land too (1 s of event loop). |
| 1010 | + for (int i = 0; i < 100; i++) { wxTheApp->Yield(true); wxMilliSleep(10); } |
| 1011 | + area->setFrameCallback(0, 0); |
| 1012 | + fclose(f.log); |
| 1013 | + return true; |
| 1014 | +} |
0 commit comments