00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00042 #define countof(x) (sizeof((x))/sizeof((*x)))
00043
00044 #include <string.h>
00045 #include <stdlib.h>
00046 #include <stdio.h>
00047 #include <ctype.h>
00048 #include <time.h>
00049 #include <sys/time.h>
00050
00051 #include <cfloat>
00052
00053 #include <archon/util/ref.H>
00054 #include <archon/util/time.H>
00055 #include <archon/util/thread.H>
00056 #include <archon/util/color.H>
00057 #include <archon/util/options.H>
00058 #include <archon/util/text.H>
00059
00060 #include <archon/x3d/proxy/exception.H>
00061 #include <archon/x3d/proxy/proxy.H>
00062
00063 using namespace Archon::Math;
00064 using namespace Archon::Utilities;
00065 using namespace Archon::X3D::Proxy;
00066
00067 namespace Archon
00068 {
00069 const char *progname;
00070
00071 struct AtomKind
00072 {
00073 string name;
00074 float size;
00075 string textureFile;
00076
00077
00078 Ref<ChildNode> model;
00079 };
00080
00081
00082
00083
00084
00085
00086 static AtomKind allAtomKinds[] =
00087 {
00088 { "H", 1.17, "h.png" },
00089 { "C", 1.75, "c.png" },
00090 { "N", 1.55, "n.png" },
00091 { "O", 1.40, "o.png" },
00092 { "P", 1.28, "p.png" },
00093 { "S", 1.80, "s.png" },
00094 { "*", 1.40, "x.png" }
00095 };
00096
00097
00098 typedef struct
00099 {
00100 int id;
00101 const char *label;
00102 float x, y, z;
00103 AtomKind *kind;
00104 }
00105 molecule_atom;
00106
00107 typedef struct
00108 {
00109 int from, to;
00110 int strength;
00111 }
00112 molecule_bond;
00113
00114 typedef struct
00115 {
00116 const char *label;
00117 int natoms, atoms_size;
00118 int nbonds, bonds_size;
00119 molecule_atom *atoms;
00120 molecule_bond *bonds;
00121 }
00122 molecule;
00123
00124 static molecule *mol = 0;
00125
00126 Ref<Application> application;
00127 Ref<Group> rootGroup;
00128
00129 Rotation3 rotate_cylinder(Vector3 p1, Vector3 p2)
00130 {
00131 double p, angle;
00132 Vector3 v = p1;
00133 v -= p2;
00134 v.normalize();
00135 p = v[1];
00136 if(p < -1 + DBL_EPSILON*10) return Rotation3::zero();
00137
00138 if(p > 1 - DBL_EPSILON*10)
00139 {
00140 v = Vector3(1,0,0);
00141 angle = M_PI;
00142 }
00143 else
00144 {
00145 v *= Vector3(0,1,0);
00146 v.normalize();
00147 angle = acos(-p);
00148 }
00149 return Rotation3(v, angle);
00150 }
00151
00152 void tube(double fx, double fy, double fz,
00153 double tx, double ty, double tz,
00154 double thickness, Vector3 &color)
00155 {
00156 Vector3 a(fx, fy, fz);
00157 Vector3 b(tx, ty, tz);
00158
00159 Ref<Transform> t_tube = Transform::create(application);
00160 t_tube->rotation = rotate_cylinder(a, b);
00161 t_tube->translation = (a+b)/2;
00162
00163 Ref<Cylinder> cyl = Cylinder::create(application);
00164 cyl->radius = thickness;
00165 cyl->height = dist(a,b);
00166
00167 Ref<Material> mat = Material::create(application);
00168 mat->diffuseColor = color;
00169
00170 Ref<Appearance> app = Appearance::create(application);
00171 app->material = mat;
00172
00173 Ref<Shape> s = Shape::create(application);
00174 s->geometry = cyl;
00175 s->appearance = app;
00176
00177 t_tube->children.add(s);
00178 rootGroup->children.add(t_tube);
00179 }
00180
00181
00182 static AtomKind *get_atom_data(const char *atom_name)
00183 {
00184 AtomKind *d = 0;
00185 char *n = strdup(atom_name);
00186 char *n2 = n;
00187 int L;
00188
00189 while(!isalpha(*n)) n++;
00190 L = strlen(n);
00191 while(L > 0 && !isalpha(n[L-1])) n[--L] = 0;
00192
00193 for(unsigned i = 0; i < countof(allAtomKinds); i++)
00194 {
00195 d = &allAtomKinds[i];
00196 if(!strcmp(n, allAtomKinds[i].name.c_str())) break;
00197 }
00198
00199 free(n2);
00200 return d;
00201 }
00202
00203
00204 struct ParseException: Utilities::Exception
00205 {
00206 ParseException(string l, string m): Utilities::Exception(l, m) {}
00207 };
00208
00209 static molecule_atom *get_atom(molecule_atom *atoms, int natoms, int id)
00210 {
00211 for(int i = 0; i < natoms; i++) if(id == atoms[i].id) return &atoms[i];
00212
00213 ARCHON_THROW1(ParseException, "Undefined atom id '" + Utilities::Text::toString(id) + "'");
00214 }
00215
00216 static Ref<ChildNode> buildAtomModel(molecule_atom *atom)
00217 {
00218
00219
00220
00221
00222
00223 double bot = 0.4;
00224 double top = 0.6;
00225 double min = 1.17;
00226 double max = 1.80;
00227 double ratio = (atom->kind->size - min) / (max - min);
00228 double size = bot + (ratio * (top - bot));
00229
00230 Ref<Sphere> sphere = Sphere::create(application);
00231 sphere->radius = size;
00232
00233 Ref<Material> material = Material::create(application);
00234 material->diffuseColor = Vector3(1, 1, 1);
00235
00236 Ref<ImageTexture> texture = ImageTexture::create(application);
00237
00238
00239 texture->url.add(atom->kind->textureFile);
00240
00241 Ref<TextureTransform> textureTransform = TextureTransform::create(application);
00242 Vector2 scale(4, 2);
00243 scale /= 4.0/3;
00244 textureTransform->scale = scale;
00245 textureTransform->translation = Vector2(0.5/scale[0]-0.5, 0.5/scale[1]-0.5);
00246
00247 Ref<Appearance> appearance = Appearance::create(application);
00248 appearance->material = material;
00249 appearance->texture = texture;
00250 appearance->textureTransform = textureTransform;
00251
00252 Ref<Shape> shape = Shape::create(application);
00253 shape->geometry = sphere;
00254 shape->appearance = appearance;
00255
00256 Ref<Billboard> billboard = Billboard::create(application);
00257 billboard->axisOfRotation = Vector3::zero();
00258 billboard->children.add(shape);
00259
00260 return billboard;
00261 }
00262
00263
00264 static void build_molecule()
00265 {
00266 molecule *m = mol;
00267
00268 for(int i = 0; i < m->nbonds; i++)
00269 {
00270 Vector3 color = Utilities::Color::gray;
00271
00272 molecule_bond *b = &m->bonds[i];
00273 molecule_atom *from = get_atom(m->atoms, m->natoms, b->from);
00274 molecule_atom *to = get_atom(m->atoms, m->natoms, b->to);
00275
00276 float thickness = 0.07 * b->strength;
00277 if(thickness > 0.3) thickness = 0.3;
00278
00279 tube(from->x, from->y, from->z, to->x, to->y, to->z, thickness, color);
00280 }
00281
00282 for(int i = 0; i < m->natoms; i++)
00283 {
00284 molecule_atom *atom = &m->atoms[i];
00285
00286 if(!atom->kind->model) atom->kind->model = buildAtomModel(atom);
00287
00288 Ref<Transform> t = Transform::create(application);
00289 t->translation = Vector3(atom->x, atom->y, atom->z);
00290 t->children.add(atom->kind->model);
00291
00292 rootGroup->children.add(t);
00293 }
00294 }
00295
00296
00297
00298 static void push_atom(molecule *m, int id, const char *label, float x, float y, float z)
00299 {
00300 m->natoms++;
00301 if(m->atoms_size < m->natoms)
00302 {
00303 m->atoms_size += 20;
00304 m->atoms = (molecule_atom *) realloc (m->atoms,
00305 m->atoms_size * sizeof(*m->atoms));
00306 }
00307 m->atoms[m->natoms-1].id = id;
00308 m->atoms[m->natoms-1].label = label;
00309 m->atoms[m->natoms-1].x = x;
00310 m->atoms[m->natoms-1].y = y;
00311 m->atoms[m->natoms-1].z = z;
00312 m->atoms[m->natoms-1].kind = get_atom_data (label);
00313 }
00314
00315
00316 static void push_bond(molecule *m, int from, int to)
00317 {
00318 int i;
00319
00320 for(i = 0; i < m->nbonds; i++)
00321 if ((m->bonds[i].from == from && m->bonds[i].to == to) ||
00322 (m->bonds[i].to == from && m->bonds[i].from == to))
00323 {
00324 m->bonds[i].strength++;
00325 return;
00326 }
00327
00328 m->nbonds++;
00329 if (m->bonds_size < m->nbonds)
00330 {
00331 m->bonds_size += 20;
00332 m->bonds = (molecule_bond *) realloc (m->bonds,
00333 m->bonds_size * sizeof(*m->bonds));
00334 }
00335 m->bonds[m->nbonds-1].from = from;
00336 m->bonds[m->nbonds-1].to = to;
00337 m->bonds[m->nbonds-1].strength = 1;
00338 }
00339
00340
00341
00342
00343
00344 static void parse_pdb_data(molecule *m, const char *data, string file, int line)
00345 {
00346 const char *s = data;
00347 char *ss;
00348 while (*s)
00349 {
00350 if((!m->label || !*m->label) &&
00351 (!strncmp (s, "HEADER", 6) || !strncmp (s, "COMPND", 6)))
00352 {
00353 char *name = new char[100];
00354 char *n2 = name;
00355 int L = strlen(s);
00356 if (L > 99) L = 99;
00357
00358 strncpy (n2, s, L);
00359 n2 += 7;
00360 while (isspace(*n2)) n2++;
00361
00362 ss = strchr (n2, '\n');
00363 if (ss) *ss = 0;
00364 ss = strchr (n2, '\r');
00365 if (ss) *ss = 0;
00366
00367 ss = n2+strlen(n2)-1;
00368 while (isspace(*ss) && ss > n2) *ss-- = 0;
00369
00370 if (strlen (n2) > 4 && !strcmp (n2 + strlen(n2) - 4, ".pdb"))
00371 n2[strlen(n2)-4] = 0;
00372
00373 if (m->label) free ((char *) m->label);
00374 m->label = strdup (n2);
00375 free (name);
00376 }
00377 else if(!strncmp (s, "TITLE ", 6) ||
00378 !strncmp (s, "HEADER", 6) ||
00379 !strncmp (s, "COMPND", 6) ||
00380 !strncmp (s, "AUTHOR", 6) ||
00381 !strncmp (s, "REVDAT", 6) ||
00382 !strncmp (s, "SOURCE", 6) ||
00383 !strncmp (s, "EXPDTA", 6) ||
00384 !strncmp (s, "JRNL ", 6) ||
00385 !strncmp (s, "REMARK", 6) ||
00386 !strncmp (s, "SEQRES", 6) ||
00387 !strncmp (s, "HET ", 6) ||
00388 !strncmp (s, "FORMUL", 6) ||
00389 !strncmp (s, "CRYST1", 6) ||
00390 !strncmp (s, "ORIGX1", 6) ||
00391 !strncmp (s, "ORIGX2", 6) ||
00392 !strncmp (s, "ORIGX3", 6) ||
00393 !strncmp (s, "SCALE1", 6) ||
00394 !strncmp (s, "SCALE2", 6) ||
00395 !strncmp (s, "SCALE3", 6) ||
00396 !strncmp (s, "MASTER", 6) ||
00397 !strncmp (s, "KEYWDS", 6) ||
00398 !strncmp (s, "DBREF ", 6) ||
00399 !strncmp (s, "HETNAM", 6) ||
00400 !strncmp (s, "HETSYN", 6) ||
00401 !strncmp (s, "HELIX ", 6) ||
00402 !strncmp (s, "LINK ", 6) ||
00403 !strncmp (s, "MTRIX1", 6) ||
00404 !strncmp (s, "MTRIX2", 6) ||
00405 !strncmp (s, "MTRIX3", 6) ||
00406 !strncmp (s, "SHEET ", 6) ||
00407 !strncmp (s, "CISPEP", 6) ||
00408 !strncmp (s, "GENERATED BY", 12) ||
00409 !strncmp (s, "TER ", 4) ||
00410 !strncmp (s, "END ", 4) ||
00411 !strncmp (s, "TER\n", 4) ||
00412 !strncmp (s, "END\n", 4) ||
00413 !strncmp (s, "\n", 1))
00414 ;
00415 else if (!strncmp (s, "ATOM ", 7))
00416 {
00417 int id;
00418 char *name = (char *) calloc (1, 4);
00419 float x = -999, y = -999, z = -999;
00420
00421 sscanf (s+7, " %d ", &id);
00422
00423 strncpy (name, s+12, 3);
00424 while (isspace(*name)) name++;
00425 ss = name + strlen(name)-1;
00426 while (isspace(*ss) && ss > name) *ss-- = 0;
00427 sscanf (s + 32, " %f %f %f ", &x, &y, &z);
00428
00429 push_atom (m, id, name, x, y, z);
00430 }
00431 else if(!strncmp (s, "HETATM ", 7))
00432 {
00433 int id;
00434 char *name = (char *) calloc (1, 4);
00435 float x = -999, y = -999, z = -999;
00436
00437 sscanf (s+7, " %d ", &id);
00438
00439 strncpy (name, s+12, 3);
00440 while (isspace(*name)) name++;
00441 ss = name + strlen(name)-1;
00442 while (isspace(*ss) && ss > name) *ss-- = 0;
00443 sscanf (s + 30, " %f %f %f ", &x, &y, &z);
00444
00445 push_atom (m, id, name, x, y, z);
00446 }
00447 else if (!strncmp (s, "CONECT ", 7))
00448 {
00449 int atoms[11];
00450 int i = sscanf(s + 8, " %d %d %d %d %d %d %d %d %d %d %d %d ",
00451 &atoms[0], &atoms[1], &atoms[2], &atoms[3],
00452 &atoms[4], &atoms[5], &atoms[6], &atoms[7],
00453 &atoms[8], &atoms[9], &atoms[10], &atoms[11]);
00454 int j;
00455 for(j = 1; j < i; j++)
00456 if(atoms[j] > 0) push_bond (m, atoms[0], atoms[j]);
00457 }
00458 else
00459 {
00460 char *s1 = strdup (s);
00461 for (ss = s1; *ss && *ss != '\n'; ss++) ;
00462 *ss = 0;
00463 fprintf(stderr, "%s: %s: %d: unrecognised line: %s\n",
00464 progname, file.c_str(), line, s1);
00465 }
00466
00467 while (*s && *s != '\n') s++;
00468 if(*s == '\n') s++;
00469 line++;
00470 }
00471 }
00472
00473
00474 static void parse_pdb_file(molecule *m, string file)
00475 {
00476 FILE *in;
00477 int buf_size = 40960;
00478 char *buf;
00479 int line = 1;
00480
00481 in = fopen(file.c_str(), "r");
00482 if(!in)
00483 {
00484 char *buf = (char *) malloc(1024 + file.size());
00485 sprintf(buf, "%s: error reading \"%s\"", progname, file.c_str());
00486 perror(buf);
00487 exit (1);
00488 }
00489
00490 buf = (char *) malloc (buf_size);
00491
00492 while (fgets (buf, buf_size-1, in))
00493 {
00494 char *s;
00495 for (s = buf; *s; s++)
00496 if (*s == '\r') *s = '\n';
00497 parse_pdb_data (m, buf, file, line++);
00498 }
00499
00500 free (buf);
00501 fclose (in);
00502
00503 if(!m->natoms)
00504 {
00505 fprintf(stderr, "%s: file %s contains no atomic coordinates!\n",
00506 progname, file.c_str());
00507 exit (1);
00508 }
00509
00510 if(!m->nbonds)
00511 {
00512 fprintf(stderr, "%s: warning: file %s contains no atomic bond info.\n",
00513 progname, file.c_str());
00514 }
00515 }
00516
00517
00518 typedef struct { char *atom; int count; } atom_and_count;
00519
00520
00521
00522
00523
00524
00525 static int cmp_atoms(const void *aa, const void *bb)
00526 {
00527 const atom_and_count *a = (atom_and_count *) aa;
00528 const atom_and_count *b = (atom_and_count *) bb;
00529 if (!a->atom) return 1;
00530 if (!b->atom) return -1;
00531 if (!strcmp(a->atom, "C")) return -1;
00532 if (!strcmp(b->atom, "C")) return 1;
00533 if (!strcmp(a->atom, "H")) return -1;
00534 if (!strcmp(b->atom, "H")) return 1;
00535 return strcmp (a->atom, b->atom);
00536 }
00537
00538
00539 static void special_case_formula (char *f)
00540 {
00541 if (!strcmp(f, "H(2)Be")) strcpy(f, "BeH(2)");
00542 else if (!strcmp(f, "H(3)B")) strcpy(f, "BH(3)");
00543 else if (!strcmp(f, "H(3)N")) strcpy(f, "NH(3)");
00544 else if (!strcmp(f, "CHN")) strcpy(f, "HCN");
00545 else if (!strcmp(f, "CKN")) strcpy(f, "KCN");
00546 else if (!strcmp(f, "H(4)N(2)")) strcpy(f, "N(2)H(4)");
00547 else if (!strcmp(f, "Cl(3)P")) strcpy(f, "PCl(3)");
00548 else if (!strcmp(f, "Cl(5)P")) strcpy(f, "PCl(5)");
00549 }
00550
00551 static void generate_molecule_formula(molecule *m)
00552 {
00553 char *buf = (char *) malloc (m->natoms * 10);
00554 char *s = buf;
00555 int i;
00556 atom_and_count counts[200];
00557 memset (counts, 0, sizeof(counts));
00558 *s = 0;
00559 for(i = 0; i < m->natoms; i++)
00560 {
00561 int j = 0;
00562 char *a = (char *) m->atoms[i].label;
00563 char *e;
00564 while(!isalpha(*a)) a++;
00565 a = strdup (a);
00566 for(e = a; isalpha(*e); e++);
00567 *e = 0;
00568 while (counts[j].atom && !!strcmp(a, counts[j].atom)) j++;
00569 if (counts[j].atom) free (a);
00570 else counts[j].atom = a;
00571 counts[j].count++;
00572 }
00573
00574 i = 0;
00575 while (counts[i].atom) i++;
00576 qsort (counts, i, sizeof(*counts), cmp_atoms);
00577
00578 i = 0;
00579 while(counts[i].atom)
00580 {
00581 strcat (s, counts[i].atom);
00582 free (counts[i].atom);
00583 s += strlen (s);
00584 if(counts[i].count > 1) sprintf (s, "(%d)", counts[i].count);
00585 s += strlen (s);
00586 i++;
00587 }
00588
00589 special_case_formula (buf);
00590
00591 if (!m->label) m->label = strdup("");
00592 s = (char *) malloc (strlen (m->label) + strlen (buf) + 2);
00593 strcpy (s, m->label);
00594 strcat (s, "\n");
00595 strcat (s, buf);
00596 free ((char *) m->label);
00597 free (buf);
00598 m->label = s;
00599 }
00600
00601
00602
00603 static void insert_vertical_whitespace (char *string)
00604 {
00605 while (*string)
00606 {
00607 if((string[0] == ',' || string[0] == ';' || string[0] == ':') && string[1] == ' ')
00608 string[0] = ' ', string[1] = '\n';
00609 string++;
00610 }
00611 }
00612
00613
00614 void init_molecule(string file)
00615 {
00616 mol = (molecule *)calloc(sizeof (molecule), 1);
00617 parse_pdb_file(mol, file);
00618 generate_molecule_formula(mol);
00619 insert_vertical_whitespace((char *)mol->label);
00620 }
00621
00622
00623
00624
00625 namespace SaiTestApps
00626 {
00627 namespace Molecule
00628 {
00629 static bool quit = false;
00630
00631 int main(int argc, const char *argv[])
00632 {
00633 bool opt_help = false;
00634 int opt_corbaEndpointPort = -1;
00635 string opt_serverName = "archon";
00636
00637 Options o;
00638
00639 o.addSwitch("h", "help", opt_help, true,
00640 "Describe the parameters");
00641 o.addConfig("p", "corba-endpoint-port", opt_corbaEndpointPort, opt_corbaEndpointPort,
00642 "The TCP/IP port on which the CORBA Orb listens (-1 for auto assign)",
00643 Options::wantArg_always);
00644 o.addConfig("n", "server-name", opt_serverName, opt_serverName,
00645 "The name of the server to connect to",
00646 Options::wantArg_always);
00647
00648 if(o.processCommandLine(argc, argv))
00649 {
00650 cerr << "Try --help\n";
00651 return 1;
00652 }
00653
00654 if(opt_help)
00655 {
00656 cout <<
00657 "Test Application for 3D-Console by Brian Kristiansen & Kristian Spangsege\n"
00658 "\n"
00659 "Synopsis: " << argv[0] << " FILE\n"
00660 "\n"
00661 "Available options:\n";
00662 cout << o.list();
00663 return 0;
00664 }
00665
00666 if(argc < 2 || argc > 2)
00667 {
00668 cerr << "Wrong number of aguments\n";
00669 cerr << "Try --help\n";
00670 return 1;
00671 }
00672
00673 Ref<Session> session = Session::create("archon", -1);
00674
00675 application = Application::create(session, Uri());
00676 rootGroup = Group::create(application);
00677
00678 progname = argv[0];
00679
00680 init_molecule(argv[1]);
00681 build_molecule();
00682
00683 application->setRootGroup(rootGroup);
00684 application->launch();
00685
00686 Archon::Utilities::Time delay(0, 100000000l);
00687 while(!quit)
00688 {
00689 session->beginUpdate();
00690 session->endUpdate();
00691
00692 Archon::Utilities::Thread::sleep(delay);
00693 }
00694
00695 return 0;
00696 }
00697 }
00698 }
00699 }
00700
00701 int main(int argc, const char *argv[]) throw()
00702 {
00703 using namespace std;
00704 using namespace Archon::X3D;
00705
00706 set_unexpected(Exception::terminal<Proxy::exceptionCatchInfo>);
00707 set_terminate (Exception::terminal<Proxy::exceptionCatchInfo>);
00708
00709 return Archon::SaiTestApps::Molecule::main(argc, argv);
00710 }