5 * Created by Toshi Nagata on 07/11/09.
6 * Copyright 2007-2008 Toshi Nagata. All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
19 #include "ruby_dialog.h"
20 #include "../MD/MDCore.h"
28 #include <version.h> /* for Ruby version */
29 #include <node.h> /* for rb_add_event_hook() */
31 #if defined(__WXMAC__) || defined(__CMDMAC__)
32 #define USE_PTHREAD_FOR_TIMER 1
36 #if USE_PTHREAD_FOR_TIMER
37 #include <unistd.h> /* for usleep() */
38 #include <pthread.h> /* for pthread */
40 #include <signal.h> /* for sigaction() */
44 #include "../Missing.h"
46 #pragma mark ====== Global Values ======
50 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
51 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
53 VALUE gMolbyBacktrace;
55 int gMolbyRunLevel = 0;
56 int gMolbyIsCheckingInterrupt = 0;
58 char *gRubyVersion, *gRubyCopyright;
61 static ID s_ID_equal; /* rb_intern("==") */
65 /* Symbols for atom attributes */
67 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
68 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
69 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
70 s_RSym, s_XSym, s_YSym, s_ZSym,
71 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
72 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
73 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
74 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
75 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
76 s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
78 /* Symbols for parameter attributes */
80 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
81 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
84 /* s_A14Sym, s_B14Sym, */
85 s_Req14Sym, s_Eps14Sym,
86 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
87 s_CommentSym, s_SourceSym;
91 * Get ary[i] by calling "[]" method
94 Ruby_ObjectAtIndex(VALUE ary, int idx)
96 static ID index_method = 0;
97 if (TYPE(ary) == T_ARRAY) {
98 int len = RARRAY_LEN(ary);
99 if (idx >= 0 && idx < len)
100 return (RARRAY_PTR(ary))[idx];
103 if (index_method == 0)
104 index_method = rb_intern("[]");
105 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
109 Ruby_FileStringValuePtr(VALUE *valp)
112 char *p = strdup(StringValuePtr(*valp));
113 translate_char(p, '/', '\\');
114 *valp = rb_str_new2(p);
116 return StringValuePtr(*valp);
118 return StringValuePtr(*valp);
123 Ruby_NewFileStringValue(const char *fstr)
127 char *p = strdup(fstr);
128 translate_char(p, '\\', '/');
129 retval = rb_str_new2(p);
133 return rb_str_new2(fstr);
138 Ruby_ObjToStringObj(VALUE val)
144 return rb_str_new2(rb_id2name(SYM2ID(val)));
146 return rb_str_to_str(val);
150 #pragma mark ====== Message input/output ======
154 * message_box(str, title, button = nil, icon = :info)
156 * Show a message box.
157 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
158 * Icon: :info, :warning, :error
161 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
163 char *str, *title, *s;
165 VALUE sval, tval, bval, ival;
166 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
167 str = StringValuePtr(sval);
168 title = StringValuePtr(tval);
170 bval = Ruby_ObjToStringObj(bval);
171 s = RSTRING_PTR(bval);
172 if (strncmp(s, "ok", 2) == 0)
174 else if (strncmp(s, "cancel", 6) == 0)
177 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
180 ival = Ruby_ObjToStringObj(ival);
181 s = RSTRING_PTR(ival);
182 if (strncmp(s, "info", 4) == 0)
184 else if (strncmp(s, "warn", 4) == 0)
186 else if (strncmp(s, "err", 3) == 0)
189 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
191 MyAppCallback_messageBox(str, title, buttons, icon);
197 * error_message_box(str)
199 * Show an error message box.
202 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
204 char *str = StringValuePtr(sval);
205 MyAppCallback_errorMessageBox("%s", str);
211 * ask(prompt, default = nil) -> string
213 * Open a modal dialog and get a line of text.
216 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
218 volatile VALUE prompt, message;
221 rb_scan_args(argc, argv, "11", &prompt, &message);
222 if (message != Qnil) {
223 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
224 buf[sizeof buf - 1] = 0;
226 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
228 return rb_str_new2(buf);
235 * show_console_window
237 * Show the console window and bring to the front.
240 s_Kernel_ShowConsoleWindow(VALUE self)
242 MyAppCallback_showConsoleWindow();
248 * hide_console_window
250 * Hide the console window.
253 s_Kernel_HideConsoleWindow(VALUE self)
255 MyAppCallback_hideConsoleWindow();
263 * Ring the system bell.
266 s_Kernel_Bell(VALUE self)
268 MyAppCallback_bell();
274 * play_sound(filename, flag = 0)
276 * Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
277 * 1, play the sound asynchronously; 3, play the sound with loop asynchronously
280 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
285 rb_scan_args(argc, argv, "11", &fnval, &flval);
288 else flag = NUM2INT(rb_Integer(flval));
289 fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
290 fname = StringValuePtr(fnval);
291 retval = MyAppCallback_playSound(fname, flag);
292 return (retval ? Qtrue : Qnil);
299 * Stop the sound if playing.
302 s_Kernel_StopSound(VALUE self)
304 MyAppCallback_stopSound();
310 * export_to_clipboard(str)
312 * Export the given string to clipboard.
315 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
317 #if !defined(__CMDMAC__)
318 const char *s = StringValuePtr(sval);
321 /* Convert the end-of-line characters */
322 { const char *p; int nc; char *np;
324 for (p = s; *p != 0; p++) {
328 ns = (char *)malloc(strlen(s) + nc + 1);
329 for (np = ns, p = s; *p != 0; p++, np++) {
337 ns = (char *)malloc(strlen(s) + 1);
341 /* wxMac still has Carbon code. Oops. */
342 for (np = ns; *np != 0; np++) {
349 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
350 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
359 * Put the message in the main text view in black color.
362 s_StandardOutput(VALUE self, VALUE str)
365 MyAppCallback_setConsoleColor(0);
366 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
374 * Put the message in the main text view in red color.
377 s_StandardErrorOutput(VALUE self, VALUE str)
380 MyAppCallback_setConsoleColor(1);
381 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
382 MyAppCallback_setConsoleColor(0);
388 * stdin.gets(rs = $/)
390 * Read one line message via dialog box.
393 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
396 pval = rb_str_new2("Enter a line:");
397 rval = s_Kernel_Ask(1, &pval, self);
400 rb_str_cat2(rval, "\n");
406 * stdin.method_missing(name, args, ...)
408 * Throw an exception, noting only gets and readline are defined.
411 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
414 rb_scan_args(argc, argv, "10", &nval);
415 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
416 return Qnil; /* Not reached */
419 #pragma mark ====== Track key events ======
421 /* User interrupt handling
422 * User interrupt (command-period on Mac OS) is handled by periodic polling of
423 * key events. This polling should only be enabled during "normal" execution
424 * of scripts and must be disabled when the rest of the application (or Ruby
425 * script itself) is handling GUI. This is ensured by appropriate calls to
426 * enable_interrupt and disable_interrupt. */
428 static VALUE s_interrupt_flag = Qfalse;
431 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
433 volatile VALUE message;
435 if (Ruby_GetInterruptFlag() == Qtrue) {
436 rb_scan_args(argc, argv, "01", &message);
438 p = StringValuePtr(message);
441 MyAppCallback_showProgressPanel(p);
447 s_HideProgressPanel(VALUE self)
449 MyAppCallback_hideProgressPanel();
454 s_SetProgressValue(VALUE self, VALUE val)
456 double dval = NUM2DBL(rb_Float(val));
457 MyAppCallback_setProgressValue(dval);
462 s_SetProgressMessage(VALUE self, VALUE msg)
467 else p = StringValuePtr(msg);
468 MyAppCallback_setProgressMessage(p);
473 s_SetInterruptFlag(VALUE self, VALUE val)
477 if (val == Qfalse || val == Qnil)
481 oldval = s_interrupt_flag;
483 s_interrupt_flag = val;
485 s_HideProgressPanel(self);
492 s_GetInterruptFlag(VALUE self)
494 return s_SetInterruptFlag(self, Qundef);
499 s_Ruby_CallMethod(VALUE val)
501 void **ptr = (void **)val;
502 VALUE receiver = (VALUE)ptr[0];
503 ID method_id = (ID)ptr[1];
504 VALUE args = (VALUE)ptr[2];
506 if (method_id == 0) {
507 /* args should be a string, which is evaluated */
508 if (receiver == Qnil) {
509 retval = rb_eval_string(StringValuePtr(args));
511 retval = rb_obj_instance_eval(1, &args, receiver);
514 /* args should be an array of arguments */
515 retval = rb_apply(receiver, method_id, args);
521 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
523 VALUE retval, save_interrupt_flag;
525 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
526 ptr[0] = (void *)receiver;
527 ptr[1] = (void *)method_id;
528 ptr[2] = (void *)args;
529 MyAppCallback_beginUndoGrouping();
530 retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
531 MyAppCallback_endUndoGrouping();
532 s_SetInterruptFlag(Qnil, save_interrupt_flag);
533 MyAppCallback_hideProgressPanel(); /* In case when the progress panel is still onscreen */
\v
539 Ruby_SetInterruptFlag(VALUE val)
541 return s_SetInterruptFlag(Qnil, val);
545 Ruby_GetInterruptFlag(void)
547 return s_SetInterruptFlag(Qnil, Qundef);
552 * check_interrupt -> integer
554 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
557 s_Kernel_CheckInterrupt(VALUE self)
559 if (Ruby_GetInterruptFlag() == Qfalse)
561 else if (MyAppCallback_checkInterrupt())
563 else return INT2NUM(0);
566 static volatile unsigned long sITimerCount = 0;
569 static HANDLE sITimerEvent;
570 static HANDLE sITimerThread;
571 static int sITimerInterval;
573 static __stdcall unsigned
574 s_ITimerThreadFunc(void *p)
576 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
582 #elif USE_PTHREAD_FOR_TIMER
585 static pthread_t sTimerThread;
587 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
588 static volatile signed char sTimerFlag = -1;
589 static volatile int sTimerIntervalMicrosec = 0;
592 s_TimerThreadEntry(void *param)
595 usleep(sTimerIntervalMicrosec);
598 else if (sTimerFlag == -2)
607 s_SignalAction(int n)
613 s_SetIntervalTimer(int n, int msec)
617 /* Start interval timer */
618 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
619 sITimerInterval = msec;
621 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
624 /* Stop interval timer */
626 SetEvent(sITimerEvent); /* Tell thread to terminate */
628 WaitForSingleObject(sITimerThread, 1000);
629 CloseHandle(sITimerThread);
632 CloseHandle(sITimerEvent);
634 sITimerThread = NULL;
636 #elif USE_PTHREAD_FOR_TIMER
638 if (sTimerFlag == -1) {
639 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
641 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
644 sTimerFlag = 0; /* Active */
645 sTimerIntervalMicrosec = msec * 1000;
646 } else if (sTimerFlag != -1)
647 sTimerFlag = 1; /* Inactive */
649 static struct itimerval sOldValue;
650 static struct sigaction sOldAction;
651 struct itimerval val;
652 struct sigaction act;
655 act.sa_handler = s_SignalAction;
658 sigaction(SIGALRM, &act, &sOldAction);
659 val.it_value.tv_sec = 0;
660 val.it_value.tv_usec = msec * 1000;
661 val.it_interval.tv_sec = 0;
662 val.it_interval.tv_usec = msec * 1000;
663 setitimer(ITIMER_REAL, &val, &sOldValue);
665 setitimer(ITIMER_REAL, &sOldValue, &val);
666 sigaction(SIGALRM, &sOldAction, &act);
672 s_GetTimerCount(void)
678 s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
680 if (s_interrupt_flag != Qfalse) {
681 static unsigned long sLastTime = 0;
682 unsigned long currentTime;
684 currentTime = s_GetTimerCount();
685 if (currentTime != sLastTime) {
686 sLastTime = currentTime;
687 gMolbyIsCheckingInterrupt = 1;
688 flag = MyAppCallback_checkInterrupt();
689 gMolbyIsCheckingInterrupt = 0;
691 s_SetInterruptFlag(Qnil, Qfalse);
698 #pragma mark ====== Menu handling ======
702 * register_menu(title, method)
704 * Register the method (specified as a symbol) in the script menu.
705 * The method must be either an instance method of Molecule with no argument,
706 * or a class method of Molecule with one argument (the current molecule).
707 * The menu associated with the class method can be invoked even when no document
708 * is open (the argument is set to Qnil in this case). On the other hand, the
709 * menu associated with the instance method can only be invoked when at least one
710 * document is active.
713 s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
715 if (TYPE(method) == T_SYMBOL) {
716 method = rb_funcall(method, rb_intern("to_s"), 0);
718 MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
723 s_Kernel_LookupMenu(VALUE self, VALUE title)
725 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
730 s_Ruby_methodType_sub(VALUE data)
732 const char **p = (const char **)data;
733 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
734 ID mid = rb_intern(p[1]);
736 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
738 else if (rb_respond_to(klass, mid))
741 return INT2FIX(ival);
744 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
745 has the singleton method (class method) with the given name, 0 otherwise. */
747 Ruby_methodType(const char *className, const char *methodName)
754 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
756 return FIX2INT(retval);
762 * execute_script_file(fname)
764 * Execute the script in the given file. If a molecule is active, then
765 * the script is evaluated as Molecule.current.instance_eval(script).
766 * Before entering the script, the current directory is set to the parent
767 * directory of the script.
770 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
773 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
774 if (retval == (VALUE)6 && status == -1)
775 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
785 * Get the directory suitable for storing user documents. On Windows
786 * it is the home directory + "My Documents". On other platforms
787 * it is the home directory.
790 s_Kernel_DocumentHome(VALUE self)
792 char *s = MyAppCallback_getDocumentHomeDir();
793 VALUE retval = Ruby_NewFileStringValue(s);
798 /* The callback function for call_subprocess */
800 s_Kernel_CallSubProcess_Callback(void *data)
803 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
804 if (status != 0 || retval == Qnil || retval == Qfalse)
811 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
813 * Call subprocess. A progress dialog window is displayed, with a message
814 * "Running #{process_name}...".
815 * A callback proc can be given, which is called periodically during execution. If the proc returns
816 * nil or false, then the execution will be interrupted.
817 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
818 * filename begins with ">>", then the message will be appended to the file.
819 * If the filename is "/dev/null" or "NUL", then the message will be lost.
820 * If the argument is nil, then the message will be sent to the Ruby console.
823 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
825 VALUE cmd, procname, cproc, stdout_val, stderr_val;
830 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
832 if (stdout_val == Qnil) {
835 sout = StringValuePtr(stdout_val);
836 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
839 if (strncmp(sout, ">>", 2) == 0) {
841 fpout = fopen(sout, "a");
845 fpout = fopen(sout, "w");
848 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
851 if (stderr_val == Qnil) {
854 serr = StringValuePtr(stderr_val);
855 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
858 if (strncmp(serr, ">>", 2) == 0) {
860 fpout = fopen(serr, "a");
864 fperr = fopen(serr, "w");
867 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
871 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
873 if (fpout != NULL && fpout != (FILE *)1)
875 if (fperr != NULL && fperr != (FILE *)1)
887 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
890 s_Kernel_Backquote(VALUE self, VALUE cmd)
895 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
897 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
899 val = rb_str_new2(buf);
902 val = rb_str_new2("");
907 #pragma mark ====== User defaults ======
911 * get_global_settings(key)
913 * Get a setting data for key from the application preferences.
916 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
918 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
920 VALUE retval = rb_eval_string(p);
928 * set_global_settings(key, value)
930 * Set a setting data for key to the application preferences.
933 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
935 VALUE sval = rb_inspect(value);
936 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
940 #pragma mark ====== Utility functions (protected funcall) ======
942 struct Ruby_funcall2_record {
950 s_Ruby_funcall2_sub(VALUE data)
952 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
953 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
957 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
959 struct Ruby_funcall2_record rec;
964 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
968 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
970 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
973 #pragma mark ====== ParameterRef Class ======
976 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
980 Data_Get_Struct(self, ParameterRef, pref);
982 *typep = pref->parType;
983 if (pref->parType == kElementParType) {
984 up = (UnionPar *)&gElementParameters[pref->idx];
986 up = ParameterRefGetPar(pref);
989 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
990 if (up->bond.src != 0 && up->bond.src != -1)
991 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
998 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1002 Data_Get_Struct(self, ParameterRef, pref);
1003 if (pref->mol == NULL)
1005 up = ParameterRefGetPar(pref);
1006 if (key != s_SourceSym)
1007 up->bond.src = 0; /* Becomes automatically molecule-local */
1008 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1011 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1012 MolActionCallback_registerUndo(pref->mol, act);
1013 MoleculeCallback_notifyModification(pref->mol, 0);
1014 pref->mol->needsMDRebuild = 1;
1019 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1021 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1023 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1025 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1029 s_AtomTypeIndexFromValue(VALUE val)
1031 if (rb_obj_is_kind_of(val, rb_cNumeric))
1032 return NUM2INT(val);
1034 return AtomTypeEncodeToUInt(StringValuePtr(val));
1037 static const char *s_ParameterTypeNames[] = {
1038 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1040 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1043 s_ParTypeFromValue(VALUE val)
1047 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1048 if (s_ParameterTypeIDs[0] == 0) {
1049 for (i = 0; i < n; i++)
1050 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1052 valid = rb_to_id(val);
1053 for (i = 0; i < n; i++) {
1054 if (valid == s_ParameterTypeIDs[i]) {
1056 return kElementParType;
1057 else return kFirstParType + i;
1060 return kInvalidParType;
1067 * Get the index in the parameter list.
1069 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1071 Data_Get_Struct(self, ParameterRef, pref);
1072 return INT2NUM(pref->idx);
1077 * par_type -> String
1079 * Get the parameter type, like "bond", "angle", etc.
1081 static VALUE s_ParameterRef_GetParType(VALUE self) {
1083 s_UnionParFromValue(self, &tp, 0);
1084 if (tp == kElementParType)
1085 return rb_str_new2("element");
1086 tp -= kFirstParType;
1087 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1088 return rb_str_new2(s_ParameterTypeNames[tp]);
1089 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1094 * atom_type -> String or Array of String
1095 * atom_types -> String or Array of String
1097 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1098 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1099 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1100 * The atom type may be "X", which is a wildcard that matches any atom type.
1102 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1107 up = s_UnionParFromValue(self, &tp, 0);
1108 n = ParameterGetAtomTypes(tp, up, types);
1110 rb_raise(rb_eMolbyError, "invalid member atom_types");
1111 for (i = 0; i < n; i++) {
1112 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1113 vals[i] = INT2NUM(types[i]);
1115 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1120 return rb_ary_new4(n, vals);
1127 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1129 static VALUE s_ParameterRef_GetK(VALUE self) {
1133 up = s_UnionParFromValue(self, &tp, 0);
1136 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1138 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1139 case kDihedralParType:
1140 case kImproperParType:
1141 if (up->torsion.mult == 1)
1142 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1143 n = up->torsion.mult;
1146 for (i = 0; i < n; i++)
1147 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1148 return rb_ary_new4(n, vals);
1150 rb_raise(rb_eMolbyError, "invalid member k");
1158 * Get the equilibrium bond length. Only available for bond parameters.
1160 static VALUE s_ParameterRef_GetR0(VALUE self) {
1163 up = s_UnionParFromValue(self, &tp, 0);
1164 if (tp == kBondParType)
1165 return rb_float_new(up->bond.r0);
1166 else rb_raise(rb_eMolbyError, "invalid member r0");
1173 * Get the equilibrium angle (in degree). Only available for angle parameters.
1175 static VALUE s_ParameterRef_GetA0(VALUE self) {
1178 up = s_UnionParFromValue(self, &tp, 0);
1179 if (tp == kAngleParType)
1180 return rb_float_new(up->angle.a0 * kRad2Deg);
1181 else rb_raise(rb_eMolbyError, "invalid member a0");
1188 * Get the multiplicity. Only available for dihedral and improper parameters.
1189 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1191 static VALUE s_ParameterRef_GetMult(VALUE self) {
1194 up = s_UnionParFromValue(self, &tp, 0);
1195 if (tp == kDihedralParType || tp == kImproperParType)
1196 return rb_float_new(up->torsion.mult);
1197 else rb_raise(rb_eMolbyError, "invalid member mult");
1202 * period -> Integer or Array of Integers
1204 * Get the periodicity. Only available for dihedral and improper parameters.
1205 * If the multiplicity is larger than 1, then an array of integers is returned.
1206 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1208 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1212 up = s_UnionParFromValue(self, &tp, 0);
1213 if (tp == kDihedralParType || tp == kImproperParType) {
1214 if (up->torsion.mult == 1)
1215 return INT2NUM(up->torsion.period[0]);
1216 n = up->torsion.mult;
1219 for (i = 0; i < n; i++)
1220 vals[i] = INT2NUM(up->torsion.period[i]);
1221 return rb_ary_new4(n, vals);
1222 } else rb_raise(rb_eMolbyError, "invalid member period");
1227 * phi0 -> Float or Array of Floats
1229 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1230 * If the multiplicity is larger than 1, then an array of floats is returned.
1231 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1233 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1237 up = s_UnionParFromValue(self, &tp, 0);
1238 if (tp == kDihedralParType || tp == kImproperParType) {
1239 if (up->torsion.mult == 1)
1240 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1241 n = up->torsion.mult;
1244 for (i = 0; i < n; i++)
1245 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1246 return rb_ary_new4(n, vals);
1247 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1254 * Get the "A" value for the van der Waals parameter.
1257 static VALUE s_ParameterRef_GetA(VALUE self) {
1260 up = s_UnionParFromValue(self, &tp, 0);
1261 if (tp == kVdwParType)
1262 return rb_float_new(up->vdw.A);
1263 else if (tp == kVdwPairParType)
1264 return rb_float_new(up->vdwp.A);
1265 else rb_raise(rb_eMolbyError, "invalid member A");
1273 * Get the "B" value for the van der Waals parameter.
1276 static VALUE s_ParameterRef_GetB(VALUE self) {
1279 up = s_UnionParFromValue(self, &tp, 0);
1280 if (tp == kVdwParType)
1281 return rb_float_new(up->vdw.B);
1282 else if (tp == kVdwPairParType)
1283 return rb_float_new(up->vdwp.B);
1284 else rb_raise(rb_eMolbyError, "invalid member B");
1292 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1294 static VALUE s_ParameterRef_GetReq(VALUE self) {
1297 /* Double a, b, r; */
1299 up = s_UnionParFromValue(self, &tp, 0);
1300 if (tp == kVdwParType) {
1304 } else if (tp == kVdwPairParType) {
1308 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1309 /* if (a == 0.0 || b == 0.0) */
1310 return rb_float_new(r);
1311 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1318 * Get the minimum energy for the van der Waals parameter.
1320 static VALUE s_ParameterRef_GetEps(VALUE self) {
1325 up = s_UnionParFromValue(self, &tp, 0);
1326 if (tp == kVdwParType) {
1330 } else if (tp == kVdwPairParType) {
1334 } else rb_raise(rb_eMolbyError, "invalid member eps");
1335 /* if (a == 0.0 || b == 0.0) */
1336 return rb_float_new(eps * INTERNAL2KCAL);
1337 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1344 * Get the "A" value for the 1-4 van der Waals parameter.
1347 static VALUE s_ParameterRef_GetA14(VALUE self) {
1350 up = s_UnionParFromValue(self, &tp, 0);
1351 if (tp == kVdwParType)
1352 return rb_float_new(up->vdw.A14);
1353 else if (tp == kVdwPairParType)
1354 return rb_float_new(up->vdwp.A14);
1355 else rb_raise(rb_eMolbyError, "invalid member A14");
1363 * Get the "B" value for the 1-4 van der Waals parameter.
1366 static VALUE s_ParameterRef_GetB14(VALUE self) {
1369 up = s_UnionParFromValue(self, &tp, 0);
1370 if (tp == kVdwParType)
1371 return rb_float_new(up->vdw.B14);
1372 else if (tp == kVdwPairParType)
1373 return rb_float_new(up->vdwp.B14);
1374 else rb_raise(rb_eMolbyError, "invalid member B14");
1382 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1384 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1387 /* Double a, b, r; */
1389 up = s_UnionParFromValue(self, &tp, 0);
1390 if (tp == kVdwParType) {
1394 } else if (tp == kVdwPairParType) {
1395 /* a = up->vdwp.A14;
1396 b = up->vdwp.B14; */
1397 r = up->vdwp.r_eq14;
1398 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1399 /* if (a == 0.0 || b == 0.0) */
1400 return rb_float_new(r);
1401 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1408 * Get the minimum energy for the 1-4 van der Waals parameter.
1410 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1415 up = s_UnionParFromValue(self, &tp, 0);
1416 if (tp == kVdwParType) {
1419 eps = up->vdw.eps14;
1420 } else if (tp == kVdwPairParType) {
1421 /* a = up->vdwp.A14;
1422 b = up->vdwp.B14; */
1423 eps = up->vdwp.eps14;
1424 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1425 /* if (a == 0.0 || b == 0.0) */
1426 return rb_float_new(eps * INTERNAL2KCAL);
1427 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1434 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1436 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1439 up = s_UnionParFromValue(self, &tp, 0);
1440 if (tp == kVdwCutoffParType)
1441 return rb_float_new(up->vdwcutoff.cutoff);
1442 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1449 * Get the atomic radius for the atom display parameter.
1451 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1454 up = s_UnionParFromValue(self, &tp, 0);
1455 if (tp == kElementParType)
1456 return rb_float_new(up->atom.radius);
1457 else rb_raise(rb_eMolbyError, "invalid member radius");
1462 * color -> [Float, Float, Float]
1464 * Get the rgb color for the atom display parameter.
1466 static VALUE s_ParameterRef_GetColor(VALUE self) {
1469 up = s_UnionParFromValue(self, &tp, 0);
1470 if (tp == kElementParType)
1471 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1472 else rb_raise(rb_eMolbyError, "invalid member color");
1477 * atomic_number -> Integer
1479 * Get the atomic number for the vdw or atom parameter.
1481 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1484 up = s_UnionParFromValue(self, &tp, 0);
1485 if (tp == kElementParType)
1486 return INT2NUM(up->atom.number);
1487 else if (tp == kVdwParType)
1488 return INT2NUM(up->vdw.atomicNumber);
1489 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1496 * Get the name for the atom display parameter.
1498 static VALUE s_ParameterRef_GetName(VALUE self) {
1501 up = s_UnionParFromValue(self, &tp, 0);
1502 if (tp == kElementParType) {
1504 strncpy(name, up->atom.name, 4);
1506 return rb_str_new2(name);
1507 } else rb_raise(rb_eMolbyError, "invalid member name");
1514 * Get the atomic weight for the atom display parameter.
1516 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1519 up = s_UnionParFromValue(self, &tp, 0);
1520 if (tp == kElementParType)
1521 return rb_float_new(up->atom.weight);
1522 else if (tp == kVdwParType)
1523 return rb_float_new(up->vdw.weight);
1524 else rb_raise(rb_eMolbyError, "invalid member weight");
1529 * fullname -> String
1531 * Get the full name for the atom display parameter.
1533 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1536 up = s_UnionParFromValue(self, &tp, 0);
1537 if (tp == kElementParType) {
1539 strncpy(fullname, up->atom.fullname, 15);
1541 return rb_str_new2(fullname);
1542 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1549 * Get the comment for the parameter.
1551 static VALUE s_ParameterRef_GetComment(VALUE self) {
1554 up = s_UnionParFromValue(self, &tp, 0);
1558 else return rb_str_new2(ParameterGetComment(com));
1565 * Get the source string for the parameter. Returns false for undefined parameter,
1566 * and nil for "local" parameter that is specific for the molecule.
1568 static VALUE s_ParameterRef_GetSource(VALUE self) {
1571 up = s_UnionParFromValue(self, &tp, 0);
1574 return Qfalse; /* undefined */
1576 return Qnil; /* local */
1577 else return rb_str_new2(ParameterGetComment(src));
1581 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1588 if (rb_obj_is_kind_of(val, rb_cString)) {
1589 char *s = StringValuePtr(val);
1591 for (i = 0; i < n; i++) {
1594 /* Skip leading separaters */
1595 while (*s == '-' || *s == ' ' || *s == '\t')
1597 for (p = s; *p != 0; p++) {
1598 if (*p == '-' || *p == ' ' || *p == '\t')
1602 if (len >= sizeof(buf))
1603 len = sizeof(buf) - 1;
1604 strncpy(buf, s, len);
1606 /* Skip trailing blanks */
1607 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1610 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1611 if (buf[0] >= '0' && buf[0] <= '9')
1612 types[i] = atoi(buf);
1614 types[i] = AtomTypeEncodeToUInt(buf);
1615 if (p == NULL || *p == 0) {
1621 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1624 val = rb_ary_to_ary(val);
1625 if (RARRAY_LEN(val) != n)
1626 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1627 valp = RARRAY_PTR(val);
1629 for (i = 0; i < n; i++) {
1630 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1631 types[i] = NUM2INT(rb_Integer(valp[i]));
1633 VALUE sval = valp[i];
1634 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1639 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1644 up = s_UnionParFromValue(self, &tp, 1);
1645 oldval = s_ParameterRef_GetAtomTypes(self);
1646 oldsrc = up->bond.src;
1649 s_ScanAtomTypes(val, 2, types);
1650 up->bond.type1 = types[0];
1651 up->bond.type2 = types[1];
1654 s_ScanAtomTypes(val, 3, types);
1655 up->angle.type1 = types[0];
1656 up->angle.type2 = types[1];
1657 up->angle.type3 = types[2];
1659 case kDihedralParType:
1660 case kImproperParType:
1661 s_ScanAtomTypes(val, 4, types);
1662 up->torsion.type1 = types[0];
1663 up->torsion.type2 = types[1];
1664 up->torsion.type3 = types[2];
1665 up->torsion.type4 = types[3];
1668 s_ScanAtomTypes(val, 1, types);
1669 up->vdw.type1 = types[0];
1671 case kVdwPairParType:
1672 s_ScanAtomTypes(val, 2, types);
1673 up->vdwp.type1 = types[0];
1674 up->vdwp.type2 = types[1];
1676 case kVdwCutoffParType:
1677 s_ScanAtomTypes(val, 2, types);
1678 up->vdwcutoff.type1 = types[0];
1679 up->vdwcutoff.type2 = types[1];
1684 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1688 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1690 Int tp, i, n, oldsrc;
1691 VALUE *valp, oldval;
1692 up = s_UnionParFromValue(self, &tp, 1);
1693 oldval = s_ParameterRef_GetK(self);
1694 oldsrc = up->bond.src;
1697 val = rb_Float(val);
1698 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1701 val = rb_Float(val);
1702 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1704 case kDihedralParType:
1705 case kImproperParType:
1706 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1707 up->torsion.mult = 1;
1708 val = rb_Float(val);
1709 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1712 n = up->torsion.mult;
1715 val = rb_ary_to_ary(val);
1716 if (RARRAY_LEN(val) != n)
1717 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1718 valp = RARRAY_PTR(val);
1719 for (i = 0; i < n; i++) {
1720 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1724 rb_raise(rb_eMolbyError, "invalid member k");
1726 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1730 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1734 up = s_UnionParFromValue(self, &tp, 1);
1735 oldval = s_ParameterRef_GetR0(self);
1736 oldsrc = up->bond.src;
1737 if (tp == kBondParType) {
1738 val = rb_Float(val);
1739 up->bond.r0 = NUM2DBL(val);
1740 } else rb_raise(rb_eMolbyError, "invalid member r0");
1741 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1745 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1749 up = s_UnionParFromValue(self, &tp, 1);
1750 oldval = s_ParameterRef_GetA0(self);
1751 oldsrc = up->bond.src;
1752 if (tp == kAngleParType) {
1753 val = rb_Float(val);
1754 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1755 } else rb_raise(rb_eMolbyError, "invalid member a0");
1756 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1760 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1764 up = s_UnionParFromValue(self, &tp, 1);
1765 oldval = s_ParameterRef_GetMult(self);
1766 oldsrc = up->bond.src;
1767 if (tp == kDihedralParType || tp == kImproperParType) {
1769 val = rb_Integer(val);
1772 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1773 up->torsion.mult = i;
1774 } else rb_raise(rb_eMolbyError, "invalid member mult");
1775 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1779 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1781 Int tp, i, n, oldsrc;
1782 VALUE *valp, oldval;
1783 up = s_UnionParFromValue(self, &tp, 1);
1784 oldval = s_ParameterRef_GetPeriod(self);
1785 oldsrc = up->bond.src;
1786 if (tp == kDihedralParType || tp == kImproperParType) {
1787 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1788 up->torsion.mult = 1;
1789 val = rb_Integer(val);
1790 up->torsion.period[0] = NUM2INT(val);
1792 n = up->torsion.mult;
1795 val = rb_ary_to_ary(val);
1796 if (RARRAY_LEN(val) != n)
1797 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1798 valp = RARRAY_PTR(val);
1799 for (i = 0; i < n; i++) {
1800 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1803 } else rb_raise(rb_eMolbyError, "invalid member period");
1804 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1808 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1810 Int tp, i, n, oldsrc;
1811 VALUE *valp, oldval;
1812 up = s_UnionParFromValue(self, &tp, 1);
1813 oldval = s_ParameterRef_GetPhi0(self);
1814 oldsrc = up->bond.src;
1815 if (tp == kDihedralParType || tp == kImproperParType) {
1816 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1817 up->torsion.mult = 1;
1818 val = rb_Float(val);
1819 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1821 n = up->torsion.mult;
1824 val = rb_ary_to_ary(val);
1825 if (RARRAY_LEN(val) != n)
1826 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1827 valp = RARRAY_PTR(val);
1828 for (i = 0; i < n; i++)
1829 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1831 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1832 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1837 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1842 up = s_UnionParFromValue(self, &tp, 1);
1843 oldval = s_ParameterRef_GetA(self);
1844 oldsrc = up->bond.src;
1845 val = rb_Float(val);
1847 if (tp == kVdwParType)
1849 else if (tp == kVdwPairParType)
1851 else rb_raise(rb_eMolbyError, "invalid member A");
1852 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1856 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1861 up = s_UnionParFromValue(self, &tp, 1);
1862 oldval = s_ParameterRef_GetB(self);
1863 oldsrc = up->bond.src;
1864 val = rb_Float(val);
1866 if (tp == kVdwParType)
1868 else if (tp == kVdwPairParType)
1870 else rb_raise(rb_eMolbyError, "invalid member B");
1871 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1876 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
1881 up = s_UnionParFromValue(self, &tp, 1);
1882 oldval = s_ParameterRef_GetReq(self);
1883 oldsrc = up->bond.src;
1884 val = rb_Float(val);
1886 if (tp == kVdwParType) {
1888 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
1889 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
1890 } else if (tp == kVdwPairParType) {
1892 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
1893 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
1894 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1895 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
1899 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
1904 up = s_UnionParFromValue(self, &tp, 1);
1905 oldval = s_ParameterRef_GetEps(self);
1906 oldsrc = up->bond.src;
1907 val = rb_Float(val);
1908 e = NUM2DBL(val) * KCAL2INTERNAL;
1909 if (tp == kVdwParType) {
1911 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
1912 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
1913 } else if (tp == kVdwPairParType) {
1915 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
1916 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
1917 } else rb_raise(rb_eMolbyError, "invalid member eps");
1918 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
1923 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
1928 up = s_UnionParFromValue(self, &tp, 1);
1929 oldval = s_ParameterRef_GetA14(self);
1930 oldsrc = up->bond.src;
1931 val = rb_Float(val);
1933 if (tp == kVdwParType)
1935 else if (tp == kVdwPairParType)
1937 else rb_raise(rb_eMolbyError, "invalid member A14");
1938 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
1942 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
1947 up = s_UnionParFromValue(self, &tp, 1);
1948 oldval = s_ParameterRef_GetB14(self);
1949 oldsrc = up->bond.src;
1950 val = rb_Float(val);
1952 if (tp == kVdwParType)
1954 else if (tp == kVdwPairParType)
1956 else rb_raise(rb_eMolbyError, "invalid member B14");
1957 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
1962 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
1967 up = s_UnionParFromValue(self, &tp, 1);
1968 oldval = s_ParameterRef_GetReq14(self);
1969 oldsrc = up->bond.src;
1970 val = rb_Float(val);
1972 if (tp == kVdwParType) {
1974 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1975 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1976 } else if (tp == kVdwPairParType) {
1977 up->vdwp.r_eq14 = r;
1978 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1979 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1980 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1981 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
1985 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
1990 up = s_UnionParFromValue(self, &tp, 1);
1991 oldval = s_ParameterRef_GetEps14(self);
1992 oldsrc = up->bond.src;
1993 val = rb_Float(val);
1994 e = NUM2DBL(val) * KCAL2INTERNAL;
1995 if (tp == kVdwParType) {
1997 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1998 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1999 } else if (tp == kVdwPairParType) {
2001 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2002 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2003 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2004 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2008 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2012 oldval = s_ParameterRef_GetCutoff(self);
2013 oldsrc = up->bond.src;
2014 up = s_UnionParFromValue(self, &tp, 1);
2015 val = rb_Float(val);
2016 if (tp == kVdwCutoffParType) {
2017 up->vdwcutoff.cutoff = NUM2DBL(val);
2018 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2019 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2023 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2027 up = s_UnionParFromValue(self, &tp, 1);
2028 oldval = s_ParameterRef_GetRadius(self);
2029 oldsrc = up->bond.src;
2030 val = rb_Float(val);
2031 if (tp == kElementParType) {
2032 up->atom.radius = NUM2DBL(val);
2033 } else rb_raise(rb_eMolbyError, "invalid member radius");
2034 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2038 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2041 VALUE *valp, oldval;
2042 up = s_UnionParFromValue(self, &tp, 1);
2043 oldval = s_ParameterRef_GetColor(self);
2044 oldsrc = up->bond.src;
2045 val = rb_ary_to_ary(val);
2046 if (RARRAY_LEN(val) != 3)
2047 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2048 valp = RARRAY_PTR(val);
2049 if (tp == kElementParType) {
2050 up->atom.r = NUM2DBL(rb_Float(valp[0]));
2051 up->atom.g = NUM2DBL(rb_Float(valp[1]));
2052 up->atom.b = NUM2DBL(rb_Float(valp[2]));
2053 } else rb_raise(rb_eMolbyError, "invalid member color");
2054 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2058 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2062 up = s_UnionParFromValue(self, &tp, 1);
2063 oldval = s_ParameterRef_GetAtomicNumber(self);
2064 oldsrc = up->bond.src;
2065 val = rb_Integer(val);
2066 if (tp == kElementParType)
2067 up->atom.number = NUM2INT(val);
2068 else if (tp == kVdwParType) {
2069 up->vdw.atomicNumber = NUM2INT(val);
2070 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2071 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2072 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2076 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2080 up = s_UnionParFromValue(self, &tp, 1);
2081 oldval = s_ParameterRef_GetName(self);
2082 oldsrc = up->bond.src;
2083 if (tp == kElementParType) {
2084 strncpy(up->atom.name, StringValuePtr(val), 4);
2085 } else rb_raise(rb_eMolbyError, "invalid member name");
2086 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2090 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2094 val = rb_Float(val);
2095 oldval = s_ParameterRef_GetWeight(self);
2096 up = s_UnionParFromValue(self, &tp, 1);
2097 oldsrc = up->bond.src;
2098 if (tp == kElementParType)
2099 up->atom.weight = NUM2DBL(val);
2100 else if (tp == kVdwParType)
2101 up->vdw.weight = NUM2DBL(val);
2102 else rb_raise(rb_eMolbyError, "invalid member weight");
2103 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2107 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2111 up = s_UnionParFromValue(self, &tp, 1);
2112 oldval = s_ParameterRef_GetFullName(self);
2113 oldsrc = up->bond.src;
2114 if (tp == kElementParType) {
2115 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2116 up->atom.fullname[15] = 0;
2117 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2118 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2122 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2124 Int tp, com, oldsrc;
2126 up = s_UnionParFromValue(self, &tp, 1);
2127 oldval = s_ParameterRef_GetComment(self);
2128 oldsrc = up->bond.src;
2132 com = ParameterCommentIndex(StringValuePtr(val));
2135 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2139 /* Only false (undefined) and nil (local) can be set */
2140 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2144 up = s_UnionParFromValue(self, &tp, 1);
2145 if (val != Qfalse && val != Qnil)
2146 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2147 oldval = s_ParameterRef_GetSource(self);
2148 oldsrc = up->bond.src;
2149 if (oldsrc != 0 && oldsrc != -1)
2150 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2151 up->bond.src = (val == Qfalse ? -1 : 0);
2152 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2156 static struct s_ParameterAttrDef {
2158 VALUE *symref; /* Address of s_IndexSymbol etc. */
2159 ID id; /* Will be set within InitMolby() */
2160 VALUE (*getter)(VALUE);
2161 VALUE (*setter)(VALUE, VALUE);
2162 } s_ParameterAttrDefTable[] = {
2163 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2164 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2165 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2166 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2167 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2168 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2169 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2170 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2171 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2172 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2173 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2174 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2175 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2176 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2177 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2178 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2179 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2180 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2181 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2182 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2183 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2184 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2185 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2186 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2187 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2188 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2189 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2190 {NULL} /* Sentinel */
2194 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2198 if (TYPE(key) != T_SYMBOL) {
2199 kid = rb_intern(StringValuePtr(key));
2201 } else kid = SYM2ID(key);
2202 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2203 if (s_ParameterAttrDefTable[i].id == kid) {
2204 if (value == Qundef)
2205 return (*(s_ParameterAttrDefTable[i].getter))(self);
2206 else if (s_ParameterAttrDefTable[i].setter == NULL)
2207 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2209 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2212 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2213 return Qnil; /* not reached */
2217 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2219 return s_ParameterRef_SetAttr(self, key, Qundef);
2224 * keys(idx) -> array of valid parameter attributes
2226 * Returns an array of valid parameter attributes (as Symbols).
2229 s_ParameterRef_Keys(VALUE self)
2232 Data_Get_Struct(self, ParameterRef, pref);
2233 switch (pref->parType) {
2235 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2237 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2238 case kDihedralParType:
2239 case kImproperParType:
2240 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2242 return rb_ary_new3(11, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_AtomicNumberSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_WeightSym, s_CommentSym, s_SourceSym);
2243 case kVdwPairParType:
2244 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2245 case kVdwCutoffParType:
2246 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2247 case kElementParType:
2248 return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_CommentSym, s_SourceSym);
2250 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2252 return Qnil; /* Not reached */
2257 * to_hash(idx) -> Hash
2259 * Returns a hash containing valid parameter names and values
2262 s_ParameterRef_ToHash(VALUE self)
2264 VALUE keys = s_ParameterRef_Keys(self);
2269 retval = rb_hash_new();
2270 for (i = 0; i < RARRAY_LEN(keys); i++) {
2271 VALUE key = RARRAY_PTR(keys)[i];
2272 VALUE val = s_ParameterRef_GetAttr(self, key);
2273 rb_hash_aset(retval, key, val);
2280 * parameter.to_s(idx) -> String
2282 * Returns a string representation of the given parameter
2285 s_ParameterRef_ToString(VALUE self)
2288 char buf[1024], types[4][8];
2289 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2292 snprintf(buf, sizeof buf, "bond %4.6s %4.6s %8.2f %8.3f", AtomTypeDecodeToString(up->bond.type1, types[0]), AtomTypeDecodeToString(up->bond.type2, types[1]), up->bond.k * INTERNAL2KCAL, up->bond.r0);
2295 snprintf(buf, sizeof buf, "angle %4.6s %4.6s %4.6s %8.2f %8.3f", AtomTypeDecodeToString(up->angle.type1, types[0]), AtomTypeDecodeToString(up->angle.type2, types[1]), AtomTypeDecodeToString(up->angle.type3, types[2]), up->angle.k * INTERNAL2KCAL, up->angle.a0 * kRad2Deg);
2297 case kDihedralParType:
2298 case kImproperParType:
2299 snprintf(buf, sizeof buf, "%s %4.6s %4.6s %4.6s %4.6s", (tp == kDihedralParType ? "dihe" : "impr"), AtomTypeDecodeToString(up->torsion.type1, types[0]), AtomTypeDecodeToString(up->torsion.type2, types[1]), AtomTypeDecodeToString(up->torsion.type3, types[2]), AtomTypeDecodeToString(up->torsion.type4, types[3]));
2301 for (i = 0; i < up->torsion.mult; i++) {
2302 snprintf(buf + n, sizeof buf - n, " %8.2f %2d %8.3f", up->torsion.k[i] * INTERNAL2KCAL, up->torsion.period[i], up->torsion.phi0[i] * kRad2Deg);
2307 snprintf(buf, sizeof buf, "nonbonded %4.6s %8.4f %8.4f %8.4f %8.4f", AtomTypeDecodeToString(up->vdw.type1, types[0]), up->vdw.A * INTERNAL2KCAL / pow(up->vdw.r_eq, 12.0), up->vdw.r_eq / 1.12246204830937, up->vdw.A14 * INTERNAL2KCAL / pow(up->vdw.r_eq14, 12.0), up->vdw.r_eq14 / 1.12246204830937);
2309 case kVdwPairParType:
2310 snprintf(buf, sizeof buf, "nbfi %4.6s %4.6s %12.8e %12.8e %12.8e %12.8e", AtomTypeDecodeToString(up->vdwp.type1, types[0]), AtomTypeDecodeToString(up->vdwp.type2, types[1]), up->vdwp.A * INTERNAL2KCAL, up->vdwp.B * INTERNAL2KCAL, up->vdwp.A14 * INTERNAL2KCAL, up->vdwp.B14 * INTERNAL2KCAL);
2312 case kVdwCutoffParType:
2313 snprintf(buf, sizeof buf, "vdwcutoff %4.6s %4.6s %8.4f", AtomTypeDecodeToString(up->vdwcutoff.type1, types[0]), AtomTypeDecodeToString(up->vdwcutoff.type2, types[1]), up->vdwcutoff.cutoff);
2315 case kElementParType:
2316 snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s", up->atom.name, up->atom.number, up->atom.radius, up->atom.r, up->atom.g, up->atom.b, up->atom.weight, up->atom.fullname);
2319 return rb_str_new2(buf);
2324 * self == parameterRef -> boolean
2326 * True if the parameters point to the same parameter record.
2329 s_ParameterRef_Equal(VALUE self, VALUE val)
2332 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2333 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2334 } else return Qfalse;
2337 #pragma mark ====== Parameter Class ======
2339 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2340 * is NULL, then the global parameters are looked for. */
2342 /* Rebuild the MD parameter record if necessary: may throw an exception */
2343 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2345 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2348 Data_Get_Struct(val, Molecule, mol);
2350 rb_raise(rb_eMolbyError, "the molecule is empty");
2351 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2352 /* Do self.md_arena.prepare */
2353 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2355 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2360 s_NewParameterValueFromValue(VALUE val)
2363 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2364 Data_Get_Struct(val, Molecule, mol);
2365 s_RebuildMDParameterIfNecessary(val, Qtrue);
2366 MoleculeRetain(mol);
2367 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2370 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2375 s_MoleculeFromParameterValue(VALUE val)
2378 Data_Get_Struct(val, Molecule, mol);
2383 s_ParameterFromParameterValue(VALUE val)
2386 Data_Get_Struct(val, Molecule, mol);
2389 return gBuiltinParameters;
2392 /* Forward declarations */
2393 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2394 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2397 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2399 if (val == rb_cParameter) {
2400 return NULL; /* Parameter class method: builtin parameters */
2401 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2402 return s_MoleculeFromParameterValue(val);
2403 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2404 return s_MoleculeFromParEnumerableValue(val);
2410 * builtin -> Parameter
2412 * Returns a parameter value that points to the global (builtin) parameters.
2413 * Equivalent to Parameter::Builtin (constant).
2416 s_Parameter_Builtin(VALUE self)
2418 static ID s_builtin_id = 0;
2419 if (s_builtin_id == 0)
2420 s_builtin_id = rb_intern("Builtin");
2421 return rb_const_get(rb_cParameter, s_builtin_id);
2426 * bond(idx) -> ParameterRef
2428 * The index-th bond parameter record is returned.
2431 s_Parameter_Bond(VALUE self, VALUE ival)
2435 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2436 idx = NUM2INT(rb_Integer(ival));
2438 n = gBuiltinParameters->nbondPars;
2439 else if (mol->par != NULL)
2440 n = mol->par->nbondPars;
2442 if (idx < -n || idx >= n)
2443 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2446 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2451 * angle(idx) -> ParameterRef
2453 * The index-th angle parameter record is returned.
2456 s_Parameter_Angle(VALUE self, VALUE ival)
2460 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2461 idx = NUM2INT(rb_Integer(ival));
2463 n = gBuiltinParameters->nanglePars;
2464 else if (mol->par != NULL)
2465 n = mol->par->nanglePars;
2467 if (idx < -n || idx >= n)
2468 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2471 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2476 * dihedral(idx) -> ParameterRef
2478 * The index-th dihedral parameter record is returned.
2481 s_Parameter_Dihedral(VALUE self, VALUE ival)
2485 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2486 idx = NUM2INT(rb_Integer(ival));
2488 n = gBuiltinParameters->ndihedralPars;
2489 else if (mol->par != NULL)
2490 n = mol->par->ndihedralPars;
2492 if (idx < -n || idx >= n)
2493 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2496 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2501 * improper(idx) -> ParameterRef
2503 * The index-th improper parameter record is returned.
2506 s_Parameter_Improper(VALUE self, VALUE ival)
2510 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2511 idx = NUM2INT(rb_Integer(ival));
2513 n = gBuiltinParameters->nimproperPars;
2514 else if (mol->par != NULL)
2515 n = mol->par->nimproperPars;
2517 if (idx < -n || idx >= n)
2518 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2521 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2526 * vdw(idx) -> ParameterRef
2528 * The index-th vdw parameter record is returned.
2531 s_Parameter_Vdw(VALUE self, VALUE ival)
2535 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2536 idx = NUM2INT(rb_Integer(ival));
2538 n = gBuiltinParameters->nvdwPars;
2539 else if (mol->par != NULL)
2540 n = mol->par->nvdwPars;
2542 if (idx < -n || idx >= n)
2543 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2546 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2551 * vdw_pair(idx) -> ParameterRef
2553 * The index-th vdw pair parameter record is returned.
2556 s_Parameter_VdwPair(VALUE self, VALUE ival)
2560 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2561 idx = NUM2INT(rb_Integer(ival));
2563 n = gBuiltinParameters->nvdwpPars;
2564 else if (mol->par != NULL)
2565 n = mol->par->nvdwpPars;
2567 if (idx < -n || idx >= n)
2568 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2571 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2576 * vdw_cutoff(idx) -> ParameterRef
2578 * The index-th vdw cutoff parameter record is returned.
2581 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2585 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2586 idx = NUM2INT(rb_Integer(ival));
2588 n = gBuiltinParameters->nvdwCutoffPars;
2589 else if (mol->par != NULL)
2590 n = mol->par->nvdwCutoffPars;
2592 if (idx < -n || idx >= n)
2593 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2596 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2601 * element(idx) -> ParameterRef
2602 * element(t1) -> ParameterRef
2604 * In the first form, the index-th element parameter record is returned. In the second
2605 * form, the element parameter for t1 is looked up (the last index first). t1
2606 * is the element name string (up to 4 characters).
2607 * Unlike other Parameter methods, this is used only for the global parameter.
2610 s_Parameter_Element(VALUE self, VALUE ival)
2613 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2614 int n = gCountElementParameters;
2615 idx1 = NUM2INT(rb_Integer(ival));
2616 if (idx1 < -n || idx1 >= n)
2617 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2620 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2625 strncpy(name, StringValuePtr(ival), 4);
2627 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2628 if (strncmp(ep->name, name, 4) == 0)
2629 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2639 * Returns the number of bond parameters.
2642 s_Parameter_Nbonds(VALUE self)
2645 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2647 n = gBuiltinParameters->nbondPars;
2648 else if (mol->par != NULL)
2649 n = mol->par->nbondPars;
2656 * nangles -> Integer
2658 * Returns the number of angle parameters.
2661 s_Parameter_Nangles(VALUE self)
2664 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2666 n = gBuiltinParameters->nanglePars;
2667 else if (mol->par != NULL)
2668 n = mol->par->nanglePars;
2675 * ndihedrals -> Integer
2677 * Returns the number of dihedral parameters.
2680 s_Parameter_Ndihedrals(VALUE self)
2683 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2685 n = gBuiltinParameters->ndihedralPars;
2686 else if (mol->par != NULL)
2687 n = mol->par->ndihedralPars;
2694 * nimpropers -> Integer
2696 * Returns the number of improper parameters.
2699 s_Parameter_Nimpropers(VALUE self)
2702 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2704 n = gBuiltinParameters->nimproperPars;
2705 else if (mol->par != NULL)
2706 n = mol->par->nimproperPars;
2715 * Returns the number of vdw parameters.
2718 s_Parameter_Nvdws(VALUE self)
2721 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2723 n = gBuiltinParameters->nvdwPars;
2724 else if (mol->par != NULL)
2725 n = mol->par->nvdwPars;
2732 * nvdw_pairs -> Integer
2734 * Returns the number of vdw pair parameters.
2737 s_Parameter_NvdwPairs(VALUE self)
2740 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2742 n = gBuiltinParameters->nvdwpPars;
2743 else if (mol->par != NULL)
2744 n = mol->par->nvdwpPars;
2751 * nvdw_cutoffs -> Integer
2753 * Returns the number of vdw cutoff parameters.
2756 s_Parameter_NvdwCutoffs(VALUE self)
2759 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2761 n = gBuiltinParameters->nvdwCutoffPars;
2762 else if (mol->par != NULL)
2763 n = mol->par->nvdwCutoffPars;
2770 * nelements -> Integer
2772 * Returns the number of element parameters.
2775 s_Parameter_Nelements(VALUE self)
2777 return INT2NUM(gCountElementParameters);
2782 * bonds -> ParEnumerable
2784 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2785 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2786 * useful when all accessible parameters should be examined by use of 'each' method.
2789 s_Parameter_Bonds(VALUE self)
2791 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2792 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2797 * angles -> ParEnumerable
2799 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2800 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2801 * useful when all accessible parameters should be examined by use of 'each' method.
2804 s_Parameter_Angles(VALUE self)
2806 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2807 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2812 * dihedrals -> ParEnumerable
2814 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2815 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2816 * useful when all accessible parameters should be examined by use of 'each' method.
2819 s_Parameter_Dihedrals(VALUE self)
2821 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2822 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2827 * impropers -> ParEnumerable
2829 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2830 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2831 * useful when all accessible parameters should be examined by use of 'each' method.
2834 s_Parameter_Impropers(VALUE self)
2836 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2837 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2842 * vdws -> ParEnumerable
2844 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2845 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2846 * useful when all accessible parameters should be examined by use of 'each' method.
2849 s_Parameter_Vdws(VALUE self)
2851 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2852 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2857 * vdw_pairs -> ParEnumerable
2859 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2860 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2861 * useful when all accessible parameters should be examined by use of 'each' method.
2864 s_Parameter_VdwPairs(VALUE self)
2866 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2867 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2872 * vdw_cutoffs -> ParEnumerable
2874 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2875 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2876 * useful when all accessible parameters should be examined by use of 'each' method.
2879 s_Parameter_VdwCutoffs(VALUE self)
2881 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2882 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
2887 * elements -> ParEnumerable
2889 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
2890 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
2891 * useful when all accessible parameters should be examined by use of 'each' method.
2894 s_Parameter_Elements(VALUE self)
2896 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2897 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
2901 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
2903 VALUE atval, optval;
2906 int i, n, idx, flags, is_global;
2908 rb_scan_args(argc, argv, "1*", &atval, &optval);
2910 /* Get the atom types */
2912 case kBondParType: n = 2; break;
2913 case kAngleParType: n = 3; break;
2914 case kDihedralParType: n = 4; break;
2915 case kImproperParType: n = 4; break;
2916 case kVdwParType: n = 1; break;
2917 case kVdwPairParType: n = 2; break;
2918 default: return Qnil;
2920 s_ScanAtomTypes(atval, n, t);
2921 for (i = 0; i < n; i++) {
2922 if (t[i] < kAtomTypeMinimum) {
2923 /* Explicit atom index */
2925 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
2926 if (t[i] >= mol->natoms)
2927 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
2929 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
2933 /* Analyze options */
2935 n = RARRAY_LEN(optval);
2936 for (i = 0; i < n; i++) {
2937 VALUE oval = RARRAY_PTR(optval)[i];
2938 if (oval == ID2SYM(rb_intern("global")))
2939 flags |= kParameterLookupGlobal;
2940 else if (oval == ID2SYM(rb_intern("local")))
2941 flags |= kParameterLookupLocal;
2942 else if (oval == ID2SYM(rb_intern("missing")))
2943 flags |= kParameterLookupMissing;
2944 else if (oval == ID2SYM(rb_intern("nowildcard")))
2945 flags |= kParameterLookupNoWildcard;
2946 else if (oval == ID2SYM(rb_intern("nobasetype")))
2947 flags |= kParameterLookupNoBaseAtomType;
2948 else if (oval == ID2SYM(rb_intern("create")))
2952 flags = kParameterLookupGlobal | kParameterLookupLocal;
2957 case kBondParType: {
2960 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
2962 idx = bp - mol->par->bondPars;
2966 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
2968 idx = bp - gBuiltinParameters->bondPars;
2973 case kAngleParType: {
2976 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
2978 idx = ap - mol->par->anglePars;
2982 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
2984 idx = ap - gBuiltinParameters->anglePars;
2989 case kDihedralParType: {
2992 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
2994 idx = tp - mol->par->dihedralPars;
2998 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3000 idx = tp - gBuiltinParameters->dihedralPars;
3005 case kImproperParType: {
3008 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3010 idx = tp - mol->par->improperPars;
3014 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3016 idx = tp - gBuiltinParameters->improperPars;
3024 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3026 idx = vp - mol->par->vdwPars;
3030 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3032 idx = vp - gBuiltinParameters->vdwPars;
3037 case kVdwPairParType: {
3040 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3042 idx = vp - mol->par->vdwpPars;
3046 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3048 idx = vp - gBuiltinParameters->vdwpPars;
3057 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3060 /* Insert a new parameter record */
3062 Int count = ParameterGetCountForType(mol->par, parType);
3063 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3064 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3065 IntGroupRelease(ig);
3068 /* Set atom types */
3069 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3074 up->bond.type1 = t[0];
3075 up->bond.type2 = t[1];
3078 up->angle.type1 = t[0];
3079 up->angle.type2 = t[1];
3080 up->angle.type3 = t[2];
3082 case kDihedralParType:
3083 case kImproperParType:
3084 up->torsion.type1 = t[0];
3085 up->torsion.type2 = t[1];
3086 up->torsion.type3 = t[2];
3087 up->torsion.type4 = t[3];
3090 up->vdw.type1 = t[0];
3092 case kVdwPairParType:
3093 up->vdwp.type1 = t[0];
3094 up->vdwp.type2 = t[1];
3101 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3106 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3107 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3109 * Find the parameter record that matches the given atom types. The atom types are given
3110 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3111 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3112 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3113 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3116 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3119 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3121 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3122 parType = s_ParTypeFromValue(argv[0]);
3123 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3128 * self == parameter -> boolean
3130 * True if the parameters point to the same parameter table.
3133 s_Parameter_Equal(VALUE self, VALUE val)
3135 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3136 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3137 } else return Qfalse;
3140 #pragma mark ====== ParEnumerable Class ======
3142 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3143 and the parameter type. If the Molecule is NULL, then it refers to the
3144 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3145 the global parameters are always accessible. */
3147 typedef struct ParEnumerable {
3149 Int parType; /* Same as parType in ParameterRef */
3152 static ParEnumerable *
3153 s_ParEnumerableNew(Molecule *mol, Int parType)
3155 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3159 MoleculeRetain(mol);
3160 pen->parType = parType;
3166 s_ParEnumerableRelease(ParEnumerable *pen)
3169 if (pen->mol != NULL)
3170 MoleculeRelease(pen->mol);
3176 s_MoleculeFromParEnumerableValue(VALUE val)
3179 Data_Get_Struct(val, ParEnumerable, pen);
3184 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3186 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3188 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3189 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3194 * par_type -> String
3196 * Get the parameter type, like "bond", "angle", etc.
3199 s_ParEnumerable_ParType(VALUE self) {
3202 Data_Get_Struct(self, ParEnumerable, pen);
3204 if (tp == kElementParType)
3205 return rb_str_new2("element");
3206 tp -= kFirstParType;
3207 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3208 return rb_str_new2(s_ParameterTypeNames[tp]);
3209 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3214 * self[idx] -> ParameterRef
3216 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3217 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3218 * parent Parameter object of self.
3220 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3221 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3224 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3227 Data_Get_Struct(self, ParEnumerable, pen);
3228 switch (pen->parType) {
3229 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3230 case kBondParType: return s_Parameter_Bond(self, ival);
3231 case kAngleParType: return s_Parameter_Angle(self, ival);
3232 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3233 case kImproperParType: return s_Parameter_Improper(self, ival);
3234 case kVdwParType: return s_Parameter_Vdw(self, ival);
3235 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3236 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3237 case kElementParType: return s_Parameter_Element(self, ival);
3239 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3241 return Qnil; /* Not reached */
3248 * Returns the number of parameters included in this enumerable.
3251 s_ParEnumerable_Length(VALUE self)
3254 Data_Get_Struct(self, ParEnumerable, pen);
3255 switch (pen->parType) {
3256 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3257 case kBondParType: return s_Parameter_Nbonds(self);
3258 case kAngleParType: return s_Parameter_Nangles(self);
3259 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3260 case kImproperParType: return s_Parameter_Nimpropers(self);
3261 case kVdwParType: return s_Parameter_Nvdws(self);
3262 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3263 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3264 case kElementParType: return s_Parameter_Nelements(self);
3266 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3268 return Qnil; /* Not reached */
3275 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3278 s_ParEnumerable_Each(VALUE self)
3284 Data_Get_Struct(self, ParEnumerable, pen);
3285 if (pen->parType == kElementParType)
3286 n = gCountElementParameters;
3288 switch (pen->parType) {
3289 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3290 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3291 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3292 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3293 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3294 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3295 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3297 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3299 if (pen->mol == NULL)
3300 n = *((Int *)((char *)gBuiltinParameters + ofs));
3301 else if (pen->mol->par != NULL)
3302 n = *((Int *)((char *)(pen->mol->par) + ofs));
3305 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3306 Data_Get_Struct(aval, ParameterRef, pref);
3307 for (i = 0; i < n; i++) {
3316 * reverse_each {|pref| ...}
3318 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3321 s_ParEnumerable_ReverseEach(VALUE self)
3327 Data_Get_Struct(self, ParEnumerable, pen);
3328 if (pen->parType == kElementParType)
3329 n = gCountElementParameters;
3331 switch (pen->parType) {
3332 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3333 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3334 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3335 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3336 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3337 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3338 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3340 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3342 if (pen->mol == NULL)
3343 n = *((Int *)((char *)gBuiltinParameters + ofs));
3344 else if (pen->mol->par != NULL)
3345 n = *((Int *)((char *)(pen->mol->par) + ofs));
3348 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3349 Data_Get_Struct(aval, ParameterRef, pref);
3350 for (i = n - 1; i >= 0; i--) {
3359 * insert(idx = nil, pref = nil) -> ParameterRef
3361 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3362 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3363 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3364 * parameter is left undefined.
3365 * Throws an exception if ParEnumerable points to the global parameter.
3368 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3376 Data_Get_Struct(self, ParEnumerable, pen);
3377 if (pen->mol == NULL)
3378 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3379 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3380 rb_scan_args(argc, argv, "02", &ival, &pval);
3382 i = NUM2INT(rb_Integer(ival));
3384 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3389 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3390 if (up == NULL || type != pen->parType)
3391 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3392 ParameterCopyOneWithType(&u, up, pen->parType);
3395 memset(&u, 0, sizeof(u));
3398 ig = IntGroupNewWithPoints(n, 1, -1);
3399 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3401 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3402 MolActionCallback_registerUndo(pen->mol, act);
3403 MolActionRelease(act);
3405 IntGroupRelease(ig);
3406 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3414 * Delete the parameter(s) specified by the argument.
3417 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3422 Data_Get_Struct(self, ParEnumerable, pen);
3423 if (pen->mol == NULL)
3424 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3425 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3426 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3427 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3430 ig = IntGroupFromValue(ival);
3431 if ((i = IntGroupGetCount(ig)) == 0) {
3432 IntGroupRelease(ig);
3436 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3437 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3440 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3441 IntGroupRelease(ig);
3447 * lookup(atom_types, options, ...) -> ParameterRef
3448 * lookup(atom_type_string, options, ...) -> ParameterRef
3450 * Find the parameter record that matches the given atom types. The arguments are
3451 * the same as Parameter#lookup, except for the parameter type which is implicitly
3455 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3458 Data_Get_Struct(self, ParEnumerable, pen);
3459 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3464 * self == parEnumerable -> boolean
3466 * True if the arguments point to the same parameter table and type.
3469 s_ParEnumerable_Equal(VALUE self, VALUE val)
3471 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3472 ParEnumerable *pen1, *pen2;
3473 Data_Get_Struct(self, ParEnumerable, pen1);
3474 Data_Get_Struct(val, ParEnumerable, pen2);
3475 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3476 } else return Qfalse;
3479 #pragma mark ====== AtomRef Class ======
3481 /* Forward declaration for register undo */
3482 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3484 /* Ruby string "set_atom_attr" */
3485 static VALUE s_SetAtomAttrString;
3488 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3492 Data_Get_Struct(self, AtomRef, aref);
3493 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3494 if (idx < 0 || idx >= aref->mol->natoms)
3495 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3497 *app = aref->mol->atoms + idx;
3504 s_AtomFromValue(VALUE self)
3507 s_AtomIndexFromValue(self, &ap, NULL);
3512 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3515 s_AtomIndexFromValue(self, &ap, mpp);
3520 s_NotifyModificationForAtomRef(VALUE self)
3523 Data_Get_Struct(self, AtomRef, aref);
3524 MoleculeIncrementModifyCount(aref->mol);
3528 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3531 Data_Get_Struct(self, AtomRef, aref);
3532 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3535 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3536 MolActionCallback_registerUndo(aref->mol, act);
3537 MoleculeCallback_notifyModification(aref->mol, 0);
3538 /* Request MD rebuilt if necessary */
3539 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3540 aref->mol->needsMDRebuild = 1;
3545 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3548 aref = AtomRefNew(mol, idx);
3549 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3553 s_AtomRef_GetMolecule(VALUE self)
3556 s_AtomIndexFromValue(self, NULL, &mpp);
3557 return ValueFromMolecule(mpp);
3560 static VALUE s_AtomRef_GetIndex(VALUE self) {
3561 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3564 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3565 return INT2NUM(s_AtomFromValue(self)->segSeq);
3568 static VALUE s_AtomRef_GetSegName(VALUE self) {
3569 char *p = s_AtomFromValue(self)->segName;
3570 return rb_str_new(p, strlen_limit(p, 4));
3573 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3574 return INT2NUM(s_AtomFromValue(self)->resSeq);
3577 static VALUE s_AtomRef_GetResName(VALUE self) {
3578 char *p = s_AtomFromValue(self)->resName;
3579 return rb_str_new(p, strlen_limit(p, 4));
3582 static VALUE s_AtomRef_GetName(VALUE self) {
3583 char *p = s_AtomFromValue(self)->aname;
3584 return rb_str_new(p, strlen_limit(p, 4));
3587 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3588 int type = s_AtomFromValue(self)->type;
3589 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3590 return rb_str_new(p, strlen_limit(p, 6));
3593 static VALUE s_AtomRef_GetCharge(VALUE self) {
3594 return rb_float_new(s_AtomFromValue(self)->charge);
3597 static VALUE s_AtomRef_GetWeight(VALUE self) {
3598 return rb_float_new(s_AtomFromValue(self)->weight);
3601 static VALUE s_AtomRef_GetElement(VALUE self) {
3602 char *p = s_AtomFromValue(self)->element;
3603 return rb_str_new(p, strlen_limit(p, 4));
3606 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3607 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3610 static VALUE s_AtomRef_GetConnects(VALUE self) {
3613 Atom *ap = s_AtomFromValue(self);
3614 retval = rb_ary_new();
3615 cp = AtomConnectData(&ap->connect);
3616 for (i = 0; i < ap->connect.count; i++)
3617 rb_ary_push(retval, INT2NUM(cp[i]));
3621 static VALUE s_AtomRef_GetR(VALUE self) {
3622 return ValueFromVector(&(s_AtomFromValue(self)->r));
3625 static VALUE s_AtomRef_GetX(VALUE self) {
3626 return rb_float_new(s_AtomFromValue(self)->r.x);
3629 static VALUE s_AtomRef_GetY(VALUE self) {
3630 return rb_float_new(s_AtomFromValue(self)->r.y);
3633 static VALUE s_AtomRef_GetZ(VALUE self) {
3634 return rb_float_new(s_AtomFromValue(self)->r.z);
3637 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3641 s_AtomIndexFromValue(self, &ap, &mp);
3643 if (mp->cell != NULL)
3644 TransformVec(&r1, mp->cell->rtr, &r1);
3648 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3649 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3650 return ValueFromVector(&r1);
3653 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3654 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3657 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3658 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3661 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3662 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3665 static VALUE s_AtomRef_GetSigma(VALUE self) {
3666 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3669 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3670 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3673 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3674 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3677 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3678 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3681 static VALUE s_AtomRef_GetV(VALUE self) {
3682 return ValueFromVector(&(s_AtomFromValue(self)->v));
3685 static VALUE s_AtomRef_GetF(VALUE self) {
3686 Vector v = s_AtomFromValue(self)->f;
3687 VecScaleSelf(v, INTERNAL2KCAL);
3688 return ValueFromVector(&v);
3691 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3692 return rb_float_new(s_AtomFromValue(self)->occupancy);
3695 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3696 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3699 static VALUE s_AtomRef_GetAniso(VALUE self) {
3702 Atom *ap = s_AtomFromValue(self);
3703 if (ap->aniso == NULL)
3705 retval = rb_ary_new();
3706 for (i = 0; i < 6; i++)
3707 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3708 if (ap->aniso->has_bsig) {
3709 rb_ary_push(retval, INT2NUM(0));
3710 for (i = 0; i < 6; i++)
3711 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3716 static VALUE s_AtomRef_GetSymop(VALUE self) {
3718 Atom *ap = s_AtomFromValue(self);
3719 if (!ap->symop.alive)
3721 retval = rb_ary_new();
3722 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3723 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3724 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3725 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3726 rb_ary_push(retval, INT2NUM(ap->symbase));
3730 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3731 return INT2NUM(s_AtomFromValue(self)->intCharge);
3734 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3735 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3738 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3739 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3742 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3746 MDExclusion *exinfo;
3749 idx = s_AtomIndexFromValue(self, &ap, &mol);
3750 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3751 VALUE mval = ValueFromMolecule(mol);
3752 s_RebuildMDParameterIfNecessary(mval, Qnil);
3754 if (mol->arena->exinfo == NULL)
3756 exinfo = mol->arena->exinfo + idx;
3757 exlist = mol->arena->exlist;
3758 retval = rb_ary_new();
3759 aval = rb_ary_new();
3760 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3761 rb_ary_push(aval, INT2NUM(exlist[i]));
3762 rb_ary_push(retval, aval);
3763 aval = rb_ary_new();
3764 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3765 rb_ary_push(aval, INT2NUM(exlist[i]));
3766 rb_ary_push(retval, aval);
3767 aval = rb_ary_new();
3768 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3769 rb_ary_push(aval, INT2NUM(exlist[i]));
3770 rb_ary_push(retval, aval);
3774 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3775 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3778 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3779 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3782 static VALUE s_AtomRef_GetHidden(VALUE self) {
3783 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3786 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3789 Atom *ap = s_AtomFromValue(self);
3790 if (ap->anchor == NULL)
3792 count = ap->anchor->connect.count;
3793 retval = rb_ary_new2(count * 2);
3794 cp = AtomConnectData(&ap->anchor->connect);
3795 for (i = 0; i < count; i++) {
3796 rb_ary_store(retval, i, INT2NUM(cp[i]));
3797 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3802 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3803 char *p = s_AtomFromValue(self)->uff_type;
3804 return rb_str_new(p, strlen_limit(p, 5));
3807 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3808 rb_raise(rb_eMolbyError, "index cannot be directly set");
3812 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3813 VALUE oval = s_AtomRef_GetSegSeq(self);
3814 val = rb_Integer(val);
3815 s_AtomFromValue(self)->segSeq = NUM2INT(val);
3816 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3820 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3821 char *p = StringValuePtr(val);
3822 VALUE oval = s_AtomRef_GetSegName(self);
3823 strncpy(s_AtomFromValue(self)->segName, p, 4);
3824 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3828 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3829 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3830 return val; /* Not reached */
3833 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3834 Atom *ap = s_AtomFromValue(self);
3835 char *p = StringValuePtr(val);
3836 VALUE oval = s_AtomRef_GetName(self);
3837 if (ap->anchor != NULL && p[0] == '_')
3838 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
3839 strncpy(ap->aname, p, 4);
3840 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3844 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3846 char *p = StringValuePtr(val);
3847 VALUE oval = s_AtomRef_GetAtomType(self);
3848 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3849 if (type != 0 && type < kAtomTypeMinimum)
3850 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3851 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3852 mp->needsMDRebuild = 1;
3853 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3857 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3859 VALUE oval = s_AtomRef_GetCharge(self);
3860 val = rb_Float(val);
3861 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3862 mp->needsMDRebuild = 1;
3863 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3867 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3869 VALUE oval = s_AtomRef_GetWeight(self);
3870 val = rb_Float(val);
3871 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3872 mp->needsMDRebuild = 1;
3873 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3877 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
3880 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3881 char *p = StringValuePtr(val);
3882 VALUE oval = s_AtomRef_GetElement(self);
3883 ap->atomicNumber = ElementToInt(p);
3884 ElementToString(ap->atomicNumber, ap->element);
3885 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3887 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
3888 mp->needsMDRebuild = 1;
3892 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
3895 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3896 VALUE oval = s_AtomRef_GetAtomicNumber(self);
3897 val = rb_Integer(val);
3898 ap->atomicNumber = NUM2INT(val);
3899 ElementToString(ap->atomicNumber, ap->element);
3900 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3902 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
3903 mp->needsMDRebuild = 1;
3907 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
3908 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
3909 return val; /* Not reached */
3912 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
3915 VALUE oval = s_AtomRef_GetR(self);
3916 VectorFromValue(val, &v);
3917 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
3918 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
3919 mp->needsMDCopyCoordinates = 1;
3923 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
3926 VALUE oval = s_AtomRef_GetX(self);
3927 val = rb_Float(val);
3929 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
3930 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
3931 mp->needsMDCopyCoordinates = 1;
3935 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
3938 VALUE oval = s_AtomRef_GetY(self);
3939 val = rb_Float(val);
3941 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
3942 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
3943 mp->needsMDCopyCoordinates = 1;
3947 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
3950 VALUE oval = s_AtomRef_GetZ(self);
3951 val = rb_Float(val);
3953 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
3954 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
3955 mp->needsMDCopyCoordinates = 1;
3959 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
3963 s_AtomIndexFromValue(self, &ap, &mp);
3965 VectorFromValue(val, &v);
3966 if (mp->cell != NULL)
3967 TransformVec(&v, mp->cell->tr, &v);
3969 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3970 mp->needsMDCopyCoordinates = 1;
3974 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
3979 s_AtomIndexFromValue(self, &ap, &mp);
3981 val = rb_Float(val);
3983 if (mp->cell != NULL) {
3984 TransformVec(&v, mp->cell->rtr, &v);
3986 TransformVec(&v, mp->cell->tr, &v);
3989 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3990 mp->needsMDCopyCoordinates = 1;
3994 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
3999 s_AtomIndexFromValue(self, &ap, &mp);
4001 val = rb_Float(val);
4003 if (mp->cell != NULL) {
4004 TransformVec(&v, mp->cell->rtr, &v);
4006 TransformVec(&v, mp->cell->tr, &v);
4009 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4010 mp->needsMDCopyCoordinates = 1;
4014 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4019 s_AtomIndexFromValue(self, &ap, &mp);
4021 val = rb_Float(val);
4023 if (mp->cell != NULL) {
4024 TransformVec(&v, mp->cell->rtr, &v);
4026 TransformVec(&v, mp->cell->tr, &v);
4029 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4030 mp->needsMDCopyCoordinates = 1;
4034 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4037 VALUE oval = s_AtomRef_GetSigma(self);
4038 VectorFromValue(val, &v);
4039 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4040 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4041 mp->needsMDCopyCoordinates = 1;
4045 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4048 VALUE oval = s_AtomRef_GetSigmaX(self);
4049 val = rb_Float(val);
4051 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4052 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4053 mp->needsMDCopyCoordinates = 1;
4057 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4060 VALUE oval = s_AtomRef_GetSigmaY(self);
4061 val = rb_Float(val);
4063 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4064 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4065 mp->needsMDCopyCoordinates = 1;
4069 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4072 VALUE oval = s_AtomRef_GetSigmaZ(self);
4073 val = rb_Float(val);
4075 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4076 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4077 mp->needsMDCopyCoordinates = 1;
4081 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4085 VALUE oval = s_AtomRef_GetV(self);
4086 VectorFromValue(val, &v);
4087 s_AtomIndexFromValue(self, &ap, &mp);
4089 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4090 mp->needsMDCopyCoordinates = 1;
4094 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4097 VALUE oval = s_AtomRef_GetF(self);
4098 VectorFromValue(val, &v);
4099 VecScaleSelf(v, KCAL2INTERNAL);
4100 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4101 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4102 mp->needsMDCopyCoordinates = 1;
4106 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4107 VALUE oval = s_AtomRef_GetOccupancy(self);
4109 val = rb_Float(val);
4110 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4111 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4112 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4116 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4117 VALUE oval = s_AtomRef_GetTempFactor(self);
4118 val = rb_Float(val);
4119 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4120 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4124 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4129 VALUE oval = s_AtomRef_GetAniso(self);
4130 Data_Get_Struct(self, AtomRef, aref);
4131 val = rb_funcall(val, rb_intern("to_a"), 0);
4132 n = RARRAY_LEN(val);
4133 valp = RARRAY_PTR(val);
4134 for (i = 0; i < 6; i++) {
4136 f[i] = NUM2DBL(rb_Float(valp[i]));
4140 type = NUM2INT(rb_Integer(valp[6]));
4143 for (i = 0; i < 6; i++)
4144 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4146 for (i = 0; i < 6; i++)
4149 i = s_AtomIndexFromValue(self, NULL, NULL);
4150 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4151 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4155 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4161 VALUE oval = s_AtomRef_GetSymop(self);
4162 i = s_AtomIndexFromValue(self, &ap, &mol);
4164 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4166 val = rb_funcall(val, rb_intern("to_a"), 0);
4167 n = RARRAY_LEN(val);
4168 valp = RARRAY_PTR(val);
4169 for (i = 0; i < 5; i++) {
4171 if (valp[i] == Qnil)
4174 ival[i] = NUM2INT(rb_Integer(valp[i]));
4175 } else ival[i] = -100000;
4178 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4179 rb_raise(rb_eMolbyError, "index of symmetry (%d) is out of range (should be 0..%d)", ival[0], (mol->nsyms == 0 ? 0 : mol->nsyms - 1));
4180 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4181 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4182 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4183 return val; /* No need to change */
4184 if (ival[0] != -100000)
4185 ap->symop.sym = ival[0];
4186 if (ival[1] != -100000)
4187 ap->symop.dx = ival[1];
4188 if (ival[2] != -100000)
4189 ap->symop.dy = ival[2];
4190 if (ival[3] != -100000)
4191 ap->symop.dz = ival[3];
4192 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4193 if (ival[4] != -100000)
4194 ap->symbase = ival[4];
4195 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4196 /* The anisotropic parameters should be recalculated */
4197 VALUE oaval = s_AtomRef_GetAniso(self);
4198 MoleculeSetAnisoBySymop(mol, i);
4199 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4201 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4205 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4206 VALUE oval = s_AtomRef_GetIntCharge(self);
4207 val = rb_Integer(val);
4208 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4209 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4213 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4215 VALUE oval = s_AtomRef_GetFixForce(self);
4216 val = rb_Float(val);
4217 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4218 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4219 mp->needsMDRebuild = 1;
4223 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4226 VALUE oval = s_AtomRef_GetFixPos(self);
4227 VectorFromValue(val, &v);
4228 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4229 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4230 mp->needsMDRebuild = 1;
4234 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4235 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4236 return val; /* Not reached */
4239 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4240 VALUE oval = s_AtomRef_GetIntCharge(self);
4241 val = rb_Integer(val);
4242 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4243 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4247 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4248 VALUE oval = s_AtomRef_GetIntCharge(self);
4249 val = rb_Integer(val);
4250 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4251 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4255 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4256 Atom *ap = s_AtomFromValue(self);
4257 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4259 ap->exflags |= kAtomHiddenFlag;
4261 ap->exflags &= ~kAtomHiddenFlag;
4263 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4267 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4268 Int idx, i, j, k, n, *ip;
4275 MolAction **undoActions;
4276 memset(&ac, 0, sizeof(ac));
4277 idx = s_AtomIndexFromValue(self, &ap, &mol);
4278 oval = s_AtomRef_GetAnchorList(self);
4280 val = rb_ary_to_ary(val);
4281 n = RARRAY_LEN(val);
4284 if (ap->anchor != NULL) {
4285 AtomConnectResize(&ap->anchor->connect, 0);
4286 free(ap->anchor->coeffs);
4289 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4294 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4295 if (ap->aname[0] == '_')
4296 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4297 ip = (Int *)malloc(sizeof(Int) * n);
4299 for (i = 0; i < n; i++) {
4300 v = RARRAY_PTR(val)[i];
4301 if (rb_obj_is_kind_of(v, rb_cFloat))
4303 j = NUM2INT(rb_Integer(v));
4304 if (j < 0 || j >= mol->natoms)
4305 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4306 for (k = 0; k < i; k++) {
4308 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4314 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4315 else if (i * 2 != n)
4316 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4317 dp = (Double *)malloc(sizeof(Double) * n / 2);
4318 for (i = 0; i < n / 2; i++) {
4319 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4321 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4327 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4331 rb_raise(rb_eMolbyError, "invalid argument");
4332 if (nUndoActions > 0) {
4333 for (i = 0; i < nUndoActions; i++) {
4334 MolActionCallback_registerUndo(mol, undoActions[i]);
4335 MolActionRelease(undoActions[i]);
4339 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4343 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4344 Atom *ap = s_AtomFromValue(self);
4345 char *p = StringValuePtr(val);
4346 VALUE oval = s_AtomRef_GetUFFType(self);
4347 strncpy(ap->uff_type, p, 5);
4348 ap->uff_type[5] = 0;
4349 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4353 static struct s_AtomAttrDef {
4355 VALUE *symref; /* Address of s_IndexSymbol etc. */
4356 ID id; /* Will be set within InitMolby() */
4357 VALUE (*getter)(VALUE);
4358 VALUE (*setter)(VALUE, VALUE);
4359 } s_AtomAttrDefTable[] = {
4360 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4361 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4362 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4363 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4364 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4365 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4366 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4367 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4368 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4369 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4370 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4371 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4372 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4373 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4374 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4375 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4376 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4377 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4378 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4379 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4380 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4381 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4382 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4383 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4384 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4385 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4386 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4387 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4388 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4389 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4390 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4391 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4392 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4393 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4394 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4395 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4396 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4397 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4398 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4399 {NULL} /* Sentinel */
4403 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4407 if (TYPE(key) != T_SYMBOL) {
4408 kid = rb_intern(StringValuePtr(key));
4410 } else kid = SYM2ID(key);
4411 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4412 if (s_AtomAttrDefTable[i].id == kid) {
4413 if (value == Qundef)
4414 return (*(s_AtomAttrDefTable[i].getter))(self);
4416 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4419 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4420 return Qnil; /* not reached */
4424 s_AtomRef_GetAttr(VALUE self, VALUE key)
4426 return s_AtomRef_SetAttr(self, key, Qundef);
4431 * self == atomRef -> boolean
4433 * True if the two references point to the same atom.
4436 s_AtomRef_Equal(VALUE self, VALUE val)
4438 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4439 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4440 } else return Qfalse;
4443 #pragma mark ====== MolEnumerable Class ======
4445 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4449 * self[idx] -> AtomRef or Array of Integers
4451 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4452 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4453 * value is a String. Otherwise, the return value is an Array of Integers.
4456 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4458 MolEnumerable *mseq;
4461 Data_Get_Struct(self, MolEnumerable, mseq);
4463 if (mseq->kind == kAtomKind) {
4464 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4466 idx1 = NUM2INT(arg1);
4467 switch (mseq->kind) {
4469 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4470 if (idx2 < 0 || idx2 >= mol->nbonds)
4471 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4472 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4475 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4476 if (idx2 < 0 || idx2 >= mol->nangles)
4477 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4478 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4480 case kDihedralKind: {
4481 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4482 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4483 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4484 return rb_ary_new3(4, INT2NUM(mol->dihedrals[idx2 * 4]), INT2NUM(mol->dihedrals[idx2 * 4 + 1]), INT2NUM(mol->dihedrals[idx2 * 4 + 2]), INT2NUM(mol->dihedrals[idx2 * 4 + 3]));
4486 case kImproperKind: {
4487 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4488 if (idx2 < 0 || idx2 >= mol->nimpropers)
4489 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4490 return rb_ary_new3(4, INT2NUM(mol->impropers[idx2 * 4]), INT2NUM(mol->impropers[idx2 * 4 + 1]), INT2NUM(mol->impropers[idx2 * 4 + 2]), INT2NUM(mol->impropers[idx2 * 4 + 3]));
4492 case kResidueKind: {
4494 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4495 if (idx2 < 0 || idx2 >= mol->nresidues)
4496 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4497 p = mol->residues[idx2];
4498 return rb_str_new(p, strlen_limit(p, 4));
4508 * Returns the number of objects included in this enumerable.
4511 s_MolEnumerable_Length(VALUE self)
4513 MolEnumerable *mseq;
4514 Data_Get_Struct(self, MolEnumerable, mseq);
4515 switch (mseq->kind) {
4517 return INT2NUM(mseq->mol->natoms);
4519 return INT2NUM(mseq->mol->nbonds);
4521 return INT2NUM(mseq->mol->nangles);
4523 return INT2NUM(mseq->mol->ndihedrals);
4525 return INT2NUM(mseq->mol->nimpropers);
4527 return INT2NUM(mseq->mol->nresidues);
4536 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4537 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4538 * For the atoms, a same AtomRef object is passed (with different internal information)
4539 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4540 * for each iteration.
4543 s_MolEnumerable_Each(VALUE self)
4545 MolEnumerable *mseq;
4547 int len = NUM2INT(s_MolEnumerable_Length(self));
4548 Data_Get_Struct(self, MolEnumerable, mseq);
4549 if (mseq->kind == kAtomKind) {
4550 /* The same AtomRef object will be used during the loop */
4551 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4552 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4553 for (i = 0; i < len; i++) {
4558 /* A new ruby object will be created at each iteration (not very efficient) */
4559 for (i = 0; i < len; i++) {
4560 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4568 * self == molEnumerable -> boolean
4570 * True if the two arguments point to the same molecule and enumerable type.
4573 s_MolEnumerable_Equal(VALUE self, VALUE val)
4575 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4576 MolEnumerable *mseq1, *mseq2;
4577 Data_Get_Struct(self, MolEnumerable, mseq1);
4578 Data_Get_Struct(val, MolEnumerable, mseq2);
4579 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4580 } else return Qfalse;
4584 #pragma mark ====== Molecule Class ======
4586 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4587 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4588 char *gLoadSaveErrorMessage = NULL;
4590 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4593 MoleculeFromValue(VALUE val)
4596 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4597 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4598 Data_Get_Struct(val, Molecule, mol);
4602 static VALUE sMoleculeRetainArray = Qnil;
4604 /* The function is called from MoleculeRelease() */
4605 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4606 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4607 /* object is always returned for the same Molecule. */
4608 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4609 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4610 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4611 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4612 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4614 /* Register/unregister the exmolobj Ruby object */
4616 MoleculeReleaseExternalObj(Molecule *mol)
4618 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4619 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4620 mol->exmolobjProtected = 0;
4625 MoleculeRetainExternalObj(Molecule *mol)
4627 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4628 if (sMoleculeRetainArray == Qnil) {
4629 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4630 sMoleculeRetainArray = rb_ary_new();
4633 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4634 mol->exmolobjProtected = 1;
4638 /* Release hook function for Ruby */
4640 MoleculeReleaseHook(Molecule *mol)
4642 if (mol->exmolobj != NULL) {
4643 /* No need to remove from sMoleculeRetainArray */
4644 mol->exmolobj = NULL;
4645 mol->exmolobjProtected = 0;
4647 MoleculeRelease(mol);
4651 ValueFromMolecule(Molecule *mol)
4655 if (mol->exmolobj != NULL)
4656 return (VALUE)mol->exmolobj;
4657 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4658 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4659 return (VALUE)mol->exmolobj;
4664 s_Molecule_Alloc(VALUE klass)
4667 Molecule *mol = MoleculeNew();
4668 val = ValueFromMolecule(mol);
4669 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4674 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4678 if (FIXNUM_P(val)) {
4680 if (n >= 0 && n < mol->natoms)
4682 n = -1; /* No such atom */
4683 val = rb_inspect(val);
4685 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4687 if (n >= 0 && n < mol->natoms)
4689 p = StringValuePtr(val);
4691 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4693 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4695 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4696 return 0; /* Not reached */
4700 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4703 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4704 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4705 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4706 Data_Get_Struct(val, IntGroup, ig);
4712 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4714 if (gLoadSaveErrorMessage != NULL) {
4715 MyAppCallback_setConsoleColor(1);
4716 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4717 MyAppCallback_setConsoleColor(0);
4720 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4727 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4728 * created object does not affect the old object in any sense.
4731 s_Molecule_InitCopy(VALUE self, VALUE arg)
4733 Molecule *mp1, *mp2;
4734 Data_Get_Struct(self, Molecule, mp1);
4735 mp2 = MoleculeFromValue(arg);
4736 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4737 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4743 * loadmbsf(file) -> bool
4745 * Read a structure from a mbsf file.
4746 * Return true if successful.
4749 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4755 MoleculeClearLoadSaveErrorMessage();
4756 Data_Get_Struct(self, Molecule, mol);
4757 rb_scan_args(argc, argv, "1", &fname);
4758 fstr = FileStringValuePtr(fname);
4759 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4760 s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4766 * loadpsf(file, pdbfile = nil) -> bool
4768 * Read a structure from a psf file. molecule must be empty. The psf may be
4769 * an "extended" version, which also contains coordinates. If pdbfile
4770 * is given, then atomic coordinates are read from that file.
4771 * Return true if successful.
4774 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4776 VALUE fname, pdbname;
4777 char *fstr, *pdbstr;
4780 Data_Get_Struct(self, Molecule, mol);
4781 if (mol->natoms > 0)
4782 return Qnil; /* Must be a new molecule */
4783 MoleculeClearLoadSaveErrorMessage();
4784 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4785 fstr = FileStringValuePtr(fname);
4786 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4787 s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4789 if (!NIL_P(pdbname)) {
4790 pdbstr = strdup(FileStringValuePtr(pdbname));
4791 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4793 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4800 * loadpdb(file) -> bool
4802 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4803 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4804 * Return true if successful.
4807 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4813 Data_Get_Struct(self, Molecule, mol);
4814 rb_scan_args(argc, argv, "1", &fname);
4815 MoleculeClearLoadSaveErrorMessage();
4816 fstr = FileStringValuePtr(fname);
4817 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4818 s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
4824 * loaddcd(file) -> bool
4826 * Read coordinates from a dcd file. The molecule should not empty.
4827 * Return true if successful.
4830 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4836 Data_Get_Struct(self, Molecule, mol);
4837 rb_scan_args(argc, argv, "1", &fname);
4838 MoleculeClearLoadSaveErrorMessage();
4839 fstr = FileStringValuePtr(fname);
4840 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
4841 s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
4847 * loadtep(file) -> bool
4849 * Read coordinates from an ortep .tep file.
4850 * Return true if successful.
4853 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4859 Data_Get_Struct(self, Molecule, mol);
4860 rb_scan_args(argc, argv, "1", &fname);
4861 MoleculeClearLoadSaveErrorMessage();
4862 fstr = FileStringValuePtr(fname);
4863 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
4864 s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
4870 * loadres(file) -> bool
4872 * Read coordinates from a shelx .res file.
4873 * Return true if successful.
4876 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
4882 Data_Get_Struct(self, Molecule, mol);
4883 rb_scan_args(argc, argv, "1", &fname);
4884 MoleculeClearLoadSaveErrorMessage();
4885 fstr = FileStringValuePtr(fname);
4886 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
4887 s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
4893 * loadfchk(file) -> bool
4895 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
4896 * Return true if successful.
4899 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
4905 Data_Get_Struct(self, Molecule, mol);
4906 rb_scan_args(argc, argv, "1", &fname);
4907 MoleculeClearLoadSaveErrorMessage();
4908 fstr = FileStringValuePtr(fname);
4909 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
4910 s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
4916 * loaddat(file) -> bool
4918 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
4919 * Return true if successful.
4922 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
4928 Data_Get_Struct(self, Molecule, mol);
4929 rb_scan_args(argc, argv, "1", &fname);
4930 MoleculeClearLoadSaveErrorMessage();
4931 fstr = FileStringValuePtr(fname);
4932 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
4933 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
4934 MyAppCallback_hideProgressPanel();
4935 s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
4941 * savembsf(file) -> bool
4943 * Write structure as a mbsf file. Returns true if successful.
4946 s_Molecule_Savembsf(VALUE self, VALUE fname)
4951 Data_Get_Struct(self, Molecule, mol);
4952 MoleculeClearLoadSaveErrorMessage();
4953 fstr = FileStringValuePtr(fname);
4954 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4955 s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
4961 * savepsf(file) -> bool
4963 * Write structure as a psf file. Returns true if successful.
4966 s_Molecule_Savepsf(VALUE self, VALUE fname)
4971 Data_Get_Struct(self, Molecule, mol);
4972 MoleculeClearLoadSaveErrorMessage();
4973 fstr = FileStringValuePtr(fname);
4974 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4975 s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
4981 * savepdb(file) -> bool
4983 * Write coordinates as a pdb file. Returns true if successful.
4986 s_Molecule_Savepdb(VALUE self, VALUE fname)
4991 Data_Get_Struct(self, Molecule, mol);
4992 MoleculeClearLoadSaveErrorMessage();
4993 fstr = FileStringValuePtr(fname);
4994 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4995 s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
5001 * savedcd(file) -> bool
5003 * Write coordinates as a dcd file. Returns true if successful.
5006 s_Molecule_Savedcd(VALUE self, VALUE fname)
5011 Data_Get_Struct(self, Molecule, mol);
5012 MoleculeClearLoadSaveErrorMessage();
5013 fstr = FileStringValuePtr(fname);
5014 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5015 s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
5021 * savetep(file) -> bool
5023 * Write coordinates as an ORTEP file. Returns true if successful.
5027 s_Molecule_Savetep(VALUE self, VALUE fname)
5032 Data_Get_Struct(self, Molecule, mol);
5033 fstr = FileStringValuePtr(fname);
5034 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
5035 rb_raise(rb_eMolbyError, errbuf);
5040 /* load([ftype, ] fname, ...) */
5042 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5045 char *argstr, *methname, *p, *type = "";
5048 const char *ls = (loadFlag ? "load" : "save");
5049 int lslen = strlen(ls);
5054 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5055 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5056 if (argstr[0] == ':') {
5057 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5058 methname = ALLOC_N(char, lslen + strlen(argstr));
5059 strcpy(methname, ls);
5060 strcat(methname, argstr + 1);
5062 for (i = lslen; methname[i] != 0; i++)
5063 methname[i] = tolower(methname[i]);
5064 mid = rb_intern(methname);
5068 rval = rb_funcall2(self, mid, argc, argv);
5074 /* Guess file type from extension */
5075 p = strrchr(argstr, '.');
5079 for (methname = p; *methname != 0; methname++) {
5080 if (!isalpha(*methname))
5083 if (*methname == 0) {
5084 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5085 if (methname == NULL)
5086 rb_raise(rb_eMolbyError, "Low memory");
5087 strcpy(methname, ls);
5088 strcat(methname, p);
5089 for (i = lslen; methname[i] != 0; i++)
5090 methname[i] = tolower(methname[i]);
5091 mid = rb_intern(methname);
5094 if (rb_respond_to(self, mid)) {
5095 /* Load: try to call the load procedure only if it is available */
5096 rval = rb_funcall2(self, mid, argc, argv);
5101 /* Save: call the save procedure, and if not found then call 'method_missing' */
5102 rval = rb_funcall2(self, mid, argc, argv);
5109 rval = rb_str_to_str(argv[0]);
5110 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5111 s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
5112 return Qnil; /* Does not reach here */
5116 /* Register the path */
5119 Data_Get_Struct(self, Molecule, mol);
5120 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5122 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5123 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5124 if (ap->occupancy != 0.0)
5127 if (i == mol->natoms) {
5128 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5129 ap->occupancy = 1.0;
5138 * molload(file, *args) -> bool
5140 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5141 * file type given by the extension). If this method fails, then all defined (public)
5142 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5145 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5147 return s_Molecule_LoadSave(argc, argv, self, 1);
5152 * molsave(file, *args) -> bool
5154 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5155 * (XXX is the file type given by the extension).
5158 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5160 return s_Molecule_LoadSave(argc, argv, self, 0);
5167 * Returns the display name of the molecule. If the molecule has no associated
5168 * document, then returns nil.
5171 s_Molecule_Name(VALUE self)
5175 Data_Get_Struct(self, Molecule, mol);
5176 MoleculeCallback_displayName(mol, buf, sizeof buf);
5180 return rb_str_new2(buf);
5185 * set_name(string) -> self
5187 * Set the name of an untitled molecule. If the molecule is not associated with window
5188 * or it already has an associated file, then exception is thrown.
5191 s_Molecule_SetName(VALUE self, VALUE nval)
5194 Data_Get_Struct(self, Molecule, mol);
5195 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5196 rb_raise(rb_eMolbyError, "Cannot change the window title");
5205 * Returns the full path name of the molecule, if it is associated with a file.
5206 * If the molecule has no associated file, then returns nil.
5209 s_Molecule_Path(VALUE self)
5213 Data_Get_Struct(self, Molecule, mol);
5214 MoleculeCallback_pathName(mol, buf, sizeof buf);
5218 return Ruby_NewFileStringValue(buf);
5225 * Returns the full path name of the directory in which the file associated with the
5226 * molecule is located. If the molecule has no associated file, then returns nil.
5229 s_Molecule_Dir(VALUE self)
5233 Data_Get_Struct(self, Molecule, mol);
5234 MoleculeCallback_pathName(mol, buf, sizeof buf);
5236 translate_char(buf, '\\', '/');
5241 p = strrchr(buf, '/');
5244 return rb_str_new2(buf);
5252 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5253 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5254 * the Molecule structure) is returned.
5257 s_Molecule_Inspect(VALUE self)
5261 Data_Get_Struct(self, Molecule, mol);
5262 MoleculeCallback_displayName(mol, buf, sizeof buf);
5264 /* No associated document */
5265 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5266 return rb_str_new2(buf);
5268 /* Check whether the document name is duplicate */
5272 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5273 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5274 if (strcmp(buf, buf2) == 0) {
5281 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5283 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5285 return rb_str_new2(buf2);
5292 * open(file) -> Molecule
5294 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5297 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5303 rb_scan_args(argc, argv, "01", &fname);
5307 p = FileStringValuePtr(fname);
5308 iflag = Ruby_SetInterruptFlag(Qfalse);
5309 mp = MoleculeCallback_openNewMolecule(p);
5310 Ruby_SetInterruptFlag(iflag);
5313 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5315 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5317 return ValueFromMolecule(mp);
5323 * new(file, *args) -> Molecule
5325 * Create a new molecule and call "load" method with the same arguments.
5328 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5331 return s_Molecule_Load(argc, argv, self);
5332 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5336 s_Molecule_MolEnumerable(VALUE self, int kind)
5339 MolEnumerable *mseq;
5340 Data_Get_Struct(self, Molecule, mol);
5341 mseq = MolEnumerableNew(mol, kind);
5342 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5347 * atoms -> MolEnumerable
5349 * Returns a MolEnumerable object representing the array of atoms.
5352 s_Molecule_Atoms(VALUE self)
5354 return s_Molecule_MolEnumerable(self, kAtomKind);
5359 * bonds -> MolEnumerable
5361 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5362 * by an array of two atom indices.
5365 s_Molecule_Bonds(VALUE self)
5367 return s_Molecule_MolEnumerable(self, kBondKind);
5372 * angles -> MolEnumerable
5374 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5375 * by an array of three atom indices.
5378 s_Molecule_Angles(VALUE self)
5380 return s_Molecule_MolEnumerable(self, kAngleKind);
5385 * dihedrals -> MolEnumerable
5387 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5388 * by an array of four atom indices.
5391 s_Molecule_Dihedrals(VALUE self)
5393 return s_Molecule_MolEnumerable(self, kDihedralKind);
5398 * impropers -> MolEnumerable
5400 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5401 * by an array of four atom indices.
5404 s_Molecule_Impropers(VALUE self)
5406 return s_Molecule_MolEnumerable(self, kImproperKind);
5411 * residues -> MolEnumerable
5413 * Returns a MolEnumerable object representing the array of residue names.
5416 s_Molecule_Residues(VALUE self)
5418 return s_Molecule_MolEnumerable(self, kResidueKind);
5425 * Returns the number of atoms.
5428 s_Molecule_Natoms(VALUE self)
5431 Data_Get_Struct(self, Molecule, mol);
5432 return INT2NUM(mol->natoms);
5439 * Returns the number of bonds.
5442 s_Molecule_Nbonds(VALUE self)
5445 Data_Get_Struct(self, Molecule, mol);
5446 return INT2NUM(mol->nbonds);
5451 * nangles -> Integer
5453 * Returns the number of angles.
5456 s_Molecule_Nangles(VALUE self)
5459 Data_Get_Struct(self, Molecule, mol);
5460 return INT2NUM(mol->nangles);
5465 * ndihedrals -> Integer
5467 * Returns the number of dihedrals.
5470 s_Molecule_Ndihedrals(VALUE self)
5473 Data_Get_Struct(self, Molecule, mol);
5474 return INT2NUM(mol->ndihedrals);
5479 * nimpropers -> Integer
5481 * Returns the number of impropers.
5484 s_Molecule_Nimpropers(VALUE self)
5487 Data_Get_Struct(self, Molecule, mol);
5488 return INT2NUM(mol->nimpropers);
5493 * nresidues -> Integer
5495 * Returns the number of residues.
5498 s_Molecule_Nresidues(VALUE self)
5501 Data_Get_Struct(self, Molecule, mol);
5502 return INT2NUM(mol->nresidues);
5506 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5508 rb_raise(rb_eMolbyError, "Molecule#bond_par, angle_par, dihedral_par, improper_par, vdw_par are now obsolete. You can use MDArena#bond_par, angle_par, dihedral_par, improper_par, vdw_par instead, and probably these are what you really want.");
5513 * bond_par(idx) -> ParameterRef
5515 * Returns the MD parameter for the idx-th bond.
5519 s_Molecule_BondPar(VALUE self, VALUE val)
5526 Data_Get_Struct(self, Molecule, mol);
5527 ival = NUM2INT(rb_Integer(val));
5528 if (ival < -mol->nbonds || ival >= mol->nbonds)
5529 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5531 ival += mol->nbonds;
5532 s_RebuildMDParameterIfNecessary(self, Qtrue);
5533 i1 = mol->bonds[ival * 2];
5534 i2 = mol->bonds[ival * 2 + 1];
5535 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5536 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5537 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5540 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5546 * angle_par(idx) -> ParameterRef
5548 * Returns the MD parameter for the idx-th angle.
5552 s_Molecule_AnglePar(VALUE self, VALUE val)
5559 Data_Get_Struct(self, Molecule, mol);
5560 ival = NUM2INT(rb_Integer(val));
5561 if (ival < -mol->nangles || ival >= mol->nangles)
5562 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5564 ival += mol->nangles;
5565 s_RebuildMDParameterIfNecessary(self, Qtrue);
5566 i1 = mol->angles[ival * 3];
5567 i2 = mol->angles[ival * 3 + 1];
5568 i3 = mol->angles[ival * 3 + 2];
5569 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5570 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5571 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5572 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5575 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5580 * dihedral_par(idx) -> ParameterRef
5582 * Returns the MD parameter for the idx-th dihedral.
5586 s_Molecule_DihedralPar(VALUE self, VALUE val)
5591 UInt t1, t2, t3, t4;
5593 Data_Get_Struct(self, Molecule, mol);
5594 ival = NUM2INT(rb_Integer(val));
5595 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5596 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5598 ival += mol->ndihedrals;
5599 s_RebuildMDParameterIfNecessary(self, Qtrue);
5600 i1 = mol->dihedrals[ival * 4];
5601 i2 = mol->dihedrals[ival * 4 + 1];
5602 i3 = mol->dihedrals[ival * 4 + 2];
5603 i4 = mol->dihedrals[ival * 4 + 3];
5604 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5605 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5606 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5607 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5608 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5611 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5616 * improper_par(idx) -> ParameterRef
5618 * Returns the MD parameter for the idx-th improper.
5622 s_Molecule_ImproperPar(VALUE self, VALUE val)
5627 UInt t1, t2, t3, t4;
5629 Data_Get_Struct(self, Molecule, mol);
5630 ival = NUM2INT(rb_Integer(val));
5631 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5632 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5634 ival += mol->nimpropers;
5635 s_RebuildMDParameterIfNecessary(self, Qtrue);
5636 i1 = mol->impropers[ival * 4];
5637 i2 = mol->impropers[ival * 4 + 1];
5638 i3 = mol->impropers[ival * 4 + 2];
5639 i4 = mol->impropers[ival * 4 + 3];
5640 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5641 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5642 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5643 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5644 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5647 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5653 * start_step -> Integer
5655 * Returns the start step (defined by dcd format).
5658 s_Molecule_StartStep(VALUE self)
5661 Data_Get_Struct(self, Molecule, mol);
5662 return INT2NUM(mol->startStep);
5667 * start_step = Integer
5669 * Set the start step (defined by dcd format).
5672 s_Molecule_SetStartStep(VALUE self, VALUE val)
5675 Data_Get_Struct(self, Molecule, mol);
5676 mol->startStep = NUM2INT(rb_Integer(val));
5682 * steps_per_frame -> Integer
5684 * Returns the number of steps between frames (defined by dcd format).
5687 s_Molecule_StepsPerFrame(VALUE self)
5690 Data_Get_Struct(self, Molecule, mol);
5691 return INT2NUM(mol->stepsPerFrame);
5696 * steps_per_frame = Integer
5698 * Set the number of steps between frames (defined by dcd format).
5701 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5704 Data_Get_Struct(self, Molecule, mol);
5705 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5711 * ps_per_step -> Float
5713 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5716 s_Molecule_PsPerStep(VALUE self)
5719 Data_Get_Struct(self, Molecule, mol);
5720 return rb_float_new(mol->psPerStep);
5725 * ps_per_step = Float
5727 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5730 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5733 Data_Get_Struct(self, Molecule, mol);
5734 mol->psPerStep = NUM2DBL(rb_Float(val));
5740 * find_angles -> Integer
5742 * Find the angles from the bonds. Returns the number of angles newly created.
5746 s_Molecule_FindAngles(VALUE self)
5752 Data_Get_Struct(self, Molecule, mol);
5753 if (mol == NULL || mol->natoms == 0)
5757 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5758 nc = ap->connect.count;
5760 for (i = 0; i < nc; i++) {
5761 n[0] = ap->connects[i];
5762 for (j = i + 1; j < nc; j++) {
5763 n[2] = ap->connects[j];
5764 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5765 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5770 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5773 return INT2NUM(nip);
5778 * find_dihedrals -> Integer
5780 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5784 s_Molecule_FindDihedrals(VALUE self)
5788 int n1, i, j, k, nc1, nc2;
5790 Data_Get_Struct(self, Molecule, mol);
5791 if (mol == NULL || mol->natoms == 0)
5795 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5796 nc1 = ap1->connect.count;
5798 for (i = 0; i < nc1; i++) {
5799 n[2] = ap1->connects[i];
5802 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5803 nc2 = ap2->connect.count;
5804 for (j = 0; j < nc1; j++) {
5805 n[0] = ap1->connects[j];
5808 for (k = 0; k < nc2; k++) {
5809 n[3] = ap2->connects[k];
5810 if (n[3] == n1 || n[3] == n[0])
5812 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5813 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5819 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5822 return INT2NUM(nip);
5828 * nresidues = Integer
5830 * Change the number of residues.
5833 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5836 int ival = NUM2INT(val);
5837 Data_Get_Struct(self, Molecule, mol);
5838 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5839 if (ival != mol->nresidues)
5840 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5846 * max_residue_number(atom_group = nil) -> Integer
5848 * Returns the maximum residue number actually used. If an atom group is given, only
5849 * these atoms are examined. If no atom is present, nil is returned.
5852 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5858 Data_Get_Struct(self, Molecule, mol);
5859 rb_scan_args(argc, argv, "01", &gval);
5860 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5861 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5862 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5867 * min_residue_number(atom_group = nil) -> Integer
5869 * Returns the minimum residue number actually used. If an atom group is given, only
5870 * these atoms are examined. If no atom is present, nil is returned.
5873 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5879 Data_Get_Struct(self, Molecule, mol);
5880 rb_scan_args(argc, argv, "01", &gval);
5881 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5882 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5883 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5888 * each_atom(atom_group = nil) {|aref| ...}
5890 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5891 * group is given, only these atoms are processed.
5892 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5893 * is self (a Molecule object).
5896 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5904 Data_Get_Struct(self, Molecule, mol);
5905 rb_scan_args(argc, argv, "01", &gval);
5906 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5907 arval = ValueFromMoleculeAndIndex(mol, 0);
5908 Data_Get_Struct(arval, AtomRef, aref);
5909 for (i = 0; i < mol->natoms; i++) {
5911 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5915 IntGroupRelease(ig);
5921 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5923 * Returns the unit cell parameters. If cell is not set, returns nil.
5926 s_Molecule_Cell(VALUE self)
5931 Data_Get_Struct(self, Molecule, mol);
5932 if (mol->cell == NULL)
5934 val = rb_ary_new2(6);
5935 for (i = 0; i < 6; i++)
5936 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
5937 if (mol->cell->has_sigma) {
5938 for (i = 0; i < 6; i++) {
5939 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
5947 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5948 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
5950 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
5951 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
5952 This operation is undoable.
5953 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
5956 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
5960 int i, convert_coord, n;
5962 Data_Get_Struct(self, Molecule, mol);
5963 rb_scan_args(argc, argv, "11", &val, &cval);
5968 val = rb_ary_to_ary(val);
5969 len = RARRAY_LEN(val);
5972 } else if (len >= 6) {
5974 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
5975 for (i = 0; i < n; i++)
5976 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
5978 convert_coord = (RTEST(cval) ? 1 : 0);
5979 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
5985 * box -> [avec, bvec, cvec, origin, flags]
5987 * Get the unit cell information in the form of a periodic bounding box.
5988 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
5989 * Integers which define whether the system is periodic along the axis.
5990 * If no unit cell is defined, nil is returned.
5993 s_Molecule_Box(VALUE self)
5997 Data_Get_Struct(self, Molecule, mol);
5998 if (mol == NULL || mol->cell == NULL)
6000 v[0] = ValueFromVector(&(mol->cell->axes[0]));
6001 v[1] = ValueFromVector(&(mol->cell->axes[1]));
6002 v[2] = ValueFromVector(&(mol->cell->axes[2]));
6003 v[3] = ValueFromVector(&(mol->cell->origin));
6004 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
6005 val = rb_ary_new4(5, v);
6011 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
6012 * set_box(d, origin = [0, 0, 0])
6015 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
6016 If it is a number, the x/y/z axis vector is multiplied with the given number and used
6018 Flags, if present, is a 3-member array of Integers defining whether the system is
6019 periodic along the axis.
6020 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
6021 In the second form, an isotropic box with cell-length d is set.
6022 In the third form, the existing box is cleared.
6023 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
6026 s_Molecule_SetBox(VALUE self, VALUE aval)
6030 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
6032 Vector origin = {0, 0, 0};
6035 int i, convertCoordinates = 0;
6036 Data_Get_Struct(self, Molecule, mol);
6038 MolActionCreateAndPerform(mol, gMolActionClearBox);
6041 aval = rb_ary_to_ary(aval);
6042 for (i = 0; i < 6; i++) {
6043 if (i < RARRAY_LEN(aval))
6044 v[i] = (RARRAY_PTR(aval))[i];
6048 MolActionCreateAndPerform(mol, gMolActionClearBox);
6051 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
6052 d = NUM2DBL(rb_Float(v[0]));
6053 for (i = 0; i < 3; i++)
6054 VecScale(vv[i], ax[i], d);
6056 VectorFromValue(v[1], &origin);
6057 flags[0] = flags[1] = flags[2] = 1;
6059 for (i = 0; i < 3; i++) {
6062 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
6063 d = NUM2DBL(rb_Float(v[i]));
6064 VecScale(vv[i], ax[i], d);
6066 VectorFromValue(v[i], &vv[i]);
6068 flags[i] = (VecLength2(vv[i]) > 0.0);
6071 VectorFromValue(v[3], &origin);
6073 for (i = 0; i < 3; i++) {
6074 VALUE val = Ruby_ObjectAtIndex(v[4], i);
6075 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
6079 convertCoordinates = 1;
6081 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
6087 * cell_periodicity -> [n1, n2, n3]
6089 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
6093 s_Molecule_CellPeriodicity(VALUE self)
6096 Data_Get_Struct(self, Molecule, mol);
6097 if (mol->cell == NULL)
6099 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
6104 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
6105 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
6107 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
6108 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
6109 * If cell is not defined, exception is raised.
6110 * This operation is undoable.
6113 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
6117 Data_Get_Struct(self, Molecule, mol);
6118 if (mol->cell == NULL)
6119 rb_raise(rb_eMolbyError, "periodic cell is not defined");
6122 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
6123 flag = NUM2INT(rb_Integer(arg));
6127 arg = rb_ary_to_ary(arg);
6129 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
6130 arg0 = RARRAY_PTR(arg)[i];
6131 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
6132 flag |= (1 << (2 - i));
6135 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
6141 * cell_flexibility -> bool
6143 * Returns the unit cell is flexible or not
6146 s_Molecule_CellFlexibility(VALUE self)
6148 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
6151 Data_Get_Struct(self, Molecule, mol);
6152 if (mol->cell == NULL)
6154 if (mol->useFlexibleCell)
6156 else return Qfalse; */
6161 * self.cell_flexibility = bool
6162 * set_cell_flexibility(bool)
6164 * Change the unit cell is flexible or not
6167 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6169 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6172 Data_Get_Struct(self, Molecule, mol);
6173 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6179 * cell_transform -> Transform
6181 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
6182 * If cell is not defined, nil is returned.
6185 s_Molecule_CellTransform(VALUE self)
6188 Data_Get_Struct(self, Molecule, mol);
6189 if (mol == NULL || mol->cell == NULL)
6191 return ValueFromTransform(&(mol->cell->tr));
6196 * symmetry -> Array of Transforms
6197 * symmetries -> Array of Transforms
6199 * Get the currently defined symmetry operations. If no symmetry operation is defined,
6200 * returns an empty array.
6203 s_Molecule_Symmetry(VALUE self)
6208 Data_Get_Struct(self, Molecule, mol);
6209 if (mol->nsyms <= 0)
6210 return rb_ary_new();
6211 val = rb_ary_new2(mol->nsyms);
6212 for (i = 0; i < mol->nsyms; i++) {
6213 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6220 * nsymmetries -> Integer
6222 * Get the number of currently defined symmetry operations.
6225 s_Molecule_Nsymmetries(VALUE self)
6228 Data_Get_Struct(self, Molecule, mol);
6229 return INT2NUM(mol->nsyms);
6234 * add_symmetry(Transform) -> Integer
6236 * Add a new symmetry operation. If no symmetry operation is defined and the
6237 * given argument is not an identity transform, then also add an identity
6238 * transform at the index 0.
6239 * Returns the total number of symmetries after operation.
6242 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6246 Data_Get_Struct(self, Molecule, mol);
6247 TransformFromValue(trans, &tr);
6248 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6249 return INT2NUM(mol->nsyms);
6254 * remove_symmetry(count = nil) -> Integer
6255 * remove_symmetries(count = nil) -> Integer
6257 * Remove the specified number of symmetry operations. The last added ones are removed
6258 * first. If count is nil, then all symmetry operations are removed. Returns the
6259 * number of leftover symmetries.
6262 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6267 Data_Get_Struct(self, Molecule, mol);
6268 rb_scan_args(argc, argv, "01", &cval);
6272 n = NUM2INT(rb_Integer(cval));
6273 if (n < 0 || n > mol->nsyms)
6274 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6275 if (n == mol->nsyms)
6278 for (i = 0; i < n; i++)
6279 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6280 return INT2NUM(mol->nsyms);
6284 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6286 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6287 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6288 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6289 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6296 * atom_group {|aref| ...}
6297 * atom_group(arg1, arg2, ...)
6298 * atom_group(arg1, arg2, ...) {|aref| ...}
6300 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6301 * If arguments are given, then the atoms reprensented by the arguments are added to the
6302 * group. For a conversion of a string to an atom index, see the description
6303 * of Molecule#atom_index.
6304 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6305 * representing each atom, and the atoms are removed from the result if the block returns false.
6309 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6311 IntGroup *ig1, *ig2;
6313 Int i, startPt, interval;
6314 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6315 Data_Get_Struct(retval, IntGroup, ig1);
6316 Data_Get_Struct(self, Molecule, mol);
6318 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6321 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6322 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6323 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6324 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6325 ig2 = IntGroupFromValue(*argv);
6326 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6327 interval = IntGroupGetInterval(ig2, i);
6328 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6330 IntGroupRelease(ig2);
6331 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6333 values[0] = (VALUE)mol;
6334 values[1] = (VALUE)ig1;
6335 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6337 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6342 if (rb_block_given_p()) {
6343 /* Evaluate the given block with an AtomRef as the argument, and delete
6344 the index if the block returns false */
6345 AtomRef *aref = AtomRefNew(mol, 0);
6346 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6347 ig2 = IntGroupNew();
6348 IntGroupCopy(ig2, ig1);
6349 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6351 if (startPt >= mol->natoms)
6353 aref->idx = startPt;
6354 resval = rb_yield(arval);
6356 IntGroupRemove(ig1, startPt, 1);
6358 IntGroupRelease(ig2);
6361 /* Remove points that are out of bounds */
6362 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6369 * atom_index(val) -> Integer
6371 * Returns the atom index represented by val. val can be either a non-negative integer
6372 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6373 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
6374 * where resname, resid, name are the residue name, residue id, and atom name respectively.
6375 * If val is a string and multiple atoms match the description, the atom with the lowest index
6379 s_Molecule_AtomIndex(VALUE self, VALUE val)
6382 Data_Get_Struct(self, Molecule, mol);
6383 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6388 * extract(group, dummy_flag = nil) -> Molecule
6390 * Extract the atoms given by group and return as a new molecule object.
6391 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6392 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6393 * names beginning with an underscore) and included in the new molecule object.
6396 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6398 Molecule *mol1, *mol2;
6400 VALUE group, dummy_flag, retval;
6401 Data_Get_Struct(self, Molecule, mol1);
6402 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6403 ig = s_Molecule_AtomGroupFromValue(self, group);
6404 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6407 retval = ValueFromMolecule(mol2);
6409 IntGroupRelease(ig);
6415 * add(molecule2) -> self
6417 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6419 This operation is undoable.
6422 s_Molecule_Add(VALUE self, VALUE val)
6424 Molecule *mol1, *mol2;
6425 Data_Get_Struct(self, Molecule, mol1);
6426 mol2 = MoleculeFromValue(val);
6427 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6433 * remove(group) -> Molecule
6435 * The atoms designated by the given group are removed from the molecule.
6436 * This operation is undoable.
6439 s_Molecule_Remove(VALUE self, VALUE group)
6444 IntGroupIterator iter;
6446 Data_Get_Struct(self, Molecule, mol1);
6447 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6448 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6449 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6450 Data_Get_Struct(group, IntGroup, ig);
6452 /* Remove the bonds between the two fragments */
6453 /* (This is necessary for undo to work correctly) */
6454 IntGroupIteratorInit(ig, &iter);
6456 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6457 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6459 cp = AtomConnectData(&ap->connect);
6460 for (j = 0; j < ap->connect.count; j++) {
6462 if (!IntGroupLookup(ig, n, NULL)) {
6463 /* bond i-n, i is in ig and n is not */
6464 int k = MoleculeLookupBond(mol1, i, n);
6468 IntGroupAdd(bg, k, 1);
6473 IntGroupIteratorRelease(&iter);
6476 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6477 IntGroupRelease(bg);
6480 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6487 * create_atom(name, pos = -1) -> AtomRef
6489 * Create a new atom with the specified name (may contain residue
6490 * information) and position (if position is out of range, the atom is appended at
6491 * the end). Returns the reference to the new atom.
6492 * This operation is undoable.
6495 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6502 char *p, resName[6], atomName[6];
6504 Data_Get_Struct(self, Molecule, mol);
6505 rb_scan_args(argc, argv, "02", &name, &ival);
6507 pos = NUM2INT(rb_Integer(ival));
6510 p = StringValuePtr(name);
6512 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6513 if (atomName[0] == 0)
6514 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6517 if (p == NULL || p[0] == 0) {
6518 memset(atomName, 0, 4);
6521 memset(&arec, 0, sizeof(arec));
6522 strncpy(arec.aname, atomName, 4);
6524 strncpy(arec.resName, resName, 4);
6525 arec.resSeq = resSeq;
6527 arec.occupancy = 1.0;
6528 // i = MoleculeCreateAnAtom(mol, &arec);
6529 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6531 aref = AtomRefNew(mol, pos);
6532 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6537 * duplicate_atom(atomref, pos = -1) -> AtomRef
6539 * Create a new atom with the same attributes (but no bonding information)
6540 * with the specified atom. Returns the reference to the new atom.
6541 * This operation is undoable.
6544 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6550 VALUE retval, aval, ival;
6552 Data_Get_Struct(self, Molecule, mol);
6553 rb_scan_args(argc, argv, "11", &aval, &ival);
6554 if (FIXNUM_P(aval)) {
6555 int idx = NUM2INT(aval);
6556 if (idx < 0 || idx >= mol->natoms)
6557 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6558 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6560 apsrc = s_AtomFromValue(aval);
6563 rb_raise(rb_eMolbyError, "bad atom specification");
6565 pos = NUM2INT(rb_Integer(ival));
6567 AtomDuplicate(&arec, apsrc);
6568 arec.connect.count = 0;
6569 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6572 aref = AtomRefNew(mol, pos);
6573 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6581 * create_bond(n1, n2, ...) -> Integer
6583 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6584 * do nothing for that pair. Returns the number of bonds actually created.
6585 * This operation is undoable.
6588 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6591 Int i, j, k, *ip, old_nbonds;
6593 rb_raise(rb_eMolbyError, "missing arguments");
6595 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6596 Data_Get_Struct(self, Molecule, mol);
6597 ip = ALLOC_N(Int, argc + 1);
6598 for (i = j = 0; i < argc; i++, j++) {
6599 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6601 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6602 j -= 2; /* This bond is already present: skip it */
6604 for (k = 0; k < j - 1; k += 2) {
6605 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6606 j -= 2; /* The same entry is already in the argument */
6613 old_nbonds = mol->nbonds;
6615 ip[j] = kInvalidIndex;
6616 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6620 rb_raise(rb_eMolbyError, "atom index out of range");
6622 rb_raise(rb_eMolbyError, "too many bonds");
6624 rb_raise(rb_eMolbyError, "duplicate bonds");
6626 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6628 rb_raise(rb_eMolbyError, "error in creating bonds");
6629 return INT2NUM(mol->nbonds - old_nbonds);
6634 * molecule.remove_bonds(n1, n2, ...) -> Integer
6636 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6637 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6638 * This operation is undoable.
6641 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6647 rb_raise(rb_eMolbyError, "missing arguments");
6649 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6650 Data_Get_Struct(self, Molecule, mol);
6652 for (i = j = 0; i < argc; i++, j = 1 - j) {
6653 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6655 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6659 IntGroupAdd(bg, k, 1);
6664 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6665 i = IntGroupGetCount(bg);
6666 IntGroupRelease(bg);
6673 * assign_bond_order(idx, d1)
6674 * assign_bond_orders(group, [d1, d2, ...])
6676 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6677 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6678 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6679 * (This may change in the future)
6680 * This operation is undoable.
6683 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6687 Data_Get_Struct(self, Molecule, mol);
6688 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6689 /* The first form */
6690 Int idx = NUM2INT(rb_Integer(idxval));
6691 Double d1 = NUM2DBL(rb_Float(dval));
6692 if (idx < 0 || idx >= mol->nbonds)
6693 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6694 ig = IntGroupNewWithPoints(idx, 1, -1);
6695 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6696 IntGroupRelease(ig);
6700 ig = IntGroupFromValue(idxval);
6701 n = IntGroupGetCount(ig);
6703 rb_raise(rb_eMolbyError, "the bond index is empty");
6704 dval = rb_ary_to_ary(dval);
6705 dp = (Double *)calloc(sizeof(Double), n);
6706 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6707 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6709 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6711 IntGroupRelease(ig);
6718 * get_bond_order(idx) -> Float
6719 * get_bond_orders(group) -> Array
6721 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6722 * In the second form, the bond orders at the indices in the group are returned as an array.
6723 * If no bond order information have been assigned, returns nil (the first form)
6724 * or an empty array (the second form).
6727 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6733 Int i, n, numericArg;
6734 Data_Get_Struct(self, Molecule, mol);
6735 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6736 /* The first form */
6737 Int idx = NUM2INT(rb_Integer(idxval));
6738 if (idx < 0 || idx >= mol->nbonds)
6739 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6740 if (mol->bondOrders == NULL)
6742 ig = IntGroupNewWithPoints(idx, 1, -1);
6746 if (mol->bondOrders == NULL)
6747 return rb_ary_new();
6748 ig = IntGroupFromValue(idxval);
6749 n = IntGroupGetCount(ig);
6751 rb_raise(rb_eMolbyError, "the bond index is empty");
6754 dp = (Double *)calloc(sizeof(Double), n);
6755 MoleculeGetBondOrders(mol, dp, ig);
6757 retval = rb_float_new(dp[0]);
6759 retval = rb_ary_new();
6760 for (i = 0; i < n; i++)
6761 rb_ary_push(retval, rb_float_new(dp[i]));
6764 IntGroupRelease(ig);
6770 * bond_exist?(idx1, idx2) -> bool
6772 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6773 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6776 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6782 Data_Get_Struct(self, Molecule, mol);
6783 idx1 = NUM2INT(rb_Integer(ival1));
6784 idx2 = NUM2INT(rb_Integer(ival2));
6785 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6786 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6787 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6788 cp = AtomConnectData(&ap->connect);
6789 for (i = 0; i < ap->connect.count; i++) {
6798 * add_angle(n1, n2, n3) -> Molecule
6800 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6801 * when a bond is created, so it is rarely necessary to use this method explicitly.
6802 * This operation is undoable.
6805 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6809 Data_Get_Struct(self, Molecule, mol);
6810 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6811 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6812 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6813 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6814 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6815 n[3] = kInvalidIndex;
6816 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6822 * remove_angle(n1, n2, n3) -> Molecule
6824 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6825 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6826 * This operation is undoable.
6829 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6834 Data_Get_Struct(self, Molecule, mol);
6835 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6836 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6837 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6838 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6839 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6840 ig = IntGroupNewWithPoints(n[3], 1, -1);
6841 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6842 IntGroupRelease(ig);
6848 * add_dihedral(n1, n2, n3, n4) -> Molecule
6850 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6851 * when a bond is created, so it is rarely necessary to use this method explicitly.
6852 * This operation is undoable.
6855 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6859 Data_Get_Struct(self, Molecule, mol);
6860 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6861 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6862 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6863 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6864 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6865 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6866 n[4] = kInvalidIndex;
6867 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6873 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6875 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6876 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6877 * This operation is undoable.
6880 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6885 Data_Get_Struct(self, Molecule, mol);
6886 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6887 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6888 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6889 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6890 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6891 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6892 ig = IntGroupNewWithPoints(n[4], 1, -1);
6893 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6894 IntGroupRelease(ig);
6900 * add_improper(n1, n2, n3, n4) -> Molecule
6902 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6903 * not automatically added when a new bond is created, so this method is more useful than
6904 * the angle/dihedral counterpart.
6905 * This operation is undoable.
6908 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6912 Data_Get_Struct(self, Molecule, mol);
6913 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6914 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6915 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6916 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6917 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6918 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6919 n[4] = kInvalidIndex;
6920 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6926 * remove_improper(n1, n2, n3, n4) -> Molecule
6927 * remove_improper(intgroup) -> Molecule
6929 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6930 * Returns self. Unlike angles and dihedrals, impropers are
6931 * not automatically added when a new bond is created, so this method is more useful than
6932 * the angle/dihedral counterpart.
6933 * This operation is undoable.
6936 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6939 VALUE v1, v2, v3, v4;
6942 Data_Get_Struct(self, Molecule, mol);
6944 ig = IntGroupFromValue(argv[0]);
6946 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6947 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6948 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6949 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6950 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6951 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6952 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6953 ig = IntGroupNewWithPoints(n[4], 1, -1);
6955 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6956 IntGroupRelease(ig);
6962 * assign_residue(group, res) -> Molecule
6964 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6965 * or "resname.resno". When the residue number is not specified, the residue number of
6966 * the first atom in the group is used.
6967 * This operation is undoable.
6970 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6974 char *p, *pp, buf[16];
6977 Data_Get_Struct(self, Molecule, mol);
6979 /* Parse the argument res */
6980 if (FIXNUM_P(res)) {
6981 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6982 resid = NUM2INT(res);
6985 p = StringValuePtr(res);
6986 pp = strchr(p, '.');
6988 resid = atoi(pp + 1);
6994 if (n > sizeof buf - 1)
6999 ig = s_Molecule_AtomGroupFromValue(self, range);
7000 if (ig == NULL || IntGroupGetCount(ig) == 0)
7004 /* Use the residue number of the first specified atom */
7005 n = IntGroupGetNthPoint(ig, 0);
7006 if (n >= mol->natoms)
7007 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
7008 ap = ATOM_AT_INDEX(mol->atoms, n);
7011 /* Change the residue number */
7012 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
7013 /* Change the residue name if necessary */
7017 seqs[1] = kInvalidIndex; */
7018 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
7020 IntGroupRelease(ig);
7026 * offset_residue(group, offset) -> Molecule
7028 * Offset the residue number of the specified atoms. If any of the residue number gets
7029 * negative, then exception is thrown.
7030 * This operation is undoable.
7033 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
7038 Data_Get_Struct(self, Molecule, mol);
7039 ig = s_Molecule_AtomGroupFromValue(self, range);
7040 ofs = NUM2INT(offset);
7041 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
7043 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
7044 IntGroupRelease(ig);
7050 * renumber_atoms(array) -> IntGroup
7052 * Change the order of atoms so that the atoms specified in the array argument appear
7053 * in this order from the top of the molecule. The atoms that are not included in array
7054 * are placed after these atoms, and these atoms are returned as an intGroup.
7055 * This operation is undoable.
7058 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
7064 VALUE *valp, retval;
7065 Data_Get_Struct(self, Molecule, mol);
7066 if (TYPE(array) != T_ARRAY)
7067 array = rb_funcall(array, rb_intern("to_a"), 0);
7068 n = RARRAY_LEN(array);
7069 valp = RARRAY_PTR(array);
7070 new2old = ALLOC_N(Int, n + 1);
7071 for (i = 0; i < n; i++)
7072 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
7073 new2old[i] = kInvalidIndex;
7074 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
7076 rb_raise(rb_eMolbyError, "Atom index out of range");
7078 rb_raise(rb_eMolbyError, "Duplicate entry");
7080 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
7081 retval = IntGroup_Alloc(rb_cIntGroup);
7082 Data_Get_Struct(retval, IntGroup, ig);
7083 if (mol->natoms > n)
7084 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
7091 * find_close_atoms(atom, limit = 1.2) -> array of Integers (atom indices)
7093 * Find atoms that are within the threshold distance from the given atom.
7094 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7095 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7096 * If limit is not given, a default value of 1.2 is used.
7097 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7100 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7105 Int n1, nbonds, *bonds;
7106 Data_Get_Struct(self, Molecule, mol);
7107 rb_scan_args(argc, argv, "11", &aval, &limval);
7108 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7112 limit = NUM2DBL(rb_Float(limval));
7113 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7115 MoleculeFindCloseAtoms(mol, n1, limit, &nbonds, &bonds, 0);
7116 aval = rb_ary_new();
7118 for (n1 = 0; n1 < nbonds; n1++)
7119 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7127 * guess_bonds(limit = 1.2) -> Integer
7129 * Create bonds between atoms that are within the threshold distance.
7130 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7131 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7132 * If limit is not given, a default value of 1.2 is used.
7133 * The number of the newly created bonds is returned.
7134 * This operation is undoable.
7137 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7143 Data_Get_Struct(self, Molecule, mol);
7144 rb_scan_args(argc, argv, "01", &limval);
7148 limit = NUM2DBL(rb_Float(limval));
7149 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7151 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7154 return INT2NUM(nbonds);
7159 * register_undo(script, *args)
7161 * Register an undo operation with the current molecule.
7164 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
7169 Data_Get_Struct(self, Molecule, mol);
7170 rb_scan_args(argc, argv, "1*", &script, &args);
7171 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
7172 MolActionCallback_registerUndo(mol, act);
7178 * undo_enabled? -> bool
7180 * Returns true if undo is enabled for this molecule; otherwise no.
7183 s_Molecule_UndoEnabled(VALUE self)
7186 Data_Get_Struct(self, Molecule, mol);
7187 if (MolActionCallback_isUndoRegistrationEnabled(mol))
7194 * undo_enabled = bool
7196 * Enable or disable undo.
7199 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
7202 Data_Get_Struct(self, Molecule, mol);
7203 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
7209 * selection -> IntGroup
7211 * Returns the current selection.
7214 s_Molecule_Selection(VALUE self)
7219 Data_Get_Struct(self, Molecule, mol);
7220 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
7221 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
7222 val = ValueFromIntGroup(ig);
7223 IntGroupRelease(ig);
7225 val = IntGroup_Alloc(rb_cIntGroup);
7231 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
7235 Data_Get_Struct(self, Molecule, mol);
7239 ig = s_Molecule_AtomGroupFromValue(self, val);
7241 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
7243 MoleculeSetSelection(mol, ig);
7245 IntGroupRelease(ig);
7251 * selection = IntGroup
7253 * Set the current selection. The right-hand operand may be nil.
7254 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
7257 s_Molecule_SetSelection(VALUE self, VALUE val)
7259 return s_Molecule_SetSelectionSub(self, val, 0);
7264 * set_undoable_selection(IntGroup)
7266 * Set the current selection with undo registration. The right-hand operand may be nil.
7267 * This operation is undoable.
7270 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
7272 return s_Molecule_SetSelectionSub(self, val, 1);
7277 * hidden_atoms -> IntGroup
7279 * Returns the currently hidden atoms.
7282 s_Molecule_HiddenAtoms(VALUE self)
7284 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7285 return Qnil; /* Not reached */
7289 Data_Get_Struct(self, Molecule, mol);
7294 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7295 if (ap->exflags & kAtomHiddenFlag)
7296 IntGroupAdd(ig, i, 1);
7298 val = ValueFromIntGroup(ig);
7299 IntGroupRelease(ig);
7302 } else return Qnil; */
7307 * set_hidden_atoms(IntGroup)
7308 * self.hidden_atoms = IntGroup
7310 * Hide the specified atoms. This operation is _not_ undoable.
7313 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7315 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7316 return Qnil; /* Not reached */
7319 Data_Get_Struct(self, Molecule, mol);
7327 ig = s_Molecule_AtomGroupFromValue(self, val);
7328 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7329 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7330 ap->exflags |= kAtomHiddenFlag;
7332 ap->exflags &= kAtomHiddenFlag;
7336 IntGroupRelease(ig);
7337 MoleculeCallback_notifyModification(mol, 0);
7344 * select_frame(index)
7347 * Select the specified frame. If successful, returns true, otherwise returns false.
7350 s_Molecule_SelectFrame(VALUE self, VALUE val)
7353 int ival = NUM2INT(val);
7354 Data_Get_Struct(self, Molecule, mol);
7355 ival = MoleculeSelectFrame(mol, ival, 1);
7365 * Get the current frame.
7368 s_Molecule_Frame(VALUE self)
7371 Data_Get_Struct(self, Molecule, mol);
7372 return INT2NUM(mol->cframe);
7377 * nframes -> Integer
7379 * Get the number of frames.
7382 s_Molecule_Nframes(VALUE self)
7385 Data_Get_Struct(self, Molecule, mol);
7386 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7391 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7392 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7394 * Insert new frames at the indices specified by the intGroup. If the first argument is
7395 * an integer, a single new frame is inserted at that index. If the first argument is
7396 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7397 * should be an array of arrays of Vector3Ds, then those coordinates are set
7398 * to the new frame. Otherwise, the coordinates of current molecule are copied
7400 * Returns an intGroup representing the inserted frames if successful, nil if not.
7403 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7405 VALUE val, coords, cells;
7408 int count, ival, i, j, len, len_c, len2, nframes;
7411 Data_Get_Struct(self, Molecule, mol);
7412 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7413 if (coords != Qnil) {
7414 if (TYPE(coords) != T_ARRAY)
7415 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7416 len = RARRAY_LEN(coords);
7418 if (cells != Qnil) {
7419 if (mol->cell == NULL)
7420 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7421 if (TYPE(cells) != T_ARRAY)
7422 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7423 len_c = RARRAY_LEN(cells);
7425 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7426 nframes = MoleculeGetNumberOfFrames(mol);
7428 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7429 val = ValueFromIntGroup(ig);
7431 ig = IntGroupFromValue(val);
7433 count = IntGroupGetCount(ig); /* Count is updated here */
7434 vp = ALLOC_N(Vector, mol->natoms * count);
7436 vp2 = ALLOC_N(Vector, 4 * count);
7440 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7441 ptr = RARRAY_PTR(coords);
7442 for (i = 0; i < count; i++) {
7443 if (TYPE(ptr[i]) != T_ARRAY)
7444 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7445 len2 = RARRAY_LEN(ptr[i]);
7446 if (len2 < mol->natoms)
7447 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7448 ptr2 = RARRAY_PTR(ptr[i]);
7449 for (j = 0; j < mol->natoms; j++)
7450 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7454 for (i = 0; i < count; i++) {
7455 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7456 vp[i * mol->natoms + j] = ap->r;
7462 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7463 ptr = RARRAY_PTR(cells);
7464 for (i = 0; i < count; i++) {
7465 if (TYPE(ptr[i]) != T_ARRAY)
7466 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7467 len2 = RARRAY_LEN(ptr[i]);
7469 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7470 ptr2 = RARRAY_PTR(ptr[i]);
7471 for (j = 0; j < 4; j++)
7472 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7475 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7476 IntGroupRelease(ig);
7480 return (ival >= 0 ? val : Qnil);
7485 * create_frame(coordinates = nil) -> Integer
7486 * create_frames(coordinates = nil) -> Integer
7488 * Same as molecule.insert_frames(nil, coordinates).
7491 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7494 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7496 return s_Molecule_InsertFrames(3, vals, self);
7501 * remove_frames(IntGroup, wantCoordinates = false)
7503 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7504 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7505 * removed frames is returned if operation is successful.
7508 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7515 Data_Get_Struct(self, Molecule, mol);
7516 rb_scan_args(argc, argv, "11", &val, &flag);
7517 ig = IntGroupFromValue(val);
7518 count = IntGroupGetCount(ig);
7520 /* Create return value before removing frames */
7525 retval = rb_ary_new2(count);
7526 for (i = 0; i < count; i++) {
7527 n = IntGroupGetNthPoint(ig, i);
7528 coords = rb_ary_new2(mol->natoms);
7529 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7530 if (n < ap->nframes && n != mol->cframe)
7533 rb_ary_push(coords, ValueFromVector(&v));
7535 rb_ary_push(retval, coords);
7537 } else retval = Qtrue;
7538 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7545 * each_frame {|n| ...}
7547 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7548 * the frame number. After completion, the original frame number is restored.
7551 s_Molecule_EachFrame(VALUE self)
7553 int i, cframe, nframes;
7555 Data_Get_Struct(self, Molecule, mol);
7556 cframe = mol->cframe;
7557 nframes = MoleculeGetNumberOfFrames(mol);
7559 for (i = 0; i < nframes; i++) {
7560 MoleculeSelectFrame(mol, i, 1);
7561 rb_yield(INT2NUM(i));
7563 MoleculeSelectFrame(mol, cframe, 1);
7570 * set_atom_attr(index, key, value)
7572 * Set the atom attribute for the specified atom.
7573 * This operation is undoable.
7576 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7580 Data_Get_Struct(self, Molecule, mol);
7581 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7582 oldval = s_AtomRef_GetAttr(aref, key);
7585 s_AtomRef_SetAttr(aref, key, val);
7591 * get_atom_attr(index, key)
7593 * Get the atom attribute for the specified atom.
7596 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7598 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7603 * get_coord_from_frame(index, group = nil)
7605 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7606 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7607 * copied; now they are always copied)
7610 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7613 VALUE ival, gval, cval;
7614 Int index, i, j, n, nn;
7616 IntGroupIterator iter;
7619 Data_Get_Struct(self, Molecule, mol);
7620 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7622 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7623 index = NUM2INT(rb_Integer(ival));
7624 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7626 rb_raise(rb_eMolbyError, "No frame is present");
7628 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7631 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7633 ig = s_Molecule_AtomGroupFromValue(self, gval);
7635 n = IntGroupGetCount(ig);
7637 vp = (Vector *)calloc(sizeof(Vector), n);
7638 IntGroupIteratorInit(ig, &iter);
7641 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7642 ap = ATOM_AT_INDEX(mol->atoms, i);
7643 if (index < ap->nframes) {
7644 vp[j] = ap->frames[index];
7652 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7654 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7655 vp = mol->frame_cells + index * 4;
7656 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7658 IntGroupIteratorRelease(&iter);
7660 IntGroupRelease(ig);
7666 * fragment(n1, *exatoms) -> IntGroup
7667 * fragment(group, *exatoms) -> IntGroup
7669 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7670 * those atoms will not be counted during the search.
7673 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7676 IntGroup *baseg, *ig, *exatoms;
7678 volatile VALUE nval, exval;
7679 Data_Get_Struct(self, Molecule, mol);
7680 rb_scan_args(argc, argv, "1*", &nval, &exval);
7681 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7683 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7685 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7687 if (RARRAY_LEN(exval) == 0) {
7690 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7691 Data_Get_Struct(exval, IntGroup, exatoms);
7693 if (baseg == NULL) {
7694 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7696 IntGroupIterator iter;
7697 IntGroupIteratorInit(baseg, &iter);
7698 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7701 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7703 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7705 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7707 IntGroupAddIntGroup(ig, subg);
7708 IntGroupRelease(subg);
7713 IntGroupIteratorRelease(&iter);
7714 IntGroupRelease(baseg);
7717 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7718 nval = ValueFromIntGroup(ig);
7719 IntGroupRelease(ig);
7725 * fragments(exclude = nil)
7727 * Returns the fragments as an array of IntGroups.
7728 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7729 * in defining the fragment.
7732 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
7735 IntGroup *ag, *fg, *eg;
7736 VALUE gval, exval, retval;
7737 Data_Get_Struct(self, Molecule, mol);
7740 if (mol->natoms == 0)
7741 return rb_ary_new();
7742 rb_scan_args(argc, argv, "01", &exval);
7746 eg = IntGroupFromValue(exval);
7747 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7749 IntGroupRemoveIntGroup(ag, eg);
7750 retval = rb_ary_new();
7751 while (IntGroupGetCount(ag) > 0) {
7752 int n = IntGroupGetNthPoint(ag, 0);
7753 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7755 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7756 gval = ValueFromIntGroup(fg);
7757 rb_ary_push(retval, gval);
7758 IntGroupRemoveIntGroup(ag, fg);
7759 IntGroupRelease(fg);
7761 IntGroupRelease(ag);
7763 IntGroupRelease(eg);
7769 * each_fragment(exclude = nil) {|group| ...}
7771 * Execute the block, with the IntGroup object for each fragment as the argument.
7772 * Atoms or bonds should not be added or removed during the execution of the block.
7773 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7774 * in defining the fragment.
7777 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
7780 IntGroup *ag, *fg, *eg;
7782 Data_Get_Struct(self, Molecule, mol);
7783 if (mol == NULL || mol->natoms == 0)
7785 rb_scan_args(argc, argv, "01", &exval);
7789 eg = IntGroupFromValue(exval);
7790 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7792 IntGroupRemoveIntGroup(ag, eg);
7793 while (IntGroupGetCount(ag) > 0) {
7794 int n = IntGroupGetNthPoint(ag, 0);
7795 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7797 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7798 gval = ValueFromIntGroup(fg);
7800 IntGroupRemoveIntGroup(ag, fg);
7801 IntGroupRelease(fg);
7803 IntGroupRelease(ag);
7805 IntGroupRelease(eg);
7811 * detachable?(group) -> [n1, n2]
7813 * Check whether the group is 'detachable', i.e. the group is bound to the rest
7814 * of the molecule via only one bond. If it is, then the indices of the atoms
7815 * belonging to the bond is returned, the first element being the atom included
7816 * in the fragment. Otherwise, Qnil is returned.
7819 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7825 Data_Get_Struct(self, Molecule, mol);
7826 ig = s_Molecule_AtomGroupFromValue(self, gval);
7827 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7828 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7829 } else retval = Qnil;
7830 IntGroupRelease(ig);
7836 * bonds_on_border(group = selection) -> Array of Array of two Integers
7838 * Returns an array of bonds that connect an atom in the group and an atom out
7839 * of the group. The first atom in the bond always belongs to the group. If no
7840 * such bonds are present, an empty array is returned.
7843 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7848 Data_Get_Struct(self, Molecule, mol);
7849 rb_scan_args(argc, argv, "01", &gval);
7851 ig = MoleculeGetSelection(mol);
7855 ig = s_Molecule_AtomGroupFromValue(self, gval);
7857 retval = rb_ary_new();
7860 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7862 IntGroupIterator iter;
7864 IntGroupIteratorInit(bg, &iter);
7865 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7866 /* The atoms at the border */
7868 n1 = mol->bonds[i * 2];
7869 n2 = mol->bonds[i * 2 + 1];
7870 if (IntGroupLookupPoint(ig, n1) < 0) {
7874 if (IntGroupLookupPoint(ig, n1) < 0)
7875 continue; /* Actually this is an internal error */
7877 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
7879 IntGroupIteratorRelease(&iter);
7881 IntGroupRelease(bg);
7882 IntGroupRelease(ig);
7888 * translate(vec, group = nil) -> Molecule
7890 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7891 * This operation is undoable.
7894 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7900 Data_Get_Struct(self, Molecule, mol);
7901 rb_scan_args(argc, argv, "11", &vec, &group);
7902 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7903 VectorFromValue(vec, &v);
7904 // MoleculeTranslate(mol, &v, ig);
7905 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7907 IntGroupRelease(ig);
7913 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7915 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7916 * If group is given, only atoms in the group are moved.
7917 * This operation is undoable.
7920 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7923 volatile VALUE aval, anval, cval, gval;
7928 Data_Get_Struct(self, Molecule, mol);
7929 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7930 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7931 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7932 VectorFromValue(aval, &av);
7934 cv.x = cv.y = cv.z = 0.0;
7936 VectorFromValue(cval, &cv);
7937 if (TransformForRotation(tr, &av, angle, &cv))
7938 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7939 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7941 IntGroupRelease(ig);
7947 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7949 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7950 * axis must not be a zero vector.
7951 * If group is given, only atoms in the group are moved.
7952 * This operation is undoable.
7955 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7958 volatile VALUE aval, cval, gval;
7962 Data_Get_Struct(self, Molecule, mol);
7963 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7964 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7965 VectorFromValue(aval, &av);
7967 cv.x = cv.y = cv.z = 0.0;
7969 VectorFromValue(cval, &cv);
7970 if (TransformForReflection(tr, &av, &cv))
7971 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7972 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7974 IntGroupRelease(ig);
7980 * invert(center = [0,0,0], group = nil) -> Molecule
7982 * Invert the molecule with the given center.
7983 * If group is given, only atoms in the group are moved.
7984 * This operation is undoable.
7987 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7990 volatile VALUE cval, gval;
7994 Data_Get_Struct(self, Molecule, mol);
7995 rb_scan_args(argc, argv, "02", &cval, &gval);
7996 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7998 cv.x = cv.y = cv.z = 0.0;
8000 VectorFromValue(cval, &cv);
8001 TransformForInversion(tr, &cv);
8002 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8004 IntGroupRelease(ig);
8010 * transform(transform, group = nil) -> Molecule
8012 * Transform the molecule by the given Transform object.
8013 * If group is given, only atoms in the group are moved.
8014 * This operation is undoable.
8017 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
8023 Data_Get_Struct(self, Molecule, mol);
8024 rb_scan_args(argc, argv, "11", &trans, &group);
8025 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8026 TransformFromValue(trans, &tr);
8027 /* MoleculeTransform(mol, tr, ig); */
8028 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8030 IntGroupRelease(ig);
8035 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
8037 switch (MoleculeCenterOfMass(mol, outv, ig)) {
8038 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
8039 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
8041 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
8047 * center_of_mass(group = nil) -> Vector3D
8049 * Calculate the center of mass for the given set of atoms. The argument
8050 * group is null, then all atoms are considered.
8053 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
8059 Data_Get_Struct(self, Molecule, mol);
8060 rb_scan_args(argc, argv, "01", &group);
8061 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8062 s_Molecule_DoCenterOfMass(mol, &v, ig);
8064 IntGroupRelease(ig);
8065 return ValueFromVector(&v);
8070 * centralize(group = nil) -> self
8072 * Translate the molecule so that the center of mass of the given group is located
8073 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
8076 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
8082 Data_Get_Struct(self, Molecule, mol);
8083 rb_scan_args(argc, argv, "01", &group);
8084 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8085 s_Molecule_DoCenterOfMass(mol, &v, ig);
8087 IntGroupRelease(ig);
8091 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
8097 * bounds(group = nil) -> [min, max]
8099 * Calculate the boundary. The return value is an array of two Vector3D objects.
8102 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
8110 Data_Get_Struct(self, Molecule, mol);
8111 rb_scan_args(argc, argv, "01", &group);
8112 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8113 if (ig != NULL && IntGroupGetCount(ig) == 0)
8114 rb_raise(rb_eMolbyError, "atom group is empty");
8115 vmin.x = vmin.y = vmin.z = 1e30;
8116 vmax.x = vmax.y = vmax.z = -1e30;
8117 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
8119 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
8135 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
8138 /* Get atom position or a vector */
8140 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
8142 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
8143 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
8144 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
8146 VectorFromValue(val, vp);
8152 * measure_bond(n1, n2) -> Float
8154 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
8155 * or Vector3D values.
8156 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8159 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
8163 Data_Get_Struct(self, Molecule, mol);
8164 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8165 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8166 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
8171 * measure_angle(n1, n2, n3) -> Float
8173 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
8174 * or Vector3D values. The return value is in degree.
8175 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8178 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
8183 Data_Get_Struct(self, Molecule, mol);
8184 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8185 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8186 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8187 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
8189 return Qnil; /* Cannot define */
8190 else return rb_float_new(d);
8195 * measure_dihedral(n1, n2, n3, n4) -> Float
8197 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
8198 * or Vector3D values. The return value is in degree.
8199 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8202 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
8205 Vector v1, v2, v3, v4;
8207 Data_Get_Struct(self, Molecule, mol);
8208 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8209 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8210 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8211 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
8212 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
8214 return Qnil; /* Cannot define */
8215 else return rb_float_new(d);
8220 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
8222 * Expand the specified part of the molecule by the given symmetry operation.
8223 * Returns the array of atom indices corresponding to the expanded atoms.
8226 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
8229 VALUE gval, sval, xval, yval, zval, rval;
8235 Data_Get_Struct(self, Molecule, mol);
8236 rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
8237 n[0] = NUM2INT(rb_Integer(sval));
8238 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
8239 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
8240 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
8241 ig = s_Molecule_AtomGroupFromValue(self, gval);
8242 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
8243 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
8244 natoms = mol->natoms;
8246 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
8248 rval = rb_ary_new2(nidx);
8249 while (--nidx >= 0) {
8250 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
8252 /* if (natoms == mol->natoms)
8255 rval = IntGroup_Alloc(rb_cIntGroup);
8256 Data_Get_Struct(rval, IntGroup, ig);
8257 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
8264 * amend_by_symmetry(group = nil) -> IntGroup
8266 * Expand the specified part of the molecule by the given symmetry operation.
8267 * Returns an IntGroup containing the added atoms.
8270 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
8275 Data_Get_Struct(self, Molecule, mol);
8276 rb_scan_args(argc, argv, "01", &gval);
8278 ig = s_Molecule_AtomGroupFromValue(self, gval);
8280 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
8281 rval = ValueFromIntGroup(ig2);
8282 IntGroupRelease(ig2);
8288 * transform_for_symop(symop, is_cartesian = nil) -> Transform
8290 * Get the transform corresponding to the symmetry operation. The symop can either be
8291 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
8292 * If is_cartesian is true, the returned transform is for cartesian coordinates.
8293 * Otherwise, the returned transform is for fractional coordinates.
8294 * Raises exception when no cell or no transform are defined.
8297 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
8303 Data_Get_Struct(self, Molecule, mol);
8304 if (mol->cell == NULL)
8305 rb_raise(rb_eMolbyError, "no unit cell is defined");
8306 if (mol->nsyms == 0)
8307 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8308 rb_scan_args(argc, argv, "11", &sval, &fval);
8309 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
8310 symop.sym = NUM2INT(rb_Integer(sval));
8311 symop.dx = symop.dy = symop.dz = 0;
8313 sval = rb_ary_to_ary(sval);
8314 if (RARRAY_LEN(sval) < 4)
8315 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
8316 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
8317 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
8318 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
8319 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
8321 if (symop.sym >= mol->nsyms)
8322 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
8323 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
8324 return ValueFromTransform(&tr);
8329 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8331 * Get the symmetry operation corresponding to the given transform.
8332 * If is_cartesian is true, the given transform is for cartesian coordinates.
8333 * Otherwise, the given transform is for fractional coordinates.
8334 * Raises exception when no cell or no transform are defined.
8337 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8344 Data_Get_Struct(self, Molecule, mol);
8345 if (mol->cell == NULL)
8346 rb_raise(rb_eMolbyError, "no unit cell is defined");
8347 if (mol->nsyms == 0)
8348 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8349 rb_scan_args(argc, argv, "11", &tval, &fval);
8350 TransformFromValue(tval, &tr);
8351 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8353 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8355 return Qnil; /* Not found */
8361 * wrap_unit_cell(group) -> Vector3D
8363 * Move the specified group so that the center of mass of the group is within the
8364 * unit cell. The offset vector is returned. If no periodic box is defined,
8365 * exception is raised.
8368 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8373 Data_Get_Struct(self, Molecule, mol);
8374 if (mol->cell == NULL)
8375 rb_raise(rb_eMolbyError, "no unit cell is defined");
8376 ig = s_Molecule_AtomGroupFromValue(self, gval);
8377 s_Molecule_DoCenterOfMass(mol, &cv, ig);
8378 TransformVec(&v, mol->cell->rtr, &cv);
8379 if (mol->cell->flags[0])
8381 if (mol->cell->flags[1])
8383 if (mol->cell->flags[2])
8385 TransformVec(&dv, mol->cell->tr, &v);
8387 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8388 IntGroupRelease(ig);
8389 return ValueFromVector(&dv);
8394 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8396 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8397 * first and second atom in the pair should belong to group1 and group2, respectively.
8398 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8401 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8404 VALUE limval, gval1, gval2, rval, igval;
8405 IntGroup *ig1, *ig2;
8406 IntGroupIterator iter1, iter2;
8412 MDExclusion *exinfo;
8415 Data_Get_Struct(self, Molecule, mol);
8416 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8417 lim = NUM2DBL(rb_Float(limval));
8419 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8421 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8423 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8425 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8427 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8429 if (!RTEST(igval)) {
8430 /* Use the exclusion table in MDArena */
8431 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8432 VALUE mval = ValueFromMolecule(mol);
8433 s_RebuildMDParameterIfNecessary(mval, Qnil);
8435 exinfo = mol->arena->exinfo; /* May be NULL */
8436 exlist = mol->arena->exlist;
8441 IntGroupIteratorInit(ig1, &iter1);
8442 IntGroupIteratorInit(ig2, &iter2);
8445 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8447 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8449 if (exinfo != NULL) {
8450 exn1 = exinfo[n[0]].index1;
8451 exn2 = exinfo[n[0] + 1].index1;
8452 } else exn1 = exn2 = -1;
8453 IntGroupIteratorReset(&iter2);
8454 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8455 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8457 continue; /* Same atom */
8458 if (exinfo != NULL) {
8459 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
8460 for (i = exn1; i < exn2; i++) {
8461 if (exlist[i] == n[1])
8465 continue; /* Should be excluded */
8467 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8468 /* Is this pair already registered? */
8470 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8471 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8475 /* Not registered yet */
8476 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8481 IntGroupIteratorRelease(&iter2);
8482 IntGroupIteratorRelease(&iter1);
8483 IntGroupRelease(ig2);
8484 IntGroupRelease(ig1);
8485 rval = rb_ary_new2(npairs);
8486 if (pairs != NULL) {
8487 for (i = 0; i < npairs; i++) {
8488 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8495 /* Calculate the transform that moves the current coordinates to the reference
8496 coordinates with least displacements. */
8498 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8506 Double eigen_val[3];
8507 Vector eigen_vec[3];
8509 IntGroupIterator iter;
8511 natoms = mol->natoms;
8513 IntGroupIteratorInit(ig, &iter);
8515 /* Calculate the weighted center */
8519 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8520 ap1 = ATOM_AT_INDEX(ap, in);
8521 w1 = (weights != NULL ? weights[i] : ap1->weight);
8522 VecScaleInc(org1, ap1->r, w1);
8523 VecScaleInc(org2, ref[i], w1);
8527 VecScaleSelf(org1, w);
8528 VecScaleSelf(org2, w);
8530 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8531 /* Matrix to diagonalize = R * tR */
8532 memset(r, 0, sizeof(Mat33));
8533 memset(q, 0, sizeof(Mat33));
8534 memset(u, 0, sizeof(Mat33));
8536 IntGroupIteratorReset(&iter);
8537 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8539 ap1 = ATOM_AT_INDEX(ap, in);
8540 w1 = (weights != NULL ? weights[i] : ap1->weight);
8542 VecSub(v1, ap1->r, org1);
8543 VecSub(v2, ref[i], org2);
8544 r[0] += w1 * v1.x * v2.x;
8545 r[1] += w1 * v1.y * v2.x;
8546 r[2] += w1 * v1.z * v2.x;
8547 r[3] += w1 * v1.x * v2.y;
8548 r[4] += w1 * v1.y * v2.y;
8549 r[5] += w1 * v1.z * v2.y;
8550 r[6] += w1 * v1.x * v2.z;
8551 r[7] += w1 * v1.y * v2.z;
8552 r[8] += w1 * v1.z * v2.z;
8555 for (i = 0; i < 9; i++)
8557 for (i = 0; i < 3; i++) {
8558 for (j = 0; j < 3; j++) {
8559 for (k = 0; k < 3; k++) {
8560 q[i+j*3] += r[i+k*3] * r[j+k*3];
8565 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8566 IntGroupIteratorRelease(&iter);
8567 return -1.0; /* Cannot determine the eigenvector */
8570 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8571 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8572 MatrixTranspose(r, r);
8573 for (i = 0; i < 3; i++) {
8574 MatrixVec(&s[i], r, &eigen_vec[i]);
8575 w1 = 1.0 / sqrt(eigen_val[i]);
8576 VecScaleSelf(s[i], w1);
8578 for (k = 0; k < 3; k++) {
8579 u[0] += s[k].x * eigen_vec[k].x;
8580 u[1] += s[k].y * eigen_vec[k].x;
8581 u[2] += s[k].z * eigen_vec[k].x;
8582 u[3] += s[k].x * eigen_vec[k].y;
8583 u[4] += s[k].y * eigen_vec[k].y;
8584 u[5] += s[k].z * eigen_vec[k].y;
8585 u[6] += s[k].x * eigen_vec[k].z;
8586 u[7] += s[k].y * eigen_vec[k].z;
8587 u[8] += s[k].z * eigen_vec[k].z;
8590 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8591 MatrixVec(&org1, u, &org1);
8593 for (i = 0; i < 9; i++)
8599 /* Calculate rmsd */
8600 IntGroupIteratorReset(&iter);
8602 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8604 ap1 = ATOM_AT_INDEX(ap, in);
8605 TransformVec(&tv, trans, &ap1->r);
8607 w += VecLength2(tv);
8610 IntGroupIteratorRelease(&iter);
8616 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8618 * Calculate the transform to fit the given group to the set of reference coordinates.
8619 * The reference coordinates ref is given as either a frame number, an array of
8620 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8621 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8622 * Return values are the transform (that converts the present coordinates to the
8623 * target coordinates) and root mean square deviation (without weight).
8626 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8630 VALUE gval, rval, wval;
8632 IntGroupIterator iter;
8633 int nn, errno, i, j, in, status;
8635 Double *weights, dval[3];
8638 Data_Get_Struct(self, Molecule, mol);
8639 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8641 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8643 ig = IntGroupFromValue(gval);
8644 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8645 IntGroupRelease(ig);
8646 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8648 ref = (Vector *)calloc(sizeof(Vector), nn);
8649 weights = (Double *)calloc(sizeof(Double), nn);
8650 IntGroupIteratorInit(ig, &iter);
8651 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8652 int fn = NUM2INT(rb_Integer(rval));
8653 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8658 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8659 ap = ATOM_AT_INDEX(mol->atoms, in);
8660 if (fn < ap->nframes)
8661 ref[i] = ap->frames[fn];
8662 else ref[i] = ap->r;
8664 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8665 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8666 if (m->row * m->column < nn * 3) {
8670 for (i = 0; i < nn; i++) {
8671 ref[i].x = m->data[i * 3];
8672 ref[i].y = m->data[i * 3 + 1];
8673 ref[i].z = m->data[i * 3 + 2];
8677 rval = rb_protect(rb_ary_to_ary, rval, &status);
8682 if (RARRAY_LEN(rval) < nn) {
8686 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8687 /* Array of 3*nn numbers */
8688 if (RARRAY_LEN(rval) < nn * 3) {
8692 for (i = 0; i < nn; i++) {
8693 for (j = 0; j < 3; j++) {
8694 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8699 dval[j] = NUM2DBL(aval);
8706 /* Array of nn Vector3Ds or Arrays */
8707 for (i = 0; i < nn; i++) {
8708 aval = (RARRAY_PTR(rval))[i];
8709 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8710 VectorFromValue(aval, &ref[i]);
8712 aval = rb_protect(rb_ary_to_ary, aval, &status);
8717 if (RARRAY_LEN(aval) < 3) {
8722 for (j = 0; j < 3; j++) {
8723 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8728 dval[j] = NUM2DBL(aaval);
8738 /* Use atomic weights */
8739 IntGroupIteratorReset(&iter);
8740 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8741 ap = ATOM_AT_INDEX(mol->atoms, in);
8742 weights[i] = ap->weight;
8745 wval = rb_protect(rb_ary_to_ary, wval, &status);
8750 if (RARRAY_LEN(wval) < nn) {
8754 for (i = 0; i < nn; i++) {
8755 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8760 weights[i] = NUM2DBL(wwval);
8763 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8770 IntGroupIteratorRelease(&iter);
8774 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8775 } else if (errno == 1) {
8776 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8777 } else if (errno == 2) {
8778 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8779 } else if (errno == 3) {
8780 rb_jump_tag(status);
8781 } else if (errno == 4) {
8782 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8783 } else if (errno == 5) {
8784 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8785 } else if (errno == 6) {
8786 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8788 return Qnil; /* Not reached */
8795 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8798 s_Molecule_Display(VALUE self)
8801 Data_Get_Struct(self, Molecule, mol);
8802 if (mol->mview != NULL)
8803 MainViewCallback_display(mol->mview);
8811 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8814 s_Molecule_MakeFront(VALUE self)
8817 Data_Get_Struct(self, Molecule, mol);
8818 if (mol->mview != NULL)
8819 MainViewCallback_makeFront(mol->mview);
8825 * update_enabled? -> bool
8827 * Returns true if screen update is enabled; otherwise no.
8830 s_Molecule_UpdateEnabled(VALUE self)
8833 Data_Get_Struct(self, Molecule, mol);
8834 if (mol->mview != NULL && !mol->mview->freezeScreen)
8841 * update_enabled = bool
8843 * Enable or disable screen update. This is effective for automatic update on modification.
8844 * Explicit call to molecule.display() always updates the screen.
8847 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8850 Data_Get_Struct(self, Molecule, mol);
8851 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8852 if (mol->mview != NULL)
8853 mol->mview->freezeScreen = (val == Qfalse);
8861 * show_unitcell(bool)
8862 * show_unitcell = bool
8864 * Set the flag whether to show the unit cell. If no argument is given, the
8865 * current flag is returned.
8868 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8871 Data_Get_Struct(self, Molecule, mol);
8872 if (mol->mview == NULL)
8875 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8876 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8878 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8884 * show_hydrogens(bool)
8885 * show_hydrogens = bool
8887 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8888 * current flag is returned.
8891 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8894 Data_Get_Struct(self, Molecule, mol);
8895 if (mol->mview == NULL)
8898 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8899 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8901 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8907 * show_dummy_atoms(bool)
8908 * show_dummy_atoms = bool
8910 * Set the flag whether to show the dummy atoms. If no argument is given, the
8911 * current flag is returned.
8914 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8917 Data_Get_Struct(self, Molecule, mol);
8918 if (mol->mview == NULL)
8921 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8922 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8924 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8930 * show_expanded(bool)
8931 * show_expanded = bool
8933 * Set the flag whether to show the expanded atoms. If no argument is given, the
8934 * current flag is returned.
8937 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8940 Data_Get_Struct(self, Molecule, mol);
8941 if (mol->mview == NULL)
8944 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8945 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8947 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8953 * show_ellipsoids(bool)
8954 * show_ellipsoids = bool
8956 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8957 * current flag is returned.
8960 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8963 Data_Get_Struct(self, Molecule, mol);
8964 if (mol->mview == NULL)
8967 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8968 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8970 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8975 * is_atom_visible(index) -> Boolean
8977 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8978 * as well as the molecule attributes (showHydrogens, etc.)
8981 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8986 Data_Get_Struct(self, Molecule, mol);
8987 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8988 if (idx < 0 || idx >= mol->natoms)
8990 ap = ATOM_AT_INDEX(mol->atoms, idx);
8991 if (mol->mview != NULL) {
8992 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8994 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8996 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8999 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9004 * show_graphite -> Integer
9005 * show_graphite = Integer
9006 * show_graphite = boolean
9008 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9009 * number of rings to display for each direction.
9010 * If the argument is boolean, only the show/hide flag is set.
9013 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9016 Data_Get_Struct(self, Molecule, mol);
9017 if (mol->mview == NULL)
9020 if (argv[0] == Qnil || argv[0] == Qfalse)
9021 mol->mview->showGraphiteFlag = 0;
9022 else if (argv[0] == Qtrue)
9023 mol->mview->showGraphiteFlag = 1;
9025 int n = NUM2INT(rb_Integer(argv[0]));
9027 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9028 mol->mview->showGraphite = n;
9030 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9032 return INT2NUM(mol->mview->showGraphite);
9037 * show_graphite? -> boolean
9039 * Return whether the graphite is set visible or not.
9042 s_Molecule_ShowGraphiteFlag(VALUE self)
9045 Data_Get_Struct(self, Molecule, mol);
9046 if (mol->mview == NULL)
9048 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9053 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9054 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9055 * show_periodic_image = boolean
9057 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9058 * set but no visual effects are observed.
9059 * If the argument is boolean, only the show/hide flag is modified.
9062 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9068 Data_Get_Struct(self, Molecule, mol);
9069 if (mol->mview == NULL)
9071 rb_scan_args(argc, argv, "01", &val);
9073 /* Change current settings */
9074 if (val == Qnil || val == Qfalse)
9075 mol->mview->showPeriodicImageFlag = 0;
9076 else if (val == Qtrue)
9077 mol->mview->showPeriodicImageFlag = 1;
9079 val = rb_ary_to_ary(val);
9080 for (i = 0; i < 6; i++) {
9081 if (i < RARRAY_LEN(val))
9082 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9084 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9085 rb_raise(rb_eMolbyError, "bad arguments");
9086 for (i = 0; i < 6; i++)
9087 mol->mview->showPeriodicImage[i] = ival[i];
9089 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9092 for (i = 0; i < 6; i++)
9093 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9099 * show_periodic_image? -> boolean
9101 * Return whether the periodic images are set to visible or not. This flag is
9102 * independent from the show_periodic_image settings.
9105 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9108 Data_Get_Struct(self, Molecule, mol);
9109 if (mol->mview == NULL)
9111 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9120 * Set the flag whether to draw the model in line mode. If no argument is given, the
9121 * current flag is returned.
9124 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9127 Data_Get_Struct(self, Molecule, mol);
9128 if (mol->mview == NULL)
9131 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9132 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9134 return (mol->mview->lineMode ? Qtrue : Qfalse);
9141 * Resize the model drawing to fit in the window.
9144 s_Molecule_ResizeToFit(VALUE self)
9147 Data_Get_Struct(self, Molecule, mol);
9148 if (mol->mview != NULL)
9149 MainView_resizeToFit(mol->mview);
9155 * get_view_rotation -> [[ax, ay, az], angle]
9157 * Get the current rotation for the view. Angle is in degree, not radian.
9160 s_Molecule_GetViewRotation(VALUE self)
9165 Data_Get_Struct(self, Molecule, mol);
9166 if (mol->mview == NULL)
9168 TrackballGetRotate(mol->mview->track, f);
9169 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9173 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9178 * get_view_scale -> float
9180 * Get the current scale for the view.
9183 s_Molecule_GetViewScale(VALUE self)
9186 Data_Get_Struct(self, Molecule, mol);
9187 if (mol->mview == NULL)
9189 return rb_float_new(TrackballGetScale(mol->mview->track));
9194 * get_view_center -> Vector
9196 * Get the current center point of the view.
9199 s_Molecule_GetViewCenter(VALUE self)
9204 Data_Get_Struct(self, Molecule, mol);
9205 if (mol->mview == NULL)
9207 TrackballGetTranslate(mol->mview->track, f);
9208 v.x = -f[0] * mol->mview->dimension;
9209 v.y = -f[1] * mol->mview->dimension;
9210 v.z = -f[2] * mol->mview->dimension;
9211 return ValueFromVector(&v);
9216 * set_view_rotation([ax, ay, az], angle) -> self
9218 * Set the current rotation for the view. Angle is in degree, not radian.
9221 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9226 Data_Get_Struct(self, Molecule, mol);
9227 if (mol->mview == NULL)
9229 VectorFromValue(aval, &v);
9230 if (NormalizeVec(&v, &v))
9231 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9235 f[0] = -NUM2DBL(rb_Float(angval));
9236 TrackballSetRotate(mol->mview->track, f);
9237 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9243 * set_view_scale(scale) -> self
9245 * Set the current scale for the view.
9248 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9251 Data_Get_Struct(self, Molecule, mol);
9252 if (mol->mview == NULL)
9254 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9255 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9261 * set_view_center(vec) -> self
9263 * Set the current center point of the view.
9266 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9271 Data_Get_Struct(self, Molecule, mol);
9272 if (mol->mview == NULL)
9274 VectorFromValue(aval, &v);
9275 f[0] = -v.x / mol->mview->dimension;
9276 f[1] = -v.y / mol->mview->dimension;
9277 f[2] = -v.z / mol->mview->dimension;
9278 TrackballSetTranslate(mol->mview->track, f);
9279 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9285 * set_background_color(red, green, blue)
9287 * Set the background color of the model window.
9290 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9293 Data_Get_Struct(self, Molecule, mol);
9294 if (mol->mview != NULL) {
9295 VALUE rval, gval, bval;
9296 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9297 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9304 * create_graphic(kind, color, points, fill = nil) -> integer
9306 * Create a new graphic object.
9307 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9308 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9309 * points: an array of Vectors
9313 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9319 VALUE kval, cval, pval, fval;
9320 Data_Get_Struct(self, Molecule, mol);
9321 if (mol->mview == NULL)
9322 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9323 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
9324 kval = rb_obj_as_string(kval);
9325 memset(&g, 0, sizeof(g));
9327 p = RSTRING_PTR(kval);
9328 if (strcmp(p, "line") == 0)
9329 g.kind = kMainViewGraphicLine;
9330 else if (strcmp(p, "poly") == 0)
9331 g.kind = kMainViewGraphicPoly;
9332 else if (strcmp(p, "cylinder") == 0)
9333 g.kind = kMainViewGraphicCylinder;
9334 else if (strcmp(p, "cone") == 0)
9335 g.kind = kMainViewGraphicCone;
9336 else if (strcmp(p, "ellipsoid") == 0)
9337 g.kind = kMainViewGraphicEllipsoid;
9338 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9339 g.closed = (RTEST(fval) ? 1 : 0);
9340 cval = rb_ary_to_ary(cval);
9341 n = RARRAY_LEN(cval);
9342 if (n < 3 || n >= 5)
9343 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9346 for (i = 0; i < n; i++)
9347 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9348 pval = rb_ary_to_ary(pval);
9349 n = RARRAY_LEN(pval);
9350 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9352 rb_raise(rb_eArgError, "no control points are given");
9354 case kMainViewGraphicLine:
9356 rb_raise(rb_eArgError, "the line object must have at least two control points");
9358 case kMainViewGraphicPoly:
9360 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9362 case kMainViewGraphicCylinder:
9363 case kMainViewGraphicCone:
9365 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9368 case kMainViewGraphicEllipsoid:
9372 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9375 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9376 for (i = 0; i < n; i++) {
9379 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9382 VectorFromValue(RARRAY_PTR(pval)[i], &v);
9384 g.points[i * 3] = v.x;
9385 g.points[i * 3 + 1] = v.y;
9386 g.points[i * 3 + 2] = v.z;
9388 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9390 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9391 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9392 g.points[7] = g.points[11] = g.points[3];
9394 MainView_insertGraphic(mol->mview, -1, &g);
9395 return INT2NUM(mol->mview->ngraphics - 1);
9400 * remove_graphic(index) -> integer
9402 * Remove a graphic object.
9405 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9409 Data_Get_Struct(self, Molecule, mol);
9410 if (mol->mview == NULL)
9411 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9412 i = NUM2INT(rb_Integer(ival));
9413 if (i < 0 || i >= mol->mview->ngraphics)
9414 rb_raise(rb_eArgError, "graphic index is out of range");
9415 MainView_removeGraphic(mol->mview, i);
9421 * ngraphics -> integer
9423 * Get the number of graphic objects.
9426 s_Molecule_NGraphics(VALUE self)
9429 Data_Get_Struct(self, Molecule, mol);
9430 if (mol->mview == NULL)
9431 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9432 return INT2NUM(mol->mview->ngraphics);
9437 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9439 * Change the point_index-th control point of graphic_index-th graphic object
9443 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9445 MainViewGraphic *gp;
9449 Data_Get_Struct(self, Molecule, mol);
9450 if (mol->mview == NULL)
9451 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9452 index = NUM2INT(rb_Integer(gval));
9453 if (index < 0 || index >= mol->mview->ngraphics)
9454 rb_raise(rb_eArgError, "the graphic index is out of range");
9455 gp = mol->mview->graphics + index;
9456 index = NUM2INT(rb_Integer(pval));
9457 if (index < 0 || index >= gp->npoints)
9458 rb_raise(rb_eArgError, "the point index is out of range");
9459 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9460 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9461 v.x = NUM2DBL(rb_Float(nval));
9463 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9464 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9465 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9467 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9470 v.x = kInvalidFloat;
9472 } else VectorFromValue(nval, &v);
9474 gp->points[index * 3] = v.x;
9475 gp->points[index * 3 + 1] = v.y;
9476 gp->points[index * 3 + 2] = v.z;
9477 MoleculeCallback_notifyModification(mol, 0);
9483 * set_graphic_color(graphic_index, new_value) -> new_value
9485 * Change the color of graphic_index-th graphic object
9489 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9491 MainViewGraphic *gp;
9494 Data_Get_Struct(self, Molecule, mol);
9495 if (mol->mview == NULL)
9496 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9497 index = NUM2INT(rb_Integer(gval));
9498 if (index < 0 || index >= mol->mview->ngraphics)
9499 rb_raise(rb_eArgError, "the graphic index is out of range");
9500 gp = mol->mview->graphics + index;
9501 cval = rb_ary_to_ary(cval);
9502 n = RARRAY_LEN(cval);
9503 if (n != 3 && n != 4)
9504 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9505 for (index = 0; index < n; index++) {
9506 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9510 MoleculeCallback_notifyModification(mol, 0);
9516 * show_graphic(graphic_index) -> self
9518 * Enable the visible flag of the graphic_index-th graphic object
9522 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9524 MainViewGraphic *gp;
9527 Data_Get_Struct(self, Molecule, mol);
9528 if (mol->mview == NULL)
9529 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9530 index = NUM2INT(rb_Integer(gval));
9531 if (index < 0 || index >= mol->mview->ngraphics)
9532 rb_raise(rb_eArgError, "the graphic index is out of range");
9533 gp = mol->mview->graphics + index;
9535 MoleculeCallback_notifyModification(mol, 0);
9541 * hide_graphic(graphic_index) -> self
9543 * Disable the visible flag of the graphic_index-th graphic object
9547 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9549 MainViewGraphic *gp;
9552 Data_Get_Struct(self, Molecule, mol);
9553 if (mol->mview == NULL)
9554 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9555 index = NUM2INT(rb_Integer(gval));
9556 if (index < 0 || index >= mol->mview->ngraphics)
9557 rb_raise(rb_eArgError, "the graphic index is out of range");
9558 gp = mol->mview->graphics + index;
9560 MoleculeCallback_notifyModification(mol, 0);
9568 * Show the string in the info text box.
9571 s_Molecule_ShowText(VALUE self, VALUE arg)
9574 Data_Get_Struct(self, Molecule, mol);
9575 if (mol->mview != NULL)
9576 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9582 * md_arena -> MDArena
9584 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
9585 * this molecule, a new arena is created.
9588 s_Molecule_MDArena(VALUE self)
9592 Data_Get_Struct(self, Molecule, mol);
9593 if (mol->arena == NULL)
9595 retval = ValueFromMDArena(mol->arena);
9601 * set_parameter_attr(type, index, key, value, src) -> value
9603 * This method is used only internally.
9606 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9608 /* This method is called from MolAction to change a MM parameter attribute. */
9613 Data_Get_Struct(self, Molecule, mol);
9614 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9615 vval = s_ParameterRef_SetAttr(pval, kval, vval);
9617 /* This is the special part of this method; it allows modification of the src field. */
9618 /* (ParameterRef#set_attr sets 0 to the src field) */
9619 Data_Get_Struct(pval, ParameterRef, pref);
9620 up = ParameterRefGetPar(pref);
9621 up->bond.src = FIX2INT(sval);
9628 * parameter -> Parameter
9630 * Get the local parameter of this molecule. If not defined, returns nil.
9633 s_Molecule_Parameter(VALUE self)
9636 Data_Get_Struct(self, Molecule, mol);
9637 /* if (mol->par == NULL)
9639 return s_NewParameterValueFromValue(self);
9644 * selectedMO -> IntGroup
9646 * Returns a group of selected mo in the "MO Info" table. If the MO info table
9647 * is not selected, returns nil. If the MO info table is selected but no MOs
9648 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
9651 s_Molecule_SelectedMO(VALUE self)
9656 Data_Get_Struct(self, Molecule, mol);
9657 if (mol->mview == NULL)
9659 ig = MainView_selectedMO(mol->mview);
9662 IntGroupOffset(ig, 1);
9663 val = ValueFromIntGroup(ig);
9664 IntGroupRelease(ig);
9670 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9672 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9673 * If the molecule does not contain a basis set information, then returns nil.
9676 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9679 Vector o, dx, dy, dz;
9682 Int npoints = 80 * 80 * 80;
9683 Data_Get_Struct(self, Molecule, mol);
9684 if (mol->bset == NULL)
9686 rb_scan_args(argc, argv, "01", &nval);
9688 npoints = NUM2INT(rb_Integer(nval));
9689 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9691 return rb_ary_new3(7, ValueFromVector(&o), rb_float_new(VecLength(dx)), rb_float_new(VecLength(dy)), rb_float_new(VecLength(dz)), INT2NUM(nx), INT2NUM(ny), INT2NUM(nz));
9695 s_Cubegen_callback(double progress, void *ref)
9697 MyAppCallback_setProgressValue(progress);
9698 if (MyAppCallback_checkInterrupt())
9705 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9706 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9708 * Calculate the molecular orbital with number mo and create a 'cube' file.
9709 * In the first form, the cube size is estimated from the atomic coordinates. In the
9710 * second form, the cube dimension is explicitly given.
9711 * Returns fname when successful, nil otherwise.
9712 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9713 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9714 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9717 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9719 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9721 Int mono, nx, ny, nz, npoints;
9722 Vector o, dx, dy, dz;
9725 Data_Get_Struct(self, Molecule, mol);
9726 if (mol->bset == NULL)
9727 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9728 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9730 /* Set up parameters */
9731 mono = NUM2INT(rb_Integer(mval));
9732 if (mono <= 0 || mono > mol->bset->ncomps)
9733 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9735 if (mol->bset->rflag != 0)
9736 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9737 mono += mol->bset->ncomps;
9740 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9741 /* Automatic grid formation */
9743 npoints = NUM2INT(rb_Integer(oval));
9747 else if (npoints < 8)
9748 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9749 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9750 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9754 VectorFromValue(oval, &o);
9755 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9756 VectorFromValue(dxval, &dx);
9758 dx.x = NUM2DBL(rb_Float(dxval));
9761 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9762 VectorFromValue(dyval, &dy);
9764 dy.y = NUM2DBL(rb_Float(dyval));
9767 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9768 VectorFromValue(dzval, &dz);
9770 dz.z = NUM2DBL(rb_Float(dzval));
9773 nx = NUM2INT(rb_Integer(nxval));
9774 ny = NUM2INT(rb_Integer(nyval));
9775 nz = NUM2INT(rb_Integer(nzval));
9776 if (nx <= 0 || ny <= 0 || nz <= 0)
9777 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9778 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9779 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) seem to be too large; please keep them less than 10000 and nx*ny*nz < 100000000", nx, ny, nz);
9783 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9787 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9789 /* Output to file */
9790 MoleculeCallback_displayName(mol, buf, sizeof buf);
9791 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9793 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9795 /* Discard the cube */
9796 MoleculeClearCubeAtIndex(mol, index);
9804 * Get the number of electrostatic potential info.
9807 s_Molecule_NElpots(VALUE self)
9810 Data_Get_Struct(self, Molecule, mol);
9811 return INT2NUM(mol->nelpots);
9818 * Get the electrostatic potential info at the given index. If present, then the
9819 * return value is [Vector, Float] (position and potential). If not present, then
9823 s_Molecule_Elpot(VALUE self, VALUE ival)
9827 Data_Get_Struct(self, Molecule, mol);
9828 idx = NUM2INT(rb_Integer(ival));
9829 if (idx < 0 || idx >= mol->nelpots)
9831 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
9836 * add_gaussian_orbital_shell(sym, nprims, atom_index)
9838 * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
9839 * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
9843 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
9846 int sym, nprims, a_idx, n;
9847 Data_Get_Struct(self, Molecule, mol);
9848 sym = NUM2INT(rb_Integer(symval));
9849 nprims = NUM2INT(rb_Integer(npval));
9850 a_idx = NUM2INT(rb_Integer(aval));
9851 n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
9853 rb_raise(rb_eMolbyError, "Molecule is emptry");
9855 rb_raise(rb_eMolbyError, "Low memory");
9857 rb_raise(rb_eMolbyError, "Unknown orbital type");
9859 rb_raise(rb_eMolbyError, "Unknown error");
9865 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
9867 * To be used internally. Add a gaussian primitive coefficients.
9870 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
9874 Double exponent, contraction, contraction_sp;
9875 Data_Get_Struct(self, Molecule, mol);
9876 exponent = NUM2DBL(rb_Float(expval));
9877 contraction = NUM2DBL(rb_Float(cval));
9878 contraction_sp = NUM2DBL(rb_Float(cspval));
9879 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
9881 rb_raise(rb_eMolbyError, "Molecule is emptry");
9883 rb_raise(rb_eMolbyError, "Low memory");
9885 rb_raise(rb_eMolbyError, "Unknown error");
9893 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
9896 s_Molecule_MOType(VALUE self)
9899 Data_Get_Struct(self, Molecule, mol);
9900 if (mol != NULL && mol->bset != NULL) {
9902 int rflag = mol->bset->rflag;
9905 else if (rflag == 2)
9908 return rb_str_new2(s);
9914 * set_mo_coefficients(idx, energy, coefficients)
9916 * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
9917 * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
9918 * of MO coefficients.
9921 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
9927 Data_Get_Struct(self, Molecule, mol);
9928 idx = NUM2INT(rb_Integer(ival));
9929 energy = NUM2DBL(rb_Float(eval));
9930 aval = rb_ary_to_ary(aval);
9931 ncomps = RARRAY_LEN(aval);
9932 coeffs = (Double *)calloc(sizeof(Double), ncomps);
9933 if (coeffs == NULL) {
9937 for (i = 0; i < ncomps; i++)
9938 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9939 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
9942 rb_raise(rb_eMolbyError, "Molecule is emptry");
9944 rb_raise(rb_eMolbyError, "Low memory");
9946 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
9948 rb_raise(rb_eMolbyError, "Bad MO index");
9950 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
9952 rb_raise(rb_eMolbyError, "Unknown error");
9958 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
9960 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
9961 * ne_alpha, ne_beta: number of alpha/beta electrons.
9964 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
9967 Int rflag, na, nb, n;
9968 Data_Get_Struct(self, Molecule, mol);
9969 rflag = NUM2INT(rb_Integer(rval));
9970 na = NUM2INT(rb_Integer(naval));
9971 nb = NUM2INT(rb_Integer(nbval));
9972 n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
9974 rb_raise(rb_eMolbyError, "Molecule is emptry");
9976 rb_raise(rb_eMolbyError, "Low memory");
9978 rb_raise(rb_eMolbyError, "Unknown error");
9984 * search_equivalent_atoms(ig = nil)
9986 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
9989 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
9995 Data_Get_Struct(self, Molecule, mol);
9996 if (mol->natoms == 0)
9998 rb_scan_args(argc, argv, "01", &val);
10000 ig = IntGroupFromValue(val);
10002 result = MoleculeSearchEquivalentAtoms(mol, ig);
10003 if (result == NULL)
10004 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10006 IntGroupRelease(ig);
10007 val = rb_ary_new2(mol->natoms);
10008 for (i = 0; i < mol->natoms; i++)
10009 rb_ary_push(val, INT2NUM(result[i]));
10016 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10018 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10019 * Name is the name of the new pi anchor, and group is the atoms that define
10020 * the pi system. Type (a String) is an atom type for MM implementation.
10021 * Weights represent the relative significance of the component atoms; if omitted, then
10022 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10023 * The weight values will be normalized so that the sum of the weights is 1.0.
10024 * The weight values must be positive.
10025 * Index is the atom index where the created pi-anchor is inserted in the
10026 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10027 * having the largest index.
10028 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10029 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10032 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10037 Int i, n, idx, last_component;
10041 if (argc < 2 || argc >= 6)
10042 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10046 Data_Get_Struct(self, Molecule, mol);
10047 ig = IntGroupFromValue(gval);
10048 memset(&a, 0, sizeof(a));
10049 memset(&an, 0, sizeof(an));
10050 strncpy(a.aname, StringValuePtr(nval), 4);
10051 if (a.aname[0] == '_')
10052 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10053 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
10054 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10055 if (n >= mol->natoms) {
10056 AtomConnectResize(&an.connect, 0);
10057 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10059 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10060 last_component = n;
10062 if (an.connect.count == 0)
10063 rb_raise(rb_eMolbyError, "no atoms are specified");
10064 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10065 for (i = 0; i < an.connect.count; i++) {
10066 an.coeffs[i] = 1.0 / an.connect.count;
10068 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10070 if (argv[0] != Qnil)
10071 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10075 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10076 if (argv[0] != Qnil) {
10077 VALUE aval = rb_ary_to_ary(argv[0]);
10079 if (RARRAY_LEN(aval) != an.connect.count)
10080 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10081 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10082 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10084 rb_raise(rb_eMolbyError, "the weight value must be positive");
10088 for (i = 0; i < an.connect.count; i++)
10089 an.coeffs[i] /= sum;
10094 if (argc > 0 && argv[0] != Qnil) {
10096 idx = NUM2INT(rb_Integer(argv[0]));
10098 if (idx < 0 || idx > mol->natoms) {
10099 /* Immediately after the last specified atom */
10100 idx = last_component + 1;
10102 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10103 memmove(a.anchor, &an, sizeof(PiAnchor));
10104 /* Use residue information of the last specified atom */
10105 ap = ATOM_AT_INDEX(mol->atoms, last_component);
10106 a.resSeq = ap->resSeq;
10107 strncpy(a.resName, ap->resName, 4);
10108 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10110 MoleculeCalculatePiAnchorPosition(mol, idx);
10111 aref = AtomRefNew(mol, idx);
10112 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10117 * current -> Molecule
10119 * Get the currently "active" molecule.
10122 s_Molecule_Current(VALUE klass)
10124 return ValueFromMolecule(MoleculeCallback_currentMolecule());
10129 * Molecule[] -> Molecule
10130 * Molecule[n] -> Molecule
10131 * Molecule[name] -> Molecule
10132 * Molecule[name, k] -> Molecule
10133 * Molecule[regex] -> Molecule
10134 * Molecule[regex, k] -> Molecule
10136 * Molecule[] is equivalent to Molecule.current.
10137 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
10138 * Molecule[name] gives the first document (in the order of creation time) that has
10139 * the given name. If a second argument (k) is given, the k-th document that has the
10140 * given name is returned.
10141 * Molecule[regex] gives the first document (in the order of creation time) that
10142 * has a name matching the regular expression. If a second argument (k) is given,
10143 * the k-th document that has a name matching the re is returned.
10146 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
10152 rb_scan_args(argc, argv, "02", &val, &kval);
10154 return s_Molecule_Current(klass);
10155 if (rb_obj_is_kind_of(val, rb_cInteger)) {
10156 idx = NUM2INT(val);
10157 mol = MoleculeCallback_moleculeAtIndex(idx);
10158 } else if (rb_obj_is_kind_of(val, rb_cString)) {
10159 char *p = StringValuePtr(val);
10160 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10161 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10162 MoleculeCallback_displayName(mol, buf, sizeof buf);
10163 if (strcmp(buf, p) == 0 && --k == 0)
10166 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
10167 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10168 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10170 MoleculeCallback_displayName(mol, buf, sizeof buf);
10171 name = rb_str_new2(buf);
10172 if (rb_reg_match(val, name) != Qnil && --k == 0)
10175 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
10179 else return ValueFromMolecule(mol);
10184 * list -> array of Molecules
10186 * Get the list of molecules associated to the documents, in the order of creation
10187 * time of the document. If no document is open, returns an empry array.
10190 s_Molecule_List(VALUE klass)
10196 ary = rb_ary_new();
10197 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
10198 rb_ary_push(ary, ValueFromMolecule(mol));
10206 * ordered_list -> array of Molecules
10208 * Get the list of molecules associated to the documents, in the order of front-to-back
10209 * ordering of the associated window. If no document is open, returns an empry array.
10212 s_Molecule_OrderedList(VALUE klass)
10218 ary = rb_ary_new();
10219 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
10220 rb_ary_push(ary, ValueFromMolecule(mol));
10228 * error_message -> String
10230 * Get the error_message from the last load/save method. If no error, returns nil.
10233 s_Molecule_ErrorMessage(VALUE klass)
10235 if (gLoadSaveErrorMessage == NULL)
10237 else return rb_str_new2(gLoadSaveErrorMessage);
10242 * set_error_message(String)
10243 * Molecule.error_message = String
10245 * Get the error_message from the last load/save method. If no error, returns nil.
10248 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
10250 if (gLoadSaveErrorMessage != NULL) {
10251 free(gLoadSaveErrorMessage);
10252 gLoadSaveErrorMessage = NULL;
10254 if (sval != Qnil) {
10255 sval = rb_str_to_str(sval);
10256 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
10263 * self == Molecule -> boolean
10265 * True if the two arguments point to the same molecule.
10268 s_Molecule_Equal(VALUE self, VALUE val)
10270 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
10271 Molecule *mol1, *mol2;
10272 Data_Get_Struct(self, Molecule, mol1);
10273 Data_Get_Struct(val, Molecule, mol2);
10274 return (mol1 == mol2 ? Qtrue : Qfalse);
10275 } else return Qfalse;
10278 /* The callback functions for call_subprocess_async */
10280 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
10283 VALUE procval, retval, args[2];
10284 args[0] = ValueFromMolecule(mol);
10285 args[1] = INT2NUM(status);
10286 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
10287 if (procval != Qnil) {
10288 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10289 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10296 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
10299 VALUE procval, retval, args[2];
10300 args[0] = ValueFromMolecule(mol);
10301 args[1] = INT2NUM(tcount);
10302 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
10303 if (procval != Qnil) {
10304 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10305 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10313 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
10315 * Call subprocess asynchronically.
10316 * If end_callback is given, it will be called (with two arguments self and termination status)
10317 * when the subprocess terminated.
10318 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
10319 * If timer_callback returns nil or false, then the subprocess will be interrupted.
10320 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
10321 * filename begins with ">>", then the message will be appended to the file.
10322 * If the filename is "/dev/null" or "NUL", then the message will be lost.
10323 * If the argument is nil, then the message will be sent to the Ruby console.
10324 * Returns the process ID as an integer.
10327 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
10329 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
10333 FILE *fpout, *fperr;
10334 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
10335 Data_Get_Struct(self, Molecule, mol);
10337 if (stdout_val == Qnil) {
10340 sout = StringValuePtr(stdout_val);
10341 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
10344 if (strncmp(sout, ">>", 2) == 0) {
10346 fpout = fopen(sout, "a");
10350 fpout = fopen(sout, "w");
10353 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
10356 if (stderr_val == Qnil) {
10359 serr = StringValuePtr(stderr_val);
10360 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
10363 if (strncmp(serr, ">>", 2) == 0) {
10365 fpout = fopen(serr, "a");
10369 fperr = fopen(serr, "w");
10372 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
10376 /* Register procs as instance variables */
10377 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
10378 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
10379 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
10380 if (fpout != NULL && fpout != (FILE *)1)
10382 if (fperr != NULL && fperr != (FILE *)1)
10392 /* Define module Molby */
10393 rb_mMolby = rb_define_module("Molby");
10395 /* Define Vector3D, Transform, IntGroup */
10398 /* Define MDArena */
10399 Init_MolbyMDTypes();
10401 /* class Molecule */
10402 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
10403 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
10404 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
10405 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
10406 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
10407 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
10408 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
10409 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
10410 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
10411 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
10412 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
10413 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
10414 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
10415 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
10416 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
10417 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
10418 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
10419 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
10420 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
10421 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
10422 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
10423 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
10424 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
10425 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
10426 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
10427 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
10428 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
10429 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
10430 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
10431 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
10432 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
10433 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
10434 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
10435 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
10436 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
10437 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
10438 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
10439 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
10441 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
10442 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
10443 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
10444 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
10445 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
10447 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
10448 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
10449 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
10450 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
10451 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
10452 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
10454 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
10455 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
10456 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
10457 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
10458 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
10459 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
10460 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
10461 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
10462 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
10463 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
10464 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
10465 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
10466 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
10467 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
10468 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
10469 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
10470 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
10471 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
10472 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
10473 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
10474 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
10475 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
10476 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
10477 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
10478 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
10479 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
10480 rb_define_alias(rb_cMolecule, "+", "add");
10481 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
10482 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
10483 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
10484 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
10485 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
10486 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
10487 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
10488 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
10489 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
10490 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
10491 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
10492 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
10493 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
10494 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
10495 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
10496 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
10497 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
10498 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
10499 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
10500 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
10501 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
10502 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
10503 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
10504 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
10505 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
10506 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
10507 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
10508 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
10509 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
10510 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
10511 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
10512 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
10513 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
10514 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
10515 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
10516 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
10517 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
10518 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
10519 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
10520 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
10521 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
10522 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
10523 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
10524 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
10525 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
10526 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
10527 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
10528 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
10529 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
10530 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
10531 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
10532 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
10533 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
10534 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
10535 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
10536 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
10537 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
10538 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
10539 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
10540 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
10541 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
10542 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
10543 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
10544 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
10545 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
10546 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
10547 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
10548 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
10549 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
10550 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
10551 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
10552 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
10553 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
10554 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
10555 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
10556 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
10557 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
10558 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
10559 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
10560 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
10561 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
10562 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
10563 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
10564 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
10565 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
10566 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
10567 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
10568 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
10569 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
10570 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
10571 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
10572 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
10573 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
10574 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
10575 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
10576 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
10577 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
10578 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
10579 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
10580 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
10581 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
10582 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
10583 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
10584 #if 1 || !defined(__CMDMAC__)
10585 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
10586 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
10587 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
10588 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
10589 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
10590 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
10591 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
10592 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
10593 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
10594 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
10595 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
10596 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
10597 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
10598 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
10600 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
10601 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
10602 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
10603 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
10604 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
10605 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
10606 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
10607 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
10608 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
10609 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
10610 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
10611 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
10612 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
10613 rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
10614 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
10616 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
10617 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
10618 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
10620 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
10621 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
10622 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
10623 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
10624 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
10625 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
10626 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
10627 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
10629 /* class MolEnumerable */
10630 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
10631 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
10632 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
10633 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
10634 rb_define_alias(rb_cMolEnumerable, "size", "length");
10635 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
10636 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
10638 /* class AtomRef */
10639 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
10640 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
10642 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
10643 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
10644 s_AtomAttrDefTable[i].id = rb_intern(buf);
10645 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
10647 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
10649 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
10650 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
10651 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
10652 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
10653 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
10654 rb_global_variable(&s_SetAtomAttrString);
10655 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
10656 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
10658 /* class Parameter */
10659 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
10660 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10661 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10662 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10663 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10664 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10665 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10666 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10667 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
10668 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10669 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10670 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10671 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10672 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10673 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10674 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10675 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10676 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10677 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10678 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10679 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10680 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10681 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10682 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10683 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10684 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10685 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
10686 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
10687 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10688 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10689 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10690 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10691 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10692 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10693 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10694 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
10695 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10696 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10697 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10698 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10699 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10700 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10701 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10702 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10703 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10704 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10705 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10706 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10707 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10708 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10709 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10710 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10711 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10712 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10714 /* class ParEnumerable */
10715 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10716 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10717 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10718 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10719 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10720 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10721 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10722 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10723 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10724 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10725 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
10727 /* class ParameterRef */
10728 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10729 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10731 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10732 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10733 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10734 if (s_ParameterAttrDefTable[i].symref != NULL)
10735 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10736 if (s_ParameterAttrDefTable[i].setter != NULL) {
10738 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10741 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10742 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10743 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10744 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10745 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10746 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10747 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10748 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
10750 /* class MolbyError */
10751 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10753 /* module Kernel */
10754 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10755 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10756 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10757 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10758 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10759 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10760 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10761 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10762 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
10763 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10764 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10765 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10766 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10767 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10768 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
10769 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
10770 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10771 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10772 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
10773 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
10774 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
10775 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
10776 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
10777 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
10779 s_ID_equal = rb_intern("==");
10780 g_RubyID_call = rb_intern("call");
10783 #pragma mark ====== External functions ======
10785 static VALUE s_ruby_top_self = Qfalse;
10788 s_evalRubyScriptOnMoleculeSub(VALUE val)
10790 void **ptr = (void **)val;
10791 Molecule *mol = (Molecule *)ptr[1];
10792 VALUE sval = rb_str_new2((char *)ptr[0]);
10794 if (s_ruby_top_self == Qfalse) {
10795 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10797 if (ptr[2] == NULL) {
10800 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
10804 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 1, sval);
10806 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 4, sval, Qnil, fnval, INT2FIX(1));
10808 VALUE mval = ValueFromMolecule(mol);
10810 return rb_funcall(mval, rb_intern("instance_eval"), 1, sval);
10811 else return rb_funcall(mval, rb_intern("instance_eval"), 3, sval, fnval, INT2FIX(1));
10816 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
10820 VALUE save_interrupt_flag;
10821 char *save_ruby_sourcefile;
10822 int save_ruby_sourceline;
10823 if (gMolbyIsCheckingInterrupt) {
10824 MolActionAlertRubyIsRunning();
10826 return (RubyValue)Qnil;
10829 args[0] = (void *)script;
10830 args[1] = (void *)mol;
10831 args[2] = (void *)fname;
10832 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
10833 save_ruby_sourcefile = ruby_sourcefile;
10834 save_ruby_sourceline = ruby_sourceline;
10835 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
10836 if (*status != 0) {
10837 /* Is this 'exit' exception? */
10838 VALUE last_exception = rb_gv_get("$!");
10839 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
10840 /* Capture exit and return the status value */
10841 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
10845 s_SetInterruptFlag(Qnil, save_interrupt_flag);
10846 ruby_sourcefile = save_ruby_sourcefile;
10847 ruby_sourceline = save_ruby_sourceline;
10853 Molby_showRubyValue(RubyValue value, char **outValueString)
10855 VALUE val = (VALUE)value;
10856 if (gMolbyIsCheckingInterrupt) {
10857 MolActionAlertRubyIsRunning();
10864 val = rb_protect(rb_inspect, val, &status);
10866 str = StringValuePtr(val);
10867 if (outValueString != NULL)
10868 *outValueString = strdup(str);
10869 MyAppCallback_showScriptMessage("%s", str);
10874 Molby_showError(int status)
10876 static const int tag_raise = 6;
10877 char *msg = NULL, *msg2;
10878 VALUE val, backtrace;
10879 int interrupted = 0;
10880 if (status == tag_raise) {
10881 VALUE eclass = CLASS_OF(ruby_errinfo);
10882 if (eclass == rb_eInterrupt) {
10888 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
10890 val = rb_eval_string_protect("$!.to_s", &status);
10892 msg = RSTRING_PTR(val);
10893 else msg = "(message not available)";
10895 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
10896 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
10902 Molby_getDescription(void)
10904 extern const char *gVersionString, *gCopyrightString;
10905 extern int gRevisionNumber;
10906 extern char *gLastBuildString;
10908 char *revisionString;
10909 if (gRevisionNumber > 0) {
10910 asprintf(&revisionString, ", revision %d", gRevisionNumber);
10911 } else revisionString = "";
10913 "Molby %s%s\n%s\nLast compile: %s\n"
10914 #if !defined(__CMDMAC__)
10920 "ruby %s, http://www.ruby-lang.org/\n"
10922 "FFTW 3.3.2, http://www.fftw.org/\n"
10923 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
10924 " and Massachusetts Institute of Technology",
10925 gVersionString, revisionString, gCopyrightString, gLastBuildString,
10926 #if !defined(__CMDMAC__)
10927 MyAppCallback_getGUIDescriptionString(),
10929 gRubyVersion, gRubyCopyright);
10930 if (revisionString[0] != 0)
10931 free(revisionString);
10936 Molby_startup(const char *script, const char *dir)
10941 char *respath, *p, *wbuf;
10943 /* Get version/copyright string from Ruby interpreter */
10945 gRubyVersion = strdup(ruby_version);
10946 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
10947 #if defined(__CMDMAC__)
10950 " ", /* Indent for displaying in About dialog */
10952 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
10955 /* Read build and revision information for Molby */
10958 extern int gRevisionNumber;
10959 extern char *gLastBuildString;
10960 FILE *fp = fopen("../buildInfo.txt", "r");
10961 gLastBuildString = "";
10963 if (fgets(buf, sizeof(buf), fp) != NULL) {
10964 char *p1 = strchr(buf, '\"');
10965 char *p2 = strrchr(buf, '\"');
10966 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
10967 memmove(buf, p1 + 1, p2 - p1 - 1);
10968 buf[p2 - p1 - 1] = 0;
10969 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
10974 fp = fopen("../revisionInfo.txt", "r");
10975 gRevisionNumber = 0;
10977 if (fgets(buf, sizeof(buf), fp) != NULL) {
10978 gRevisionNumber = strtol(buf, NULL, 0);
10984 #if defined(__CMDMAC__)
10985 wbuf = Molby_getDescription();
10986 printf("%s\n", wbuf);
10990 /* Read atom display parameters */
10991 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
10992 #if defined(__CMDMAC__)
10993 fprintf(stderr, "%s\n", wbuf);
10995 MyAppCallback_setConsoleColor(1);
10996 MyAppCallback_showScriptMessage("%s", wbuf);
10997 MyAppCallback_setConsoleColor(0);
11002 /* Read default parameters */
11003 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
11004 if (wbuf != NULL) {
11005 #if defined(__CMDMAC__)
11006 fprintf(stderr, "%s\n", wbuf);
11008 MyAppCallback_setConsoleColor(1);
11009 MyAppCallback_showScriptMessage("%s", wbuf);
11010 MyAppCallback_setConsoleColor(0);
11015 /* Initialize Ruby interpreter */
11018 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
11020 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
11021 ruby_incpush(libpath);
11025 ruby_script("Molby");
11027 /* Find the resource path (the parent directory of the given directory) */
11028 respath = strdup(dir);
11029 p = strrchr(respath, '/');
11030 if (p == NULL && PATH_SEPARATOR != '/')
11031 p = strrchr(respath, PATH_SEPARATOR);
11034 val = Ruby_NewFileStringValue(respath);
11035 rb_define_global_const("MolbyResourcePath", val);
11038 /* Define Molby classes */
11040 RubyDialogInitClass();
11042 rb_define_const(rb_mMolby, "ResourcePath", val);
11043 val = Ruby_NewFileStringValue(dir);
11044 rb_define_const(rb_mMolby, "ScriptPath", val);
11045 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
11046 val = Ruby_NewFileStringValue(p);
11047 rb_define_const(rb_mMolby, "MbsfPath", val);
11050 #if defined(__CMDMAC__)
11051 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
11053 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
11058 /* Create objects for stdout and stderr */
11059 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11060 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
11061 rb_gv_set("$stdout", val);
11062 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11063 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
11064 rb_gv_set("$stderr", val);
11066 /* Create objects for stdin */
11067 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11068 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
11069 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
11070 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
11071 rb_gv_set("$stdin", val);
11075 /* Global variable to hold backtrace */
11076 rb_define_variable("$backtrace", &gMolbyBacktrace);
11079 /* Register interrupt check code */
11080 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
11084 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
11085 s_SetIntervalTimer(0, 50);
11088 /* Read the startup script */
11089 if (script != NULL && script[0] != 0) {
11090 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
11092 rb_load_protect(rb_str_new2(script), 0, &status);
11095 Molby_showError(status);
11097 MyAppCallback_showScriptMessage("Done.\n");
11102 Molby_buildARGV(int argc, const char **argv)
11105 rb_ary_clear(rb_argv);
11106 for (i = 0; i < argc; i++) {
11107 VALUE arg = rb_tainted_str_new2(argv[i]);
11109 rb_ary_push(rb_argv, arg);