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 "ruby/version.h" /* for RUBY_BIRTH_YEAR etc. */
30 /*#include <node.h> *//* for rb_add_event_hook() */
32 #if defined(__WXMAC__) || defined(__CMDMAC__)
33 #define USE_PTHREAD_FOR_TIMER 1
37 #if USE_PTHREAD_FOR_TIMER
38 #include <unistd.h> /* for usleep() */
39 #include <pthread.h> /* for pthread */
41 #include <signal.h> /* for sigaction() */
45 #include "../Missing.h"
47 #pragma mark ====== Global Values ======
51 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
52 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
54 VALUE gMolbyBacktrace;
55 VALUE gScriptMenuCommands;
56 VALUE gScriptMenuEnablers;
58 int gMolbyRunLevel = 0;
59 int gMolbyIsCheckingInterrupt = 0;
61 char *gRubyVersion, *gRubyCopyright;
64 static ID s_ID_equal; /* rb_intern("==") */
68 /* Symbols for atom attributes */
70 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
71 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
72 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
73 s_RSym, s_XSym, s_YSym, s_ZSym,
74 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
75 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
76 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
77 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
78 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
79 s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
81 /* Symbols for parameter attributes */
83 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
84 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
87 /* s_A14Sym, s_B14Sym, */
88 s_Req14Sym, s_Eps14Sym,
89 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
90 s_CommentSym, s_SourceSym;
94 * Get ary[i] by calling "[]" method
97 Ruby_ObjectAtIndex(VALUE ary, int idx)
99 static ID index_method = 0;
100 if (TYPE(ary) == T_ARRAY) {
101 int len = RARRAY_LEN(ary);
102 if (idx >= 0 && idx < len)
103 return (RARRAY_PTR(ary))[idx];
106 if (index_method == 0)
107 index_method = rb_intern("[]");
108 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
112 Ruby_FileStringValuePtr(VALUE *valp)
115 char *p = strdup(StringValuePtr(*valp));
116 translate_char(p, '/', '\\');
117 *valp = rb_str_new2(p);
119 return StringValuePtr(*valp);
121 return StringValuePtr(*valp);
126 Ruby_NewFileStringValue(const char *fstr)
130 char *p = strdup(fstr);
131 translate_char(p, '\\', '/');
132 retval = rb_str_new2(p);
136 return rb_str_new2(fstr);
141 Ruby_ObjToStringObj(VALUE val)
147 return rb_str_new2(rb_id2name(SYM2ID(val)));
149 return rb_str_to_str(val);
153 #pragma mark ====== Message input/output ======
157 * message_box(str, title, button = nil, icon = :info)
159 * Show a message box.
160 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
161 * Icon: :info, :warning, :error
164 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
166 char *str, *title, *s;
168 VALUE sval, tval, bval, ival;
169 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
170 str = StringValuePtr(sval);
171 title = StringValuePtr(tval);
173 bval = Ruby_ObjToStringObj(bval);
174 s = RSTRING_PTR(bval);
175 if (strncmp(s, "ok", 2) == 0)
177 else if (strncmp(s, "cancel", 6) == 0)
180 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
183 ival = Ruby_ObjToStringObj(ival);
184 s = RSTRING_PTR(ival);
185 if (strncmp(s, "info", 4) == 0)
187 else if (strncmp(s, "warn", 4) == 0)
189 else if (strncmp(s, "err", 3) == 0)
192 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
194 MyAppCallback_messageBox(str, title, buttons, icon);
200 * error_message_box(str)
202 * Show an error message box.
205 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
207 char *str = StringValuePtr(sval);
208 MyAppCallback_errorMessageBox("%s", str);
214 * ask(prompt, default = nil) -> string
216 * Open a modal dialog and get a line of text.
219 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
221 volatile VALUE prompt, message;
224 rb_scan_args(argc, argv, "11", &prompt, &message);
225 if (message != Qnil) {
226 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
227 buf[sizeof buf - 1] = 0;
229 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
231 return rb_str_new2(buf);
238 * show_console_window
240 * Show the console window and bring to the front.
243 s_Kernel_ShowConsoleWindow(VALUE self)
245 MyAppCallback_showConsoleWindow();
251 * hide_console_window
253 * Hide the console window.
256 s_Kernel_HideConsoleWindow(VALUE self)
258 MyAppCallback_hideConsoleWindow();
266 * Ring the system bell.
269 s_Kernel_Bell(VALUE self)
271 MyAppCallback_bell();
277 * play_sound(filename, flag = 0)
279 * Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
280 * 1, play the sound asynchronously; 3, play the sound with loop asynchronously
283 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
288 rb_scan_args(argc, argv, "11", &fnval, &flval);
291 else flag = NUM2INT(rb_Integer(flval));
292 fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
293 fname = StringValuePtr(fnval);
294 retval = MyAppCallback_playSound(fname, flag);
295 return (retval ? Qtrue : Qnil);
302 * Stop the sound if playing.
305 s_Kernel_StopSound(VALUE self)
307 MyAppCallback_stopSound();
313 * export_to_clipboard(str)
315 * Export the given string to clipboard.
318 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
320 #if !defined(__CMDMAC__)
321 const char *s = StringValuePtr(sval);
324 /* Convert the end-of-line characters */
325 { const char *p; int nc; char *np;
327 for (p = s; *p != 0; p++) {
331 ns = (char *)malloc(strlen(s) + nc + 1);
332 for (np = ns, p = s; *p != 0; p++, np++) {
340 ns = (char *)malloc(strlen(s) + 1);
344 /* wxMac still has Carbon code. Oops. */
345 for (np = ns; *np != 0; np++) {
352 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
353 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
362 * Put the message in the main text view in black color.
365 s_StandardOutput(VALUE self, VALUE str)
368 MyAppCallback_setConsoleColor(0);
369 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
377 * Put the message in the main text view in red color.
380 s_StandardErrorOutput(VALUE self, VALUE str)
383 MyAppCallback_setConsoleColor(1);
384 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
385 MyAppCallback_setConsoleColor(0);
394 * Flush the standard (error) output. Actually do nothing.
397 s_FlushConsoleOutput(VALUE self)
404 * stdin.gets(rs = $/)
406 * Read one line message via dialog box.
409 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
412 pval = rb_str_new2("Enter a line:");
413 rval = s_Kernel_Ask(1, &pval, self);
416 rb_str_cat2(rval, "\n");
422 * stdin.method_missing(name, args, ...)
424 * Throw an exception, noting only gets and readline are defined.
427 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
430 rb_scan_args(argc, argv, "10", &nval);
431 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
432 return Qnil; /* Not reached */
435 #pragma mark ====== Track key events ======
437 /* User interrupt handling
438 * User interrupt (command-period on Mac OS) is handled by periodic polling of
439 * key events. This polling should only be enabled during "normal" execution
440 * of scripts and must be disabled when the rest of the application (or Ruby
441 * script itself) is handling GUI. This is ensured by appropriate calls to
442 * enable_interrupt and disable_interrupt. */
444 static VALUE s_interrupt_flag = Qfalse;
447 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
449 volatile VALUE message;
451 if (Ruby_GetInterruptFlag() == Qtrue) {
452 rb_scan_args(argc, argv, "01", &message);
454 p = StringValuePtr(message);
457 MyAppCallback_showProgressPanel(p);
463 s_HideProgressPanel(VALUE self)
465 MyAppCallback_hideProgressPanel();
470 s_SetProgressValue(VALUE self, VALUE val)
472 double dval = NUM2DBL(rb_Float(val));
473 MyAppCallback_setProgressValue(dval);
478 s_SetProgressMessage(VALUE self, VALUE msg)
483 else p = StringValuePtr(msg);
484 MyAppCallback_setProgressMessage(p);
489 s_SetInterruptFlag(VALUE self, VALUE val)
493 if (val == Qfalse || val == Qnil)
497 oldval = s_interrupt_flag;
499 s_interrupt_flag = val;
501 s_HideProgressPanel(self);
508 s_GetInterruptFlag(VALUE self)
510 return s_SetInterruptFlag(self, Qundef);
515 s_Ruby_CallMethod(VALUE val)
517 void **ptr = (void **)val;
518 VALUE receiver = (VALUE)ptr[0];
519 ID method_id = (ID)ptr[1];
520 VALUE args = (VALUE)ptr[2];
522 if (method_id == 0) {
523 /* args should be a string, which is evaluated */
524 if (receiver == Qnil) {
525 retval = rb_eval_string(StringValuePtr(args));
527 retval = rb_obj_instance_eval(1, &args, receiver);
530 /* args should be an array of arguments */
531 retval = rb_apply(receiver, method_id, args);
537 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
539 VALUE retval, save_interrupt_flag;
541 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
542 ptr[0] = (void *)receiver;
543 ptr[1] = (void *)method_id;
544 ptr[2] = (void *)args;
545 MyAppCallback_beginUndoGrouping();
546 retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
547 MyAppCallback_endUndoGrouping();
548 s_SetInterruptFlag(Qnil, save_interrupt_flag);
549 MyAppCallback_hideProgressPanel(); /* In case when the progress panel is still onscreen */
\v
555 Ruby_SetInterruptFlag(VALUE val)
557 return s_SetInterruptFlag(Qnil, val);
561 Ruby_GetInterruptFlag(void)
563 return s_SetInterruptFlag(Qnil, Qundef);
568 * check_interrupt -> integer
570 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
573 s_Kernel_CheckInterrupt(VALUE self)
575 if (Ruby_GetInterruptFlag() == Qfalse)
577 else if (MyAppCallback_checkInterrupt())
579 else return INT2NUM(0);
582 static volatile unsigned long sITimerCount = 0;
585 static HANDLE sITimerEvent;
586 static HANDLE sITimerThread;
587 static int sITimerInterval;
589 static __stdcall unsigned
590 s_ITimerThreadFunc(void *p)
592 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
598 #elif USE_PTHREAD_FOR_TIMER
601 static pthread_t sTimerThread;
603 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
604 static volatile signed char sTimerFlag = -1;
605 static volatile int sTimerIntervalMicrosec = 0;
608 s_TimerThreadEntry(void *param)
611 usleep(sTimerIntervalMicrosec);
614 else if (sTimerFlag == -2)
623 s_SignalAction(int n)
629 s_SetIntervalTimer(int n, int msec)
633 /* Start interval timer */
634 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
635 sITimerInterval = msec;
637 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
640 /* Stop interval timer */
642 SetEvent(sITimerEvent); /* Tell thread to terminate */
644 WaitForSingleObject(sITimerThread, 1000);
645 CloseHandle(sITimerThread);
648 CloseHandle(sITimerEvent);
650 sITimerThread = NULL;
652 #elif USE_PTHREAD_FOR_TIMER
654 if (sTimerFlag == -1) {
655 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
657 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
660 sTimerFlag = 0; /* Active */
661 sTimerIntervalMicrosec = msec * 1000;
662 } else if (sTimerFlag != -1)
663 sTimerFlag = 1; /* Inactive */
665 static struct itimerval sOldValue;
666 static struct sigaction sOldAction;
667 struct itimerval val;
668 struct sigaction act;
671 act.sa_handler = s_SignalAction;
674 sigaction(SIGALRM, &act, &sOldAction);
675 val.it_value.tv_sec = 0;
676 val.it_value.tv_usec = msec * 1000;
677 val.it_interval.tv_sec = 0;
678 val.it_interval.tv_usec = msec * 1000;
679 setitimer(ITIMER_REAL, &val, &sOldValue);
681 setitimer(ITIMER_REAL, &sOldValue, &val);
682 sigaction(SIGALRM, &sOldAction, &act);
688 s_GetTimerCount(void)
694 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
695 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
697 if (s_interrupt_flag != Qfalse) {
698 static unsigned long sLastTime = 0;
699 unsigned long currentTime;
701 currentTime = s_GetTimerCount();
702 if (currentTime != sLastTime) {
703 sLastTime = currentTime;
704 gMolbyIsCheckingInterrupt = 1;
705 flag = MyAppCallback_checkInterrupt();
706 gMolbyIsCheckingInterrupt = 0;
708 s_SetInterruptFlag(Qnil, Qfalse);
715 #pragma mark ====== Menu handling ======
719 * register_menu(title, method, enable_proc = nil)
721 * Register the method (specified as a symbol) in the script menu.
722 * The method must be either an instance method of Molecule with no argument,
723 * or a class method of Molecule with one argument (the current molecule),
724 * or a proc object with one argument (the current molecule).
725 * The menu associated with the class method can be invoked even when no document
726 * is open (the argument is set to Qnil in this case). On the other hand, the
727 * menu associated with the instance method can only be invoked when at least one
728 * document is active.
729 * If enable_proc is non-nil, then it is called whenever the availability of
730 * the menu command is tested. It is usually a proc object with one argument
731 * (the current molecule or nil). As a special case, the following symbols can
732 * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
733 * the top-level molecule has at least one atom), :selection (enabled when
734 * the top-level molecule has one or more selected atoms).
737 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
740 VALUE tval, mval, pval;
741 static VALUE sMolSym, sNonEmptySym, sSelectionSym;
742 static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
743 rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
744 tval = rb_str_to_str(tval);
745 n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
748 if (TYPE(mval) == T_SYMBOL) {
749 /* Create an appropriate proc object */
750 const char *name = rb_id2name(SYM2ID(mval));
752 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
753 /* Defined as a Molecule method */
754 asprintf(&s, "lambda { |m| m.%s }", name);
756 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
757 /* Defined as a Molecule class method */
758 asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
760 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
761 mval = rb_eval_string(s);
764 if (sMolSym == Qfalse) {
765 sMolSym = ID2SYM(rb_intern("mol"));
766 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
767 sSelectionSym = ID2SYM(rb_intern("selection"));
768 sMolProc = rb_eval_string("lambda { |m| m != nil }");
769 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
770 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
771 sTrueProc = rb_eval_string("lambda { |m| true }");
772 rb_global_variable(&sMolProc);
773 rb_global_variable(&sNonEmptyProc);
774 rb_global_variable(&sSelectionProc);
775 rb_global_variable(&sTrueProc);
783 } else if (pval == sMolSym)
785 else if (pval == sNonEmptySym)
786 pval = sNonEmptyProc;
787 else if (pval == sSelectionSym)
788 pval = sSelectionProc;
789 rb_ary_store(gScriptMenuCommands, n, mval);
790 rb_ary_store(gScriptMenuEnablers, n, pval);
795 s_Kernel_LookupMenu(VALUE self, VALUE title)
797 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
802 s_Ruby_UpdateUI_handler(VALUE data)
804 void **p = (void **)data;
805 int index = (int)p[0];
806 Molecule *mol = (Molecule *)p[1];
807 int *outChecked = (int *)p[2];
808 char **outTitle = (char **)p[3];
809 VALUE mval = ValueFromMolecule(mol);
810 VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
811 static ID call_id = 0;
813 call_id = rb_intern("call");
816 pval = rb_funcall(pval, call_id, 1, mval);
817 if (rb_obj_is_kind_of(pval, rb_cArray)) {
819 if (outChecked != NULL) {
820 val = rb_ary_entry(pval, 1); /* Checked or not */
821 *outChecked = (RTEST(val) ? 1 : 0);
823 if (outTitle != NULL) {
824 val = rb_ary_entry(pval, 2); /* Text */
825 if (TYPE(val) == T_STRING) {
826 *outTitle = strdup(StringValuePtr(val));
829 pval = rb_ary_entry(pval, 0);
835 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
840 p[0] = (void *)index;
844 retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
845 return (RTEST(retval) ? 1 : 0);
850 s_Ruby_methodType_sub(VALUE data)
852 const char **p = (const char **)data;
853 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
854 ID mid = rb_intern(p[1]);
856 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
858 else if (rb_respond_to(klass, mid))
861 return INT2FIX(ival);
864 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
865 has the singleton method (class method) with the given name, 0 otherwise. */
867 Ruby_methodType(const char *className, const char *methodName)
874 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
876 return FIX2INT(retval);
883 * execute_script_file(fname)
885 * Execute the script in the given file. If a molecule is active, then
886 * the script is evaluated as Molecule.current.instance_eval(script).
887 * Before entering the script, the current directory is set to the parent
888 * directory of the script.
891 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
894 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
895 if (retval == (VALUE)6 && status == -1)
896 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
906 * Get the directory suitable for storing user documents. On Windows
907 * it is the home directory + "My Documents". On other platforms
908 * it is the home directory.
911 s_Kernel_DocumentHome(VALUE self)
913 char *s = MyAppCallback_getDocumentHomeDir();
914 VALUE retval = Ruby_NewFileStringValue(s);
919 /* The callback function for call_subprocess */
921 s_Kernel_CallSubProcess_Callback(void *data)
924 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
925 if (status != 0 || retval == Qnil || retval == Qfalse)
932 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
934 * Call subprocess. A progress dialog window is displayed, with a message
935 * "Running #{process_name}...".
936 * A callback proc can be given, which is called periodically during execution. If the proc returns
937 * nil or false, then the execution will be interrupted.
938 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
939 * filename begins with ">>", then the message will be appended to the file.
940 * If the filename is "/dev/null" or "NUL", then the message will be lost.
941 * If the argument is nil, then the message will be sent to the Ruby console.
944 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
946 VALUE cmd, procname, cproc, stdout_val, stderr_val;
951 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
953 if (stdout_val == Qnil) {
956 sout = StringValuePtr(stdout_val);
957 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
960 if (strncmp(sout, ">>", 2) == 0) {
962 fpout = fopen(sout, "a");
966 fpout = fopen(sout, "w");
969 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
972 if (stderr_val == Qnil) {
975 serr = StringValuePtr(stderr_val);
976 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
979 if (strncmp(serr, ">>", 2) == 0) {
981 fpout = fopen(serr, "a");
985 fperr = fopen(serr, "w");
988 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
992 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
994 if (fpout != NULL && fpout != (FILE *)1)
996 if (fperr != NULL && fperr != (FILE *)1)
1008 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
1011 s_Kernel_Backquote(VALUE self, VALUE cmd)
1016 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
1018 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
1020 val = rb_str_new2(buf);
1023 val = rb_str_new2("");
1028 #pragma mark ====== User defaults ======
1032 * get_global_settings(key)
1034 * Get a setting data for key from the application preferences.
1037 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1039 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1041 VALUE retval = rb_eval_string(p);
1049 * set_global_settings(key, value)
1051 * Set a setting data for key to the application preferences.
1054 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1056 VALUE sval = rb_inspect(value);
1057 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1061 #pragma mark ====== Utility functions (protected funcall) ======
1063 struct Ruby_funcall2_record {
1071 s_Ruby_funcall2_sub(VALUE data)
1073 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1074 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1078 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1080 struct Ruby_funcall2_record rec;
1085 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1089 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1091 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1094 #pragma mark ====== ParameterRef Class ======
1097 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1101 Data_Get_Struct(self, ParameterRef, pref);
1103 *typep = pref->parType;
1104 if (pref->parType == kElementParType) {
1105 up = (UnionPar *)&gElementParameters[pref->idx];
1107 up = ParameterRefGetPar(pref);
1108 if (checkEditable) {
1110 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1111 if (up->bond.src != 0 && up->bond.src != -1)
1112 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1119 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1123 Data_Get_Struct(self, ParameterRef, pref);
1124 if (pref->mol == NULL)
1126 up = ParameterRefGetPar(pref);
1127 if (key != s_SourceSym)
1128 up->bond.src = 0; /* Becomes automatically molecule-local */
1129 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1132 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1133 MolActionCallback_registerUndo(pref->mol, act);
1134 MoleculeCallback_notifyModification(pref->mol, 0);
1135 pref->mol->needsMDRebuild = 1;
1140 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1142 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1144 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1146 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1150 s_AtomTypeIndexFromValue(VALUE val)
1152 if (rb_obj_is_kind_of(val, rb_cNumeric))
1153 return NUM2INT(val);
1155 return AtomTypeEncodeToUInt(StringValuePtr(val));
1158 static const char *s_ParameterTypeNames[] = {
1159 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1161 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1164 s_ParTypeFromValue(VALUE val)
1168 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1169 if (s_ParameterTypeIDs[0] == 0) {
1170 for (i = 0; i < n; i++)
1171 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1173 valid = rb_to_id(val);
1174 for (i = 0; i < n; i++) {
1175 if (valid == s_ParameterTypeIDs[i]) {
1177 return kElementParType;
1178 else return kFirstParType + i;
1181 return kInvalidParType;
1188 * Get the index in the parameter list.
1190 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1192 Data_Get_Struct(self, ParameterRef, pref);
1193 return INT2NUM(pref->idx);
1198 * par_type -> String
1200 * Get the parameter type, like "bond", "angle", etc.
1202 static VALUE s_ParameterRef_GetParType(VALUE self) {
1204 s_UnionParFromValue(self, &tp, 0);
1205 if (tp == kElementParType)
1206 return rb_str_new2("element");
1207 tp -= kFirstParType;
1208 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1209 return rb_str_new2(s_ParameterTypeNames[tp]);
1210 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1215 * atom_type -> String or Array of String
1216 * atom_types -> String or Array of String
1218 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1219 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1220 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1221 * The atom type may be "X", which is a wildcard that matches any atom type.
1223 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1228 up = s_UnionParFromValue(self, &tp, 0);
1229 n = ParameterGetAtomTypes(tp, up, types);
1231 rb_raise(rb_eMolbyError, "invalid member atom_types");
1232 for (i = 0; i < n; i++) {
1233 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1234 vals[i] = INT2NUM(types[i]);
1236 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1241 return rb_ary_new4(n, vals);
1248 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1250 static VALUE s_ParameterRef_GetK(VALUE self) {
1254 up = s_UnionParFromValue(self, &tp, 0);
1257 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1259 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1260 case kDihedralParType:
1261 case kImproperParType:
1262 if (up->torsion.mult == 1)
1263 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1264 n = up->torsion.mult;
1267 for (i = 0; i < n; i++)
1268 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1269 return rb_ary_new4(n, vals);
1271 rb_raise(rb_eMolbyError, "invalid member k");
1279 * Get the equilibrium bond length. Only available for bond parameters.
1281 static VALUE s_ParameterRef_GetR0(VALUE self) {
1284 up = s_UnionParFromValue(self, &tp, 0);
1285 if (tp == kBondParType)
1286 return rb_float_new(up->bond.r0);
1287 else rb_raise(rb_eMolbyError, "invalid member r0");
1294 * Get the equilibrium angle (in degree). Only available for angle parameters.
1296 static VALUE s_ParameterRef_GetA0(VALUE self) {
1299 up = s_UnionParFromValue(self, &tp, 0);
1300 if (tp == kAngleParType)
1301 return rb_float_new(up->angle.a0 * kRad2Deg);
1302 else rb_raise(rb_eMolbyError, "invalid member a0");
1309 * Get the multiplicity. Only available for dihedral and improper parameters.
1310 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1312 static VALUE s_ParameterRef_GetMult(VALUE self) {
1315 up = s_UnionParFromValue(self, &tp, 0);
1316 if (tp == kDihedralParType || tp == kImproperParType)
1317 return rb_float_new(up->torsion.mult);
1318 else rb_raise(rb_eMolbyError, "invalid member mult");
1323 * period -> Integer or Array of Integers
1325 * Get the periodicity. Only available for dihedral and improper parameters.
1326 * If the multiplicity is larger than 1, then an array of integers is returned.
1327 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1329 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1333 up = s_UnionParFromValue(self, &tp, 0);
1334 if (tp == kDihedralParType || tp == kImproperParType) {
1335 if (up->torsion.mult == 1)
1336 return INT2NUM(up->torsion.period[0]);
1337 n = up->torsion.mult;
1340 for (i = 0; i < n; i++)
1341 vals[i] = INT2NUM(up->torsion.period[i]);
1342 return rb_ary_new4(n, vals);
1343 } else rb_raise(rb_eMolbyError, "invalid member period");
1348 * phi0 -> Float or Array of Floats
1350 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1351 * If the multiplicity is larger than 1, then an array of floats is returned.
1352 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1354 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1358 up = s_UnionParFromValue(self, &tp, 0);
1359 if (tp == kDihedralParType || tp == kImproperParType) {
1360 if (up->torsion.mult == 1)
1361 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1362 n = up->torsion.mult;
1365 for (i = 0; i < n; i++)
1366 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1367 return rb_ary_new4(n, vals);
1368 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1375 * Get the "A" value for the van der Waals parameter.
1378 static VALUE s_ParameterRef_GetA(VALUE self) {
1381 up = s_UnionParFromValue(self, &tp, 0);
1382 if (tp == kVdwParType)
1383 return rb_float_new(up->vdw.A);
1384 else if (tp == kVdwPairParType)
1385 return rb_float_new(up->vdwp.A);
1386 else rb_raise(rb_eMolbyError, "invalid member A");
1394 * Get the "B" value for the van der Waals parameter.
1397 static VALUE s_ParameterRef_GetB(VALUE self) {
1400 up = s_UnionParFromValue(self, &tp, 0);
1401 if (tp == kVdwParType)
1402 return rb_float_new(up->vdw.B);
1403 else if (tp == kVdwPairParType)
1404 return rb_float_new(up->vdwp.B);
1405 else rb_raise(rb_eMolbyError, "invalid member B");
1413 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1415 static VALUE s_ParameterRef_GetReq(VALUE self) {
1418 /* Double a, b, r; */
1420 up = s_UnionParFromValue(self, &tp, 0);
1421 if (tp == kVdwParType) {
1425 } else if (tp == kVdwPairParType) {
1429 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1430 /* if (a == 0.0 || b == 0.0) */
1431 return rb_float_new(r);
1432 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1439 * Get the minimum energy for the van der Waals parameter.
1441 static VALUE s_ParameterRef_GetEps(VALUE self) {
1446 up = s_UnionParFromValue(self, &tp, 0);
1447 if (tp == kVdwParType) {
1451 } else if (tp == kVdwPairParType) {
1455 } else rb_raise(rb_eMolbyError, "invalid member eps");
1456 /* if (a == 0.0 || b == 0.0) */
1457 return rb_float_new(eps * INTERNAL2KCAL);
1458 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1465 * Get the "A" value for the 1-4 van der Waals parameter.
1468 static VALUE s_ParameterRef_GetA14(VALUE self) {
1471 up = s_UnionParFromValue(self, &tp, 0);
1472 if (tp == kVdwParType)
1473 return rb_float_new(up->vdw.A14);
1474 else if (tp == kVdwPairParType)
1475 return rb_float_new(up->vdwp.A14);
1476 else rb_raise(rb_eMolbyError, "invalid member A14");
1484 * Get the "B" value for the 1-4 van der Waals parameter.
1487 static VALUE s_ParameterRef_GetB14(VALUE self) {
1490 up = s_UnionParFromValue(self, &tp, 0);
1491 if (tp == kVdwParType)
1492 return rb_float_new(up->vdw.B14);
1493 else if (tp == kVdwPairParType)
1494 return rb_float_new(up->vdwp.B14);
1495 else rb_raise(rb_eMolbyError, "invalid member B14");
1503 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1505 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1508 /* Double a, b, r; */
1510 up = s_UnionParFromValue(self, &tp, 0);
1511 if (tp == kVdwParType) {
1515 } else if (tp == kVdwPairParType) {
1516 /* a = up->vdwp.A14;
1517 b = up->vdwp.B14; */
1518 r = up->vdwp.r_eq14;
1519 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1520 /* if (a == 0.0 || b == 0.0) */
1521 return rb_float_new(r);
1522 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1529 * Get the minimum energy for the 1-4 van der Waals parameter.
1531 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1536 up = s_UnionParFromValue(self, &tp, 0);
1537 if (tp == kVdwParType) {
1540 eps = up->vdw.eps14;
1541 } else if (tp == kVdwPairParType) {
1542 /* a = up->vdwp.A14;
1543 b = up->vdwp.B14; */
1544 eps = up->vdwp.eps14;
1545 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1546 /* if (a == 0.0 || b == 0.0) */
1547 return rb_float_new(eps * INTERNAL2KCAL);
1548 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1555 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1557 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1560 up = s_UnionParFromValue(self, &tp, 0);
1561 if (tp == kVdwCutoffParType)
1562 return rb_float_new(up->vdwcutoff.cutoff);
1563 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1570 * Get the atomic radius for the atom display parameter.
1572 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1575 up = s_UnionParFromValue(self, &tp, 0);
1576 if (tp == kElementParType)
1577 return rb_float_new(up->atom.radius);
1578 else rb_raise(rb_eMolbyError, "invalid member radius");
1583 * color -> [Float, Float, Float]
1585 * Get the rgb color for the atom display parameter.
1587 static VALUE s_ParameterRef_GetColor(VALUE self) {
1590 up = s_UnionParFromValue(self, &tp, 0);
1591 if (tp == kElementParType)
1592 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1593 else rb_raise(rb_eMolbyError, "invalid member color");
1598 * atomic_number -> Integer
1600 * Get the atomic number for the vdw or atom parameter.
1602 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1605 up = s_UnionParFromValue(self, &tp, 0);
1606 if (tp == kElementParType)
1607 return INT2NUM(up->atom.number);
1608 else if (tp == kVdwParType)
1609 return INT2NUM(up->vdw.atomicNumber);
1610 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1617 * Get the name for the atom display parameter.
1619 static VALUE s_ParameterRef_GetName(VALUE self) {
1622 up = s_UnionParFromValue(self, &tp, 0);
1623 if (tp == kElementParType) {
1625 strncpy(name, up->atom.name, 4);
1627 return rb_str_new2(name);
1628 } else rb_raise(rb_eMolbyError, "invalid member name");
1635 * Get the atomic weight for the atom display parameter.
1637 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1640 up = s_UnionParFromValue(self, &tp, 0);
1641 if (tp == kElementParType)
1642 return rb_float_new(up->atom.weight);
1643 else if (tp == kVdwParType)
1644 return rb_float_new(up->vdw.weight);
1645 else rb_raise(rb_eMolbyError, "invalid member weight");
1650 * fullname -> String
1652 * Get the full name for the atom display parameter.
1654 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1657 up = s_UnionParFromValue(self, &tp, 0);
1658 if (tp == kElementParType) {
1660 strncpy(fullname, up->atom.fullname, 15);
1662 return rb_str_new2(fullname);
1663 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1670 * Get the comment for the parameter.
1672 static VALUE s_ParameterRef_GetComment(VALUE self) {
1675 up = s_UnionParFromValue(self, &tp, 0);
1679 else return rb_str_new2(ParameterGetComment(com));
1686 * Get the source string for the parameter. Returns false for undefined parameter,
1687 * and nil for "local" parameter that is specific for the molecule.
1689 static VALUE s_ParameterRef_GetSource(VALUE self) {
1692 up = s_UnionParFromValue(self, &tp, 0);
1695 return Qfalse; /* undefined */
1697 return Qnil; /* local */
1698 else return rb_str_new2(ParameterGetComment(src));
1702 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1709 if (rb_obj_is_kind_of(val, rb_cString)) {
1710 char *s = StringValuePtr(val);
1712 for (i = 0; i < n; i++) {
1715 /* Skip leading separaters */
1716 while (*s == '-' || *s == ' ' || *s == '\t')
1718 for (p = s; *p != 0; p++) {
1719 if (*p == '-' || *p == ' ' || *p == '\t')
1723 if (len >= sizeof(buf))
1724 len = sizeof(buf) - 1;
1725 strncpy(buf, s, len);
1727 /* Skip trailing blanks */
1728 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1731 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1732 if (buf[0] >= '0' && buf[0] <= '9')
1733 types[i] = atoi(buf);
1735 types[i] = AtomTypeEncodeToUInt(buf);
1736 if (p == NULL || *p == 0) {
1742 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1745 val = rb_ary_to_ary(val);
1746 if (RARRAY_LEN(val) != n)
1747 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1748 valp = RARRAY_PTR(val);
1750 for (i = 0; i < n; i++) {
1751 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1752 types[i] = NUM2INT(rb_Integer(valp[i]));
1754 VALUE sval = valp[i];
1755 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1760 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1765 up = s_UnionParFromValue(self, &tp, 1);
1766 oldval = s_ParameterRef_GetAtomTypes(self);
1767 oldsrc = up->bond.src;
1770 s_ScanAtomTypes(val, 2, types);
1771 up->bond.type1 = types[0];
1772 up->bond.type2 = types[1];
1775 s_ScanAtomTypes(val, 3, types);
1776 up->angle.type1 = types[0];
1777 up->angle.type2 = types[1];
1778 up->angle.type3 = types[2];
1780 case kDihedralParType:
1781 case kImproperParType:
1782 s_ScanAtomTypes(val, 4, types);
1783 up->torsion.type1 = types[0];
1784 up->torsion.type2 = types[1];
1785 up->torsion.type3 = types[2];
1786 up->torsion.type4 = types[3];
1789 s_ScanAtomTypes(val, 1, types);
1790 up->vdw.type1 = types[0];
1792 case kVdwPairParType:
1793 s_ScanAtomTypes(val, 2, types);
1794 up->vdwp.type1 = types[0];
1795 up->vdwp.type2 = types[1];
1797 case kVdwCutoffParType:
1798 s_ScanAtomTypes(val, 2, types);
1799 up->vdwcutoff.type1 = types[0];
1800 up->vdwcutoff.type2 = types[1];
1805 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1809 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1811 Int tp, i, n, oldsrc;
1812 VALUE *valp, oldval;
1813 up = s_UnionParFromValue(self, &tp, 1);
1814 oldval = s_ParameterRef_GetK(self);
1815 oldsrc = up->bond.src;
1818 val = rb_Float(val);
1819 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1822 val = rb_Float(val);
1823 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1825 case kDihedralParType:
1826 case kImproperParType:
1827 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1828 up->torsion.mult = 1;
1829 val = rb_Float(val);
1830 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1833 n = up->torsion.mult;
1836 val = rb_ary_to_ary(val);
1837 if (RARRAY_LEN(val) != n)
1838 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1839 valp = RARRAY_PTR(val);
1840 for (i = 0; i < n; i++) {
1841 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1845 rb_raise(rb_eMolbyError, "invalid member k");
1847 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1851 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1855 up = s_UnionParFromValue(self, &tp, 1);
1856 oldval = s_ParameterRef_GetR0(self);
1857 oldsrc = up->bond.src;
1858 if (tp == kBondParType) {
1859 val = rb_Float(val);
1860 up->bond.r0 = NUM2DBL(val);
1861 } else rb_raise(rb_eMolbyError, "invalid member r0");
1862 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1866 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1870 up = s_UnionParFromValue(self, &tp, 1);
1871 oldval = s_ParameterRef_GetA0(self);
1872 oldsrc = up->bond.src;
1873 if (tp == kAngleParType) {
1874 val = rb_Float(val);
1875 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1876 } else rb_raise(rb_eMolbyError, "invalid member a0");
1877 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1881 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1885 up = s_UnionParFromValue(self, &tp, 1);
1886 oldval = s_ParameterRef_GetMult(self);
1887 oldsrc = up->bond.src;
1888 if (tp == kDihedralParType || tp == kImproperParType) {
1890 val = rb_Integer(val);
1893 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1894 up->torsion.mult = i;
1895 } else rb_raise(rb_eMolbyError, "invalid member mult");
1896 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1900 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1902 Int tp, i, n, oldsrc;
1903 VALUE *valp, oldval;
1904 up = s_UnionParFromValue(self, &tp, 1);
1905 oldval = s_ParameterRef_GetPeriod(self);
1906 oldsrc = up->bond.src;
1907 if (tp == kDihedralParType || tp == kImproperParType) {
1908 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1909 up->torsion.mult = 1;
1910 val = rb_Integer(val);
1911 up->torsion.period[0] = NUM2INT(val);
1913 n = up->torsion.mult;
1916 val = rb_ary_to_ary(val);
1917 if (RARRAY_LEN(val) != n)
1918 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1919 valp = RARRAY_PTR(val);
1920 for (i = 0; i < n; i++) {
1921 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1924 } else rb_raise(rb_eMolbyError, "invalid member period");
1925 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1929 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1931 Int tp, i, n, oldsrc;
1932 VALUE *valp, oldval;
1933 up = s_UnionParFromValue(self, &tp, 1);
1934 oldval = s_ParameterRef_GetPhi0(self);
1935 oldsrc = up->bond.src;
1936 if (tp == kDihedralParType || tp == kImproperParType) {
1937 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1938 up->torsion.mult = 1;
1939 val = rb_Float(val);
1940 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1942 n = up->torsion.mult;
1945 val = rb_ary_to_ary(val);
1946 if (RARRAY_LEN(val) != n)
1947 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1948 valp = RARRAY_PTR(val);
1949 for (i = 0; i < n; i++)
1950 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1952 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1953 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1958 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1963 up = s_UnionParFromValue(self, &tp, 1);
1964 oldval = s_ParameterRef_GetA(self);
1965 oldsrc = up->bond.src;
1966 val = rb_Float(val);
1968 if (tp == kVdwParType)
1970 else if (tp == kVdwPairParType)
1972 else rb_raise(rb_eMolbyError, "invalid member A");
1973 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1977 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1982 up = s_UnionParFromValue(self, &tp, 1);
1983 oldval = s_ParameterRef_GetB(self);
1984 oldsrc = up->bond.src;
1985 val = rb_Float(val);
1987 if (tp == kVdwParType)
1989 else if (tp == kVdwPairParType)
1991 else rb_raise(rb_eMolbyError, "invalid member B");
1992 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1997 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2002 up = s_UnionParFromValue(self, &tp, 1);
2003 oldval = s_ParameterRef_GetReq(self);
2004 oldsrc = up->bond.src;
2005 val = rb_Float(val);
2007 if (tp == kVdwParType) {
2009 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2010 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2011 } else if (tp == kVdwPairParType) {
2013 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2014 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2015 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2016 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2020 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2025 up = s_UnionParFromValue(self, &tp, 1);
2026 oldval = s_ParameterRef_GetEps(self);
2027 oldsrc = up->bond.src;
2028 val = rb_Float(val);
2029 e = NUM2DBL(val) * KCAL2INTERNAL;
2030 if (tp == kVdwParType) {
2032 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2033 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2034 } else if (tp == kVdwPairParType) {
2036 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2037 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2038 } else rb_raise(rb_eMolbyError, "invalid member eps");
2039 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2044 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2049 up = s_UnionParFromValue(self, &tp, 1);
2050 oldval = s_ParameterRef_GetA14(self);
2051 oldsrc = up->bond.src;
2052 val = rb_Float(val);
2054 if (tp == kVdwParType)
2056 else if (tp == kVdwPairParType)
2058 else rb_raise(rb_eMolbyError, "invalid member A14");
2059 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2063 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2068 up = s_UnionParFromValue(self, &tp, 1);
2069 oldval = s_ParameterRef_GetB14(self);
2070 oldsrc = up->bond.src;
2071 val = rb_Float(val);
2073 if (tp == kVdwParType)
2075 else if (tp == kVdwPairParType)
2077 else rb_raise(rb_eMolbyError, "invalid member B14");
2078 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2083 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2088 up = s_UnionParFromValue(self, &tp, 1);
2089 oldval = s_ParameterRef_GetReq14(self);
2090 oldsrc = up->bond.src;
2091 val = rb_Float(val);
2093 if (tp == kVdwParType) {
2095 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2096 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2097 } else if (tp == kVdwPairParType) {
2098 up->vdwp.r_eq14 = r;
2099 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2100 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2101 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2102 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2106 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2111 up = s_UnionParFromValue(self, &tp, 1);
2112 oldval = s_ParameterRef_GetEps14(self);
2113 oldsrc = up->bond.src;
2114 val = rb_Float(val);
2115 e = NUM2DBL(val) * KCAL2INTERNAL;
2116 if (tp == kVdwParType) {
2118 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2119 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2120 } else if (tp == kVdwPairParType) {
2122 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2123 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2124 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2125 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2129 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2133 oldval = s_ParameterRef_GetCutoff(self);
2134 oldsrc = up->bond.src;
2135 up = s_UnionParFromValue(self, &tp, 1);
2136 val = rb_Float(val);
2137 if (tp == kVdwCutoffParType) {
2138 up->vdwcutoff.cutoff = NUM2DBL(val);
2139 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2140 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2144 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2148 up = s_UnionParFromValue(self, &tp, 1);
2149 oldval = s_ParameterRef_GetRadius(self);
2150 oldsrc = up->bond.src;
2151 val = rb_Float(val);
2152 if (tp == kElementParType) {
2153 up->atom.radius = NUM2DBL(val);
2154 } else rb_raise(rb_eMolbyError, "invalid member radius");
2155 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2159 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2162 VALUE *valp, oldval;
2163 up = s_UnionParFromValue(self, &tp, 1);
2164 oldval = s_ParameterRef_GetColor(self);
2165 oldsrc = up->bond.src;
2166 val = rb_ary_to_ary(val);
2167 if (RARRAY_LEN(val) != 3)
2168 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2169 valp = RARRAY_PTR(val);
2170 if (tp == kElementParType) {
2171 up->atom.r = NUM2DBL(rb_Float(valp[0]));
2172 up->atom.g = NUM2DBL(rb_Float(valp[1]));
2173 up->atom.b = NUM2DBL(rb_Float(valp[2]));
2174 } else rb_raise(rb_eMolbyError, "invalid member color");
2175 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2179 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2183 up = s_UnionParFromValue(self, &tp, 1);
2184 oldval = s_ParameterRef_GetAtomicNumber(self);
2185 oldsrc = up->bond.src;
2186 val = rb_Integer(val);
2187 if (tp == kElementParType)
2188 up->atom.number = NUM2INT(val);
2189 else if (tp == kVdwParType) {
2190 up->vdw.atomicNumber = NUM2INT(val);
2191 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2192 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2193 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2197 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2201 up = s_UnionParFromValue(self, &tp, 1);
2202 oldval = s_ParameterRef_GetName(self);
2203 oldsrc = up->bond.src;
2204 if (tp == kElementParType) {
2205 strncpy(up->atom.name, StringValuePtr(val), 4);
2206 } else rb_raise(rb_eMolbyError, "invalid member name");
2207 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2211 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2215 val = rb_Float(val);
2216 oldval = s_ParameterRef_GetWeight(self);
2217 up = s_UnionParFromValue(self, &tp, 1);
2218 oldsrc = up->bond.src;
2219 if (tp == kElementParType)
2220 up->atom.weight = NUM2DBL(val);
2221 else if (tp == kVdwParType)
2222 up->vdw.weight = NUM2DBL(val);
2223 else rb_raise(rb_eMolbyError, "invalid member weight");
2224 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2228 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2232 up = s_UnionParFromValue(self, &tp, 1);
2233 oldval = s_ParameterRef_GetFullName(self);
2234 oldsrc = up->bond.src;
2235 if (tp == kElementParType) {
2236 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2237 up->atom.fullname[15] = 0;
2238 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2239 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2243 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2245 Int tp, com, oldsrc;
2247 up = s_UnionParFromValue(self, &tp, 1);
2248 oldval = s_ParameterRef_GetComment(self);
2249 oldsrc = up->bond.src;
2253 com = ParameterCommentIndex(StringValuePtr(val));
2256 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2260 /* Only false (undefined) and nil (local) can be set */
2261 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2265 up = s_UnionParFromValue(self, &tp, 1);
2266 if (val != Qfalse && val != Qnil)
2267 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2268 oldval = s_ParameterRef_GetSource(self);
2269 oldsrc = up->bond.src;
2270 if (oldsrc != 0 && oldsrc != -1)
2271 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2272 up->bond.src = (val == Qfalse ? -1 : 0);
2273 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2277 static struct s_ParameterAttrDef {
2279 VALUE *symref; /* Address of s_IndexSymbol etc. */
2280 ID id; /* Will be set within InitMolby() */
2281 VALUE (*getter)(VALUE);
2282 VALUE (*setter)(VALUE, VALUE);
2283 } s_ParameterAttrDefTable[] = {
2284 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2285 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2286 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2287 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2288 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2289 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2290 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2291 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2292 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2293 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2294 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2295 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2296 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2297 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2298 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2299 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2300 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2301 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2302 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2303 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2304 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2305 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2306 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2307 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2308 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2309 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2310 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2311 {NULL} /* Sentinel */
2315 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2319 if (TYPE(key) != T_SYMBOL) {
2320 kid = rb_intern(StringValuePtr(key));
2322 } else kid = SYM2ID(key);
2323 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2324 if (s_ParameterAttrDefTable[i].id == kid) {
2325 if (value == Qundef)
2326 return (*(s_ParameterAttrDefTable[i].getter))(self);
2327 else if (s_ParameterAttrDefTable[i].setter == NULL)
2328 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2330 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2333 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2334 return Qnil; /* not reached */
2338 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2340 return s_ParameterRef_SetAttr(self, key, Qundef);
2345 * keys(idx) -> array of valid parameter attributes
2347 * Returns an array of valid parameter attributes (as Symbols).
2350 s_ParameterRef_Keys(VALUE self)
2353 Data_Get_Struct(self, ParameterRef, pref);
2354 switch (pref->parType) {
2356 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2358 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2359 case kDihedralParType:
2360 case kImproperParType:
2361 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2363 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);
2364 case kVdwPairParType:
2365 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2366 case kVdwCutoffParType:
2367 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2368 case kElementParType:
2369 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);
2371 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2373 return Qnil; /* Not reached */
2378 * to_hash(idx) -> Hash
2380 * Returns a hash containing valid parameter names and values
2383 s_ParameterRef_ToHash(VALUE self)
2385 VALUE keys = s_ParameterRef_Keys(self);
2390 retval = rb_hash_new();
2391 for (i = 0; i < RARRAY_LEN(keys); i++) {
2392 VALUE key = RARRAY_PTR(keys)[i];
2393 VALUE val = s_ParameterRef_GetAttr(self, key);
2394 rb_hash_aset(retval, key, val);
2401 * parameter.to_s(idx) -> String
2403 * Returns a string representation of the given parameter
2406 s_ParameterRef_ToString(VALUE self)
2409 char buf[1024], types[4][8];
2410 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2413 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);
2416 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);
2418 case kDihedralParType:
2419 case kImproperParType:
2420 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]));
2422 for (i = 0; i < up->torsion.mult; i++) {
2423 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);
2428 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);
2430 case kVdwPairParType:
2431 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);
2433 case kVdwCutoffParType:
2434 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);
2436 case kElementParType:
2437 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);
2440 return rb_str_new2(buf);
2445 * self == parameterRef -> boolean
2447 * True if the parameters point to the same parameter record.
2450 s_ParameterRef_Equal(VALUE self, VALUE val)
2453 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2454 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2455 } else return Qfalse;
2458 #pragma mark ====== Parameter Class ======
2460 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2461 * is NULL, then the global parameters are looked for. */
2463 /* Rebuild the MD parameter record if necessary: may throw an exception */
2464 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2466 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2469 Data_Get_Struct(val, Molecule, mol);
2471 rb_raise(rb_eMolbyError, "the molecule is empty");
2472 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2473 /* Do self.md_arena.prepare */
2474 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2476 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2481 s_NewParameterValueFromValue(VALUE val)
2484 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2485 Data_Get_Struct(val, Molecule, mol);
2486 s_RebuildMDParameterIfNecessary(val, Qtrue);
2487 MoleculeRetain(mol);
2488 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2491 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2496 s_MoleculeFromParameterValue(VALUE val)
2499 Data_Get_Struct(val, Molecule, mol);
2504 s_ParameterFromParameterValue(VALUE val)
2507 Data_Get_Struct(val, Molecule, mol);
2510 return gBuiltinParameters;
2513 /* Forward declarations */
2514 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2515 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2518 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2520 if (val == rb_cParameter) {
2521 return NULL; /* Parameter class method: builtin parameters */
2522 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2523 return s_MoleculeFromParameterValue(val);
2524 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2525 return s_MoleculeFromParEnumerableValue(val);
2531 * builtin -> Parameter
2533 * Returns a parameter value that points to the global (builtin) parameters.
2534 * Equivalent to Parameter::Builtin (constant).
2537 s_Parameter_Builtin(VALUE self)
2539 static ID s_builtin_id = 0;
2540 if (s_builtin_id == 0)
2541 s_builtin_id = rb_intern("Builtin");
2542 return rb_const_get(rb_cParameter, s_builtin_id);
2547 * bond(idx) -> ParameterRef
2549 * The index-th bond parameter record is returned.
2552 s_Parameter_Bond(VALUE self, VALUE ival)
2556 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2557 idx = NUM2INT(rb_Integer(ival));
2559 n = gBuiltinParameters->nbondPars;
2560 else if (mol->par != NULL)
2561 n = mol->par->nbondPars;
2563 if (idx < -n || idx >= n)
2564 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2567 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2572 * angle(idx) -> ParameterRef
2574 * The index-th angle parameter record is returned.
2577 s_Parameter_Angle(VALUE self, VALUE ival)
2581 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2582 idx = NUM2INT(rb_Integer(ival));
2584 n = gBuiltinParameters->nanglePars;
2585 else if (mol->par != NULL)
2586 n = mol->par->nanglePars;
2588 if (idx < -n || idx >= n)
2589 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2592 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2597 * dihedral(idx) -> ParameterRef
2599 * The index-th dihedral parameter record is returned.
2602 s_Parameter_Dihedral(VALUE self, VALUE ival)
2606 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2607 idx = NUM2INT(rb_Integer(ival));
2609 n = gBuiltinParameters->ndihedralPars;
2610 else if (mol->par != NULL)
2611 n = mol->par->ndihedralPars;
2613 if (idx < -n || idx >= n)
2614 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2617 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2622 * improper(idx) -> ParameterRef
2624 * The index-th improper parameter record is returned.
2627 s_Parameter_Improper(VALUE self, VALUE ival)
2631 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2632 idx = NUM2INT(rb_Integer(ival));
2634 n = gBuiltinParameters->nimproperPars;
2635 else if (mol->par != NULL)
2636 n = mol->par->nimproperPars;
2638 if (idx < -n || idx >= n)
2639 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2642 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2647 * vdw(idx) -> ParameterRef
2649 * The index-th vdw parameter record is returned.
2652 s_Parameter_Vdw(VALUE self, VALUE ival)
2656 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2657 idx = NUM2INT(rb_Integer(ival));
2659 n = gBuiltinParameters->nvdwPars;
2660 else if (mol->par != NULL)
2661 n = mol->par->nvdwPars;
2663 if (idx < -n || idx >= n)
2664 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2667 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2672 * vdw_pair(idx) -> ParameterRef
2674 * The index-th vdw pair parameter record is returned.
2677 s_Parameter_VdwPair(VALUE self, VALUE ival)
2681 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2682 idx = NUM2INT(rb_Integer(ival));
2684 n = gBuiltinParameters->nvdwpPars;
2685 else if (mol->par != NULL)
2686 n = mol->par->nvdwpPars;
2688 if (idx < -n || idx >= n)
2689 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2692 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2697 * vdw_cutoff(idx) -> ParameterRef
2699 * The index-th vdw cutoff parameter record is returned.
2702 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2706 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2707 idx = NUM2INT(rb_Integer(ival));
2709 n = gBuiltinParameters->nvdwCutoffPars;
2710 else if (mol->par != NULL)
2711 n = mol->par->nvdwCutoffPars;
2713 if (idx < -n || idx >= n)
2714 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2717 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2722 * element(idx) -> ParameterRef
2723 * element(t1) -> ParameterRef
2725 * In the first form, the index-th element parameter record is returned. In the second
2726 * form, the element parameter for t1 is looked up (the last index first). t1
2727 * is the element name string (up to 4 characters).
2728 * Unlike other Parameter methods, this is used only for the global parameter.
2731 s_Parameter_Element(VALUE self, VALUE ival)
2734 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2735 int n = gCountElementParameters;
2736 idx1 = NUM2INT(rb_Integer(ival));
2737 if (idx1 < -n || idx1 >= n)
2738 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2741 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2746 strncpy(name, StringValuePtr(ival), 4);
2748 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2749 if (strncmp(ep->name, name, 4) == 0)
2750 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2760 * Returns the number of bond parameters.
2763 s_Parameter_Nbonds(VALUE self)
2766 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2768 n = gBuiltinParameters->nbondPars;
2769 else if (mol->par != NULL)
2770 n = mol->par->nbondPars;
2777 * nangles -> Integer
2779 * Returns the number of angle parameters.
2782 s_Parameter_Nangles(VALUE self)
2785 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2787 n = gBuiltinParameters->nanglePars;
2788 else if (mol->par != NULL)
2789 n = mol->par->nanglePars;
2796 * ndihedrals -> Integer
2798 * Returns the number of dihedral parameters.
2801 s_Parameter_Ndihedrals(VALUE self)
2804 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2806 n = gBuiltinParameters->ndihedralPars;
2807 else if (mol->par != NULL)
2808 n = mol->par->ndihedralPars;
2815 * nimpropers -> Integer
2817 * Returns the number of improper parameters.
2820 s_Parameter_Nimpropers(VALUE self)
2823 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2825 n = gBuiltinParameters->nimproperPars;
2826 else if (mol->par != NULL)
2827 n = mol->par->nimproperPars;
2836 * Returns the number of vdw parameters.
2839 s_Parameter_Nvdws(VALUE self)
2842 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2844 n = gBuiltinParameters->nvdwPars;
2845 else if (mol->par != NULL)
2846 n = mol->par->nvdwPars;
2853 * nvdw_pairs -> Integer
2855 * Returns the number of vdw pair parameters.
2858 s_Parameter_NvdwPairs(VALUE self)
2861 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2863 n = gBuiltinParameters->nvdwpPars;
2864 else if (mol->par != NULL)
2865 n = mol->par->nvdwpPars;
2872 * nvdw_cutoffs -> Integer
2874 * Returns the number of vdw cutoff parameters.
2877 s_Parameter_NvdwCutoffs(VALUE self)
2880 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2882 n = gBuiltinParameters->nvdwCutoffPars;
2883 else if (mol->par != NULL)
2884 n = mol->par->nvdwCutoffPars;
2891 * nelements -> Integer
2893 * Returns the number of element parameters.
2896 s_Parameter_Nelements(VALUE self)
2898 return INT2NUM(gCountElementParameters);
2903 * bonds -> ParEnumerable
2905 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2906 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2907 * useful when all accessible parameters should be examined by use of 'each' method.
2910 s_Parameter_Bonds(VALUE self)
2912 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2913 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2918 * angles -> ParEnumerable
2920 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2921 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2922 * useful when all accessible parameters should be examined by use of 'each' method.
2925 s_Parameter_Angles(VALUE self)
2927 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2928 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2933 * dihedrals -> ParEnumerable
2935 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2936 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2937 * useful when all accessible parameters should be examined by use of 'each' method.
2940 s_Parameter_Dihedrals(VALUE self)
2942 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2943 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2948 * impropers -> ParEnumerable
2950 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2951 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2952 * useful when all accessible parameters should be examined by use of 'each' method.
2955 s_Parameter_Impropers(VALUE self)
2957 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2958 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2963 * vdws -> ParEnumerable
2965 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2966 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2967 * useful when all accessible parameters should be examined by use of 'each' method.
2970 s_Parameter_Vdws(VALUE self)
2972 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2973 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2978 * vdw_pairs -> ParEnumerable
2980 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2981 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2982 * useful when all accessible parameters should be examined by use of 'each' method.
2985 s_Parameter_VdwPairs(VALUE self)
2987 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2988 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2993 * vdw_cutoffs -> ParEnumerable
2995 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2996 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2997 * useful when all accessible parameters should be examined by use of 'each' method.
3000 s_Parameter_VdwCutoffs(VALUE self)
3002 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3003 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3008 * elements -> ParEnumerable
3010 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3011 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3012 * useful when all accessible parameters should be examined by use of 'each' method.
3015 s_Parameter_Elements(VALUE self)
3017 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3018 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3022 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3024 VALUE atval, optval;
3027 int i, n, idx, flags, is_global;
3029 rb_scan_args(argc, argv, "1*", &atval, &optval);
3031 /* Get the atom types */
3033 case kBondParType: n = 2; break;
3034 case kAngleParType: n = 3; break;
3035 case kDihedralParType: n = 4; break;
3036 case kImproperParType: n = 4; break;
3037 case kVdwParType: n = 1; break;
3038 case kVdwPairParType: n = 2; break;
3039 default: return Qnil;
3041 s_ScanAtomTypes(atval, n, t);
3042 for (i = 0; i < n; i++) {
3043 if (t[i] < kAtomTypeMinimum) {
3044 /* Explicit atom index */
3046 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3047 if (t[i] >= mol->natoms)
3048 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3050 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3054 /* Analyze options */
3056 n = RARRAY_LEN(optval);
3057 for (i = 0; i < n; i++) {
3058 VALUE oval = RARRAY_PTR(optval)[i];
3059 if (oval == ID2SYM(rb_intern("global")))
3060 flags |= kParameterLookupGlobal;
3061 else if (oval == ID2SYM(rb_intern("local")))
3062 flags |= kParameterLookupLocal;
3063 else if (oval == ID2SYM(rb_intern("missing")))
3064 flags |= kParameterLookupMissing;
3065 else if (oval == ID2SYM(rb_intern("nowildcard")))
3066 flags |= kParameterLookupNoWildcard;
3067 else if (oval == ID2SYM(rb_intern("nobasetype")))
3068 flags |= kParameterLookupNoBaseAtomType;
3069 else if (oval == ID2SYM(rb_intern("create")))
3073 flags = kParameterLookupGlobal | kParameterLookupLocal;
3078 case kBondParType: {
3081 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3083 idx = bp - mol->par->bondPars;
3087 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3089 idx = bp - gBuiltinParameters->bondPars;
3094 case kAngleParType: {
3097 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3099 idx = ap - mol->par->anglePars;
3103 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3105 idx = ap - gBuiltinParameters->anglePars;
3110 case kDihedralParType: {
3113 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3115 idx = tp - mol->par->dihedralPars;
3119 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3121 idx = tp - gBuiltinParameters->dihedralPars;
3126 case kImproperParType: {
3129 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3131 idx = tp - mol->par->improperPars;
3135 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3137 idx = tp - gBuiltinParameters->improperPars;
3145 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3147 idx = vp - mol->par->vdwPars;
3151 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3153 idx = vp - gBuiltinParameters->vdwPars;
3158 case kVdwPairParType: {
3161 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3163 idx = vp - mol->par->vdwpPars;
3167 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3169 idx = vp - gBuiltinParameters->vdwpPars;
3178 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3181 /* Insert a new parameter record */
3183 Int count = ParameterGetCountForType(mol->par, parType);
3184 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3185 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3186 IntGroupRelease(ig);
3189 /* Set atom types */
3190 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3195 up->bond.type1 = t[0];
3196 up->bond.type2 = t[1];
3199 up->angle.type1 = t[0];
3200 up->angle.type2 = t[1];
3201 up->angle.type3 = t[2];
3203 case kDihedralParType:
3204 case kImproperParType:
3205 up->torsion.type1 = t[0];
3206 up->torsion.type2 = t[1];
3207 up->torsion.type3 = t[2];
3208 up->torsion.type4 = t[3];
3211 up->vdw.type1 = t[0];
3213 case kVdwPairParType:
3214 up->vdwp.type1 = t[0];
3215 up->vdwp.type2 = t[1];
3222 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3227 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3228 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3230 * Find the parameter record that matches the given atom types. The atom types are given
3231 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3232 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3233 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3234 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3237 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3240 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3242 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3243 parType = s_ParTypeFromValue(argv[0]);
3244 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3249 * self == parameter -> boolean
3251 * True if the parameters point to the same parameter table.
3254 s_Parameter_Equal(VALUE self, VALUE val)
3256 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3257 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3258 } else return Qfalse;
3261 #pragma mark ====== ParEnumerable Class ======
3263 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3264 and the parameter type. If the Molecule is NULL, then it refers to the
3265 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3266 the global parameters are always accessible. */
3268 typedef struct ParEnumerable {
3270 Int parType; /* Same as parType in ParameterRef */
3273 static ParEnumerable *
3274 s_ParEnumerableNew(Molecule *mol, Int parType)
3276 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3280 MoleculeRetain(mol);
3281 pen->parType = parType;
3287 s_ParEnumerableRelease(ParEnumerable *pen)
3290 if (pen->mol != NULL)
3291 MoleculeRelease(pen->mol);
3297 s_MoleculeFromParEnumerableValue(VALUE val)
3300 Data_Get_Struct(val, ParEnumerable, pen);
3305 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3307 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3309 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3310 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3315 * par_type -> String
3317 * Get the parameter type, like "bond", "angle", etc.
3320 s_ParEnumerable_ParType(VALUE self) {
3323 Data_Get_Struct(self, ParEnumerable, pen);
3325 if (tp == kElementParType)
3326 return rb_str_new2("element");
3327 tp -= kFirstParType;
3328 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3329 return rb_str_new2(s_ParameterTypeNames[tp]);
3330 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3335 * self[idx] -> ParameterRef
3337 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3338 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3339 * parent Parameter object of self.
3341 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3342 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3345 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3348 Data_Get_Struct(self, ParEnumerable, pen);
3349 switch (pen->parType) {
3350 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3351 case kBondParType: return s_Parameter_Bond(self, ival);
3352 case kAngleParType: return s_Parameter_Angle(self, ival);
3353 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3354 case kImproperParType: return s_Parameter_Improper(self, ival);
3355 case kVdwParType: return s_Parameter_Vdw(self, ival);
3356 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3357 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3358 case kElementParType: return s_Parameter_Element(self, ival);
3360 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3362 return Qnil; /* Not reached */
3369 * Returns the number of parameters included in this enumerable.
3372 s_ParEnumerable_Length(VALUE self)
3375 Data_Get_Struct(self, ParEnumerable, pen);
3376 switch (pen->parType) {
3377 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3378 case kBondParType: return s_Parameter_Nbonds(self);
3379 case kAngleParType: return s_Parameter_Nangles(self);
3380 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3381 case kImproperParType: return s_Parameter_Nimpropers(self);
3382 case kVdwParType: return s_Parameter_Nvdws(self);
3383 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3384 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3385 case kElementParType: return s_Parameter_Nelements(self);
3387 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3389 return Qnil; /* Not reached */
3396 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3399 s_ParEnumerable_Each(VALUE self)
3405 Data_Get_Struct(self, ParEnumerable, pen);
3406 if (pen->parType == kElementParType)
3407 n = gCountElementParameters;
3409 switch (pen->parType) {
3410 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3411 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3412 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3413 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3414 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3415 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3416 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3418 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3420 if (pen->mol == NULL)
3421 n = *((Int *)((char *)gBuiltinParameters + ofs));
3422 else if (pen->mol->par != NULL)
3423 n = *((Int *)((char *)(pen->mol->par) + ofs));
3426 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3427 Data_Get_Struct(aval, ParameterRef, pref);
3428 for (i = 0; i < n; i++) {
3437 * reverse_each {|pref| ...}
3439 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3442 s_ParEnumerable_ReverseEach(VALUE self)
3448 Data_Get_Struct(self, ParEnumerable, pen);
3449 if (pen->parType == kElementParType)
3450 n = gCountElementParameters;
3452 switch (pen->parType) {
3453 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3454 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3455 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3456 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3457 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3458 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3459 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3461 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3463 if (pen->mol == NULL)
3464 n = *((Int *)((char *)gBuiltinParameters + ofs));
3465 else if (pen->mol->par != NULL)
3466 n = *((Int *)((char *)(pen->mol->par) + ofs));
3469 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3470 Data_Get_Struct(aval, ParameterRef, pref);
3471 for (i = n - 1; i >= 0; i--) {
3480 * insert(idx = nil, pref = nil) -> ParameterRef
3482 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3483 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3484 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3485 * parameter is left undefined.
3486 * Throws an exception if ParEnumerable points to the global parameter.
3489 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3497 Data_Get_Struct(self, ParEnumerable, pen);
3498 if (pen->mol == NULL)
3499 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3500 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3501 rb_scan_args(argc, argv, "02", &ival, &pval);
3503 i = NUM2INT(rb_Integer(ival));
3505 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3510 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3511 if (up == NULL || type != pen->parType)
3512 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3513 ParameterCopyOneWithType(&u, up, pen->parType);
3516 memset(&u, 0, sizeof(u));
3519 ig = IntGroupNewWithPoints(n, 1, -1);
3520 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3522 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3523 MolActionCallback_registerUndo(pen->mol, act);
3524 MolActionRelease(act);
3526 IntGroupRelease(ig);
3527 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3535 * Delete the parameter(s) specified by the argument.
3538 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3543 Data_Get_Struct(self, ParEnumerable, pen);
3544 if (pen->mol == NULL)
3545 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3546 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3547 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3548 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3551 ig = IntGroupFromValue(ival);
3552 if ((i = IntGroupGetCount(ig)) == 0) {
3553 IntGroupRelease(ig);
3557 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3558 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3561 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3562 IntGroupRelease(ig);
3568 * lookup(atom_types, options, ...) -> ParameterRef
3569 * lookup(atom_type_string, options, ...) -> ParameterRef
3571 * Find the parameter record that matches the given atom types. The arguments are
3572 * the same as Parameter#lookup, except for the parameter type which is implicitly
3576 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3579 Data_Get_Struct(self, ParEnumerable, pen);
3580 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3585 * self == parEnumerable -> boolean
3587 * True if the arguments point to the same parameter table and type.
3590 s_ParEnumerable_Equal(VALUE self, VALUE val)
3592 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3593 ParEnumerable *pen1, *pen2;
3594 Data_Get_Struct(self, ParEnumerable, pen1);
3595 Data_Get_Struct(val, ParEnumerable, pen2);
3596 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3597 } else return Qfalse;
3600 #pragma mark ====== AtomRef Class ======
3602 /* Forward declaration for register undo */
3603 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3605 /* Ruby string "set_atom_attr" */
3606 static VALUE s_SetAtomAttrString;
3609 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3613 Data_Get_Struct(self, AtomRef, aref);
3614 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3615 if (idx < 0 || idx >= aref->mol->natoms)
3616 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3618 *app = aref->mol->atoms + idx;
3625 s_AtomFromValue(VALUE self)
3628 s_AtomIndexFromValue(self, &ap, NULL);
3633 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3636 s_AtomIndexFromValue(self, &ap, mpp);
3641 s_NotifyModificationForAtomRef(VALUE self)
3644 Data_Get_Struct(self, AtomRef, aref);
3645 MoleculeIncrementModifyCount(aref->mol);
3649 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3652 Data_Get_Struct(self, AtomRef, aref);
3653 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3656 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3657 MolActionCallback_registerUndo(aref->mol, act);
3658 MoleculeCallback_notifyModification(aref->mol, 0);
3659 /* Request MD rebuilt if necessary */
3660 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3661 aref->mol->needsMDRebuild = 1;
3666 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3669 aref = AtomRefNew(mol, idx);
3670 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3674 s_AtomRef_GetMolecule(VALUE self)
3677 s_AtomIndexFromValue(self, NULL, &mpp);
3678 return ValueFromMolecule(mpp);
3681 static VALUE s_AtomRef_GetIndex(VALUE self) {
3682 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3685 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3686 return INT2NUM(s_AtomFromValue(self)->segSeq);
3689 static VALUE s_AtomRef_GetSegName(VALUE self) {
3690 char *p = s_AtomFromValue(self)->segName;
3691 return rb_str_new(p, strlen_limit(p, 4));
3694 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3695 return INT2NUM(s_AtomFromValue(self)->resSeq);
3698 static VALUE s_AtomRef_GetResName(VALUE self) {
3699 char *p = s_AtomFromValue(self)->resName;
3700 return rb_str_new(p, strlen_limit(p, 4));
3703 static VALUE s_AtomRef_GetName(VALUE self) {
3704 char *p = s_AtomFromValue(self)->aname;
3705 return rb_str_new(p, strlen_limit(p, 4));
3708 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3709 int type = s_AtomFromValue(self)->type;
3710 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3711 return rb_str_new(p, strlen_limit(p, 6));
3714 static VALUE s_AtomRef_GetCharge(VALUE self) {
3715 return rb_float_new(s_AtomFromValue(self)->charge);
3718 static VALUE s_AtomRef_GetWeight(VALUE self) {
3719 return rb_float_new(s_AtomFromValue(self)->weight);
3722 static VALUE s_AtomRef_GetElement(VALUE self) {
3723 char *p = s_AtomFromValue(self)->element;
3724 return rb_str_new(p, strlen_limit(p, 4));
3727 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3728 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3731 static VALUE s_AtomRef_GetConnects(VALUE self) {
3734 Atom *ap = s_AtomFromValue(self);
3735 retval = rb_ary_new();
3736 cp = AtomConnectData(&ap->connect);
3737 for (i = 0; i < ap->connect.count; i++)
3738 rb_ary_push(retval, INT2NUM(cp[i]));
3742 static VALUE s_AtomRef_GetR(VALUE self) {
3743 return ValueFromVector(&(s_AtomFromValue(self)->r));
3746 static VALUE s_AtomRef_GetX(VALUE self) {
3747 return rb_float_new(s_AtomFromValue(self)->r.x);
3750 static VALUE s_AtomRef_GetY(VALUE self) {
3751 return rb_float_new(s_AtomFromValue(self)->r.y);
3754 static VALUE s_AtomRef_GetZ(VALUE self) {
3755 return rb_float_new(s_AtomFromValue(self)->r.z);
3758 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3762 s_AtomIndexFromValue(self, &ap, &mp);
3764 if (mp->cell != NULL)
3765 TransformVec(&r1, mp->cell->rtr, &r1);
3769 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3770 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3771 return ValueFromVector(&r1);
3774 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3775 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3778 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3779 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3782 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3783 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3786 static VALUE s_AtomRef_GetSigma(VALUE self) {
3787 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3790 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3791 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3794 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3795 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3798 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3799 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3802 static VALUE s_AtomRef_GetV(VALUE self) {
3803 return ValueFromVector(&(s_AtomFromValue(self)->v));
3806 static VALUE s_AtomRef_GetF(VALUE self) {
3807 Vector v = s_AtomFromValue(self)->f;
3808 VecScaleSelf(v, INTERNAL2KCAL);
3809 return ValueFromVector(&v);
3812 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3813 return rb_float_new(s_AtomFromValue(self)->occupancy);
3816 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3817 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3820 static VALUE s_AtomRef_GetAniso(VALUE self) {
3823 Atom *ap = s_AtomFromValue(self);
3824 if (ap->aniso == NULL)
3826 retval = rb_ary_new();
3827 for (i = 0; i < 6; i++)
3828 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3829 if (ap->aniso->has_bsig) {
3830 rb_ary_push(retval, INT2NUM(0));
3831 for (i = 0; i < 6; i++)
3832 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3837 static VALUE s_AtomRef_GetSymop(VALUE self) {
3839 Atom *ap = s_AtomFromValue(self);
3840 if (!ap->symop.alive)
3842 retval = rb_ary_new();
3843 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3844 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3845 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3846 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3847 rb_ary_push(retval, INT2NUM(ap->symbase));
3851 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3852 return INT2NUM(s_AtomFromValue(self)->intCharge);
3855 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3856 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3859 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3860 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3863 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3867 MDExclusion *exinfo;
3870 idx = s_AtomIndexFromValue(self, &ap, &mol);
3871 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3872 VALUE mval = ValueFromMolecule(mol);
3873 s_RebuildMDParameterIfNecessary(mval, Qnil);
3875 if (mol->arena->exinfo == NULL)
3877 exinfo = mol->arena->exinfo + idx;
3878 exlist = mol->arena->exlist;
3879 retval = rb_ary_new();
3880 aval = rb_ary_new();
3881 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3882 rb_ary_push(aval, INT2NUM(exlist[i]));
3883 rb_ary_push(retval, aval);
3884 aval = rb_ary_new();
3885 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3886 rb_ary_push(aval, INT2NUM(exlist[i]));
3887 rb_ary_push(retval, aval);
3888 aval = rb_ary_new();
3889 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3890 rb_ary_push(aval, INT2NUM(exlist[i]));
3891 rb_ary_push(retval, aval);
3895 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3896 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3899 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3900 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3903 static VALUE s_AtomRef_GetHidden(VALUE self) {
3904 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3907 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3910 Atom *ap = s_AtomFromValue(self);
3911 if (ap->anchor == NULL)
3913 count = ap->anchor->connect.count;
3914 retval = rb_ary_new2(count * 2);
3915 cp = AtomConnectData(&ap->anchor->connect);
3916 for (i = 0; i < count; i++) {
3917 rb_ary_store(retval, i, INT2NUM(cp[i]));
3918 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3923 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3924 char *p = s_AtomFromValue(self)->uff_type;
3925 return rb_str_new(p, strlen_limit(p, 5));
3928 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3929 rb_raise(rb_eMolbyError, "index cannot be directly set");
3933 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3934 VALUE oval = s_AtomRef_GetSegSeq(self);
3935 val = rb_Integer(val);
3936 s_AtomFromValue(self)->segSeq = NUM2INT(val);
3937 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3941 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3942 char *p = StringValuePtr(val);
3943 VALUE oval = s_AtomRef_GetSegName(self);
3944 strncpy(s_AtomFromValue(self)->segName, p, 4);
3945 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3949 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3950 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3951 return val; /* Not reached */
3954 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3955 Atom *ap = s_AtomFromValue(self);
3956 char *p = StringValuePtr(val);
3957 VALUE oval = s_AtomRef_GetName(self);
3958 if (ap->anchor != NULL && p[0] == '_')
3959 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
3960 strncpy(ap->aname, p, 4);
3961 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3965 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3967 char *p = StringValuePtr(val);
3968 VALUE oval = s_AtomRef_GetAtomType(self);
3969 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3970 if (type != 0 && type < kAtomTypeMinimum)
3971 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3972 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3973 mp->needsMDRebuild = 1;
3974 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3978 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3980 VALUE oval = s_AtomRef_GetCharge(self);
3981 val = rb_Float(val);
3982 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3983 mp->needsMDRebuild = 1;
3984 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3988 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3990 VALUE oval = s_AtomRef_GetWeight(self);
3991 val = rb_Float(val);
3992 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3993 mp->needsMDRebuild = 1;
3994 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3998 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4001 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4002 char *p = StringValuePtr(val);
4003 VALUE oval = s_AtomRef_GetElement(self);
4004 ap->atomicNumber = ElementToInt(p);
4005 ElementToString(ap->atomicNumber, ap->element);
4006 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4008 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4009 mp->needsMDRebuild = 1;
4013 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4016 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4017 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4018 val = rb_Integer(val);
4019 ap->atomicNumber = NUM2INT(val);
4020 ElementToString(ap->atomicNumber, ap->element);
4021 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4023 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4024 mp->needsMDRebuild = 1;
4028 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4029 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4030 return val; /* Not reached */
4033 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4036 VALUE oval = s_AtomRef_GetR(self);
4037 VectorFromValue(val, &v);
4038 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4039 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4040 mp->needsMDCopyCoordinates = 1;
4044 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4047 VALUE oval = s_AtomRef_GetX(self);
4048 val = rb_Float(val);
4050 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4051 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4052 mp->needsMDCopyCoordinates = 1;
4056 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4059 VALUE oval = s_AtomRef_GetY(self);
4060 val = rb_Float(val);
4062 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4063 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4064 mp->needsMDCopyCoordinates = 1;
4068 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4071 VALUE oval = s_AtomRef_GetZ(self);
4072 val = rb_Float(val);
4074 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4075 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4076 mp->needsMDCopyCoordinates = 1;
4080 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4084 s_AtomIndexFromValue(self, &ap, &mp);
4086 VectorFromValue(val, &v);
4087 if (mp->cell != NULL)
4088 TransformVec(&v, mp->cell->tr, &v);
4090 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4091 mp->needsMDCopyCoordinates = 1;
4095 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4100 s_AtomIndexFromValue(self, &ap, &mp);
4102 val = rb_Float(val);
4104 if (mp->cell != NULL) {
4105 TransformVec(&v, mp->cell->rtr, &v);
4107 TransformVec(&v, mp->cell->tr, &v);
4110 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4111 mp->needsMDCopyCoordinates = 1;
4115 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4120 s_AtomIndexFromValue(self, &ap, &mp);
4122 val = rb_Float(val);
4124 if (mp->cell != NULL) {
4125 TransformVec(&v, mp->cell->rtr, &v);
4127 TransformVec(&v, mp->cell->tr, &v);
4130 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4131 mp->needsMDCopyCoordinates = 1;
4135 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4140 s_AtomIndexFromValue(self, &ap, &mp);
4142 val = rb_Float(val);
4144 if (mp->cell != NULL) {
4145 TransformVec(&v, mp->cell->rtr, &v);
4147 TransformVec(&v, mp->cell->tr, &v);
4150 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4151 mp->needsMDCopyCoordinates = 1;
4155 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4158 VALUE oval = s_AtomRef_GetSigma(self);
4159 VectorFromValue(val, &v);
4160 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4161 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4162 mp->needsMDCopyCoordinates = 1;
4166 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4169 VALUE oval = s_AtomRef_GetSigmaX(self);
4170 val = rb_Float(val);
4172 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4173 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4174 mp->needsMDCopyCoordinates = 1;
4178 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4181 VALUE oval = s_AtomRef_GetSigmaY(self);
4182 val = rb_Float(val);
4184 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4185 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4186 mp->needsMDCopyCoordinates = 1;
4190 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4193 VALUE oval = s_AtomRef_GetSigmaZ(self);
4194 val = rb_Float(val);
4196 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4197 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4198 mp->needsMDCopyCoordinates = 1;
4202 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4206 VALUE oval = s_AtomRef_GetV(self);
4207 VectorFromValue(val, &v);
4208 s_AtomIndexFromValue(self, &ap, &mp);
4210 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4211 mp->needsMDCopyCoordinates = 1;
4215 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4218 VALUE oval = s_AtomRef_GetF(self);
4219 VectorFromValue(val, &v);
4220 VecScaleSelf(v, KCAL2INTERNAL);
4221 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4222 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4223 mp->needsMDCopyCoordinates = 1;
4227 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4228 VALUE oval = s_AtomRef_GetOccupancy(self);
4230 val = rb_Float(val);
4231 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4232 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4233 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4237 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4238 VALUE oval = s_AtomRef_GetTempFactor(self);
4239 val = rb_Float(val);
4240 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4241 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4245 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4250 VALUE oval = s_AtomRef_GetAniso(self);
4251 Data_Get_Struct(self, AtomRef, aref);
4252 val = rb_funcall(val, rb_intern("to_a"), 0);
4253 n = RARRAY_LEN(val);
4254 valp = RARRAY_PTR(val);
4255 for (i = 0; i < 6; i++) {
4257 f[i] = NUM2DBL(rb_Float(valp[i]));
4261 type = NUM2INT(rb_Integer(valp[6]));
4264 for (i = 0; i < 6; i++)
4265 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4267 for (i = 0; i < 6; i++)
4270 i = s_AtomIndexFromValue(self, NULL, NULL);
4271 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4272 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4276 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4282 VALUE oval = s_AtomRef_GetSymop(self);
4283 i = s_AtomIndexFromValue(self, &ap, &mol);
4285 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4287 val = rb_funcall(val, rb_intern("to_a"), 0);
4288 n = RARRAY_LEN(val);
4289 valp = RARRAY_PTR(val);
4290 for (i = 0; i < 5; i++) {
4292 if (valp[i] == Qnil)
4295 ival[i] = NUM2INT(rb_Integer(valp[i]));
4296 } else ival[i] = -100000;
4299 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4300 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));
4301 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4302 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4303 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4304 return val; /* No need to change */
4305 if (ival[0] != -100000)
4306 ap->symop.sym = ival[0];
4307 if (ival[1] != -100000)
4308 ap->symop.dx = ival[1];
4309 if (ival[2] != -100000)
4310 ap->symop.dy = ival[2];
4311 if (ival[3] != -100000)
4312 ap->symop.dz = ival[3];
4313 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4314 if (ival[4] != -100000)
4315 ap->symbase = ival[4];
4316 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4317 /* The anisotropic parameters should be recalculated */
4318 VALUE oaval = s_AtomRef_GetAniso(self);
4319 MoleculeSetAnisoBySymop(mol, i);
4320 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4322 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4326 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4327 VALUE oval = s_AtomRef_GetIntCharge(self);
4328 val = rb_Integer(val);
4329 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4330 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4334 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4336 VALUE oval = s_AtomRef_GetFixForce(self);
4337 val = rb_Float(val);
4338 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4339 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4340 mp->needsMDRebuild = 1;
4344 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4347 VALUE oval = s_AtomRef_GetFixPos(self);
4348 VectorFromValue(val, &v);
4349 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4350 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4351 mp->needsMDRebuild = 1;
4355 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4356 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4357 return val; /* Not reached */
4360 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4361 VALUE oval = s_AtomRef_GetIntCharge(self);
4362 val = rb_Integer(val);
4363 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4364 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4368 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4369 VALUE oval = s_AtomRef_GetIntCharge(self);
4370 val = rb_Integer(val);
4371 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4372 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4376 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4377 Atom *ap = s_AtomFromValue(self);
4378 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4380 ap->exflags |= kAtomHiddenFlag;
4382 ap->exflags &= ~kAtomHiddenFlag;
4384 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4388 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4389 Int idx, i, j, k, n, *ip;
4396 MolAction **undoActions;
4397 memset(&ac, 0, sizeof(ac));
4398 idx = s_AtomIndexFromValue(self, &ap, &mol);
4399 oval = s_AtomRef_GetAnchorList(self);
4401 val = rb_ary_to_ary(val);
4402 n = RARRAY_LEN(val);
4405 if (ap->anchor != NULL) {
4406 AtomConnectResize(&ap->anchor->connect, 0);
4407 free(ap->anchor->coeffs);
4410 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4415 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4416 if (ap->aname[0] == '_')
4417 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4418 ip = (Int *)malloc(sizeof(Int) * n);
4420 for (i = 0; i < n; i++) {
4421 v = RARRAY_PTR(val)[i];
4422 if (rb_obj_is_kind_of(v, rb_cFloat))
4424 j = NUM2INT(rb_Integer(v));
4425 if (j < 0 || j >= mol->natoms)
4426 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4427 for (k = 0; k < i; k++) {
4429 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4435 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4436 else if (i * 2 != n)
4437 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4438 dp = (Double *)malloc(sizeof(Double) * n / 2);
4439 for (i = 0; i < n / 2; i++) {
4440 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4442 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4448 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4452 rb_raise(rb_eMolbyError, "invalid argument");
4453 if (nUndoActions > 0) {
4454 for (i = 0; i < nUndoActions; i++) {
4455 MolActionCallback_registerUndo(mol, undoActions[i]);
4456 MolActionRelease(undoActions[i]);
4460 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4464 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4465 Atom *ap = s_AtomFromValue(self);
4466 char *p = StringValuePtr(val);
4467 VALUE oval = s_AtomRef_GetUFFType(self);
4468 strncpy(ap->uff_type, p, 5);
4469 ap->uff_type[5] = 0;
4470 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4474 static struct s_AtomAttrDef {
4476 VALUE *symref; /* Address of s_IndexSymbol etc. */
4477 ID id; /* Will be set within InitMolby() */
4478 VALUE (*getter)(VALUE);
4479 VALUE (*setter)(VALUE, VALUE);
4480 } s_AtomAttrDefTable[] = {
4481 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4482 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4483 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4484 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4485 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4486 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4487 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4488 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4489 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4490 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4491 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4492 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4493 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4494 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4495 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4496 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4497 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4498 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4499 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4500 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4501 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4502 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4503 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4504 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4505 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4506 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4507 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4508 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4509 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4510 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4511 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4512 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4513 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4514 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4515 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4516 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4517 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4518 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4519 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4520 {NULL} /* Sentinel */
4524 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4528 if (TYPE(key) != T_SYMBOL) {
4529 kid = rb_intern(StringValuePtr(key));
4531 } else kid = SYM2ID(key);
4532 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4533 if (s_AtomAttrDefTable[i].id == kid) {
4534 if (value == Qundef)
4535 return (*(s_AtomAttrDefTable[i].getter))(self);
4537 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4540 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4541 return Qnil; /* not reached */
4545 s_AtomRef_GetAttr(VALUE self, VALUE key)
4547 return s_AtomRef_SetAttr(self, key, Qundef);
4552 * self == atomRef -> boolean
4554 * True if the two references point to the same atom.
4557 s_AtomRef_Equal(VALUE self, VALUE val)
4559 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4560 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4561 } else return Qfalse;
4564 #pragma mark ====== MolEnumerable Class ======
4566 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4570 * self[idx] -> AtomRef or Array of Integers
4572 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4573 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4574 * value is a String. Otherwise, the return value is an Array of Integers.
4577 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4579 MolEnumerable *mseq;
4582 Data_Get_Struct(self, MolEnumerable, mseq);
4584 if (mseq->kind == kAtomKind) {
4585 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4587 idx1 = NUM2INT(arg1);
4588 switch (mseq->kind) {
4590 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4591 if (idx2 < 0 || idx2 >= mol->nbonds)
4592 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4593 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4596 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4597 if (idx2 < 0 || idx2 >= mol->nangles)
4598 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4599 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4601 case kDihedralKind: {
4602 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4603 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4604 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4605 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]));
4607 case kImproperKind: {
4608 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4609 if (idx2 < 0 || idx2 >= mol->nimpropers)
4610 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4611 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]));
4613 case kResidueKind: {
4615 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4616 if (idx2 < 0 || idx2 >= mol->nresidues)
4617 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4618 p = mol->residues[idx2];
4619 return rb_str_new(p, strlen_limit(p, 4));
4629 * Returns the number of objects included in this enumerable.
4632 s_MolEnumerable_Length(VALUE self)
4634 MolEnumerable *mseq;
4635 Data_Get_Struct(self, MolEnumerable, mseq);
4636 switch (mseq->kind) {
4638 return INT2NUM(mseq->mol->natoms);
4640 return INT2NUM(mseq->mol->nbonds);
4642 return INT2NUM(mseq->mol->nangles);
4644 return INT2NUM(mseq->mol->ndihedrals);
4646 return INT2NUM(mseq->mol->nimpropers);
4648 return INT2NUM(mseq->mol->nresidues);
4657 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4658 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4659 * For the atoms, a same AtomRef object is passed (with different internal information)
4660 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4661 * for each iteration.
4664 s_MolEnumerable_Each(VALUE self)
4666 MolEnumerable *mseq;
4668 int len = NUM2INT(s_MolEnumerable_Length(self));
4669 Data_Get_Struct(self, MolEnumerable, mseq);
4670 if (mseq->kind == kAtomKind) {
4671 /* The same AtomRef object will be used during the loop */
4672 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4673 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4674 for (i = 0; i < len; i++) {
4679 /* A new ruby object will be created at each iteration (not very efficient) */
4680 for (i = 0; i < len; i++) {
4681 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4689 * self == molEnumerable -> boolean
4691 * True if the two arguments point to the same molecule and enumerable type.
4694 s_MolEnumerable_Equal(VALUE self, VALUE val)
4696 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4697 MolEnumerable *mseq1, *mseq2;
4698 Data_Get_Struct(self, MolEnumerable, mseq1);
4699 Data_Get_Struct(val, MolEnumerable, mseq2);
4700 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4701 } else return Qfalse;
4705 #pragma mark ====== Molecule Class ======
4707 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4708 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4709 char *gLoadSaveErrorMessage = NULL;
4711 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4714 MoleculeFromValue(VALUE val)
4717 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4718 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4719 Data_Get_Struct(val, Molecule, mol);
4723 static VALUE sMoleculeRetainArray = Qnil;
4725 /* The function is called from MoleculeRelease() */
4726 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4727 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4728 /* object is always returned for the same Molecule. */
4729 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4730 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4731 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4732 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4733 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4735 /* Register/unregister the exmolobj Ruby object */
4737 MoleculeReleaseExternalObj(Molecule *mol)
4739 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4740 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4741 mol->exmolobjProtected = 0;
4746 MoleculeRetainExternalObj(Molecule *mol)
4748 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4749 if (sMoleculeRetainArray == Qnil) {
4750 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4751 sMoleculeRetainArray = rb_ary_new();
4754 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4755 mol->exmolobjProtected = 1;
4759 /* Release hook function for Ruby */
4761 MoleculeReleaseHook(Molecule *mol)
4763 if (mol->exmolobj != NULL) {
4764 /* No need to remove from sMoleculeRetainArray */
4765 mol->exmolobj = NULL;
4766 mol->exmolobjProtected = 0;
4768 MoleculeRelease(mol);
4772 ValueFromMolecule(Molecule *mol)
4776 if (mol->exmolobj != NULL)
4777 return (VALUE)mol->exmolobj;
4778 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4779 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4780 return (VALUE)mol->exmolobj;
4785 s_Molecule_Alloc(VALUE klass)
4788 Molecule *mol = MoleculeNew();
4789 val = ValueFromMolecule(mol);
4790 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4795 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4799 if (FIXNUM_P(val)) {
4801 if (n >= 0 && n < mol->natoms)
4803 n = -1; /* No such atom */
4804 val = rb_inspect(val);
4806 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4808 if (n >= 0 && n < mol->natoms)
4810 p = StringValuePtr(val);
4812 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4814 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4816 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4817 return 0; /* Not reached */
4821 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4824 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4825 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4826 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4827 Data_Get_Struct(val, IntGroup, ig);
4833 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4835 if (gLoadSaveErrorMessage != NULL) {
4836 MyAppCallback_setConsoleColor(1);
4837 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4838 MyAppCallback_setConsoleColor(0);
4841 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4848 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4849 * created object does not affect the old object in any sense.
4852 s_Molecule_InitCopy(VALUE self, VALUE arg)
4854 Molecule *mp1, *mp2;
4855 Data_Get_Struct(self, Molecule, mp1);
4856 mp2 = MoleculeFromValue(arg);
4857 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4858 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4864 * loadmbsf(file) -> bool
4866 * Read a structure from a mbsf file.
4867 * Return true if successful.
4870 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4876 MoleculeClearLoadSaveErrorMessage();
4877 Data_Get_Struct(self, Molecule, mol);
4878 rb_scan_args(argc, argv, "1", &fname);
4879 fstr = FileStringValuePtr(fname);
4880 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4881 s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4887 * loadpsf(file, pdbfile = nil) -> bool
4889 * Read a structure from a psf file. molecule must be empty. The psf may be
4890 * an "extended" version, which also contains coordinates. If pdbfile
4891 * is given, then atomic coordinates are read from that file.
4892 * Return true if successful.
4895 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4897 VALUE fname, pdbname;
4898 char *fstr, *pdbstr;
4901 Data_Get_Struct(self, Molecule, mol);
4902 if (mol->natoms > 0)
4903 return Qnil; /* Must be a new molecule */
4904 MoleculeClearLoadSaveErrorMessage();
4905 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4906 fstr = FileStringValuePtr(fname);
4907 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4908 s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4910 if (!NIL_P(pdbname)) {
4911 pdbstr = strdup(FileStringValuePtr(pdbname));
4912 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4914 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4921 * loadpdb(file) -> bool
4923 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4924 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4925 * Return true if successful.
4928 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4934 Data_Get_Struct(self, Molecule, mol);
4935 rb_scan_args(argc, argv, "1", &fname);
4936 MoleculeClearLoadSaveErrorMessage();
4937 fstr = FileStringValuePtr(fname);
4938 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4939 s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
4945 * loaddcd(file) -> bool
4947 * Read coordinates from a dcd file. The molecule should not empty.
4948 * Return true if successful.
4951 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4957 Data_Get_Struct(self, Molecule, mol);
4958 rb_scan_args(argc, argv, "1", &fname);
4959 MoleculeClearLoadSaveErrorMessage();
4960 fstr = FileStringValuePtr(fname);
4961 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
4962 s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
4968 * loadtep(file) -> bool
4970 * Read coordinates from an ortep .tep file.
4971 * Return true if successful.
4974 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4980 Data_Get_Struct(self, Molecule, mol);
4981 rb_scan_args(argc, argv, "1", &fname);
4982 MoleculeClearLoadSaveErrorMessage();
4983 fstr = FileStringValuePtr(fname);
4984 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
4985 s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
4991 * loadres(file) -> bool
4993 * Read coordinates from a shelx .res file.
4994 * Return true if successful.
4997 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5003 Data_Get_Struct(self, Molecule, mol);
5004 rb_scan_args(argc, argv, "1", &fname);
5005 MoleculeClearLoadSaveErrorMessage();
5006 fstr = FileStringValuePtr(fname);
5007 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5008 s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
5014 * loadfchk(file) -> bool
5016 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5017 * Return true if successful.
5020 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5026 Data_Get_Struct(self, Molecule, mol);
5027 rb_scan_args(argc, argv, "1", &fname);
5028 MoleculeClearLoadSaveErrorMessage();
5029 fstr = FileStringValuePtr(fname);
5030 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5031 s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
5037 * loaddat(file) -> bool
5039 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5040 * Return true if successful.
5043 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5049 Data_Get_Struct(self, Molecule, mol);
5050 rb_scan_args(argc, argv, "1", &fname);
5051 MoleculeClearLoadSaveErrorMessage();
5052 fstr = FileStringValuePtr(fname);
5053 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5054 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5055 MyAppCallback_hideProgressPanel();
5056 s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
5062 * savembsf(file) -> bool
5064 * Write structure as a mbsf file. Returns true if successful.
5067 s_Molecule_Savembsf(VALUE self, VALUE fname)
5072 Data_Get_Struct(self, Molecule, mol);
5073 MoleculeClearLoadSaveErrorMessage();
5074 fstr = FileStringValuePtr(fname);
5075 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5076 s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
5082 * savepsf(file) -> bool
5084 * Write structure as a psf file. Returns true if successful.
5087 s_Molecule_Savepsf(VALUE self, VALUE fname)
5092 Data_Get_Struct(self, Molecule, mol);
5093 MoleculeClearLoadSaveErrorMessage();
5094 fstr = FileStringValuePtr(fname);
5095 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5096 s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
5102 * savepdb(file) -> bool
5104 * Write coordinates as a pdb file. Returns true if successful.
5107 s_Molecule_Savepdb(VALUE self, VALUE fname)
5112 Data_Get_Struct(self, Molecule, mol);
5113 MoleculeClearLoadSaveErrorMessage();
5114 fstr = FileStringValuePtr(fname);
5115 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5116 s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
5122 * savedcd(file) -> bool
5124 * Write coordinates as a dcd file. Returns true if successful.
5127 s_Molecule_Savedcd(VALUE self, VALUE fname)
5132 Data_Get_Struct(self, Molecule, mol);
5133 MoleculeClearLoadSaveErrorMessage();
5134 fstr = FileStringValuePtr(fname);
5135 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5136 s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
5142 * savetep(file) -> bool
5144 * Write coordinates as an ORTEP file. Returns true if successful.
5148 s_Molecule_Savetep(VALUE self, VALUE fname)
5153 Data_Get_Struct(self, Molecule, mol);
5154 fstr = FileStringValuePtr(fname);
5155 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
5156 rb_raise(rb_eMolbyError, errbuf);
5161 /* load([ftype, ] fname, ...) */
5163 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5166 char *argstr, *methname, *p, *type = "";
5169 const char *ls = (loadFlag ? "load" : "save");
5170 int lslen = strlen(ls);
5175 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5176 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5177 if (argstr[0] == ':') {
5178 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5179 methname = ALLOC_N(char, lslen + strlen(argstr));
5180 strcpy(methname, ls);
5181 strcat(methname, argstr + 1);
5183 for (i = lslen; methname[i] != 0; i++)
5184 methname[i] = tolower(methname[i]);
5185 mid = rb_intern(methname);
5189 rval = rb_funcall2(self, mid, argc, argv);
5195 /* Guess file type from extension */
5196 p = strrchr(argstr, '.');
5200 for (methname = p; *methname != 0; methname++) {
5201 if (!isalpha(*methname))
5204 if (*methname == 0) {
5205 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5206 if (methname == NULL)
5207 rb_raise(rb_eMolbyError, "Low memory");
5208 strcpy(methname, ls);
5209 strcat(methname, p);
5210 for (i = lslen; methname[i] != 0; i++)
5211 methname[i] = tolower(methname[i]);
5212 mid = rb_intern(methname);
5215 if (rb_respond_to(self, mid)) {
5216 /* Load: try to call the load procedure only if it is available */
5217 rval = rb_funcall2(self, mid, argc, argv);
5222 /* Save: call the save procedure, and if not found then call 'method_missing' */
5223 rval = rb_funcall2(self, mid, argc, argv);
5230 rval = rb_str_to_str(argv[0]);
5231 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5232 s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
5233 return Qnil; /* Does not reach here */
5237 /* Register the path */
5240 Data_Get_Struct(self, Molecule, mol);
5241 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5243 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5244 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5245 if (ap->occupancy != 0.0)
5248 if (i == mol->natoms) {
5249 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5250 ap->occupancy = 1.0;
5259 * molload(file, *args) -> bool
5261 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5262 * file type given by the extension). If this method fails, then all defined (public)
5263 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5266 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5268 return s_Molecule_LoadSave(argc, argv, self, 1);
5273 * molsave(file, *args) -> bool
5275 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5276 * (XXX is the file type given by the extension).
5279 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5281 return s_Molecule_LoadSave(argc, argv, self, 0);
5288 * Returns the display name of the molecule. If the molecule has no associated
5289 * document, then returns nil.
5292 s_Molecule_Name(VALUE self)
5296 Data_Get_Struct(self, Molecule, mol);
5297 MoleculeCallback_displayName(mol, buf, sizeof buf);
5301 return rb_str_new2(buf);
5306 * set_name(string) -> self
5308 * Set the name of an untitled molecule. If the molecule is not associated with window
5309 * or it already has an associated file, then exception is thrown.
5312 s_Molecule_SetName(VALUE self, VALUE nval)
5315 Data_Get_Struct(self, Molecule, mol);
5316 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5317 rb_raise(rb_eMolbyError, "Cannot change the window title");
5326 * Returns the full path name of the molecule, if it is associated with a file.
5327 * If the molecule has no associated file, then returns nil.
5330 s_Molecule_Path(VALUE self)
5334 Data_Get_Struct(self, Molecule, mol);
5335 MoleculeCallback_pathName(mol, buf, sizeof buf);
5339 return Ruby_NewFileStringValue(buf);
5346 * Returns the full path name of the directory in which the file associated with the
5347 * molecule is located. If the molecule has no associated file, then returns nil.
5350 s_Molecule_Dir(VALUE self)
5354 Data_Get_Struct(self, Molecule, mol);
5355 MoleculeCallback_pathName(mol, buf, sizeof buf);
5357 translate_char(buf, '\\', '/');
5362 p = strrchr(buf, '/');
5365 return rb_str_new2(buf);
5373 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5374 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5375 * the Molecule structure) is returned.
5378 s_Molecule_Inspect(VALUE self)
5382 Data_Get_Struct(self, Molecule, mol);
5383 MoleculeCallback_displayName(mol, buf, sizeof buf);
5385 /* No associated document */
5386 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5387 return rb_str_new2(buf);
5389 /* Check whether the document name is duplicate */
5393 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5394 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5395 if (strcmp(buf, buf2) == 0) {
5402 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5404 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5406 return rb_str_new2(buf2);
5413 * open(file) -> Molecule
5415 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5418 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5424 rb_scan_args(argc, argv, "01", &fname);
5428 p = FileStringValuePtr(fname);
5429 iflag = Ruby_SetInterruptFlag(Qfalse);
5430 mp = MoleculeCallback_openNewMolecule(p);
5431 Ruby_SetInterruptFlag(iflag);
5434 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5436 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5438 return ValueFromMolecule(mp);
5444 * new(file, *args) -> Molecule
5446 * Create a new molecule and call "load" method with the same arguments.
5449 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5452 return s_Molecule_Load(argc, argv, self);
5453 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5457 s_Molecule_MolEnumerable(VALUE self, int kind)
5460 MolEnumerable *mseq;
5461 Data_Get_Struct(self, Molecule, mol);
5462 mseq = MolEnumerableNew(mol, kind);
5463 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5468 * atoms -> MolEnumerable
5470 * Returns a MolEnumerable object representing the array of atoms.
5473 s_Molecule_Atoms(VALUE self)
5475 return s_Molecule_MolEnumerable(self, kAtomKind);
5480 * bonds -> MolEnumerable
5482 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5483 * by an array of two atom indices.
5486 s_Molecule_Bonds(VALUE self)
5488 return s_Molecule_MolEnumerable(self, kBondKind);
5493 * angles -> MolEnumerable
5495 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5496 * by an array of three atom indices.
5499 s_Molecule_Angles(VALUE self)
5501 return s_Molecule_MolEnumerable(self, kAngleKind);
5506 * dihedrals -> MolEnumerable
5508 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5509 * by an array of four atom indices.
5512 s_Molecule_Dihedrals(VALUE self)
5514 return s_Molecule_MolEnumerable(self, kDihedralKind);
5519 * impropers -> MolEnumerable
5521 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5522 * by an array of four atom indices.
5525 s_Molecule_Impropers(VALUE self)
5527 return s_Molecule_MolEnumerable(self, kImproperKind);
5532 * residues -> MolEnumerable
5534 * Returns a MolEnumerable object representing the array of residue names.
5537 s_Molecule_Residues(VALUE self)
5539 return s_Molecule_MolEnumerable(self, kResidueKind);
5546 * Returns the number of atoms.
5549 s_Molecule_Natoms(VALUE self)
5552 Data_Get_Struct(self, Molecule, mol);
5553 return INT2NUM(mol->natoms);
5560 * Returns the number of bonds.
5563 s_Molecule_Nbonds(VALUE self)
5566 Data_Get_Struct(self, Molecule, mol);
5567 return INT2NUM(mol->nbonds);
5572 * nangles -> Integer
5574 * Returns the number of angles.
5577 s_Molecule_Nangles(VALUE self)
5580 Data_Get_Struct(self, Molecule, mol);
5581 return INT2NUM(mol->nangles);
5586 * ndihedrals -> Integer
5588 * Returns the number of dihedrals.
5591 s_Molecule_Ndihedrals(VALUE self)
5594 Data_Get_Struct(self, Molecule, mol);
5595 return INT2NUM(mol->ndihedrals);
5600 * nimpropers -> Integer
5602 * Returns the number of impropers.
5605 s_Molecule_Nimpropers(VALUE self)
5608 Data_Get_Struct(self, Molecule, mol);
5609 return INT2NUM(mol->nimpropers);
5614 * nresidues -> Integer
5616 * Returns the number of residues.
5619 s_Molecule_Nresidues(VALUE self)
5622 Data_Get_Struct(self, Molecule, mol);
5623 return INT2NUM(mol->nresidues);
5627 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5629 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.");
5634 * bond_par(idx) -> ParameterRef
5636 * Returns the MD parameter for the idx-th bond.
5640 s_Molecule_BondPar(VALUE self, VALUE val)
5647 Data_Get_Struct(self, Molecule, mol);
5648 ival = NUM2INT(rb_Integer(val));
5649 if (ival < -mol->nbonds || ival >= mol->nbonds)
5650 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5652 ival += mol->nbonds;
5653 s_RebuildMDParameterIfNecessary(self, Qtrue);
5654 i1 = mol->bonds[ival * 2];
5655 i2 = mol->bonds[ival * 2 + 1];
5656 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5657 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5658 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5661 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5667 * angle_par(idx) -> ParameterRef
5669 * Returns the MD parameter for the idx-th angle.
5673 s_Molecule_AnglePar(VALUE self, VALUE val)
5680 Data_Get_Struct(self, Molecule, mol);
5681 ival = NUM2INT(rb_Integer(val));
5682 if (ival < -mol->nangles || ival >= mol->nangles)
5683 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5685 ival += mol->nangles;
5686 s_RebuildMDParameterIfNecessary(self, Qtrue);
5687 i1 = mol->angles[ival * 3];
5688 i2 = mol->angles[ival * 3 + 1];
5689 i3 = mol->angles[ival * 3 + 2];
5690 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5691 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5692 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5693 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5696 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5701 * dihedral_par(idx) -> ParameterRef
5703 * Returns the MD parameter for the idx-th dihedral.
5707 s_Molecule_DihedralPar(VALUE self, VALUE val)
5712 UInt t1, t2, t3, t4;
5714 Data_Get_Struct(self, Molecule, mol);
5715 ival = NUM2INT(rb_Integer(val));
5716 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5717 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5719 ival += mol->ndihedrals;
5720 s_RebuildMDParameterIfNecessary(self, Qtrue);
5721 i1 = mol->dihedrals[ival * 4];
5722 i2 = mol->dihedrals[ival * 4 + 1];
5723 i3 = mol->dihedrals[ival * 4 + 2];
5724 i4 = mol->dihedrals[ival * 4 + 3];
5725 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5726 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5727 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5728 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5729 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5732 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5737 * improper_par(idx) -> ParameterRef
5739 * Returns the MD parameter for the idx-th improper.
5743 s_Molecule_ImproperPar(VALUE self, VALUE val)
5748 UInt t1, t2, t3, t4;
5750 Data_Get_Struct(self, Molecule, mol);
5751 ival = NUM2INT(rb_Integer(val));
5752 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5753 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5755 ival += mol->nimpropers;
5756 s_RebuildMDParameterIfNecessary(self, Qtrue);
5757 i1 = mol->impropers[ival * 4];
5758 i2 = mol->impropers[ival * 4 + 1];
5759 i3 = mol->impropers[ival * 4 + 2];
5760 i4 = mol->impropers[ival * 4 + 3];
5761 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5762 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5763 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5764 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5765 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5768 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5774 * start_step -> Integer
5776 * Returns the start step (defined by dcd format).
5779 s_Molecule_StartStep(VALUE self)
5782 Data_Get_Struct(self, Molecule, mol);
5783 return INT2NUM(mol->startStep);
5788 * start_step = Integer
5790 * Set the start step (defined by dcd format).
5793 s_Molecule_SetStartStep(VALUE self, VALUE val)
5796 Data_Get_Struct(self, Molecule, mol);
5797 mol->startStep = NUM2INT(rb_Integer(val));
5803 * steps_per_frame -> Integer
5805 * Returns the number of steps between frames (defined by dcd format).
5808 s_Molecule_StepsPerFrame(VALUE self)
5811 Data_Get_Struct(self, Molecule, mol);
5812 return INT2NUM(mol->stepsPerFrame);
5817 * steps_per_frame = Integer
5819 * Set the number of steps between frames (defined by dcd format).
5822 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5825 Data_Get_Struct(self, Molecule, mol);
5826 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5832 * ps_per_step -> Float
5834 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5837 s_Molecule_PsPerStep(VALUE self)
5840 Data_Get_Struct(self, Molecule, mol);
5841 return rb_float_new(mol->psPerStep);
5846 * ps_per_step = Float
5848 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5851 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5854 Data_Get_Struct(self, Molecule, mol);
5855 mol->psPerStep = NUM2DBL(rb_Float(val));
5861 * find_angles -> Integer
5863 * Find the angles from the bonds. Returns the number of angles newly created.
5867 s_Molecule_FindAngles(VALUE self)
5873 Data_Get_Struct(self, Molecule, mol);
5874 if (mol == NULL || mol->natoms == 0)
5878 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5879 nc = ap->connect.count;
5881 for (i = 0; i < nc; i++) {
5882 n[0] = ap->connects[i];
5883 for (j = i + 1; j < nc; j++) {
5884 n[2] = ap->connects[j];
5885 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5886 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5891 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5894 return INT2NUM(nip);
5899 * find_dihedrals -> Integer
5901 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5905 s_Molecule_FindDihedrals(VALUE self)
5909 int n1, i, j, k, nc1, nc2;
5911 Data_Get_Struct(self, Molecule, mol);
5912 if (mol == NULL || mol->natoms == 0)
5916 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5917 nc1 = ap1->connect.count;
5919 for (i = 0; i < nc1; i++) {
5920 n[2] = ap1->connects[i];
5923 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5924 nc2 = ap2->connect.count;
5925 for (j = 0; j < nc1; j++) {
5926 n[0] = ap1->connects[j];
5929 for (k = 0; k < nc2; k++) {
5930 n[3] = ap2->connects[k];
5931 if (n[3] == n1 || n[3] == n[0])
5933 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5934 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5940 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5943 return INT2NUM(nip);
5949 * nresidues = Integer
5951 * Change the number of residues.
5954 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5957 int ival = NUM2INT(val);
5958 Data_Get_Struct(self, Molecule, mol);
5959 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5960 if (ival != mol->nresidues)
5961 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5967 * max_residue_number(atom_group = nil) -> Integer
5969 * Returns the maximum residue number actually used. If an atom group is given, only
5970 * these atoms are examined. If no atom is present, nil is returned.
5973 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5979 Data_Get_Struct(self, Molecule, mol);
5980 rb_scan_args(argc, argv, "01", &gval);
5981 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5982 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5983 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5988 * min_residue_number(atom_group = nil) -> Integer
5990 * Returns the minimum residue number actually used. If an atom group is given, only
5991 * these atoms are examined. If no atom is present, nil is returned.
5994 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
6000 Data_Get_Struct(self, Molecule, mol);
6001 rb_scan_args(argc, argv, "01", &gval);
6002 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6003 minSeq = MoleculeMinimumResidueNumber(mol, ig);
6004 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
6009 * each_atom(atom_group = nil) {|aref| ...}
6011 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
6012 * group is given, only these atoms are processed.
6013 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
6014 * is self (a Molecule object).
6017 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
6025 Data_Get_Struct(self, Molecule, mol);
6026 rb_scan_args(argc, argv, "01", &gval);
6027 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6028 arval = ValueFromMoleculeAndIndex(mol, 0);
6029 Data_Get_Struct(arval, AtomRef, aref);
6030 for (i = 0; i < mol->natoms; i++) {
6032 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6036 IntGroupRelease(ig);
6042 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
6044 * Returns the unit cell parameters. If cell is not set, returns nil.
6047 s_Molecule_Cell(VALUE self)
6052 Data_Get_Struct(self, Molecule, mol);
6053 if (mol->cell == NULL)
6055 val = rb_ary_new2(6);
6056 for (i = 0; i < 6; i++)
6057 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
6058 if (mol->cell->has_sigma) {
6059 for (i = 0; i < 6; i++) {
6060 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
6068 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
6069 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
6071 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
6072 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
6073 This operation is undoable.
6074 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
6077 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
6081 int i, convert_coord, n;
6083 Data_Get_Struct(self, Molecule, mol);
6084 rb_scan_args(argc, argv, "11", &val, &cval);
6089 val = rb_ary_to_ary(val);
6090 len = RARRAY_LEN(val);
6093 } else if (len >= 6) {
6095 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
6096 for (i = 0; i < n; i++)
6097 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
6099 convert_coord = (RTEST(cval) ? 1 : 0);
6100 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
6106 * box -> [avec, bvec, cvec, origin, flags]
6108 * Get the unit cell information in the form of a periodic bounding box.
6109 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
6110 * Integers which define whether the system is periodic along the axis.
6111 * If no unit cell is defined, nil is returned.
6114 s_Molecule_Box(VALUE self)
6118 Data_Get_Struct(self, Molecule, mol);
6119 if (mol == NULL || mol->cell == NULL)
6121 v[0] = ValueFromVector(&(mol->cell->axes[0]));
6122 v[1] = ValueFromVector(&(mol->cell->axes[1]));
6123 v[2] = ValueFromVector(&(mol->cell->axes[2]));
6124 v[3] = ValueFromVector(&(mol->cell->origin));
6125 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
6126 val = rb_ary_new4(5, v);
6132 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
6133 * set_box(d, origin = [0, 0, 0])
6136 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
6137 If it is a number, the x/y/z axis vector is multiplied with the given number and used
6139 Flags, if present, is a 3-member array of Integers defining whether the system is
6140 periodic along the axis.
6141 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
6142 In the second form, an isotropic box with cell-length d is set.
6143 In the third form, the existing box is cleared.
6144 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
6147 s_Molecule_SetBox(VALUE self, VALUE aval)
6151 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
6153 Vector origin = {0, 0, 0};
6156 int i, convertCoordinates = 0;
6157 Data_Get_Struct(self, Molecule, mol);
6159 MolActionCreateAndPerform(mol, gMolActionClearBox);
6162 aval = rb_ary_to_ary(aval);
6163 for (i = 0; i < 6; i++) {
6164 if (i < RARRAY_LEN(aval))
6165 v[i] = (RARRAY_PTR(aval))[i];
6169 MolActionCreateAndPerform(mol, gMolActionClearBox);
6172 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
6173 d = NUM2DBL(rb_Float(v[0]));
6174 for (i = 0; i < 3; i++)
6175 VecScale(vv[i], ax[i], d);
6177 VectorFromValue(v[1], &origin);
6178 flags[0] = flags[1] = flags[2] = 1;
6180 for (i = 0; i < 3; i++) {
6183 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
6184 d = NUM2DBL(rb_Float(v[i]));
6185 VecScale(vv[i], ax[i], d);
6187 VectorFromValue(v[i], &vv[i]);
6189 flags[i] = (VecLength2(vv[i]) > 0.0);
6192 VectorFromValue(v[3], &origin);
6194 for (i = 0; i < 3; i++) {
6195 VALUE val = Ruby_ObjectAtIndex(v[4], i);
6196 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
6200 convertCoordinates = 1;
6202 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
6208 * cell_periodicity -> [n1, n2, n3]
6210 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
6214 s_Molecule_CellPeriodicity(VALUE self)
6217 Data_Get_Struct(self, Molecule, mol);
6218 if (mol->cell == NULL)
6220 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
6225 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
6226 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
6228 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
6229 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
6230 * If cell is not defined, exception is raised.
6231 * This operation is undoable.
6234 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
6238 Data_Get_Struct(self, Molecule, mol);
6239 if (mol->cell == NULL)
6240 rb_raise(rb_eMolbyError, "periodic cell is not defined");
6243 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
6244 flag = NUM2INT(rb_Integer(arg));
6248 arg = rb_ary_to_ary(arg);
6250 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
6251 arg0 = RARRAY_PTR(arg)[i];
6252 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
6253 flag |= (1 << (2 - i));
6256 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
6262 * cell_flexibility -> bool
6264 * Returns the unit cell is flexible or not
6267 s_Molecule_CellFlexibility(VALUE self)
6269 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
6272 Data_Get_Struct(self, Molecule, mol);
6273 if (mol->cell == NULL)
6275 if (mol->useFlexibleCell)
6277 else return Qfalse; */
6282 * self.cell_flexibility = bool
6283 * set_cell_flexibility(bool)
6285 * Change the unit cell is flexible or not
6288 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6290 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6293 Data_Get_Struct(self, Molecule, mol);
6294 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6300 * cell_transform -> Transform
6302 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
6303 * If cell is not defined, nil is returned.
6306 s_Molecule_CellTransform(VALUE self)
6309 Data_Get_Struct(self, Molecule, mol);
6310 if (mol == NULL || mol->cell == NULL)
6312 return ValueFromTransform(&(mol->cell->tr));
6317 * symmetry -> Array of Transforms
6318 * symmetries -> Array of Transforms
6320 * Get the currently defined symmetry operations. If no symmetry operation is defined,
6321 * returns an empty array.
6324 s_Molecule_Symmetry(VALUE self)
6329 Data_Get_Struct(self, Molecule, mol);
6330 if (mol->nsyms <= 0)
6331 return rb_ary_new();
6332 val = rb_ary_new2(mol->nsyms);
6333 for (i = 0; i < mol->nsyms; i++) {
6334 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6341 * nsymmetries -> Integer
6343 * Get the number of currently defined symmetry operations.
6346 s_Molecule_Nsymmetries(VALUE self)
6349 Data_Get_Struct(self, Molecule, mol);
6350 return INT2NUM(mol->nsyms);
6355 * add_symmetry(Transform) -> Integer
6357 * Add a new symmetry operation. If no symmetry operation is defined and the
6358 * given argument is not an identity transform, then also add an identity
6359 * transform at the index 0.
6360 * Returns the total number of symmetries after operation.
6363 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6367 Data_Get_Struct(self, Molecule, mol);
6368 TransformFromValue(trans, &tr);
6369 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6370 return INT2NUM(mol->nsyms);
6375 * remove_symmetry(count = nil) -> Integer
6376 * remove_symmetries(count = nil) -> Integer
6378 * Remove the specified number of symmetry operations. The last added ones are removed
6379 * first. If count is nil, then all symmetry operations are removed. Returns the
6380 * number of leftover symmetries.
6383 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6388 Data_Get_Struct(self, Molecule, mol);
6389 rb_scan_args(argc, argv, "01", &cval);
6393 n = NUM2INT(rb_Integer(cval));
6394 if (n < 0 || n > mol->nsyms)
6395 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6396 if (n == mol->nsyms)
6399 for (i = 0; i < n; i++)
6400 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6401 return INT2NUM(mol->nsyms);
6405 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6407 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6408 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6409 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6410 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6417 * atom_group {|aref| ...}
6418 * atom_group(arg1, arg2, ...)
6419 * atom_group(arg1, arg2, ...) {|aref| ...}
6421 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6422 * If arguments are given, then the atoms reprensented by the arguments are added to the
6423 * group. For a conversion of a string to an atom index, see the description
6424 * of Molecule#atom_index.
6425 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6426 * representing each atom, and the atoms are removed from the result if the block returns false.
6430 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6432 IntGroup *ig1, *ig2;
6434 Int i, startPt, interval;
6435 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6436 Data_Get_Struct(retval, IntGroup, ig1);
6437 Data_Get_Struct(self, Molecule, mol);
6439 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6442 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6443 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6444 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6445 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6446 ig2 = IntGroupFromValue(*argv);
6447 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6448 interval = IntGroupGetInterval(ig2, i);
6449 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6451 IntGroupRelease(ig2);
6452 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6454 values[0] = (VALUE)mol;
6455 values[1] = (VALUE)ig1;
6456 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6458 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6463 if (rb_block_given_p()) {
6464 /* Evaluate the given block with an AtomRef as the argument, and delete
6465 the index if the block returns false */
6466 AtomRef *aref = AtomRefNew(mol, 0);
6467 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6468 ig2 = IntGroupNew();
6469 IntGroupCopy(ig2, ig1);
6470 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6472 if (startPt >= mol->natoms)
6474 aref->idx = startPt;
6475 resval = rb_yield(arval);
6477 IntGroupRemove(ig1, startPt, 1);
6479 IntGroupRelease(ig2);
6482 /* Remove points that are out of bounds */
6483 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6490 * atom_index(val) -> Integer
6492 * Returns the atom index represented by val. val can be either a non-negative integer
6493 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6494 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
6495 * where resname, resid, name are the residue name, residue id, and atom name respectively.
6496 * If val is a string and multiple atoms match the description, the atom with the lowest index
6500 s_Molecule_AtomIndex(VALUE self, VALUE val)
6503 Data_Get_Struct(self, Molecule, mol);
6504 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6509 * extract(group, dummy_flag = nil) -> Molecule
6511 * Extract the atoms given by group and return as a new molecule object.
6512 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6513 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6514 * names beginning with an underscore) and included in the new molecule object.
6517 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6519 Molecule *mol1, *mol2;
6521 VALUE group, dummy_flag, retval;
6522 Data_Get_Struct(self, Molecule, mol1);
6523 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6524 ig = s_Molecule_AtomGroupFromValue(self, group);
6525 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6528 retval = ValueFromMolecule(mol2);
6530 IntGroupRelease(ig);
6536 * add(molecule2) -> self
6538 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6540 This operation is undoable.
6543 s_Molecule_Add(VALUE self, VALUE val)
6545 Molecule *mol1, *mol2;
6546 Data_Get_Struct(self, Molecule, mol1);
6547 mol2 = MoleculeFromValue(val);
6548 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6554 * remove(group) -> Molecule
6556 * The atoms designated by the given group are removed from the molecule.
6557 * This operation is undoable.
6560 s_Molecule_Remove(VALUE self, VALUE group)
6565 IntGroupIterator iter;
6567 Data_Get_Struct(self, Molecule, mol1);
6568 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6569 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6570 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6571 Data_Get_Struct(group, IntGroup, ig);
6573 /* Remove the bonds between the two fragments */
6574 /* (This is necessary for undo to work correctly) */
6575 IntGroupIteratorInit(ig, &iter);
6577 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6578 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6580 cp = AtomConnectData(&ap->connect);
6581 for (j = 0; j < ap->connect.count; j++) {
6583 if (!IntGroupLookup(ig, n, NULL)) {
6584 /* bond i-n, i is in ig and n is not */
6585 int k = MoleculeLookupBond(mol1, i, n);
6589 IntGroupAdd(bg, k, 1);
6594 IntGroupIteratorRelease(&iter);
6597 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6598 IntGroupRelease(bg);
6601 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6608 * create_atom(name, pos = -1) -> AtomRef
6610 * Create a new atom with the specified name (may contain residue
6611 * information) and position (if position is out of range, the atom is appended at
6612 * the end). Returns the reference to the new atom.
6613 * This operation is undoable.
6616 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6623 char *p, resName[6], atomName[6];
6625 Data_Get_Struct(self, Molecule, mol);
6626 rb_scan_args(argc, argv, "02", &name, &ival);
6628 pos = NUM2INT(rb_Integer(ival));
6631 p = StringValuePtr(name);
6633 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6634 if (atomName[0] == 0)
6635 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6638 if (p == NULL || p[0] == 0) {
6639 memset(atomName, 0, 4);
6642 memset(&arec, 0, sizeof(arec));
6643 strncpy(arec.aname, atomName, 4);
6645 strncpy(arec.resName, resName, 4);
6646 arec.resSeq = resSeq;
6648 arec.occupancy = 1.0;
6649 // i = MoleculeCreateAnAtom(mol, &arec);
6650 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6652 aref = AtomRefNew(mol, pos);
6653 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6658 * duplicate_atom(atomref, pos = -1) -> AtomRef
6660 * Create a new atom with the same attributes (but no bonding information)
6661 * with the specified atom. Returns the reference to the new atom.
6662 * This operation is undoable.
6665 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6671 VALUE retval, aval, ival;
6673 Data_Get_Struct(self, Molecule, mol);
6674 rb_scan_args(argc, argv, "11", &aval, &ival);
6675 if (FIXNUM_P(aval)) {
6676 int idx = NUM2INT(aval);
6677 if (idx < 0 || idx >= mol->natoms)
6678 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6679 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6681 apsrc = s_AtomFromValue(aval);
6684 rb_raise(rb_eMolbyError, "bad atom specification");
6686 pos = NUM2INT(rb_Integer(ival));
6688 AtomDuplicate(&arec, apsrc);
6689 arec.connect.count = 0;
6690 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6693 aref = AtomRefNew(mol, pos);
6694 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6702 * create_bond(n1, n2, ...) -> Integer
6704 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6705 * do nothing for that pair. Returns the number of bonds actually created.
6706 * This operation is undoable.
6709 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6712 Int i, j, k, *ip, old_nbonds;
6714 rb_raise(rb_eMolbyError, "missing arguments");
6716 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6717 Data_Get_Struct(self, Molecule, mol);
6718 ip = ALLOC_N(Int, argc + 1);
6719 for (i = j = 0; i < argc; i++, j++) {
6720 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6722 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6723 j -= 2; /* This bond is already present: skip it */
6725 for (k = 0; k < j - 1; k += 2) {
6726 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6727 j -= 2; /* The same entry is already in the argument */
6734 old_nbonds = mol->nbonds;
6736 ip[j] = kInvalidIndex;
6737 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6741 rb_raise(rb_eMolbyError, "atom index out of range");
6743 rb_raise(rb_eMolbyError, "too many bonds");
6745 rb_raise(rb_eMolbyError, "duplicate bonds");
6747 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6749 rb_raise(rb_eMolbyError, "error in creating bonds");
6750 return INT2NUM(mol->nbonds - old_nbonds);
6755 * molecule.remove_bonds(n1, n2, ...) -> Integer
6757 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6758 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6759 * This operation is undoable.
6762 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6768 rb_raise(rb_eMolbyError, "missing arguments");
6770 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6771 Data_Get_Struct(self, Molecule, mol);
6773 for (i = j = 0; i < argc; i++, j = 1 - j) {
6774 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6776 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6780 IntGroupAdd(bg, k, 1);
6785 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6786 i = IntGroupGetCount(bg);
6787 IntGroupRelease(bg);
6794 * assign_bond_order(idx, d1)
6795 * assign_bond_orders(group, [d1, d2, ...])
6797 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6798 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6799 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6800 * (This may change in the future)
6801 * This operation is undoable.
6804 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6808 Data_Get_Struct(self, Molecule, mol);
6809 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6810 /* The first form */
6811 Int idx = NUM2INT(rb_Integer(idxval));
6812 Double d1 = NUM2DBL(rb_Float(dval));
6813 if (idx < 0 || idx >= mol->nbonds)
6814 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6815 ig = IntGroupNewWithPoints(idx, 1, -1);
6816 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6817 IntGroupRelease(ig);
6821 ig = IntGroupFromValue(idxval);
6822 n = IntGroupGetCount(ig);
6824 rb_raise(rb_eMolbyError, "the bond index is empty");
6825 dval = rb_ary_to_ary(dval);
6826 dp = (Double *)calloc(sizeof(Double), n);
6827 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6828 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6830 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6832 IntGroupRelease(ig);
6839 * get_bond_order(idx) -> Float
6840 * get_bond_orders(group) -> Array
6842 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6843 * In the second form, the bond orders at the indices in the group are returned as an array.
6844 * If no bond order information have been assigned, returns nil (the first form)
6845 * or an empty array (the second form).
6848 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6854 Int i, n, numericArg;
6855 Data_Get_Struct(self, Molecule, mol);
6856 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6857 /* The first form */
6858 Int idx = NUM2INT(rb_Integer(idxval));
6859 if (idx < 0 || idx >= mol->nbonds)
6860 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6861 if (mol->bondOrders == NULL)
6863 ig = IntGroupNewWithPoints(idx, 1, -1);
6867 if (mol->bondOrders == NULL)
6868 return rb_ary_new();
6869 ig = IntGroupFromValue(idxval);
6870 n = IntGroupGetCount(ig);
6872 rb_raise(rb_eMolbyError, "the bond index is empty");
6875 dp = (Double *)calloc(sizeof(Double), n);
6876 MoleculeGetBondOrders(mol, dp, ig);
6878 retval = rb_float_new(dp[0]);
6880 retval = rb_ary_new();
6881 for (i = 0; i < n; i++)
6882 rb_ary_push(retval, rb_float_new(dp[i]));
6885 IntGroupRelease(ig);
6891 * bond_exist?(idx1, idx2) -> bool
6893 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6894 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6897 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6903 Data_Get_Struct(self, Molecule, mol);
6904 idx1 = NUM2INT(rb_Integer(ival1));
6905 idx2 = NUM2INT(rb_Integer(ival2));
6906 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6907 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6908 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6909 cp = AtomConnectData(&ap->connect);
6910 for (i = 0; i < ap->connect.count; i++) {
6919 * add_angle(n1, n2, n3) -> Molecule
6921 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6922 * when a bond is created, so it is rarely necessary to use this method explicitly.
6923 * This operation is undoable.
6926 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6930 Data_Get_Struct(self, Molecule, mol);
6931 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6932 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6933 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6934 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6935 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6936 n[3] = kInvalidIndex;
6937 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6943 * remove_angle(n1, n2, n3) -> Molecule
6945 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6946 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6947 * This operation is undoable.
6950 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6955 Data_Get_Struct(self, Molecule, mol);
6956 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6957 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6958 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6959 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6960 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6961 ig = IntGroupNewWithPoints(n[3], 1, -1);
6962 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6963 IntGroupRelease(ig);
6969 * add_dihedral(n1, n2, n3, n4) -> Molecule
6971 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6972 * when a bond is created, so it is rarely necessary to use this method explicitly.
6973 * This operation is undoable.
6976 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6980 Data_Get_Struct(self, Molecule, mol);
6981 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6982 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6983 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6984 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6985 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6986 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6987 n[4] = kInvalidIndex;
6988 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6994 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6996 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6997 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6998 * This operation is undoable.
7001 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7006 Data_Get_Struct(self, Molecule, mol);
7007 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7008 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7009 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7010 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7011 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
7012 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
7013 ig = IntGroupNewWithPoints(n[4], 1, -1);
7014 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
7015 IntGroupRelease(ig);
7021 * add_improper(n1, n2, n3, n4) -> Molecule
7023 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
7024 * not automatically added when a new bond is created, so this method is more useful than
7025 * the angle/dihedral counterpart.
7026 * This operation is undoable.
7029 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7033 Data_Get_Struct(self, Molecule, mol);
7034 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7035 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7036 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7037 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7038 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
7039 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
7040 n[4] = kInvalidIndex;
7041 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
7047 * remove_improper(n1, n2, n3, n4) -> Molecule
7048 * remove_improper(intgroup) -> Molecule
7050 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
7051 * Returns self. Unlike angles and dihedrals, impropers are
7052 * not automatically added when a new bond is created, so this method is more useful than
7053 * the angle/dihedral counterpart.
7054 * This operation is undoable.
7057 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
7060 VALUE v1, v2, v3, v4;
7063 Data_Get_Struct(self, Molecule, mol);
7065 ig = IntGroupFromValue(argv[0]);
7067 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
7068 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7069 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7070 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7071 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7072 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
7073 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
7074 ig = IntGroupNewWithPoints(n[4], 1, -1);
7076 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
7077 IntGroupRelease(ig);
7083 * assign_residue(group, res) -> Molecule
7085 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
7086 * or "resname.resno". When the residue number is not specified, the residue number of
7087 * the first atom in the group is used.
7088 * This operation is undoable.
7091 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
7095 char *p, *pp, buf[16];
7098 Data_Get_Struct(self, Molecule, mol);
7100 /* Parse the argument res */
7101 if (FIXNUM_P(res)) {
7102 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
7103 resid = NUM2INT(res);
7106 p = StringValuePtr(res);
7107 pp = strchr(p, '.');
7109 resid = atoi(pp + 1);
7115 if (n > sizeof buf - 1)
7120 ig = s_Molecule_AtomGroupFromValue(self, range);
7121 if (ig == NULL || IntGroupGetCount(ig) == 0)
7125 /* Use the residue number of the first specified atom */
7126 n = IntGroupGetNthPoint(ig, 0);
7127 if (n >= mol->natoms)
7128 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
7129 ap = ATOM_AT_INDEX(mol->atoms, n);
7132 /* Change the residue number */
7133 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
7134 /* Change the residue name if necessary */
7138 seqs[1] = kInvalidIndex; */
7139 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
7141 IntGroupRelease(ig);
7147 * offset_residue(group, offset) -> Molecule
7149 * Offset the residue number of the specified atoms. If any of the residue number gets
7150 * negative, then exception is thrown.
7151 * This operation is undoable.
7154 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
7159 Data_Get_Struct(self, Molecule, mol);
7160 ig = s_Molecule_AtomGroupFromValue(self, range);
7161 ofs = NUM2INT(offset);
7162 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
7164 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
7165 IntGroupRelease(ig);
7171 * renumber_atoms(array) -> IntGroup
7173 * Change the order of atoms so that the atoms specified in the array argument appear
7174 * in this order from the top of the molecule. The atoms that are not included in array
7175 * are placed after these atoms, and these atoms are returned as an intGroup.
7176 * This operation is undoable.
7179 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
7185 VALUE *valp, retval;
7186 Data_Get_Struct(self, Molecule, mol);
7187 if (TYPE(array) != T_ARRAY)
7188 array = rb_funcall(array, rb_intern("to_a"), 0);
7189 n = RARRAY_LEN(array);
7190 valp = RARRAY_PTR(array);
7191 new2old = ALLOC_N(Int, n + 1);
7192 for (i = 0; i < n; i++)
7193 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
7194 new2old[i] = kInvalidIndex;
7195 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
7197 rb_raise(rb_eMolbyError, "Atom index out of range");
7199 rb_raise(rb_eMolbyError, "Duplicate entry");
7201 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
7202 retval = IntGroup_Alloc(rb_cIntGroup);
7203 Data_Get_Struct(retval, IntGroup, ig);
7204 if (mol->natoms > n)
7205 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
7212 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7214 * Find atoms that are within the threshold distance from the given atom.
7215 * (The atom argument can also be a vector, representing a cartesian coordinate. In that case, the van der Waals of the atom can also be specified.)
7216 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7217 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7218 * If limit is not given, a default value of 1.2 is used.
7219 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7222 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7225 VALUE aval, limval, radval;
7226 double limit, radius;
7227 Int n1, nbonds, *bonds, an;
7229 Data_Get_Struct(self, Molecule, mol);
7230 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7231 if (rb_obj_is_kind_of(aval, rb_cVector3D) || rb_obj_is_kind_of(aval, rb_cLAMatrix) || rb_obj_is_kind_of(aval, rb_mEnumerable)) {
7232 VectorFromValue(aval, &v);
7234 radius = gElementParameters[6].radius;
7236 radius = NUM2DBL(rb_Float(radval));
7239 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7240 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7241 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7242 if (an >= 0 && an < gCountElementParameters)
7243 radius = gElementParameters[an].radius;
7244 else radius = gElementParameters[6].radius;
7249 limit = NUM2DBL(rb_Float(limval));
7250 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7252 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7253 aval = rb_ary_new();
7255 for (n1 = 0; n1 < nbonds; n1++)
7256 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7264 * guess_bonds(limit = 1.2) -> Integer
7266 * Create bonds between atoms that are within the threshold distance.
7267 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7268 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7269 * If limit is not given, a default value of 1.2 is used.
7270 * The number of the newly created bonds is returned.
7271 * This operation is undoable.
7274 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7280 Data_Get_Struct(self, Molecule, mol);
7281 rb_scan_args(argc, argv, "01", &limval);
7285 limit = NUM2DBL(rb_Float(limval));
7286 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7288 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7291 return INT2NUM(nbonds);
7296 * register_undo(script, *args)
7298 * Register an undo operation with the current molecule.
7301 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
7306 Data_Get_Struct(self, Molecule, mol);
7307 rb_scan_args(argc, argv, "1*", &script, &args);
7308 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
7309 MolActionCallback_registerUndo(mol, act);
7315 * undo_enabled? -> bool
7317 * Returns true if undo is enabled for this molecule; otherwise no.
7320 s_Molecule_UndoEnabled(VALUE self)
7323 Data_Get_Struct(self, Molecule, mol);
7324 if (MolActionCallback_isUndoRegistrationEnabled(mol))
7331 * undo_enabled = bool
7333 * Enable or disable undo.
7336 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
7339 Data_Get_Struct(self, Molecule, mol);
7340 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
7346 * selection -> IntGroup
7348 * Returns the current selection.
7351 s_Molecule_Selection(VALUE self)
7356 Data_Get_Struct(self, Molecule, mol);
7357 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
7358 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
7359 val = ValueFromIntGroup(ig);
7360 IntGroupRelease(ig);
7362 val = IntGroup_Alloc(rb_cIntGroup);
7368 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
7372 Data_Get_Struct(self, Molecule, mol);
7376 ig = s_Molecule_AtomGroupFromValue(self, val);
7378 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
7380 MoleculeSetSelection(mol, ig);
7382 IntGroupRelease(ig);
7388 * selection = IntGroup
7390 * Set the current selection. The right-hand operand may be nil.
7391 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
7394 s_Molecule_SetSelection(VALUE self, VALUE val)
7396 return s_Molecule_SetSelectionSub(self, val, 0);
7401 * set_undoable_selection(IntGroup)
7403 * Set the current selection with undo registration. The right-hand operand may be nil.
7404 * This operation is undoable.
7407 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
7409 return s_Molecule_SetSelectionSub(self, val, 1);
7414 * hidden_atoms -> IntGroup
7416 * Returns the currently hidden atoms.
7419 s_Molecule_HiddenAtoms(VALUE self)
7421 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7422 return Qnil; /* Not reached */
7426 Data_Get_Struct(self, Molecule, mol);
7431 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7432 if (ap->exflags & kAtomHiddenFlag)
7433 IntGroupAdd(ig, i, 1);
7435 val = ValueFromIntGroup(ig);
7436 IntGroupRelease(ig);
7439 } else return Qnil; */
7444 * set_hidden_atoms(IntGroup)
7445 * self.hidden_atoms = IntGroup
7447 * Hide the specified atoms. This operation is _not_ undoable.
7450 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7452 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7453 return Qnil; /* Not reached */
7456 Data_Get_Struct(self, Molecule, mol);
7464 ig = s_Molecule_AtomGroupFromValue(self, val);
7465 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7466 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7467 ap->exflags |= kAtomHiddenFlag;
7469 ap->exflags &= kAtomHiddenFlag;
7473 IntGroupRelease(ig);
7474 MoleculeCallback_notifyModification(mol, 0);
7481 * select_frame(index)
7484 * Select the specified frame. If successful, returns true, otherwise returns false.
7487 s_Molecule_SelectFrame(VALUE self, VALUE val)
7490 int ival = NUM2INT(val);
7491 Data_Get_Struct(self, Molecule, mol);
7492 ival = MoleculeSelectFrame(mol, ival, 1);
7502 * Get the current frame.
7505 s_Molecule_Frame(VALUE self)
7508 Data_Get_Struct(self, Molecule, mol);
7509 return INT2NUM(mol->cframe);
7514 * nframes -> Integer
7516 * Get the number of frames.
7519 s_Molecule_Nframes(VALUE self)
7522 Data_Get_Struct(self, Molecule, mol);
7523 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7528 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7529 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7531 * Insert new frames at the indices specified by the intGroup. If the first argument is
7532 * an integer, a single new frame is inserted at that index. If the first argument is
7533 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7534 * should be an array of arrays of Vector3Ds, then those coordinates are set
7535 * to the new frame. Otherwise, the coordinates of current molecule are copied
7537 * Returns an intGroup representing the inserted frames if successful, nil if not.
7540 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7542 VALUE val, coords, cells;
7545 int count, ival, i, j, len, len_c, len2, nframes;
7548 Data_Get_Struct(self, Molecule, mol);
7549 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7550 if (coords != Qnil) {
7551 if (TYPE(coords) != T_ARRAY)
7552 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7553 len = RARRAY_LEN(coords);
7555 if (cells != Qnil) {
7556 if (mol->cell == NULL)
7557 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7558 if (TYPE(cells) != T_ARRAY)
7559 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7560 len_c = RARRAY_LEN(cells);
7562 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7563 nframes = MoleculeGetNumberOfFrames(mol);
7565 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7566 val = ValueFromIntGroup(ig);
7568 ig = IntGroupFromValue(val);
7570 count = IntGroupGetCount(ig); /* Count is updated here */
7571 vp = ALLOC_N(Vector, mol->natoms * count);
7573 vp2 = ALLOC_N(Vector, 4 * count);
7577 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7578 ptr = RARRAY_PTR(coords);
7579 for (i = 0; i < count; i++) {
7580 if (TYPE(ptr[i]) != T_ARRAY)
7581 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7582 len2 = RARRAY_LEN(ptr[i]);
7583 if (len2 < mol->natoms)
7584 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7585 ptr2 = RARRAY_PTR(ptr[i]);
7586 for (j = 0; j < mol->natoms; j++)
7587 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7591 for (i = 0; i < count; i++) {
7592 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7593 vp[i * mol->natoms + j] = ap->r;
7599 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7600 ptr = RARRAY_PTR(cells);
7601 for (i = 0; i < count; i++) {
7602 if (TYPE(ptr[i]) != T_ARRAY)
7603 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7604 len2 = RARRAY_LEN(ptr[i]);
7606 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7607 ptr2 = RARRAY_PTR(ptr[i]);
7608 for (j = 0; j < 4; j++)
7609 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7612 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7613 IntGroupRelease(ig);
7617 return (ival >= 0 ? val : Qnil);
7622 * create_frame(coordinates = nil) -> Integer
7623 * create_frames(coordinates = nil) -> Integer
7625 * Same as molecule.insert_frames(nil, coordinates).
7628 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7631 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7633 return s_Molecule_InsertFrames(3, vals, self);
7638 * remove_frames(IntGroup, wantCoordinates = false)
7640 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7641 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7642 * removed frames is returned if operation is successful.
7645 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7652 Data_Get_Struct(self, Molecule, mol);
7653 rb_scan_args(argc, argv, "11", &val, &flag);
7654 ig = IntGroupFromValue(val);
7655 count = IntGroupGetCount(ig);
7657 /* Create return value before removing frames */
7662 retval = rb_ary_new2(count);
7663 for (i = 0; i < count; i++) {
7664 n = IntGroupGetNthPoint(ig, i);
7665 coords = rb_ary_new2(mol->natoms);
7666 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7667 if (n < ap->nframes && n != mol->cframe)
7670 rb_ary_push(coords, ValueFromVector(&v));
7672 rb_ary_push(retval, coords);
7674 } else retval = Qtrue;
7675 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7682 * each_frame {|n| ...}
7684 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7685 * the frame number. After completion, the original frame number is restored.
7688 s_Molecule_EachFrame(VALUE self)
7690 int i, cframe, nframes;
7692 Data_Get_Struct(self, Molecule, mol);
7693 cframe = mol->cframe;
7694 nframes = MoleculeGetNumberOfFrames(mol);
7696 for (i = 0; i < nframes; i++) {
7697 MoleculeSelectFrame(mol, i, 1);
7698 rb_yield(INT2NUM(i));
7700 MoleculeSelectFrame(mol, cframe, 1);
7707 * set_atom_attr(index, key, value)
7709 * Set the atom attribute for the specified atom.
7710 * This operation is undoable.
7713 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7717 Data_Get_Struct(self, Molecule, mol);
7718 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7719 oldval = s_AtomRef_GetAttr(aref, key);
7722 s_AtomRef_SetAttr(aref, key, val);
7728 * get_atom_attr(index, key)
7730 * Get the atom attribute for the specified atom.
7733 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7735 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7740 * get_coord_from_frame(index, group = nil)
7742 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7743 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7744 * copied; now they are always copied)
7747 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7750 VALUE ival, gval, cval;
7751 Int index, i, j, n, nn;
7753 IntGroupIterator iter;
7756 Data_Get_Struct(self, Molecule, mol);
7757 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7759 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7760 index = NUM2INT(rb_Integer(ival));
7761 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7763 rb_raise(rb_eMolbyError, "No frame is present");
7765 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7768 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7770 ig = s_Molecule_AtomGroupFromValue(self, gval);
7772 n = IntGroupGetCount(ig);
7774 vp = (Vector *)calloc(sizeof(Vector), n);
7775 IntGroupIteratorInit(ig, &iter);
7778 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7779 ap = ATOM_AT_INDEX(mol->atoms, i);
7780 if (index < ap->nframes) {
7781 vp[j] = ap->frames[index];
7789 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7791 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7792 vp = mol->frame_cells + index * 4;
7793 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7795 IntGroupIteratorRelease(&iter);
7797 IntGroupRelease(ig);
7803 * fragment(n1, *exatoms) -> IntGroup
7804 * fragment(group, *exatoms) -> IntGroup
7806 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7807 * those atoms will not be counted during the search.
7810 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7813 IntGroup *baseg, *ig, *exatoms;
7815 volatile VALUE nval, exval;
7816 Data_Get_Struct(self, Molecule, mol);
7817 rb_scan_args(argc, argv, "1*", &nval, &exval);
7818 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7820 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7822 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7824 if (RARRAY_LEN(exval) == 0) {
7827 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7828 Data_Get_Struct(exval, IntGroup, exatoms);
7830 if (baseg == NULL) {
7831 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7833 IntGroupIterator iter;
7834 IntGroupIteratorInit(baseg, &iter);
7835 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7838 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7840 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7842 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7844 IntGroupAddIntGroup(ig, subg);
7845 IntGroupRelease(subg);
7850 IntGroupIteratorRelease(&iter);
7851 IntGroupRelease(baseg);
7854 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7855 nval = ValueFromIntGroup(ig);
7856 IntGroupRelease(ig);
7862 * fragments(exclude = nil)
7864 * Returns the fragments as an array of IntGroups.
7865 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7866 * in defining the fragment.
7869 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
7872 IntGroup *ag, *fg, *eg;
7873 VALUE gval, exval, retval;
7874 Data_Get_Struct(self, Molecule, mol);
7877 if (mol->natoms == 0)
7878 return rb_ary_new();
7879 rb_scan_args(argc, argv, "01", &exval);
7883 eg = IntGroupFromValue(exval);
7884 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7886 IntGroupRemoveIntGroup(ag, eg);
7887 retval = rb_ary_new();
7888 while (IntGroupGetCount(ag) > 0) {
7889 int n = IntGroupGetNthPoint(ag, 0);
7890 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7892 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7893 gval = ValueFromIntGroup(fg);
7894 rb_ary_push(retval, gval);
7895 IntGroupRemoveIntGroup(ag, fg);
7896 IntGroupRelease(fg);
7898 IntGroupRelease(ag);
7900 IntGroupRelease(eg);
7906 * each_fragment(exclude = nil) {|group| ...}
7908 * Execute the block, with the IntGroup object for each fragment as the argument.
7909 * Atoms or bonds should not be added or removed during the execution of the block.
7910 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7911 * in defining the fragment.
7914 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
7917 IntGroup *ag, *fg, *eg;
7919 Data_Get_Struct(self, Molecule, mol);
7920 if (mol == NULL || mol->natoms == 0)
7922 rb_scan_args(argc, argv, "01", &exval);
7926 eg = IntGroupFromValue(exval);
7927 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7929 IntGroupRemoveIntGroup(ag, eg);
7930 while (IntGroupGetCount(ag) > 0) {
7931 int n = IntGroupGetNthPoint(ag, 0);
7932 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7934 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7935 gval = ValueFromIntGroup(fg);
7937 IntGroupRemoveIntGroup(ag, fg);
7938 IntGroupRelease(fg);
7940 IntGroupRelease(ag);
7942 IntGroupRelease(eg);
7948 * detachable?(group) -> [n1, n2]
7950 * Check whether the group is 'detachable', i.e. the group is bound to the rest
7951 * of the molecule via only one bond. If it is, then the indices of the atoms
7952 * belonging to the bond is returned, the first element being the atom included
7953 * in the fragment. Otherwise, Qnil is returned.
7956 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7962 Data_Get_Struct(self, Molecule, mol);
7963 ig = s_Molecule_AtomGroupFromValue(self, gval);
7964 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7965 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7966 } else retval = Qnil;
7967 IntGroupRelease(ig);
7973 * bonds_on_border(group = selection) -> Array of Array of two Integers
7975 * Returns an array of bonds that connect an atom in the group and an atom out
7976 * of the group. The first atom in the bond always belongs to the group. If no
7977 * such bonds are present, an empty array is returned.
7980 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7985 Data_Get_Struct(self, Molecule, mol);
7986 rb_scan_args(argc, argv, "01", &gval);
7988 ig = MoleculeGetSelection(mol);
7992 ig = s_Molecule_AtomGroupFromValue(self, gval);
7994 retval = rb_ary_new();
7997 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7999 IntGroupIterator iter;
8001 IntGroupIteratorInit(bg, &iter);
8002 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8003 /* The atoms at the border */
8005 n1 = mol->bonds[i * 2];
8006 n2 = mol->bonds[i * 2 + 1];
8007 if (IntGroupLookupPoint(ig, n1) < 0) {
8011 if (IntGroupLookupPoint(ig, n1) < 0)
8012 continue; /* Actually this is an internal error */
8014 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8016 IntGroupIteratorRelease(&iter);
8018 IntGroupRelease(bg);
8019 IntGroupRelease(ig);
8025 * translate(vec, group = nil) -> Molecule
8027 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
8028 * This operation is undoable.
8031 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
8037 Data_Get_Struct(self, Molecule, mol);
8038 rb_scan_args(argc, argv, "11", &vec, &group);
8039 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8040 VectorFromValue(vec, &v);
8041 // MoleculeTranslate(mol, &v, ig);
8042 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
8044 IntGroupRelease(ig);
8050 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
8052 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
8053 * If group is given, only atoms in the group are moved.
8054 * This operation is undoable.
8057 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
8060 volatile VALUE aval, anval, cval, gval;
8065 Data_Get_Struct(self, Molecule, mol);
8066 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
8067 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8068 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
8069 VectorFromValue(aval, &av);
8071 cv.x = cv.y = cv.z = 0.0;
8073 VectorFromValue(cval, &cv);
8074 if (TransformForRotation(tr, &av, angle, &cv))
8075 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
8076 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8078 IntGroupRelease(ig);
8084 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
8086 * Reflect the molecule by the plane which is perpendicular to axis and including center.
8087 * axis must not be a zero vector.
8088 * If group is given, only atoms in the group are moved.
8089 * This operation is undoable.
8092 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
8095 volatile VALUE aval, cval, gval;
8099 Data_Get_Struct(self, Molecule, mol);
8100 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
8101 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8102 VectorFromValue(aval, &av);
8104 cv.x = cv.y = cv.z = 0.0;
8106 VectorFromValue(cval, &cv);
8107 if (TransformForReflection(tr, &av, &cv))
8108 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
8109 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8111 IntGroupRelease(ig);
8117 * invert(center = [0,0,0], group = nil) -> Molecule
8119 * Invert the molecule with the given center.
8120 * If group is given, only atoms in the group are moved.
8121 * This operation is undoable.
8124 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
8127 volatile VALUE cval, gval;
8131 Data_Get_Struct(self, Molecule, mol);
8132 rb_scan_args(argc, argv, "02", &cval, &gval);
8133 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8135 cv.x = cv.y = cv.z = 0.0;
8137 VectorFromValue(cval, &cv);
8138 TransformForInversion(tr, &cv);
8139 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8141 IntGroupRelease(ig);
8147 * transform(transform, group = nil) -> Molecule
8149 * Transform the molecule by the given Transform object.
8150 * If group is given, only atoms in the group are moved.
8151 * This operation is undoable.
8154 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
8160 Data_Get_Struct(self, Molecule, mol);
8161 rb_scan_args(argc, argv, "11", &trans, &group);
8162 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8163 TransformFromValue(trans, &tr);
8164 /* MoleculeTransform(mol, tr, ig); */
8165 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8167 IntGroupRelease(ig);
8172 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
8174 switch (MoleculeCenterOfMass(mol, outv, ig)) {
8175 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
8176 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
8178 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
8184 * center_of_mass(group = nil) -> Vector3D
8186 * Calculate the center of mass for the given set of atoms. The argument
8187 * group is null, then all atoms are considered.
8190 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
8196 Data_Get_Struct(self, Molecule, mol);
8197 rb_scan_args(argc, argv, "01", &group);
8198 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8199 s_Molecule_DoCenterOfMass(mol, &v, ig);
8201 IntGroupRelease(ig);
8202 return ValueFromVector(&v);
8207 * centralize(group = nil) -> self
8209 * Translate the molecule so that the center of mass of the given group is located
8210 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
8213 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
8219 Data_Get_Struct(self, Molecule, mol);
8220 rb_scan_args(argc, argv, "01", &group);
8221 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8222 s_Molecule_DoCenterOfMass(mol, &v, ig);
8224 IntGroupRelease(ig);
8228 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
8234 * bounds(group = nil) -> [min, max]
8236 * Calculate the boundary. The return value is an array of two Vector3D objects.
8239 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
8247 Data_Get_Struct(self, Molecule, mol);
8248 rb_scan_args(argc, argv, "01", &group);
8249 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8250 if (ig != NULL && IntGroupGetCount(ig) == 0)
8251 rb_raise(rb_eMolbyError, "atom group is empty");
8252 vmin.x = vmin.y = vmin.z = 1e30;
8253 vmax.x = vmax.y = vmax.z = -1e30;
8254 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
8256 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
8272 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
8275 /* Get atom position or a vector */
8277 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
8279 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
8280 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
8281 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
8283 VectorFromValue(val, vp);
8289 * measure_bond(n1, n2) -> Float
8291 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
8292 * or Vector3D values.
8293 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8296 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
8300 Data_Get_Struct(self, Molecule, mol);
8301 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8302 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8303 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
8308 * measure_angle(n1, n2, n3) -> Float
8310 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
8311 * or Vector3D values. The return value is in degree.
8312 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8315 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
8320 Data_Get_Struct(self, Molecule, mol);
8321 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8322 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8323 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8324 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
8326 return Qnil; /* Cannot define */
8327 else return rb_float_new(d);
8332 * measure_dihedral(n1, n2, n3, n4) -> Float
8334 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
8335 * or Vector3D values. The return value is in degree.
8336 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8339 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
8342 Vector v1, v2, v3, v4;
8344 Data_Get_Struct(self, Molecule, mol);
8345 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8346 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8347 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8348 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
8349 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
8351 return Qnil; /* Cannot define */
8352 else return rb_float_new(d);
8357 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
8359 * Expand the specified part of the molecule by the given symmetry operation.
8360 * Returns the array of atom indices corresponding to the expanded atoms.
8361 * If allow_overlap is true, then new atoms are created even when the
8362 * coordinates coincide with the some other atom (special position) of the
8363 * same element; otherwise, such atom will not be created and the index of the
8364 * existing atom is given in the returned array.
8367 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
8370 VALUE gval, sval, xval, yval, zval, rval, oval;
8372 Int n[4], allow_overlap;
8376 Data_Get_Struct(self, Molecule, mol);
8377 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
8378 n[0] = NUM2INT(rb_Integer(sval));
8379 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
8380 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
8381 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
8382 allow_overlap = (RTEST(oval) ? 1 : 0);
8383 ig = s_Molecule_AtomGroupFromValue(self, gval);
8384 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
8385 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
8386 natoms = mol->natoms;
8388 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
8390 rval = rb_ary_new2(nidx);
8391 while (--nidx >= 0) {
8392 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
8394 /* if (natoms == mol->natoms)
8397 rval = IntGroup_Alloc(rb_cIntGroup);
8398 Data_Get_Struct(rval, IntGroup, ig);
8399 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
8406 * amend_by_symmetry(group = nil) -> IntGroup
8408 * Expand the specified part of the molecule by the given symmetry operation.
8409 * Returns an IntGroup containing the added atoms.
8412 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
8417 Data_Get_Struct(self, Molecule, mol);
8418 rb_scan_args(argc, argv, "01", &gval);
8420 ig = s_Molecule_AtomGroupFromValue(self, gval);
8422 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
8423 rval = ValueFromIntGroup(ig2);
8424 IntGroupRelease(ig2);
8430 * transform_for_symop(symop, is_cartesian = nil) -> Transform
8432 * Get the transform corresponding to the symmetry operation. The symop can either be
8433 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
8434 * If is_cartesian is true, the returned transform is for cartesian coordinates.
8435 * Otherwise, the returned transform is for fractional coordinates.
8436 * Raises exception when no cell or no transform are defined.
8439 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
8445 Data_Get_Struct(self, Molecule, mol);
8446 if (mol->cell == NULL)
8447 rb_raise(rb_eMolbyError, "no unit cell is defined");
8448 if (mol->nsyms == 0)
8449 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8450 rb_scan_args(argc, argv, "11", &sval, &fval);
8451 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
8452 symop.sym = NUM2INT(rb_Integer(sval));
8453 symop.dx = symop.dy = symop.dz = 0;
8455 sval = rb_ary_to_ary(sval);
8456 if (RARRAY_LEN(sval) < 4)
8457 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
8458 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
8459 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
8460 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
8461 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
8463 if (symop.sym >= mol->nsyms)
8464 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
8465 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
8466 return ValueFromTransform(&tr);
8471 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8473 * Get the symmetry operation corresponding to the given transform.
8474 * If is_cartesian is true, the given transform is for cartesian coordinates.
8475 * Otherwise, the given transform is for fractional coordinates.
8476 * Raises exception when no cell or no transform are defined.
8479 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8486 Data_Get_Struct(self, Molecule, mol);
8487 if (mol->cell == NULL)
8488 rb_raise(rb_eMolbyError, "no unit cell is defined");
8489 if (mol->nsyms == 0)
8490 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8491 rb_scan_args(argc, argv, "11", &tval, &fval);
8492 TransformFromValue(tval, &tr);
8493 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8495 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8497 return Qnil; /* Not found */
8503 * wrap_unit_cell(group) -> Vector3D
8505 * Move the specified group so that the center of mass of the group is within the
8506 * unit cell. The offset vector is returned. If no periodic box is defined,
8507 * exception is raised.
8510 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8515 Data_Get_Struct(self, Molecule, mol);
8516 if (mol->cell == NULL)
8517 rb_raise(rb_eMolbyError, "no unit cell is defined");
8518 ig = s_Molecule_AtomGroupFromValue(self, gval);
8519 s_Molecule_DoCenterOfMass(mol, &cv, ig);
8520 TransformVec(&v, mol->cell->rtr, &cv);
8521 if (mol->cell->flags[0])
8523 if (mol->cell->flags[1])
8525 if (mol->cell->flags[2])
8527 TransformVec(&dv, mol->cell->tr, &v);
8529 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8530 IntGroupRelease(ig);
8531 return ValueFromVector(&dv);
8536 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8538 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8539 * first and second atom in the pair should belong to group1 and group2, respectively.
8540 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8543 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8546 VALUE limval, gval1, gval2, rval, igval;
8547 IntGroup *ig1, *ig2;
8548 IntGroupIterator iter1, iter2;
8554 MDExclusion *exinfo;
8557 Data_Get_Struct(self, Molecule, mol);
8558 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8559 lim = NUM2DBL(rb_Float(limval));
8561 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8563 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8565 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8567 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8569 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8571 if (!RTEST(igval)) {
8572 /* Use the exclusion table in MDArena */
8573 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8574 VALUE mval = ValueFromMolecule(mol);
8575 s_RebuildMDParameterIfNecessary(mval, Qnil);
8577 exinfo = mol->arena->exinfo; /* May be NULL */
8578 exlist = mol->arena->exlist;
8583 IntGroupIteratorInit(ig1, &iter1);
8584 IntGroupIteratorInit(ig2, &iter2);
8587 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8589 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8591 if (exinfo != NULL) {
8592 exn1 = exinfo[n[0]].index1;
8593 exn2 = exinfo[n[0] + 1].index1;
8594 } else exn1 = exn2 = -1;
8595 IntGroupIteratorReset(&iter2);
8596 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8597 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8599 continue; /* Same atom */
8600 if (exinfo != NULL) {
8601 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
8602 for (i = exn1; i < exn2; i++) {
8603 if (exlist[i] == n[1])
8607 continue; /* Should be excluded */
8609 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8610 /* Is this pair already registered? */
8612 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8613 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8617 /* Not registered yet */
8618 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8623 IntGroupIteratorRelease(&iter2);
8624 IntGroupIteratorRelease(&iter1);
8625 IntGroupRelease(ig2);
8626 IntGroupRelease(ig1);
8627 rval = rb_ary_new2(npairs);
8628 if (pairs != NULL) {
8629 for (i = 0; i < npairs; i++) {
8630 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8637 /* Calculate the transform that moves the current coordinates to the reference
8638 coordinates with least displacements. */
8640 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8648 Double eigen_val[3];
8649 Vector eigen_vec[3];
8651 IntGroupIterator iter;
8653 natoms = mol->natoms;
8655 IntGroupIteratorInit(ig, &iter);
8657 /* Calculate the weighted center */
8661 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8662 ap1 = ATOM_AT_INDEX(ap, in);
8663 w1 = (weights != NULL ? weights[i] : ap1->weight);
8664 VecScaleInc(org1, ap1->r, w1);
8665 VecScaleInc(org2, ref[i], w1);
8669 VecScaleSelf(org1, w);
8670 VecScaleSelf(org2, w);
8672 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8673 /* Matrix to diagonalize = R * tR */
8674 memset(r, 0, sizeof(Mat33));
8675 memset(q, 0, sizeof(Mat33));
8676 memset(u, 0, sizeof(Mat33));
8678 IntGroupIteratorReset(&iter);
8679 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8681 ap1 = ATOM_AT_INDEX(ap, in);
8682 w1 = (weights != NULL ? weights[i] : ap1->weight);
8684 VecSub(v1, ap1->r, org1);
8685 VecSub(v2, ref[i], org2);
8686 r[0] += w1 * v1.x * v2.x;
8687 r[1] += w1 * v1.y * v2.x;
8688 r[2] += w1 * v1.z * v2.x;
8689 r[3] += w1 * v1.x * v2.y;
8690 r[4] += w1 * v1.y * v2.y;
8691 r[5] += w1 * v1.z * v2.y;
8692 r[6] += w1 * v1.x * v2.z;
8693 r[7] += w1 * v1.y * v2.z;
8694 r[8] += w1 * v1.z * v2.z;
8697 for (i = 0; i < 9; i++)
8699 for (i = 0; i < 3; i++) {
8700 for (j = 0; j < 3; j++) {
8701 for (k = 0; k < 3; k++) {
8702 q[i+j*3] += r[i+k*3] * r[j+k*3];
8707 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8708 IntGroupIteratorRelease(&iter);
8709 return -1.0; /* Cannot determine the eigenvector */
8712 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8713 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8714 MatrixTranspose(r, r);
8715 for (i = 0; i < 3; i++) {
8716 MatrixVec(&s[i], r, &eigen_vec[i]);
8717 w1 = 1.0 / sqrt(eigen_val[i]);
8718 VecScaleSelf(s[i], w1);
8720 for (k = 0; k < 3; k++) {
8721 u[0] += s[k].x * eigen_vec[k].x;
8722 u[1] += s[k].y * eigen_vec[k].x;
8723 u[2] += s[k].z * eigen_vec[k].x;
8724 u[3] += s[k].x * eigen_vec[k].y;
8725 u[4] += s[k].y * eigen_vec[k].y;
8726 u[5] += s[k].z * eigen_vec[k].y;
8727 u[6] += s[k].x * eigen_vec[k].z;
8728 u[7] += s[k].y * eigen_vec[k].z;
8729 u[8] += s[k].z * eigen_vec[k].z;
8732 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8733 MatrixVec(&org1, u, &org1);
8735 for (i = 0; i < 9; i++)
8741 /* Calculate rmsd */
8742 IntGroupIteratorReset(&iter);
8744 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8746 ap1 = ATOM_AT_INDEX(ap, in);
8747 TransformVec(&tv, trans, &ap1->r);
8749 w += VecLength2(tv);
8752 IntGroupIteratorRelease(&iter);
8758 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8760 * Calculate the transform to fit the given group to the set of reference coordinates.
8761 * The reference coordinates ref is given as either a frame number, an array of
8762 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8763 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8764 * Return values are the transform (that converts the present coordinates to the
8765 * target coordinates) and root mean square deviation (without weight).
8768 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8772 VALUE gval, rval, wval;
8774 IntGroupIterator iter;
8775 int nn, errno, i, j, in, status;
8777 Double *weights, dval[3];
8780 Data_Get_Struct(self, Molecule, mol);
8781 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8783 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8785 ig = IntGroupFromValue(gval);
8786 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8787 IntGroupRelease(ig);
8788 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8790 ref = (Vector *)calloc(sizeof(Vector), nn);
8791 weights = (Double *)calloc(sizeof(Double), nn);
8792 IntGroupIteratorInit(ig, &iter);
8793 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8794 int fn = NUM2INT(rb_Integer(rval));
8795 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8800 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8801 ap = ATOM_AT_INDEX(mol->atoms, in);
8802 if (fn < ap->nframes)
8803 ref[i] = ap->frames[fn];
8804 else ref[i] = ap->r;
8806 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8807 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8808 if (m->row * m->column < nn * 3) {
8812 for (i = 0; i < nn; i++) {
8813 ref[i].x = m->data[i * 3];
8814 ref[i].y = m->data[i * 3 + 1];
8815 ref[i].z = m->data[i * 3 + 2];
8819 rval = rb_protect(rb_ary_to_ary, rval, &status);
8824 if (RARRAY_LEN(rval) < nn) {
8828 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8829 /* Array of 3*nn numbers */
8830 if (RARRAY_LEN(rval) < nn * 3) {
8834 for (i = 0; i < nn; i++) {
8835 for (j = 0; j < 3; j++) {
8836 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8841 dval[j] = NUM2DBL(aval);
8848 /* Array of nn Vector3Ds or Arrays */
8849 for (i = 0; i < nn; i++) {
8850 aval = (RARRAY_PTR(rval))[i];
8851 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8852 VectorFromValue(aval, &ref[i]);
8854 aval = rb_protect(rb_ary_to_ary, aval, &status);
8859 if (RARRAY_LEN(aval) < 3) {
8864 for (j = 0; j < 3; j++) {
8865 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8870 dval[j] = NUM2DBL(aaval);
8880 /* Use atomic weights */
8881 IntGroupIteratorReset(&iter);
8882 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8883 ap = ATOM_AT_INDEX(mol->atoms, in);
8884 weights[i] = ap->weight;
8887 wval = rb_protect(rb_ary_to_ary, wval, &status);
8892 if (RARRAY_LEN(wval) < nn) {
8896 for (i = 0; i < nn; i++) {
8897 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8902 weights[i] = NUM2DBL(wwval);
8905 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8912 IntGroupIteratorRelease(&iter);
8916 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8917 } else if (errno == 1) {
8918 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8919 } else if (errno == 2) {
8920 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8921 } else if (errno == 3) {
8922 rb_jump_tag(status);
8923 } else if (errno == 4) {
8924 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8925 } else if (errno == 5) {
8926 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8927 } else if (errno == 6) {
8928 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8930 return Qnil; /* Not reached */
8937 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8940 s_Molecule_Display(VALUE self)
8943 Data_Get_Struct(self, Molecule, mol);
8944 if (mol->mview != NULL)
8945 MainViewCallback_display(mol->mview);
8953 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8956 s_Molecule_MakeFront(VALUE self)
8959 Data_Get_Struct(self, Molecule, mol);
8960 if (mol->mview != NULL)
8961 MainViewCallback_makeFront(mol->mview);
8967 * update_enabled? -> bool
8969 * Returns true if screen update is enabled; otherwise no.
8972 s_Molecule_UpdateEnabled(VALUE self)
8975 Data_Get_Struct(self, Molecule, mol);
8976 if (mol->mview != NULL && !mol->mview->freezeScreen)
8983 * update_enabled = bool
8985 * Enable or disable screen update. This is effective for automatic update on modification.
8986 * Explicit call to molecule.display() always updates the screen.
8989 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8992 Data_Get_Struct(self, Molecule, mol);
8993 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8994 if (mol->mview != NULL)
8995 mol->mview->freezeScreen = (val == Qfalse);
9003 * show_unitcell(bool)
9004 * show_unitcell = bool
9006 * Set the flag whether to show the unit cell. If no argument is given, the
9007 * current flag is returned.
9010 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
9013 Data_Get_Struct(self, Molecule, mol);
9014 if (mol->mview == NULL)
9017 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
9018 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9020 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
9026 * show_hydrogens(bool)
9027 * show_hydrogens = bool
9029 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
9030 * current flag is returned.
9033 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
9036 Data_Get_Struct(self, Molecule, mol);
9037 if (mol->mview == NULL)
9040 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9041 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9043 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9049 * show_dummy_atoms(bool)
9050 * show_dummy_atoms = bool
9052 * Set the flag whether to show the dummy atoms. If no argument is given, the
9053 * current flag is returned.
9056 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9059 Data_Get_Struct(self, Molecule, mol);
9060 if (mol->mview == NULL)
9063 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9064 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9066 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9072 * show_expanded(bool)
9073 * show_expanded = bool
9075 * Set the flag whether to show the expanded atoms. If no argument is given, the
9076 * current flag is returned.
9079 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9082 Data_Get_Struct(self, Molecule, mol);
9083 if (mol->mview == NULL)
9086 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9087 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9089 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9095 * show_ellipsoids(bool)
9096 * show_ellipsoids = bool
9098 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9099 * current flag is returned.
9102 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9105 Data_Get_Struct(self, Molecule, mol);
9106 if (mol->mview == NULL)
9109 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9110 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9112 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9117 * is_atom_visible(index) -> Boolean
9119 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9120 * as well as the molecule attributes (showHydrogens, etc.)
9123 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9128 Data_Get_Struct(self, Molecule, mol);
9129 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9130 if (idx < 0 || idx >= mol->natoms)
9132 ap = ATOM_AT_INDEX(mol->atoms, idx);
9133 if (mol->mview != NULL) {
9134 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9136 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9138 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9141 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9146 * show_graphite -> Integer
9147 * show_graphite = Integer
9148 * show_graphite = boolean
9150 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9151 * number of rings to display for each direction.
9152 * If the argument is boolean, only the show/hide flag is set.
9155 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9158 Data_Get_Struct(self, Molecule, mol);
9159 if (mol->mview == NULL)
9162 if (argv[0] == Qnil || argv[0] == Qfalse)
9163 mol->mview->showGraphiteFlag = 0;
9164 else if (argv[0] == Qtrue)
9165 mol->mview->showGraphiteFlag = 1;
9167 int n = NUM2INT(rb_Integer(argv[0]));
9169 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9170 mol->mview->showGraphite = n;
9172 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9174 return INT2NUM(mol->mview->showGraphite);
9179 * show_graphite? -> boolean
9181 * Return whether the graphite is set visible or not.
9184 s_Molecule_ShowGraphiteFlag(VALUE self)
9187 Data_Get_Struct(self, Molecule, mol);
9188 if (mol->mview == NULL)
9190 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9195 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9196 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9197 * show_periodic_image = boolean
9199 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9200 * set but no visual effects are observed.
9201 * If the argument is boolean, only the show/hide flag is modified.
9204 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9210 Data_Get_Struct(self, Molecule, mol);
9211 if (mol->mview == NULL)
9213 rb_scan_args(argc, argv, "01", &val);
9215 /* Change current settings */
9216 if (val == Qnil || val == Qfalse)
9217 mol->mview->showPeriodicImageFlag = 0;
9218 else if (val == Qtrue)
9219 mol->mview->showPeriodicImageFlag = 1;
9221 val = rb_ary_to_ary(val);
9222 for (i = 0; i < 6; i++) {
9223 if (i < RARRAY_LEN(val))
9224 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9226 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9227 rb_raise(rb_eMolbyError, "bad arguments");
9228 for (i = 0; i < 6; i++)
9229 mol->mview->showPeriodicImage[i] = ival[i];
9231 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9234 for (i = 0; i < 6; i++)
9235 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9241 * show_periodic_image? -> boolean
9243 * Return whether the periodic images are set to visible or not. This flag is
9244 * independent from the show_periodic_image settings.
9247 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9250 Data_Get_Struct(self, Molecule, mol);
9251 if (mol->mview == NULL)
9253 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9258 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9259 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9260 * show_rotation_center = boolean
9262 * Set to show the rotation center of the screen.
9263 * If the argument is boolean, only the show/hide flag is modified.
9266 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9269 Data_Get_Struct(self, Molecule, mol);
9270 if (mol->mview == NULL)
9273 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9274 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9276 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9285 * Set the flag whether to draw the model in line mode. If no argument is given, the
9286 * current flag is returned.
9289 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9292 Data_Get_Struct(self, Molecule, mol);
9293 if (mol->mview == NULL)
9296 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9297 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9299 return (mol->mview->lineMode ? Qtrue : Qfalse);
9306 * Resize the model drawing to fit in the window.
9309 s_Molecule_ResizeToFit(VALUE self)
9312 Data_Get_Struct(self, Molecule, mol);
9313 if (mol->mview != NULL)
9314 MainView_resizeToFit(mol->mview);
9320 * get_view_rotation -> [[ax, ay, az], angle]
9322 * Get the current rotation for the view. Angle is in degree, not radian.
9325 s_Molecule_GetViewRotation(VALUE self)
9330 Data_Get_Struct(self, Molecule, mol);
9331 if (mol->mview == NULL)
9333 TrackballGetRotate(mol->mview->track, f);
9334 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9338 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9343 * get_view_scale -> float
9345 * Get the current scale for the view.
9348 s_Molecule_GetViewScale(VALUE self)
9351 Data_Get_Struct(self, Molecule, mol);
9352 if (mol->mview == NULL)
9354 return rb_float_new(TrackballGetScale(mol->mview->track));
9359 * get_view_center -> Vector
9361 * Get the current center point of the view.
9364 s_Molecule_GetViewCenter(VALUE self)
9369 Data_Get_Struct(self, Molecule, mol);
9370 if (mol->mview == NULL)
9372 TrackballGetTranslate(mol->mview->track, f);
9373 v.x = -f[0] * mol->mview->dimension;
9374 v.y = -f[1] * mol->mview->dimension;
9375 v.z = -f[2] * mol->mview->dimension;
9376 return ValueFromVector(&v);
9381 * set_view_rotation([ax, ay, az], angle) -> self
9383 * Set the current rotation for the view. Angle is in degree, not radian.
9386 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9391 Data_Get_Struct(self, Molecule, mol);
9392 if (mol->mview == NULL)
9394 VectorFromValue(aval, &v);
9395 if (NormalizeVec(&v, &v))
9396 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9400 f[0] = -NUM2DBL(rb_Float(angval));
9401 TrackballSetRotate(mol->mview->track, f);
9402 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9408 * set_view_scale(scale) -> self
9410 * Set the current scale for the view.
9413 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9416 Data_Get_Struct(self, Molecule, mol);
9417 if (mol->mview == NULL)
9419 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9420 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9426 * set_view_center(vec) -> self
9428 * Set the current center point of the view.
9431 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9436 Data_Get_Struct(self, Molecule, mol);
9437 if (mol->mview == NULL)
9439 VectorFromValue(aval, &v);
9440 f[0] = -v.x / mol->mview->dimension;
9441 f[1] = -v.y / mol->mview->dimension;
9442 f[2] = -v.z / mol->mview->dimension;
9443 TrackballSetTranslate(mol->mview->track, f);
9444 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9450 * set_background_color(red, green, blue)
9452 * Set the background color of the model window.
9455 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9458 Data_Get_Struct(self, Molecule, mol);
9459 if (mol->mview != NULL) {
9460 VALUE rval, gval, bval;
9461 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9462 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9469 * create_graphic(kind, color, points, fill = nil) -> integer
9471 * Create a new graphic object.
9472 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9473 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9474 * points: an array of Vectors
9478 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9484 VALUE kval, cval, pval, fval;
9485 Data_Get_Struct(self, Molecule, mol);
9486 if (mol->mview == NULL)
9487 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9488 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
9489 kval = rb_obj_as_string(kval);
9490 memset(&g, 0, sizeof(g));
9492 p = RSTRING_PTR(kval);
9493 if (strcmp(p, "line") == 0)
9494 g.kind = kMainViewGraphicLine;
9495 else if (strcmp(p, "poly") == 0)
9496 g.kind = kMainViewGraphicPoly;
9497 else if (strcmp(p, "cylinder") == 0)
9498 g.kind = kMainViewGraphicCylinder;
9499 else if (strcmp(p, "cone") == 0)
9500 g.kind = kMainViewGraphicCone;
9501 else if (strcmp(p, "ellipsoid") == 0)
9502 g.kind = kMainViewGraphicEllipsoid;
9503 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9504 g.closed = (RTEST(fval) ? 1 : 0);
9505 cval = rb_ary_to_ary(cval);
9506 n = RARRAY_LEN(cval);
9507 if (n < 3 || n >= 5)
9508 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9511 for (i = 0; i < n; i++)
9512 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9513 pval = rb_ary_to_ary(pval);
9514 n = RARRAY_LEN(pval);
9515 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9517 rb_raise(rb_eArgError, "no control points are given");
9519 case kMainViewGraphicLine:
9521 rb_raise(rb_eArgError, "the line object must have at least two control points");
9523 case kMainViewGraphicPoly:
9525 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9527 case kMainViewGraphicCylinder:
9528 case kMainViewGraphicCone:
9530 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9533 case kMainViewGraphicEllipsoid:
9537 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9540 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9541 for (i = 0; i < n; i++) {
9544 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9547 VectorFromValue(RARRAY_PTR(pval)[i], &v);
9549 g.points[i * 3] = v.x;
9550 g.points[i * 3 + 1] = v.y;
9551 g.points[i * 3 + 2] = v.z;
9553 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9555 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9556 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9557 g.points[7] = g.points[11] = g.points[3];
9559 MainView_insertGraphic(mol->mview, -1, &g);
9560 return INT2NUM(mol->mview->ngraphics - 1);
9565 * remove_graphic(index) -> integer
9567 * Remove a graphic object.
9570 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9574 Data_Get_Struct(self, Molecule, mol);
9575 if (mol->mview == NULL)
9576 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9577 i = NUM2INT(rb_Integer(ival));
9578 if (i < 0 || i >= mol->mview->ngraphics)
9579 rb_raise(rb_eArgError, "graphic index is out of range");
9580 MainView_removeGraphic(mol->mview, i);
9586 * ngraphics -> integer
9588 * Get the number of graphic objects.
9591 s_Molecule_NGraphics(VALUE self)
9594 Data_Get_Struct(self, Molecule, mol);
9595 if (mol->mview == NULL)
9596 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9597 return INT2NUM(mol->mview->ngraphics);
9602 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9604 * Change the point_index-th control point of graphic_index-th graphic object
9608 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9610 MainViewGraphic *gp;
9614 Data_Get_Struct(self, Molecule, mol);
9615 if (mol->mview == NULL)
9616 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9617 index = NUM2INT(rb_Integer(gval));
9618 if (index < 0 || index >= mol->mview->ngraphics)
9619 rb_raise(rb_eArgError, "the graphic index is out of range");
9620 gp = mol->mview->graphics + index;
9621 index = NUM2INT(rb_Integer(pval));
9622 if (index < 0 || index >= gp->npoints)
9623 rb_raise(rb_eArgError, "the point index is out of range");
9624 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9625 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9626 v.x = NUM2DBL(rb_Float(nval));
9628 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9629 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9630 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9632 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9635 v.x = kInvalidFloat;
9637 } else VectorFromValue(nval, &v);
9639 gp->points[index * 3] = v.x;
9640 gp->points[index * 3 + 1] = v.y;
9641 gp->points[index * 3 + 2] = v.z;
9642 MoleculeCallback_notifyModification(mol, 0);
9648 * set_graphic_color(graphic_index, new_value) -> new_value
9650 * Change the color of graphic_index-th graphic object
9654 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9656 MainViewGraphic *gp;
9659 Data_Get_Struct(self, Molecule, mol);
9660 if (mol->mview == NULL)
9661 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9662 index = NUM2INT(rb_Integer(gval));
9663 if (index < 0 || index >= mol->mview->ngraphics)
9664 rb_raise(rb_eArgError, "the graphic index is out of range");
9665 gp = mol->mview->graphics + index;
9666 cval = rb_ary_to_ary(cval);
9667 n = RARRAY_LEN(cval);
9668 if (n != 3 && n != 4)
9669 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9670 for (index = 0; index < n; index++) {
9671 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9675 MoleculeCallback_notifyModification(mol, 0);
9681 * show_graphic(graphic_index) -> self
9683 * Enable the visible flag of the graphic_index-th graphic object
9687 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9689 MainViewGraphic *gp;
9692 Data_Get_Struct(self, Molecule, mol);
9693 if (mol->mview == NULL)
9694 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9695 index = NUM2INT(rb_Integer(gval));
9696 if (index < 0 || index >= mol->mview->ngraphics)
9697 rb_raise(rb_eArgError, "the graphic index is out of range");
9698 gp = mol->mview->graphics + index;
9700 MoleculeCallback_notifyModification(mol, 0);
9706 * hide_graphic(graphic_index) -> self
9708 * Disable the visible flag of the graphic_index-th graphic object
9712 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9714 MainViewGraphic *gp;
9717 Data_Get_Struct(self, Molecule, mol);
9718 if (mol->mview == NULL)
9719 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9720 index = NUM2INT(rb_Integer(gval));
9721 if (index < 0 || index >= mol->mview->ngraphics)
9722 rb_raise(rb_eArgError, "the graphic index is out of range");
9723 gp = mol->mview->graphics + index;
9725 MoleculeCallback_notifyModification(mol, 0);
9733 * Show the string in the info text box.
9736 s_Molecule_ShowText(VALUE self, VALUE arg)
9739 Data_Get_Struct(self, Molecule, mol);
9740 if (mol->mview != NULL)
9741 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9747 * md_arena -> MDArena
9749 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
9750 * this molecule, a new arena is created.
9753 s_Molecule_MDArena(VALUE self)
9757 Data_Get_Struct(self, Molecule, mol);
9758 if (mol->arena == NULL)
9760 retval = ValueFromMDArena(mol->arena);
9766 * set_parameter_attr(type, index, key, value, src) -> value
9768 * This method is used only internally.
9771 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9773 /* This method is called from MolAction to change a MM parameter attribute. */
9778 Data_Get_Struct(self, Molecule, mol);
9779 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9780 vval = s_ParameterRef_SetAttr(pval, kval, vval);
9782 /* This is the special part of this method; it allows modification of the src field. */
9783 /* (ParameterRef#set_attr sets 0 to the src field) */
9784 Data_Get_Struct(pval, ParameterRef, pref);
9785 up = ParameterRefGetPar(pref);
9786 up->bond.src = FIX2INT(sval);
9793 * parameter -> Parameter
9795 * Get the local parameter of this molecule. If not defined, returns nil.
9798 s_Molecule_Parameter(VALUE self)
9801 Data_Get_Struct(self, Molecule, mol);
9802 /* if (mol->par == NULL)
9804 return s_NewParameterValueFromValue(self);
9809 * selectedMO -> IntGroup
9811 * Returns a group of selected mo in the "MO Info" table. If the MO info table
9812 * is not selected, returns nil. If the MO info table is selected but no MOs
9813 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
9816 s_Molecule_SelectedMO(VALUE self)
9821 Data_Get_Struct(self, Molecule, mol);
9822 if (mol->mview == NULL)
9824 ig = MainView_selectedMO(mol->mview);
9827 IntGroupOffset(ig, 1);
9828 val = ValueFromIntGroup(ig);
9829 IntGroupRelease(ig);
9835 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9837 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9838 * If the molecule does not contain a basis set information, then returns nil.
9841 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9844 Vector o, dx, dy, dz;
9847 Int npoints = 80 * 80 * 80;
9848 Data_Get_Struct(self, Molecule, mol);
9849 if (mol->bset == NULL)
9851 rb_scan_args(argc, argv, "01", &nval);
9853 npoints = NUM2INT(rb_Integer(nval));
9854 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9856 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));
9860 s_Cubegen_callback(double progress, void *ref)
9862 MyAppCallback_setProgressValue(progress);
9863 if (MyAppCallback_checkInterrupt())
9870 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9871 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9873 * Calculate the molecular orbital with number mo and create a 'cube' file.
9874 * In the first form, the cube size is estimated from the atomic coordinates. In the
9875 * second form, the cube dimension is explicitly given.
9876 * Returns fname when successful, nil otherwise.
9877 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9878 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9879 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9882 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9884 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9886 Int mono, nx, ny, nz, npoints;
9887 Vector o, dx, dy, dz;
9890 Data_Get_Struct(self, Molecule, mol);
9891 if (mol->bset == NULL)
9892 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9893 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9895 /* Set up parameters */
9896 mono = NUM2INT(rb_Integer(mval));
9897 if (mono <= 0 || mono > mol->bset->ncomps)
9898 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9900 if (mol->bset->rflag != 0)
9901 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9902 mono += mol->bset->ncomps;
9905 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9906 /* Automatic grid formation */
9908 npoints = NUM2INT(rb_Integer(oval));
9912 else if (npoints < 8)
9913 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9914 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9915 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9919 VectorFromValue(oval, &o);
9920 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9921 VectorFromValue(dxval, &dx);
9923 dx.x = NUM2DBL(rb_Float(dxval));
9926 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9927 VectorFromValue(dyval, &dy);
9929 dy.y = NUM2DBL(rb_Float(dyval));
9932 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9933 VectorFromValue(dzval, &dz);
9935 dz.z = NUM2DBL(rb_Float(dzval));
9938 nx = NUM2INT(rb_Integer(nxval));
9939 ny = NUM2INT(rb_Integer(nyval));
9940 nz = NUM2INT(rb_Integer(nzval));
9941 if (nx <= 0 || ny <= 0 || nz <= 0)
9942 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9943 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9944 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);
9948 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9952 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9954 /* Output to file */
9955 MoleculeCallback_displayName(mol, buf, sizeof buf);
9956 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9958 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9960 /* Discard the cube */
9961 MoleculeClearCubeAtIndex(mol, index);
9969 * Get the number of electrostatic potential info.
9972 s_Molecule_NElpots(VALUE self)
9975 Data_Get_Struct(self, Molecule, mol);
9976 return INT2NUM(mol->nelpots);
9983 * Get the electrostatic potential info at the given index. If present, then the
9984 * return value is [Vector, Float] (position and potential). If not present, then
9988 s_Molecule_Elpot(VALUE self, VALUE ival)
9992 Data_Get_Struct(self, Molecule, mol);
9993 idx = NUM2INT(rb_Integer(ival));
9994 if (idx < 0 || idx >= mol->nelpots)
9996 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10001 * add_gaussian_orbital_shell(sym, nprims, atom_index)
10003 * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
10004 * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10008 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
10011 int sym, nprims, a_idx, n;
10012 Data_Get_Struct(self, Molecule, mol);
10013 sym = NUM2INT(rb_Integer(symval));
10014 nprims = NUM2INT(rb_Integer(npval));
10015 a_idx = NUM2INT(rb_Integer(aval));
10016 n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
10018 rb_raise(rb_eMolbyError, "Molecule is emptry");
10020 rb_raise(rb_eMolbyError, "Low memory");
10022 rb_raise(rb_eMolbyError, "Unknown orbital type");
10024 rb_raise(rb_eMolbyError, "Unknown error");
10030 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10032 * To be used internally. Add a gaussian primitive coefficients.
10035 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10039 Double exponent, contraction, contraction_sp;
10040 Data_Get_Struct(self, Molecule, mol);
10041 exponent = NUM2DBL(rb_Float(expval));
10042 contraction = NUM2DBL(rb_Float(cval));
10043 contraction_sp = NUM2DBL(rb_Float(cspval));
10044 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10046 rb_raise(rb_eMolbyError, "Molecule is emptry");
10048 rb_raise(rb_eMolbyError, "Low memory");
10050 rb_raise(rb_eMolbyError, "Unknown error");
10058 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10061 s_Molecule_MOType(VALUE self)
10064 Data_Get_Struct(self, Molecule, mol);
10065 if (mol != NULL && mol->bset != NULL) {
10067 int rflag = mol->bset->rflag;
10070 else if (rflag == 2)
10073 return rb_str_new2(s);
10074 } else return Qnil;
10079 * set_mo_coefficients(idx, energy, coefficients)
10081 * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
10082 * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
10083 * of MO coefficients.
10086 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10089 Int idx, ncomps, i;
10092 Data_Get_Struct(self, Molecule, mol);
10093 idx = NUM2INT(rb_Integer(ival));
10094 energy = NUM2DBL(rb_Float(eval));
10095 aval = rb_ary_to_ary(aval);
10096 ncomps = RARRAY_LEN(aval);
10097 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10098 if (coeffs == NULL) {
10102 for (i = 0; i < ncomps; i++)
10103 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10104 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
10107 rb_raise(rb_eMolbyError, "Molecule is emptry");
10109 rb_raise(rb_eMolbyError, "Low memory");
10111 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10113 rb_raise(rb_eMolbyError, "Bad MO index");
10115 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10117 rb_raise(rb_eMolbyError, "Unknown error");
10123 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
10125 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
10126 * ne_alpha, ne_beta: number of alpha/beta electrons.
10129 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
10132 Int rflag, na, nb, n;
10133 Data_Get_Struct(self, Molecule, mol);
10134 rflag = NUM2INT(rb_Integer(rval));
10135 na = NUM2INT(rb_Integer(naval));
10136 nb = NUM2INT(rb_Integer(nbval));
10137 n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
10139 rb_raise(rb_eMolbyError, "Molecule is emptry");
10141 rb_raise(rb_eMolbyError, "Low memory");
10143 rb_raise(rb_eMolbyError, "Unknown error");
10149 * search_equivalent_atoms(ig = nil)
10151 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10154 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10160 Data_Get_Struct(self, Molecule, mol);
10161 if (mol->natoms == 0)
10163 rb_scan_args(argc, argv, "01", &val);
10165 ig = IntGroupFromValue(val);
10167 result = MoleculeSearchEquivalentAtoms(mol, ig);
10168 if (result == NULL)
10169 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10171 IntGroupRelease(ig);
10172 val = rb_ary_new2(mol->natoms);
10173 for (i = 0; i < mol->natoms; i++)
10174 rb_ary_push(val, INT2NUM(result[i]));
10181 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10183 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10184 * Name is the name of the new pi anchor, and group is the atoms that define
10185 * the pi system. Type (a String) is an atom type for MM implementation.
10186 * Weights represent the relative significance of the component atoms; if omitted, then
10187 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10188 * The weight values will be normalized so that the sum of the weights is 1.0.
10189 * The weight values must be positive.
10190 * Index is the atom index where the created pi-anchor is inserted in the
10191 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10192 * having the largest index.
10193 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10194 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10197 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10202 Int i, n, idx, last_component;
10206 if (argc < 2 || argc >= 6)
10207 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10211 Data_Get_Struct(self, Molecule, mol);
10212 ig = IntGroupFromValue(gval);
10213 memset(&a, 0, sizeof(a));
10214 memset(&an, 0, sizeof(an));
10215 strncpy(a.aname, StringValuePtr(nval), 4);
10216 if (a.aname[0] == '_')
10217 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10218 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
10219 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10220 if (n >= mol->natoms) {
10221 AtomConnectResize(&an.connect, 0);
10222 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10224 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10225 last_component = n;
10227 if (an.connect.count == 0)
10228 rb_raise(rb_eMolbyError, "no atoms are specified");
10229 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10230 for (i = 0; i < an.connect.count; i++) {
10231 an.coeffs[i] = 1.0 / an.connect.count;
10233 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10235 if (argv[0] != Qnil)
10236 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10240 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10241 if (argv[0] != Qnil) {
10242 VALUE aval = rb_ary_to_ary(argv[0]);
10244 if (RARRAY_LEN(aval) != an.connect.count)
10245 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10246 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10247 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10249 rb_raise(rb_eMolbyError, "the weight value must be positive");
10253 for (i = 0; i < an.connect.count; i++)
10254 an.coeffs[i] /= sum;
10259 if (argc > 0 && argv[0] != Qnil) {
10261 idx = NUM2INT(rb_Integer(argv[0]));
10263 if (idx < 0 || idx > mol->natoms) {
10264 /* Immediately after the last specified atom */
10265 idx = last_component + 1;
10267 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10268 memmove(a.anchor, &an, sizeof(PiAnchor));
10269 /* Use residue information of the last specified atom */
10270 ap = ATOM_AT_INDEX(mol->atoms, last_component);
10271 a.resSeq = ap->resSeq;
10272 strncpy(a.resName, ap->resName, 4);
10273 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10275 MoleculeCalculatePiAnchorPosition(mol, idx);
10276 aref = AtomRefNew(mol, idx);
10277 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10282 * current -> Molecule
10284 * Get the currently "active" molecule.
10287 s_Molecule_Current(VALUE klass)
10289 return ValueFromMolecule(MoleculeCallback_currentMolecule());
10294 * Molecule[] -> Molecule
10295 * Molecule[n] -> Molecule
10296 * Molecule[name] -> Molecule
10297 * Molecule[name, k] -> Molecule
10298 * Molecule[regex] -> Molecule
10299 * Molecule[regex, k] -> Molecule
10301 * Molecule[] is equivalent to Molecule.current.
10302 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
10303 * Molecule[name] gives the first document (in the order of creation time) that has
10304 * the given name. If a second argument (k) is given, the k-th document that has the
10305 * given name is returned.
10306 * Molecule[regex] gives the first document (in the order of creation time) that
10307 * has a name matching the regular expression. If a second argument (k) is given,
10308 * the k-th document that has a name matching the re is returned.
10311 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
10317 rb_scan_args(argc, argv, "02", &val, &kval);
10319 return s_Molecule_Current(klass);
10320 if (rb_obj_is_kind_of(val, rb_cInteger)) {
10321 idx = NUM2INT(val);
10322 mol = MoleculeCallback_moleculeAtIndex(idx);
10323 } else if (rb_obj_is_kind_of(val, rb_cString)) {
10324 char *p = StringValuePtr(val);
10325 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10326 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10327 MoleculeCallback_displayName(mol, buf, sizeof buf);
10328 if (strcmp(buf, p) == 0 && --k == 0)
10331 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
10332 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10333 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10335 MoleculeCallback_displayName(mol, buf, sizeof buf);
10336 name = rb_str_new2(buf);
10337 if (rb_reg_match(val, name) != Qnil && --k == 0)
10340 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
10344 else return ValueFromMolecule(mol);
10349 * list -> array of Molecules
10351 * Get the list of molecules associated to the documents, in the order of creation
10352 * time of the document. If no document is open, returns an empry array.
10355 s_Molecule_List(VALUE klass)
10361 ary = rb_ary_new();
10362 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
10363 rb_ary_push(ary, ValueFromMolecule(mol));
10371 * ordered_list -> array of Molecules
10373 * Get the list of molecules associated to the documents, in the order of front-to-back
10374 * ordering of the associated window. If no document is open, returns an empry array.
10377 s_Molecule_OrderedList(VALUE klass)
10383 ary = rb_ary_new();
10384 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
10385 rb_ary_push(ary, ValueFromMolecule(mol));
10393 * error_message -> String
10395 * Get the error_message from the last load/save method. If no error, returns nil.
10398 s_Molecule_ErrorMessage(VALUE klass)
10400 if (gLoadSaveErrorMessage == NULL)
10402 else return rb_str_new2(gLoadSaveErrorMessage);
10407 * set_error_message(String)
10408 * Molecule.error_message = String
10410 * Get the error_message from the last load/save method. If no error, returns nil.
10413 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
10415 if (gLoadSaveErrorMessage != NULL) {
10416 free(gLoadSaveErrorMessage);
10417 gLoadSaveErrorMessage = NULL;
10419 if (sval != Qnil) {
10420 sval = rb_str_to_str(sval);
10421 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
10428 * self == Molecule -> boolean
10430 * True if the two arguments point to the same molecule.
10433 s_Molecule_Equal(VALUE self, VALUE val)
10435 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
10436 Molecule *mol1, *mol2;
10437 Data_Get_Struct(self, Molecule, mol1);
10438 Data_Get_Struct(val, Molecule, mol2);
10439 return (mol1 == mol2 ? Qtrue : Qfalse);
10440 } else return Qfalse;
10443 /* The callback functions for call_subprocess_async */
10445 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
10448 VALUE procval, retval, args[2];
10449 args[0] = ValueFromMolecule(mol);
10450 args[1] = INT2NUM(status);
10451 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
10452 if (procval != Qnil) {
10453 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10454 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10461 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
10464 VALUE procval, retval, args[2];
10465 args[0] = ValueFromMolecule(mol);
10466 args[1] = INT2NUM(tcount);
10467 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
10468 if (procval != Qnil) {
10469 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10470 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10478 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
10480 * Call subprocess asynchronically.
10481 * If end_callback is given, it will be called (with two arguments self and termination status)
10482 * when the subprocess terminated.
10483 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
10484 * If timer_callback returns nil or false, then the subprocess will be interrupted.
10485 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
10486 * filename begins with ">>", then the message will be appended to the file.
10487 * If the filename is "/dev/null" or "NUL", then the message will be lost.
10488 * If the argument is nil, then the message will be sent to the Ruby console.
10489 * Returns the process ID as an integer.
10492 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
10494 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
10498 FILE *fpout, *fperr;
10499 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
10500 Data_Get_Struct(self, Molecule, mol);
10502 if (stdout_val == Qnil) {
10505 sout = StringValuePtr(stdout_val);
10506 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
10509 if (strncmp(sout, ">>", 2) == 0) {
10511 fpout = fopen(sout, "a");
10515 fpout = fopen(sout, "w");
10518 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
10521 if (stderr_val == Qnil) {
10524 serr = StringValuePtr(stderr_val);
10525 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
10528 if (strncmp(serr, ">>", 2) == 0) {
10530 fpout = fopen(serr, "a");
10534 fperr = fopen(serr, "w");
10537 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
10541 /* Register procs as instance variables */
10542 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
10543 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
10544 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
10545 if (fpout != NULL && fpout != (FILE *)1)
10547 if (fperr != NULL && fperr != (FILE *)1)
10557 /* Define module Molby */
10558 rb_mMolby = rb_define_module("Molby");
10560 /* Define Vector3D, Transform, IntGroup */
10563 /* Define MDArena */
10564 Init_MolbyMDTypes();
10566 /* class Molecule */
10567 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
10568 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
10569 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
10570 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
10571 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
10572 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
10573 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
10574 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
10575 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
10576 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
10577 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
10578 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
10579 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
10580 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
10581 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
10582 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
10583 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
10584 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
10585 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
10586 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
10587 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
10588 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
10589 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
10590 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
10591 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
10592 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
10593 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
10594 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
10595 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
10596 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
10597 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
10598 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
10599 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
10600 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
10601 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
10602 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
10603 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
10604 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
10606 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
10607 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
10608 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
10609 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
10610 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
10612 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
10613 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
10614 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
10615 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
10616 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
10617 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
10619 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
10620 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
10621 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
10622 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
10623 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
10624 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
10625 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
10626 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
10627 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
10628 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
10629 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
10630 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
10631 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
10632 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
10633 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
10634 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
10635 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
10636 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
10637 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
10638 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
10639 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
10640 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
10641 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
10642 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
10643 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
10644 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
10645 rb_define_alias(rb_cMolecule, "+", "add");
10646 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
10647 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
10648 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
10649 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
10650 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
10651 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
10652 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
10653 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
10654 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
10655 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
10656 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
10657 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
10658 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
10659 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
10660 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
10661 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
10662 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
10663 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
10664 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
10665 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
10666 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
10667 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
10668 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
10669 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
10670 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
10671 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
10672 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
10673 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
10674 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
10675 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
10676 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
10677 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
10678 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
10679 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
10680 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
10681 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
10682 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
10683 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
10684 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
10685 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
10686 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
10687 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
10688 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
10689 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
10690 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
10691 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
10692 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
10693 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
10694 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
10695 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
10696 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
10697 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
10698 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
10699 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
10700 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
10701 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
10702 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
10703 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
10704 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
10705 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
10706 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
10707 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
10708 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
10709 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
10710 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
10711 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
10712 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
10713 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
10714 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
10715 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
10716 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
10717 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
10718 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
10719 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
10720 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
10721 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
10722 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
10723 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
10724 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
10725 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
10726 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
10727 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
10728 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
10729 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
10730 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
10731 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
10732 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
10733 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
10734 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
10735 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
10736 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
10737 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
10738 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
10739 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
10740 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
10741 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
10742 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
10743 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
10744 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
10745 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
10746 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
10747 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
10748 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
10749 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
10750 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
10751 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
10752 #if 1 || !defined(__CMDMAC__)
10753 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
10754 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
10755 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
10756 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
10757 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
10758 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
10759 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
10760 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
10761 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
10762 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
10763 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
10764 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
10765 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
10766 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
10768 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
10769 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
10770 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
10771 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
10772 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
10773 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
10774 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
10775 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
10776 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
10777 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
10778 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
10779 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
10780 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
10781 rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
10782 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
10784 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
10785 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
10786 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
10788 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
10789 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
10790 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
10791 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
10792 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
10793 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
10794 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
10795 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
10797 /* class MolEnumerable */
10798 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
10799 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
10800 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
10801 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
10802 rb_define_alias(rb_cMolEnumerable, "size", "length");
10803 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
10804 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
10806 /* class AtomRef */
10807 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
10808 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
10810 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
10811 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
10812 s_AtomAttrDefTable[i].id = rb_intern(buf);
10813 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
10815 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
10817 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
10818 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
10819 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
10820 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
10821 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
10822 rb_global_variable(&s_SetAtomAttrString);
10823 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
10824 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
10826 /* class Parameter */
10827 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
10828 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10829 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10830 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10831 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10832 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10833 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10834 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10835 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
10836 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10837 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10838 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10839 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10840 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10841 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10842 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10843 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10844 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10845 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10846 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10847 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10848 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10849 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10850 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10851 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10852 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10853 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
10854 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
10855 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10856 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10857 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10858 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10859 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10860 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10861 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10862 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
10863 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10864 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10865 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10866 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10867 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10868 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10869 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10870 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10871 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10872 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10873 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10874 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10875 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10876 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10877 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10878 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10879 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10880 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10882 /* class ParEnumerable */
10883 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10884 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10885 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10886 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10887 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10888 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10889 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10890 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10891 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10892 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10893 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
10895 /* class ParameterRef */
10896 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10897 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10899 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10900 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10901 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10902 if (s_ParameterAttrDefTable[i].symref != NULL)
10903 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10904 if (s_ParameterAttrDefTable[i].setter != NULL) {
10906 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10909 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10910 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10911 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10912 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10913 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10914 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10915 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10916 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
10918 /* class MolbyError */
10919 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10921 /* module Kernel */
10922 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10923 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10924 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10925 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10926 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10927 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10928 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10929 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10930 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
10931 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10932 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10933 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10934 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10935 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10936 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
10937 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
10938 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10939 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10940 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
10941 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
10942 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
10943 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
10944 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
10945 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
10947 s_ID_equal = rb_intern("==");
10948 g_RubyID_call = rb_intern("call");
10951 #pragma mark ====== External functions ======
10953 static VALUE s_ruby_top_self = Qfalse;
10954 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
10955 static VALUE s_ruby_export_local_variables = Qfalse;
10958 s_evalRubyScriptOnMoleculeSub(VALUE val)
10960 void **ptr = (void **)val;
10961 Molecule *mol = (Molecule *)ptr[1];
10962 VALUE sval, fnval, lnval, retval;
10965 if (s_ruby_top_self == Qfalse) {
10966 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10968 if (s_ruby_get_binding_for_molecule == Qfalse) {
10970 "lambda { |_mol_, _bind_| \n"
10971 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
10972 " _proc_.call(_mol_) } ";
10973 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
10974 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
10976 if (s_ruby_export_local_variables == Qfalse) {
10978 "lambda { |_bind_| \n"
10979 " # find local variables newly defined in _bind_ \n"
10980 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
10981 " _a_.each { |_vsym_| \n"
10982 " _vname_ = _vsym_.to_s \n"
10983 " _vval_ = _bind_.eval(_vname_) \n"
10984 " # Define local variable \n"
10985 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
10986 " # Then set value \n"
10987 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
10990 s_ruby_export_local_variables = rb_eval_string(s2);
10991 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
10993 if (ptr[2] == NULL) {
10995 /* String literal: we need to specify string encoding */
10996 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
10997 sval = rb_str_new2(scr);
10999 fnval = rb_str_new2("(eval)");
11000 lnval = INT2FIX(0);
11002 sval = rb_str_new2((char *)ptr[0]);
11003 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11004 lnval = INT2FIX(1);
11006 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11008 VALUE mval = ValueFromMolecule(mol);
11009 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11011 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11013 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11019 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11023 VALUE save_interrupt_flag;
11024 /* char *save_ruby_sourcefile;
11025 int save_ruby_sourceline; */
11026 if (gMolbyIsCheckingInterrupt) {
11027 MolActionAlertRubyIsRunning();
11029 return (RubyValue)Qnil;
11032 args[0] = (void *)script;
11033 args[1] = (void *)mol;
11034 args[2] = (void *)fname;
11035 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
11036 /* save_ruby_sourcefile = ruby_sourcefile;
11037 save_ruby_sourceline = ruby_sourceline; */
11038 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
11039 if (*status != 0) {
11040 /* Is this 'exit' exception? */
11041 VALUE last_exception = rb_gv_get("$!");
11042 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
11043 /* Capture exit and return the status value */
11044 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
11048 s_SetInterruptFlag(Qnil, save_interrupt_flag);
11049 /* ruby_sourcefile = save_ruby_sourcefile;
11050 ruby_sourceline = save_ruby_sourceline; */
11056 Molby_showRubyValue(RubyValue value, char **outValueString)
11058 VALUE val = (VALUE)value;
11059 if (gMolbyIsCheckingInterrupt) {
11060 MolActionAlertRubyIsRunning();
11067 val = rb_protect(rb_inspect, val, &status);
11069 str = StringValuePtr(val);
11070 if (outValueString != NULL)
11071 *outValueString = strdup(str);
11072 MyAppCallback_showScriptMessage("%s", str);
11077 Molby_showError(int status)
11079 static const int tag_raise = 6;
11080 char *msg = NULL, *msg2;
11081 VALUE val, backtrace;
11082 int interrupted = 0;
11083 if (status == tag_raise) {
11084 VALUE errinfo = rb_errinfo();
11085 VALUE eclass = CLASS_OF(errinfo);
11086 if (eclass == rb_eInterrupt) {
11092 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
11094 val = rb_eval_string_protect("$!.to_s", &status);
11096 msg = RSTRING_PTR(val);
11097 else msg = "(message not available)";
11099 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
11100 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
11106 Molby_getDescription(void)
11108 extern const char *gVersionString, *gCopyrightString;
11109 extern int gRevisionNumber;
11110 extern char *gLastBuildString;
11112 char *revisionString;
11113 if (gRevisionNumber > 0) {
11114 asprintf(&revisionString, ", revision %d", gRevisionNumber);
11115 } else revisionString = "";
11117 "Molby %s%s\n%s\nLast compile: %s\n"
11118 #if !defined(__CMDMAC__)
11124 "ruby %s, http://www.ruby-lang.org/\n"
11126 "FFTW 3.3.2, http://www.fftw.org/\n"
11127 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
11128 " and Massachusetts Institute of Technology",
11129 gVersionString, revisionString, gCopyrightString, gLastBuildString,
11130 #if !defined(__CMDMAC__)
11131 MyAppCallback_getGUIDescriptionString(),
11133 gRubyVersion, gRubyCopyright);
11134 if (revisionString[0] != 0)
11135 free(revisionString);
11140 Molby_startup(const char *script, const char *dir)
11145 char *respath, *p, *wbuf;
11147 /* Get version/copyright string from Ruby interpreter */
11149 gRubyVersion = strdup(ruby_version);
11150 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
11151 #if defined(__CMDMAC__)
11154 " ", /* Indent for displaying in About dialog */
11156 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
11159 /* Read build and revision information for Molby */
11162 extern int gRevisionNumber;
11163 extern char *gLastBuildString;
11164 FILE *fp = fopen("../buildInfo.txt", "r");
11165 gLastBuildString = "";
11167 if (fgets(buf, sizeof(buf), fp) != NULL) {
11168 char *p1 = strchr(buf, '\"');
11169 char *p2 = strrchr(buf, '\"');
11170 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
11171 memmove(buf, p1 + 1, p2 - p1 - 1);
11172 buf[p2 - p1 - 1] = 0;
11173 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
11178 fp = fopen("../revisionInfo.txt", "r");
11179 gRevisionNumber = 0;
11181 if (fgets(buf, sizeof(buf), fp) != NULL) {
11182 gRevisionNumber = strtol(buf, NULL, 0);
11188 #if defined(__CMDMAC__)
11189 wbuf = Molby_getDescription();
11190 printf("%s\n", wbuf);
11194 /* Read atom display parameters */
11195 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
11196 #if defined(__CMDMAC__)
11197 fprintf(stderr, "%s\n", wbuf);
11199 MyAppCallback_setConsoleColor(1);
11200 MyAppCallback_showScriptMessage("%s", wbuf);
11201 MyAppCallback_setConsoleColor(0);
11206 /* Read default parameters */
11207 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
11208 if (wbuf != NULL) {
11209 #if defined(__CMDMAC__)
11210 fprintf(stderr, "%s\n", wbuf);
11212 MyAppCallback_setConsoleColor(1);
11213 MyAppCallback_showScriptMessage("%s", wbuf);
11214 MyAppCallback_setConsoleColor(0);
11219 /* Initialize Ruby interpreter */
11222 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
11223 it causes rb_bug() (= fatal error) during ruby_init().
11224 As a workaround, these standard streams are reopend as
11226 freopen("NUL", "r", stdin);
11227 freopen("NUL", "w", stdout);
11228 freopen("NUL", "w", stderr);
11233 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
11235 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
11236 ruby_incpush(libpath);
11240 ruby_script("Molby");
11242 /* Find the resource path (the parent directory of the given directory) */
11243 respath = strdup(dir);
11244 p = strrchr(respath, '/');
11245 if (p == NULL && PATH_SEPARATOR != '/')
11246 p = strrchr(respath, PATH_SEPARATOR);
11249 val = Ruby_NewFileStringValue(respath);
11250 rb_define_global_const("MolbyResourcePath", val);
11253 /* Define Molby classes */
11255 RubyDialogInitClass();
11257 rb_define_const(rb_mMolby, "ResourcePath", val);
11258 val = Ruby_NewFileStringValue(dir);
11259 rb_define_const(rb_mMolby, "ScriptPath", val);
11260 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
11261 val = Ruby_NewFileStringValue(p);
11262 rb_define_const(rb_mMolby, "MbsfPath", val);
11265 p = MyAppCallback_getHomeDir();
11266 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
11267 rb_define_const(rb_mMolby, "HomeDirectory", val);
11269 p = MyAppCallback_getDocumentHomeDir();
11270 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
11271 rb_define_const(rb_mMolby, "DocumentDirectory", val);
11274 #if defined(__CMDMAC__)
11275 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
11277 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
11282 /* Create objects for stdout and stderr */
11283 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11284 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
11285 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
11286 rb_gv_set("$stdout", val);
11287 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11288 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
11289 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
11290 rb_gv_set("$stderr", val);
11292 /* Create objects for stdin */
11293 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11294 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
11295 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
11296 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
11297 rb_gv_set("$stdin", val);
11301 /* Global variable to hold backtrace */
11302 rb_define_variable("$backtrace", &gMolbyBacktrace);
11304 /* Global variables for script menus */
11305 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
11306 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
11307 gScriptMenuCommands = rb_ary_new();
11308 gScriptMenuEnablers = rb_ary_new();
11311 /* Register interrupt check code */
11312 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
11316 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
11317 s_SetIntervalTimer(0, 50);
11320 /* Read the startup script */
11321 if (script != NULL && script[0] != 0) {
11322 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
11324 rb_load_protect(rb_str_new2(script), 0, &status);
11327 Molby_showError(status);
11329 MyAppCallback_showScriptMessage("Done.\n");
11334 Molby_buildARGV(int argc, const char **argv)
11337 rb_ary_clear(rb_argv);
11338 for (i = 0; i < argc; i++) {
11339 VALUE arg = rb_tainted_str_new2(argv[i]);
11341 rb_ary_push(rb_argv, arg);