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 "ruby/encoding.h" /* for rb_str_encode() etc. */
31 /*#include <node.h> *//* for rb_add_event_hook() */
33 #if defined(__WXMAC__) || defined(__CMDMAC__)
34 #define USE_PTHREAD_FOR_TIMER 1
38 #if USE_PTHREAD_FOR_TIMER
39 #include <unistd.h> /* for usleep() */
40 #include <pthread.h> /* for pthread */
42 #include <signal.h> /* for sigaction() */
46 #include "../Missing.h"
48 #pragma mark ====== Global Values ======
52 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
53 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
55 VALUE gMolbyBacktrace;
56 VALUE gMolbyErrorHistory;
57 VALUE gScriptMenuCommands;
58 VALUE gScriptMenuEnablers;
60 int gMolbyRunLevel = 0;
61 int gMolbyIsCheckingInterrupt = 0;
63 char *gRubyVersion, *gRubyCopyright;
66 static ID s_ID_equal; /* rb_intern("==") */
70 /* Symbols for atom attributes */
72 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
73 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
74 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
75 s_RSym, s_XSym, s_YSym, s_ZSym,
76 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
77 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
78 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
79 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
80 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
81 s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
83 /* Symbols for parameter attributes */
85 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
86 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
89 /* s_A14Sym, s_B14Sym, */
90 s_Req14Sym, s_Eps14Sym,
91 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
92 s_CommentSym, s_SourceSym;
94 /* Symbols for graphics */
96 s_LineSym, s_PolySym, s_CylinderSym, s_ConeSym, s_EllipsoidSym;
100 * Get ary[i] by calling "[]" method
103 Ruby_ObjectAtIndex(VALUE ary, int idx)
105 static ID index_method = 0;
106 if (TYPE(ary) == T_ARRAY) {
107 int len = RARRAY_LEN(ary);
108 if (idx >= 0 && idx < len)
109 return (RARRAY_PTR(ary))[idx];
112 if (index_method == 0)
113 index_method = rb_intern("[]");
114 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
118 Ruby_FileStringValuePtr(VALUE *valp)
121 char *p = strdup(StringValuePtr(*valp));
122 translate_char(p, '/', '\\');
123 *valp = rb_str_new2(p);
125 return StringValuePtr(*valp);
127 return StringValuePtr(*valp);
132 Ruby_NewFileStringValue(const char *fstr)
136 char *p = strdup(fstr);
137 translate_char(p, '\\', '/');
138 retval = rb_enc_str_new(p, strlen(p), rb_default_external_encoding());
142 return rb_str_new2(fstr);
147 Ruby_EncodedStringValuePtr(VALUE *valp)
149 rb_string_value(valp);
150 *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
151 return RSTRING_PTR(*valp);
155 Ruby_NewEncodedStringValue(const char *str, int len)
159 return rb_enc_str_new(str, len, rb_default_external_encoding());
163 Ruby_ObjToStringObj(VALUE val)
169 return rb_str_new2(rb_id2name(SYM2ID(val)));
171 return rb_str_to_str(val);
175 #pragma mark ====== Message input/output ======
179 * message_box(str, title, button = nil, icon = :info)
181 * Show a message box.
182 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
183 * Icon: :info, :warning, :error
186 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
188 char *str, *title, *s;
190 VALUE sval, tval, bval, ival;
191 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
192 str = StringValuePtr(sval);
193 title = StringValuePtr(tval);
195 bval = Ruby_ObjToStringObj(bval);
196 s = RSTRING_PTR(bval);
197 if (strncmp(s, "ok", 2) == 0)
199 else if (strncmp(s, "cancel", 6) == 0)
202 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
205 ival = Ruby_ObjToStringObj(ival);
206 s = RSTRING_PTR(ival);
207 if (strncmp(s, "info", 4) == 0)
209 else if (strncmp(s, "warn", 4) == 0)
211 else if (strncmp(s, "err", 3) == 0)
214 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
216 MyAppCallback_messageBox(str, title, buttons, icon);
222 * error_message_box(str)
224 * Show an error message box.
227 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
229 char *str = StringValuePtr(sval);
230 MyAppCallback_errorMessageBox("%s", str);
236 * ask(prompt, default = nil) -> string
238 * Open a modal dialog and get a line of text.
241 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
243 volatile VALUE prompt, message;
246 rb_scan_args(argc, argv, "11", &prompt, &message);
247 if (message != Qnil) {
248 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
249 buf[sizeof buf - 1] = 0;
251 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
253 return rb_str_new2(buf);
260 * show_console_window
262 * Show the console window and bring to the front.
265 s_Kernel_ShowConsoleWindow(VALUE self)
267 MyAppCallback_showConsoleWindow();
273 * hide_console_window
275 * Hide the console window.
278 s_Kernel_HideConsoleWindow(VALUE self)
280 MyAppCallback_hideConsoleWindow();
288 * Ring the system bell.
291 s_Kernel_Bell(VALUE self)
293 MyAppCallback_bell();
299 * play_sound(filename, flag = 0)
301 * Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
302 * 1, play the sound asynchronously; 3, play the sound with loop asynchronously
305 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
310 rb_scan_args(argc, argv, "11", &fnval, &flval);
313 else flag = NUM2INT(rb_Integer(flval));
314 fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
315 fname = StringValuePtr(fnval);
316 retval = MyAppCallback_playSound(fname, flag);
317 return (retval ? Qtrue : Qnil);
324 * Stop the sound if playing.
327 s_Kernel_StopSound(VALUE self)
329 MyAppCallback_stopSound();
335 * export_to_clipboard(str)
337 * Export the given string to clipboard.
340 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
342 #if !defined(__CMDMAC__)
343 const char *s = StringValuePtr(sval);
346 /* Convert the end-of-line characters */
347 { const char *p; int nc; char *np;
349 for (p = s; *p != 0; p++) {
353 ns = (char *)malloc(strlen(s) + nc + 1);
354 for (np = ns, p = s; *p != 0; p++, np++) {
362 ns = (char *)malloc(strlen(s) + 1);
366 /* wxMac still has Carbon code. Oops. */
367 for (np = ns; *np != 0; np++) {
374 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
375 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
384 * Put the message in the main text view in black color.
387 s_StandardOutput(VALUE self, VALUE str)
390 MyAppCallback_setConsoleColor(0);
391 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
399 * Put the message in the main text view in red color.
402 s_StandardErrorOutput(VALUE self, VALUE str)
405 MyAppCallback_setConsoleColor(1);
406 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
407 MyAppCallback_setConsoleColor(0);
416 * Flush the standard (error) output. Actually do nothing.
419 s_FlushConsoleOutput(VALUE self)
426 * stdin.gets(rs = $/)
428 * Read one line message via dialog box.
431 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
434 pval = rb_str_new2("Enter a line:");
435 rval = s_Kernel_Ask(1, &pval, self);
438 rb_str_cat2(rval, "\n");
444 * stdin.method_missing(name, args, ...)
446 * Throw an exception, noting only gets and readline are defined.
449 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
452 rb_scan_args(argc, argv, "10", &nval);
453 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
454 return Qnil; /* Not reached */
457 #pragma mark ====== Track key events ======
459 /* User interrupt handling
460 * User interrupt (command-period on Mac OS) is handled by periodic polling of
461 * key events. This polling should only be enabled during "normal" execution
462 * of scripts and must be disabled when the rest of the application (or Ruby
463 * script itself) is handling GUI. This is ensured by appropriate calls to
464 * enable_interrupt and disable_interrupt. */
466 static VALUE s_interrupt_flag = Qfalse;
469 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
471 volatile VALUE message;
473 if (Ruby_GetInterruptFlag() == Qtrue) {
474 rb_scan_args(argc, argv, "01", &message);
476 p = StringValuePtr(message);
479 MyAppCallback_showProgressPanel(p);
485 s_HideProgressPanel(VALUE self)
487 MyAppCallback_hideProgressPanel();
492 s_SetProgressValue(VALUE self, VALUE val)
494 double dval = NUM2DBL(rb_Float(val));
495 MyAppCallback_setProgressValue(dval);
500 s_SetProgressMessage(VALUE self, VALUE msg)
505 else p = StringValuePtr(msg);
506 MyAppCallback_setProgressMessage(p);
511 s_SetInterruptFlag(VALUE self, VALUE val)
515 if (val == Qfalse || val == Qnil)
519 oldval = s_interrupt_flag;
521 s_interrupt_flag = val;
523 s_HideProgressPanel(self);
530 s_GetInterruptFlag(VALUE self)
532 return s_SetInterruptFlag(self, Qundef);
536 Ruby_SetInterruptFlag(VALUE val)
538 return s_SetInterruptFlag(Qnil, val);
542 Ruby_GetInterruptFlag(void)
544 return s_SetInterruptFlag(Qnil, Qundef);
549 * check_interrupt -> integer
551 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
554 s_Kernel_CheckInterrupt(VALUE self)
556 if (Ruby_GetInterruptFlag() == Qfalse)
558 else if (MyAppCallback_checkInterrupt())
560 else return INT2NUM(0);
563 static volatile unsigned long sITimerCount = 0;
566 static HANDLE sITimerEvent;
567 static HANDLE sITimerThread;
568 static int sITimerInterval;
570 static __stdcall unsigned
571 s_ITimerThreadFunc(void *p)
573 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
579 #elif USE_PTHREAD_FOR_TIMER
582 static pthread_t sTimerThread;
584 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
585 static volatile signed char sTimerFlag = -1;
586 static volatile int sTimerIntervalMicrosec = 0;
589 s_TimerThreadEntry(void *param)
592 usleep(sTimerIntervalMicrosec);
595 else if (sTimerFlag == -2)
604 s_SignalAction(int n)
610 s_SetIntervalTimer(int n, int msec)
614 /* Start interval timer */
615 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
616 sITimerInterval = msec;
618 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
621 /* Stop interval timer */
623 SetEvent(sITimerEvent); /* Tell thread to terminate */
625 WaitForSingleObject(sITimerThread, 1000);
626 CloseHandle(sITimerThread);
629 CloseHandle(sITimerEvent);
631 sITimerThread = NULL;
633 #elif USE_PTHREAD_FOR_TIMER
635 if (sTimerFlag == -1) {
636 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
638 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
641 sTimerFlag = 0; /* Active */
642 sTimerIntervalMicrosec = msec * 1000;
643 } else if (sTimerFlag != -1)
644 sTimerFlag = 1; /* Inactive */
646 static struct itimerval sOldValue;
647 static struct sigaction sOldAction;
648 struct itimerval val;
649 struct sigaction act;
652 act.sa_handler = s_SignalAction;
655 sigaction(SIGALRM, &act, &sOldAction);
656 val.it_value.tv_sec = 0;
657 val.it_value.tv_usec = msec * 1000;
658 val.it_interval.tv_sec = 0;
659 val.it_interval.tv_usec = msec * 1000;
660 setitimer(ITIMER_REAL, &val, &sOldValue);
662 setitimer(ITIMER_REAL, &sOldValue, &val);
663 sigaction(SIGALRM, &sOldAction, &act);
669 s_GetTimerCount(void)
675 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
676 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
678 if (s_interrupt_flag != Qfalse) {
679 static unsigned long sLastTime = 0;
680 unsigned long currentTime;
682 currentTime = s_GetTimerCount();
683 if (currentTime != sLastTime) {
684 sLastTime = currentTime;
685 gMolbyIsCheckingInterrupt = 1;
686 flag = MyAppCallback_checkInterrupt();
687 gMolbyIsCheckingInterrupt = 0;
689 s_SetInterruptFlag(Qnil, Qfalse);
696 #pragma mark ====== Menu handling ======
700 * register_menu(title, method, enable_proc = nil)
702 * Register the method (specified as a symbol) in the script menu.
703 * The method must be either an instance method of Molecule with no argument,
704 * or a class method of Molecule with one argument (the current molecule),
705 * or a proc object with one argument (the current molecule).
706 * The menu associated with the class method can be invoked even when no document
707 * is open (the argument is set to Qnil in this case). On the other hand, the
708 * menu associated with the instance method can only be invoked when at least one
709 * document is active.
710 * If enable_proc is non-nil, then it is called whenever the availability of
711 * the menu command is tested. It is usually a proc object with one argument
712 * (the current molecule or nil). As a special case, the following symbols can
713 * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
714 * the top-level molecule has at least one atom), :selection (enabled when
715 * the top-level molecule has one or more selected atoms).
718 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
721 VALUE tval, mval, pval;
722 static VALUE sMolSym, sNonEmptySym, sSelectionSym;
723 static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
724 rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
725 tval = rb_str_to_str(tval);
726 n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
729 if (TYPE(mval) == T_SYMBOL) {
730 /* Create an appropriate proc object */
731 const char *name = rb_id2name(SYM2ID(mval));
733 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
734 /* Defined as a Molecule method */
735 asprintf(&s, "lambda { |m| m.%s }", name);
737 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
738 /* Defined as a Molecule class method */
739 asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
741 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
742 mval = rb_eval_string(s);
745 if (sMolSym == Qfalse) {
746 sMolSym = ID2SYM(rb_intern("mol"));
747 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
748 sSelectionSym = ID2SYM(rb_intern("selection"));
749 sMolProc = rb_eval_string("lambda { |m| m != nil }");
750 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
751 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
752 sTrueProc = rb_eval_string("lambda { |m| true }");
753 rb_global_variable(&sMolProc);
754 rb_global_variable(&sNonEmptyProc);
755 rb_global_variable(&sSelectionProc);
756 rb_global_variable(&sTrueProc);
764 } else if (pval == sMolSym)
766 else if (pval == sNonEmptySym)
767 pval = sNonEmptyProc;
768 else if (pval == sSelectionSym)
769 pval = sSelectionProc;
770 rb_ary_store(gScriptMenuCommands, n, mval);
771 rb_ary_store(gScriptMenuEnablers, n, pval);
776 s_Kernel_LookupMenu(VALUE self, VALUE title)
778 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
783 s_Ruby_UpdateUI_handler(VALUE data)
785 void **p = (void **)data;
786 int index = (int)p[0];
787 Molecule *mol = (Molecule *)p[1];
788 int *outChecked = (int *)p[2];
789 char **outTitle = (char **)p[3];
790 VALUE mval = ValueFromMolecule(mol);
791 VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
792 static ID call_id = 0;
794 call_id = rb_intern("call");
797 pval = rb_funcall(pval, call_id, 1, mval);
798 if (rb_obj_is_kind_of(pval, rb_cArray)) {
800 if (outChecked != NULL) {
801 val = rb_ary_entry(pval, 1); /* Checked or not */
802 *outChecked = (RTEST(val) ? 1 : 0);
804 if (outTitle != NULL) {
805 val = rb_ary_entry(pval, 2); /* Text */
806 if (TYPE(val) == T_STRING) {
807 *outTitle = strdup(StringValuePtr(val));
810 pval = rb_ary_entry(pval, 0);
816 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
821 p[0] = (void *)index;
825 retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
826 return (RTEST(retval) ? 1 : 0);
831 s_Ruby_methodType_sub(VALUE data)
833 const char **p = (const char **)data;
834 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
835 ID mid = rb_intern(p[1]);
837 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
839 else if (rb_respond_to(klass, mid))
842 return INT2FIX(ival);
845 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
846 has the singleton method (class method) with the given name, 0 otherwise. */
848 Ruby_methodType(const char *className, const char *methodName)
855 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
857 return FIX2INT(retval);
864 * execute_script_file(fname)
866 * Execute the script in the given file. If a molecule is active, then
867 * the script is evaluated as Molecule.current.instance_eval(script).
868 * Before entering the script, the current directory is set to the parent
869 * directory of the script.
872 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
875 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
876 if (retval == (VALUE)6 && status == -1)
877 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
887 * Get the directory suitable for storing user documents. On Windows
888 * it is the home directory + "My Documents". On other platforms
889 * it is the home directory.
892 s_Kernel_DocumentHome(VALUE self)
894 char *s = MyAppCallback_getDocumentHomeDir();
895 VALUE retval = Ruby_NewFileStringValue(s);
900 /* The callback function for call_subprocess */
902 s_Kernel_CallSubProcess_Callback(void *data)
905 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
906 if (status != 0 || retval == Qnil || retval == Qfalse)
913 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
915 * Call subprocess. A progress dialog window is displayed, with a message
916 * "Running #{process_name}...".
917 * A callback proc can be given, which is called periodically during execution. If the proc returns
918 * nil or false, then the execution will be interrupted.
919 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
920 * filename begins with ">>", then the message will be appended to the file.
921 * If the filename is "/dev/null" or "NUL", then the message will be lost.
922 * If the argument is nil, then the message will be sent to the Ruby console.
925 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
927 VALUE cmd, procname, cproc, stdout_val, stderr_val;
928 int n, exitstatus, pid;
932 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
934 if (stdout_val == Qnil) {
937 sout = StringValuePtr(stdout_val);
938 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
941 if (strncmp(sout, ">>", 2) == 0) {
943 fpout = fopen(sout, "a");
947 fpout = fopen(sout, "w");
950 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
953 if (stderr_val == Qnil) {
956 serr = StringValuePtr(stderr_val);
957 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
960 if (strncmp(serr, ">>", 2) == 0) {
962 fpout = fopen(serr, "a");
966 fperr = fopen(serr, "w");
969 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
973 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
975 if (fpout != NULL && fpout != (FILE *)1)
977 if (fperr != NULL && fperr != (FILE *)1)
989 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
992 s_Kernel_Backquote(VALUE self, VALUE cmd)
995 int n, exitstatus, pid;
997 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
998 /* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
999 if (n >= 0 && buf != NULL) {
1000 val = Ruby_NewEncodedStringValue(buf, 0);
1003 val = Ruby_NewEncodedStringValue("", 0);
1005 rb_last_status_set(exitstatus, pid);
1009 #pragma mark ====== User defaults ======
1013 * get_global_settings(key)
1015 * Get a setting data for key from the application preferences.
1018 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1020 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1022 VALUE retval = rb_eval_string(p);
1030 * set_global_settings(key, value)
1032 * Set a setting data for key to the application preferences.
1035 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1037 VALUE sval = rb_inspect(value);
1038 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1042 #pragma mark ====== IO extension ======
1045 s_Ruby_str_encode_protected(VALUE val)
1047 return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1048 ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1055 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1058 s_IO_gets_any_eol(VALUE self)
1060 VALUE val, val2, cval;
1063 static ID id_getbyte = 0, id_ungetbyte;
1064 if (id_getbyte == 0) {
1065 id_getbyte = rb_intern("getbyte");
1066 id_ungetbyte = rb_intern("ungetbyte");
1070 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1071 c = NUM2INT(rb_Integer(cval));
1073 cval = rb_funcall(self, id_getbyte, 0);
1075 c = NUM2INT(rb_Integer(cval));
1077 rb_funcall(self, id_ungetbyte, 1, cval);
1080 } else if (c != 0x0a) {
1085 val = rb_str_new(buf, i);
1087 rb_str_append(val, rb_str_new(buf, i));
1092 if (cval == Qnil && i == 0 && val == Qnil)
1093 return Qnil; /* End of file */
1096 val = rb_str_new(buf, i);
1098 rb_str_append(val, rb_str_new(buf, i));
1099 val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
1103 /* Needs a end-of-line mark */
1104 cval = rb_gv_get("$/");
1105 rb_str_append(val, cval);
1107 rb_gv_set("$_", val);
1111 #pragma mark ====== Utility functions (protected funcall) ======
1113 struct Ruby_funcall2_record {
1121 s_Ruby_funcall2_sub(VALUE data)
1123 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1124 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1128 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1130 struct Ruby_funcall2_record rec;
1135 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1139 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1141 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1144 #pragma mark ====== ParameterRef Class ======
1147 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1151 Data_Get_Struct(self, ParameterRef, pref);
1153 *typep = pref->parType;
1154 if (pref->parType == kElementParType) {
1155 up = (UnionPar *)&gElementParameters[pref->idx];
1157 up = ParameterRefGetPar(pref);
1158 if (checkEditable) {
1160 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1161 if (up->bond.src != 0 && up->bond.src != -1)
1162 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1169 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1173 Data_Get_Struct(self, ParameterRef, pref);
1174 if (pref->mol == NULL)
1176 up = ParameterRefGetPar(pref);
1177 if (key != s_SourceSym)
1178 up->bond.src = 0; /* Becomes automatically molecule-local */
1179 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1182 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1183 MolActionCallback_registerUndo(pref->mol, act);
1184 MoleculeCallback_notifyModification(pref->mol, 0);
1185 pref->mol->needsMDRebuild = 1;
1190 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1192 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1194 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1196 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1200 s_AtomTypeIndexFromValue(VALUE val)
1202 if (rb_obj_is_kind_of(val, rb_cNumeric))
1203 return NUM2INT(val);
1205 return AtomTypeEncodeToUInt(StringValuePtr(val));
1208 static const char *s_ParameterTypeNames[] = {
1209 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1211 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1214 s_ParTypeFromValue(VALUE val)
1218 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1219 if (s_ParameterTypeIDs[0] == 0) {
1220 for (i = 0; i < n; i++)
1221 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1223 valid = rb_to_id(val);
1224 for (i = 0; i < n; i++) {
1225 if (valid == s_ParameterTypeIDs[i]) {
1227 return kElementParType;
1228 else return kFirstParType + i;
1231 return kInvalidParType;
1238 * Get the index in the parameter list.
1240 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1242 Data_Get_Struct(self, ParameterRef, pref);
1243 return INT2NUM(pref->idx);
1248 * par_type -> String
1250 * Get the parameter type, like "bond", "angle", etc.
1252 static VALUE s_ParameterRef_GetParType(VALUE self) {
1254 s_UnionParFromValue(self, &tp, 0);
1255 if (tp == kElementParType)
1256 return rb_str_new2("element");
1257 tp -= kFirstParType;
1258 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1259 return rb_str_new2(s_ParameterTypeNames[tp]);
1260 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1265 * atom_type -> String or Array of String
1266 * atom_types -> String or Array of String
1268 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1269 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1270 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1271 * The atom type may be "X", which is a wildcard that matches any atom type.
1273 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1278 up = s_UnionParFromValue(self, &tp, 0);
1279 n = ParameterGetAtomTypes(tp, up, types);
1281 rb_raise(rb_eMolbyError, "invalid member atom_types");
1282 for (i = 0; i < n; i++) {
1283 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1284 vals[i] = INT2NUM(types[i]);
1286 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1291 return rb_ary_new4(n, vals);
1298 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1300 static VALUE s_ParameterRef_GetK(VALUE self) {
1304 up = s_UnionParFromValue(self, &tp, 0);
1307 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1309 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1310 case kDihedralParType:
1311 case kImproperParType:
1312 if (up->torsion.mult == 1)
1313 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1314 n = up->torsion.mult;
1317 for (i = 0; i < n; i++)
1318 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1319 return rb_ary_new4(n, vals);
1321 rb_raise(rb_eMolbyError, "invalid member k");
1329 * Get the equilibrium bond length. Only available for bond parameters.
1331 static VALUE s_ParameterRef_GetR0(VALUE self) {
1334 up = s_UnionParFromValue(self, &tp, 0);
1335 if (tp == kBondParType)
1336 return rb_float_new(up->bond.r0);
1337 else rb_raise(rb_eMolbyError, "invalid member r0");
1344 * Get the equilibrium angle (in degree). Only available for angle parameters.
1346 static VALUE s_ParameterRef_GetA0(VALUE self) {
1349 up = s_UnionParFromValue(self, &tp, 0);
1350 if (tp == kAngleParType)
1351 return rb_float_new(up->angle.a0 * kRad2Deg);
1352 else rb_raise(rb_eMolbyError, "invalid member a0");
1359 * Get the multiplicity. Only available for dihedral and improper parameters.
1360 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1362 static VALUE s_ParameterRef_GetMult(VALUE self) {
1365 up = s_UnionParFromValue(self, &tp, 0);
1366 if (tp == kDihedralParType || tp == kImproperParType)
1367 return rb_float_new(up->torsion.mult);
1368 else rb_raise(rb_eMolbyError, "invalid member mult");
1373 * period -> Integer or Array of Integers
1375 * Get the periodicity. Only available for dihedral and improper parameters.
1376 * If the multiplicity is larger than 1, then an array of integers is returned.
1377 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1379 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1383 up = s_UnionParFromValue(self, &tp, 0);
1384 if (tp == kDihedralParType || tp == kImproperParType) {
1385 if (up->torsion.mult == 1)
1386 return INT2NUM(up->torsion.period[0]);
1387 n = up->torsion.mult;
1390 for (i = 0; i < n; i++)
1391 vals[i] = INT2NUM(up->torsion.period[i]);
1392 return rb_ary_new4(n, vals);
1393 } else rb_raise(rb_eMolbyError, "invalid member period");
1398 * phi0 -> Float or Array of Floats
1400 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1401 * If the multiplicity is larger than 1, then an array of floats is returned.
1402 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1404 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1408 up = s_UnionParFromValue(self, &tp, 0);
1409 if (tp == kDihedralParType || tp == kImproperParType) {
1410 if (up->torsion.mult == 1)
1411 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1412 n = up->torsion.mult;
1415 for (i = 0; i < n; i++)
1416 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1417 return rb_ary_new4(n, vals);
1418 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1425 * Get the "A" value for the van der Waals parameter.
1428 static VALUE s_ParameterRef_GetA(VALUE self) {
1431 up = s_UnionParFromValue(self, &tp, 0);
1432 if (tp == kVdwParType)
1433 return rb_float_new(up->vdw.A);
1434 else if (tp == kVdwPairParType)
1435 return rb_float_new(up->vdwp.A);
1436 else rb_raise(rb_eMolbyError, "invalid member A");
1444 * Get the "B" value for the van der Waals parameter.
1447 static VALUE s_ParameterRef_GetB(VALUE self) {
1450 up = s_UnionParFromValue(self, &tp, 0);
1451 if (tp == kVdwParType)
1452 return rb_float_new(up->vdw.B);
1453 else if (tp == kVdwPairParType)
1454 return rb_float_new(up->vdwp.B);
1455 else rb_raise(rb_eMolbyError, "invalid member B");
1463 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1465 static VALUE s_ParameterRef_GetReq(VALUE self) {
1468 /* Double a, b, r; */
1470 up = s_UnionParFromValue(self, &tp, 0);
1471 if (tp == kVdwParType) {
1475 } else if (tp == kVdwPairParType) {
1479 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1480 /* if (a == 0.0 || b == 0.0) */
1481 return rb_float_new(r);
1482 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1489 * Get the minimum energy for the van der Waals parameter.
1491 static VALUE s_ParameterRef_GetEps(VALUE self) {
1496 up = s_UnionParFromValue(self, &tp, 0);
1497 if (tp == kVdwParType) {
1501 } else if (tp == kVdwPairParType) {
1505 } else rb_raise(rb_eMolbyError, "invalid member eps");
1506 /* if (a == 0.0 || b == 0.0) */
1507 return rb_float_new(eps * INTERNAL2KCAL);
1508 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1515 * Get the "A" value for the 1-4 van der Waals parameter.
1518 static VALUE s_ParameterRef_GetA14(VALUE self) {
1521 up = s_UnionParFromValue(self, &tp, 0);
1522 if (tp == kVdwParType)
1523 return rb_float_new(up->vdw.A14);
1524 else if (tp == kVdwPairParType)
1525 return rb_float_new(up->vdwp.A14);
1526 else rb_raise(rb_eMolbyError, "invalid member A14");
1534 * Get the "B" value for the 1-4 van der Waals parameter.
1537 static VALUE s_ParameterRef_GetB14(VALUE self) {
1540 up = s_UnionParFromValue(self, &tp, 0);
1541 if (tp == kVdwParType)
1542 return rb_float_new(up->vdw.B14);
1543 else if (tp == kVdwPairParType)
1544 return rb_float_new(up->vdwp.B14);
1545 else rb_raise(rb_eMolbyError, "invalid member B14");
1553 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1555 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1558 /* Double a, b, r; */
1560 up = s_UnionParFromValue(self, &tp, 0);
1561 if (tp == kVdwParType) {
1565 } else if (tp == kVdwPairParType) {
1566 /* a = up->vdwp.A14;
1567 b = up->vdwp.B14; */
1568 r = up->vdwp.r_eq14;
1569 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1570 /* if (a == 0.0 || b == 0.0) */
1571 return rb_float_new(r);
1572 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1579 * Get the minimum energy for the 1-4 van der Waals parameter.
1581 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1586 up = s_UnionParFromValue(self, &tp, 0);
1587 if (tp == kVdwParType) {
1590 eps = up->vdw.eps14;
1591 } else if (tp == kVdwPairParType) {
1592 /* a = up->vdwp.A14;
1593 b = up->vdwp.B14; */
1594 eps = up->vdwp.eps14;
1595 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1596 /* if (a == 0.0 || b == 0.0) */
1597 return rb_float_new(eps * INTERNAL2KCAL);
1598 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1605 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1607 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1610 up = s_UnionParFromValue(self, &tp, 0);
1611 if (tp == kVdwCutoffParType)
1612 return rb_float_new(up->vdwcutoff.cutoff);
1613 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1620 * Get the atomic (covalent) radius for the element parameter.
1622 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1625 up = s_UnionParFromValue(self, &tp, 0);
1626 if (tp == kElementParType)
1627 return rb_float_new(up->atom.radius);
1628 else rb_raise(rb_eMolbyError, "invalid member radius");
1633 * vdw_radius -> Float
1635 * Get the van der Waals radius for the element parameter. (0 if not given)
1637 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1640 up = s_UnionParFromValue(self, &tp, 0);
1641 if (tp == kElementParType)
1642 return rb_float_new(up->atom.vdw_radius);
1643 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1648 * color -> [Float, Float, Float]
1650 * Get the rgb color for the element parameter.
1652 static VALUE s_ParameterRef_GetColor(VALUE self) {
1655 up = s_UnionParFromValue(self, &tp, 0);
1656 if (tp == kElementParType)
1657 return rb_ary_new3(3, rb_float_new(up->atom.red / 65535.0), rb_float_new(up->atom.green / 65535.0), rb_float_new(up->atom.blue / 65535.0));
1658 else rb_raise(rb_eMolbyError, "invalid member color");
1663 * atomic_number -> Integer
1665 * Get the atomic number for the vdw or element parameter.
1667 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1670 up = s_UnionParFromValue(self, &tp, 0);
1671 if (tp == kElementParType)
1672 return INT2NUM(up->atom.number);
1673 else if (tp == kVdwParType)
1674 return INT2NUM(up->vdw.atomicNumber);
1675 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1682 * Get the name for the element parameter.
1684 static VALUE s_ParameterRef_GetName(VALUE self) {
1687 up = s_UnionParFromValue(self, &tp, 0);
1688 if (tp == kElementParType) {
1690 strncpy(name, up->atom.name, 4);
1692 return rb_str_new2(name);
1693 } else rb_raise(rb_eMolbyError, "invalid member name");
1700 * Get the atomic weight for the element parameter.
1702 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1705 up = s_UnionParFromValue(self, &tp, 0);
1706 if (tp == kElementParType)
1707 return rb_float_new(up->atom.weight);
1708 else if (tp == kVdwParType)
1709 return rb_float_new(up->vdw.weight);
1710 else rb_raise(rb_eMolbyError, "invalid member weight");
1715 * fullname -> String
1717 * Get the full name for the element parameter.
1719 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1722 up = s_UnionParFromValue(self, &tp, 0);
1723 if (tp == kElementParType) {
1725 strncpy(fullname, up->atom.fullname, 15);
1727 return rb_str_new2(fullname);
1728 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1735 * Get the comment for the parameter.
1737 static VALUE s_ParameterRef_GetComment(VALUE self) {
1740 up = s_UnionParFromValue(self, &tp, 0);
1744 else return rb_str_new2(ParameterGetComment(com));
1751 * Get the source string for the parameter. Returns false for undefined parameter,
1752 * and nil for "local" parameter that is specific for the molecule.
1754 static VALUE s_ParameterRef_GetSource(VALUE self) {
1757 up = s_UnionParFromValue(self, &tp, 0);
1760 return Qfalse; /* undefined */
1762 return Qnil; /* local */
1763 else return rb_str_new2(ParameterGetComment(src));
1767 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1774 if (rb_obj_is_kind_of(val, rb_cString)) {
1775 char *s = StringValuePtr(val);
1777 for (i = 0; i < n; i++) {
1780 /* Skip leading separaters */
1781 while (*s == '-' || *s == ' ' || *s == '\t')
1783 for (p = s; *p != 0; p++) {
1784 if (*p == '-' || *p == ' ' || *p == '\t')
1788 if (len >= sizeof(buf))
1789 len = sizeof(buf) - 1;
1790 strncpy(buf, s, len);
1792 /* Skip trailing blanks */
1793 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1796 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1797 if (buf[0] >= '0' && buf[0] <= '9')
1798 types[i] = atoi(buf);
1800 types[i] = AtomTypeEncodeToUInt(buf);
1801 if (p == NULL || *p == 0) {
1807 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1810 val = rb_ary_to_ary(val);
1811 if (RARRAY_LEN(val) != n)
1812 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1813 valp = RARRAY_PTR(val);
1815 for (i = 0; i < n; i++) {
1816 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1817 types[i] = NUM2INT(rb_Integer(valp[i]));
1819 VALUE sval = valp[i];
1820 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1825 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1830 up = s_UnionParFromValue(self, &tp, 1);
1831 oldval = s_ParameterRef_GetAtomTypes(self);
1832 oldsrc = up->bond.src;
1835 s_ScanAtomTypes(val, 2, types);
1836 up->bond.type1 = types[0];
1837 up->bond.type2 = types[1];
1840 s_ScanAtomTypes(val, 3, types);
1841 up->angle.type1 = types[0];
1842 up->angle.type2 = types[1];
1843 up->angle.type3 = types[2];
1845 case kDihedralParType:
1846 case kImproperParType:
1847 s_ScanAtomTypes(val, 4, types);
1848 up->torsion.type1 = types[0];
1849 up->torsion.type2 = types[1];
1850 up->torsion.type3 = types[2];
1851 up->torsion.type4 = types[3];
1854 s_ScanAtomTypes(val, 1, types);
1855 up->vdw.type1 = types[0];
1857 case kVdwPairParType:
1858 s_ScanAtomTypes(val, 2, types);
1859 up->vdwp.type1 = types[0];
1860 up->vdwp.type2 = types[1];
1862 case kVdwCutoffParType:
1863 s_ScanAtomTypes(val, 2, types);
1864 up->vdwcutoff.type1 = types[0];
1865 up->vdwcutoff.type2 = types[1];
1870 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1874 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1876 Int tp, i, n, oldsrc;
1877 VALUE *valp, oldval;
1878 up = s_UnionParFromValue(self, &tp, 1);
1879 oldval = s_ParameterRef_GetK(self);
1880 oldsrc = up->bond.src;
1883 val = rb_Float(val);
1884 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1887 val = rb_Float(val);
1888 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1890 case kDihedralParType:
1891 case kImproperParType:
1892 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1893 up->torsion.mult = 1;
1894 val = rb_Float(val);
1895 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1898 n = up->torsion.mult;
1901 val = rb_ary_to_ary(val);
1902 if (RARRAY_LEN(val) != n)
1903 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1904 valp = RARRAY_PTR(val);
1905 for (i = 0; i < n; i++) {
1906 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1910 rb_raise(rb_eMolbyError, "invalid member k");
1912 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1916 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1920 up = s_UnionParFromValue(self, &tp, 1);
1921 oldval = s_ParameterRef_GetR0(self);
1922 oldsrc = up->bond.src;
1923 if (tp == kBondParType) {
1924 val = rb_Float(val);
1925 up->bond.r0 = NUM2DBL(val);
1926 } else rb_raise(rb_eMolbyError, "invalid member r0");
1927 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1931 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1935 up = s_UnionParFromValue(self, &tp, 1);
1936 oldval = s_ParameterRef_GetA0(self);
1937 oldsrc = up->bond.src;
1938 if (tp == kAngleParType) {
1939 val = rb_Float(val);
1940 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1941 } else rb_raise(rb_eMolbyError, "invalid member a0");
1942 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1946 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1950 up = s_UnionParFromValue(self, &tp, 1);
1951 oldval = s_ParameterRef_GetMult(self);
1952 oldsrc = up->bond.src;
1953 if (tp == kDihedralParType || tp == kImproperParType) {
1955 val = rb_Integer(val);
1958 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1959 up->torsion.mult = i;
1960 } else rb_raise(rb_eMolbyError, "invalid member mult");
1961 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1965 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1967 Int tp, i, n, oldsrc;
1968 VALUE *valp, oldval;
1969 up = s_UnionParFromValue(self, &tp, 1);
1970 oldval = s_ParameterRef_GetPeriod(self);
1971 oldsrc = up->bond.src;
1972 if (tp == kDihedralParType || tp == kImproperParType) {
1973 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1974 up->torsion.mult = 1;
1975 val = rb_Integer(val);
1976 up->torsion.period[0] = NUM2INT(val);
1978 n = up->torsion.mult;
1981 val = rb_ary_to_ary(val);
1982 if (RARRAY_LEN(val) != n)
1983 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1984 valp = RARRAY_PTR(val);
1985 for (i = 0; i < n; i++) {
1986 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1989 } else rb_raise(rb_eMolbyError, "invalid member period");
1990 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1994 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1996 Int tp, i, n, oldsrc;
1997 VALUE *valp, oldval;
1998 up = s_UnionParFromValue(self, &tp, 1);
1999 oldval = s_ParameterRef_GetPhi0(self);
2000 oldsrc = up->bond.src;
2001 if (tp == kDihedralParType || tp == kImproperParType) {
2002 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2003 up->torsion.mult = 1;
2004 val = rb_Float(val);
2005 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2007 n = up->torsion.mult;
2010 val = rb_ary_to_ary(val);
2011 if (RARRAY_LEN(val) != n)
2012 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2013 valp = RARRAY_PTR(val);
2014 for (i = 0; i < n; i++)
2015 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2017 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2018 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2023 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2028 up = s_UnionParFromValue(self, &tp, 1);
2029 oldval = s_ParameterRef_GetA(self);
2030 oldsrc = up->bond.src;
2031 val = rb_Float(val);
2033 if (tp == kVdwParType)
2035 else if (tp == kVdwPairParType)
2037 else rb_raise(rb_eMolbyError, "invalid member A");
2038 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2042 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2047 up = s_UnionParFromValue(self, &tp, 1);
2048 oldval = s_ParameterRef_GetB(self);
2049 oldsrc = up->bond.src;
2050 val = rb_Float(val);
2052 if (tp == kVdwParType)
2054 else if (tp == kVdwPairParType)
2056 else rb_raise(rb_eMolbyError, "invalid member B");
2057 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2062 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2067 up = s_UnionParFromValue(self, &tp, 1);
2068 oldval = s_ParameterRef_GetReq(self);
2069 oldsrc = up->bond.src;
2070 val = rb_Float(val);
2072 if (tp == kVdwParType) {
2074 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2075 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2076 } else if (tp == kVdwPairParType) {
2078 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2079 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2080 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2081 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2085 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2090 up = s_UnionParFromValue(self, &tp, 1);
2091 oldval = s_ParameterRef_GetEps(self);
2092 oldsrc = up->bond.src;
2093 val = rb_Float(val);
2094 e = NUM2DBL(val) * KCAL2INTERNAL;
2095 if (tp == kVdwParType) {
2097 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2098 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2099 } else if (tp == kVdwPairParType) {
2101 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2102 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2103 } else rb_raise(rb_eMolbyError, "invalid member eps");
2104 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2109 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2114 up = s_UnionParFromValue(self, &tp, 1);
2115 oldval = s_ParameterRef_GetA14(self);
2116 oldsrc = up->bond.src;
2117 val = rb_Float(val);
2119 if (tp == kVdwParType)
2121 else if (tp == kVdwPairParType)
2123 else rb_raise(rb_eMolbyError, "invalid member A14");
2124 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2128 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2133 up = s_UnionParFromValue(self, &tp, 1);
2134 oldval = s_ParameterRef_GetB14(self);
2135 oldsrc = up->bond.src;
2136 val = rb_Float(val);
2138 if (tp == kVdwParType)
2140 else if (tp == kVdwPairParType)
2142 else rb_raise(rb_eMolbyError, "invalid member B14");
2143 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2148 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2153 up = s_UnionParFromValue(self, &tp, 1);
2154 oldval = s_ParameterRef_GetReq14(self);
2155 oldsrc = up->bond.src;
2156 val = rb_Float(val);
2158 if (tp == kVdwParType) {
2160 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2161 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2162 } else if (tp == kVdwPairParType) {
2163 up->vdwp.r_eq14 = r;
2164 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2165 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2166 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2167 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2171 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2176 up = s_UnionParFromValue(self, &tp, 1);
2177 oldval = s_ParameterRef_GetEps14(self);
2178 oldsrc = up->bond.src;
2179 val = rb_Float(val);
2180 e = NUM2DBL(val) * KCAL2INTERNAL;
2181 if (tp == kVdwParType) {
2183 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2184 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2185 } else if (tp == kVdwPairParType) {
2187 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2188 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2189 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2190 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2194 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2198 oldval = s_ParameterRef_GetCutoff(self);
2199 oldsrc = up->bond.src;
2200 up = s_UnionParFromValue(self, &tp, 1);
2201 val = rb_Float(val);
2202 if (tp == kVdwCutoffParType) {
2203 up->vdwcutoff.cutoff = NUM2DBL(val);
2204 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2205 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2209 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2213 up = s_UnionParFromValue(self, &tp, 1);
2214 oldval = s_ParameterRef_GetRadius(self);
2215 oldsrc = up->bond.src;
2216 val = rb_Float(val);
2217 if (tp == kElementParType) {
2218 up->atom.radius = NUM2DBL(val);
2219 } else rb_raise(rb_eMolbyError, "invalid member radius");
2220 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2224 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2228 up = s_UnionParFromValue(self, &tp, 1);
2229 oldval = s_ParameterRef_GetVdwRadius(self);
2230 oldsrc = up->bond.src;
2231 val = rb_Float(val);
2232 if (tp == kElementParType) {
2233 up->atom.vdw_radius = NUM2DBL(val);
2234 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2235 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2239 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2242 VALUE *valp, oldval;
2243 up = s_UnionParFromValue(self, &tp, 1);
2244 oldval = s_ParameterRef_GetColor(self);
2245 oldsrc = up->bond.src;
2246 val = rb_ary_to_ary(val);
2247 if (RARRAY_LEN(val) != 3)
2248 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2249 valp = RARRAY_PTR(val);
2250 if (tp == kElementParType) {
2251 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2252 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2253 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2254 } else rb_raise(rb_eMolbyError, "invalid member color");
2255 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2259 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2263 up = s_UnionParFromValue(self, &tp, 1);
2264 oldval = s_ParameterRef_GetAtomicNumber(self);
2265 oldsrc = up->bond.src;
2266 val = rb_Integer(val);
2267 if (tp == kElementParType)
2268 up->atom.number = NUM2INT(val);
2269 else if (tp == kVdwParType) {
2270 up->vdw.atomicNumber = NUM2INT(val);
2271 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2272 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2273 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2277 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2281 up = s_UnionParFromValue(self, &tp, 1);
2282 oldval = s_ParameterRef_GetName(self);
2283 oldsrc = up->bond.src;
2284 if (tp == kElementParType) {
2285 strncpy(up->atom.name, StringValuePtr(val), 4);
2286 } else rb_raise(rb_eMolbyError, "invalid member name");
2287 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2291 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2295 val = rb_Float(val);
2296 oldval = s_ParameterRef_GetWeight(self);
2297 up = s_UnionParFromValue(self, &tp, 1);
2298 oldsrc = up->bond.src;
2299 if (tp == kElementParType)
2300 up->atom.weight = NUM2DBL(val);
2301 else if (tp == kVdwParType)
2302 up->vdw.weight = NUM2DBL(val);
2303 else rb_raise(rb_eMolbyError, "invalid member weight");
2304 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2308 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2312 up = s_UnionParFromValue(self, &tp, 1);
2313 oldval = s_ParameterRef_GetFullName(self);
2314 oldsrc = up->bond.src;
2315 if (tp == kElementParType) {
2316 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2317 up->atom.fullname[15] = 0;
2318 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2319 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2323 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2325 Int tp, com, oldsrc;
2327 up = s_UnionParFromValue(self, &tp, 1);
2328 oldval = s_ParameterRef_GetComment(self);
2329 oldsrc = up->bond.src;
2333 com = ParameterCommentIndex(StringValuePtr(val));
2336 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2340 /* Only false (undefined) and nil (local) can be set */
2341 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2345 up = s_UnionParFromValue(self, &tp, 1);
2346 if (val != Qfalse && val != Qnil)
2347 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2348 oldval = s_ParameterRef_GetSource(self);
2349 oldsrc = up->bond.src;
2350 if (oldsrc != 0 && oldsrc != -1)
2351 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2352 up->bond.src = (val == Qfalse ? -1 : 0);
2353 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2357 static struct s_ParameterAttrDef {
2359 VALUE *symref; /* Address of s_IndexSymbol etc. */
2360 ID id; /* Will be set within InitMolby() */
2361 VALUE (*getter)(VALUE);
2362 VALUE (*setter)(VALUE, VALUE);
2363 } s_ParameterAttrDefTable[] = {
2364 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2365 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2366 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2367 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2368 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2369 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2370 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2371 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2372 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2373 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2374 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2375 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2376 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2377 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2378 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2379 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2380 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2381 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2382 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2383 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2384 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2385 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2386 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2387 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2388 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2389 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2390 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2391 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2392 {NULL} /* Sentinel */
2396 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2400 if (TYPE(key) != T_SYMBOL) {
2401 kid = rb_intern(StringValuePtr(key));
2403 } else kid = SYM2ID(key);
2404 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2405 if (s_ParameterAttrDefTable[i].id == kid) {
2406 if (value == Qundef)
2407 return (*(s_ParameterAttrDefTable[i].getter))(self);
2408 else if (s_ParameterAttrDefTable[i].setter == NULL)
2409 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2411 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2414 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2415 return Qnil; /* not reached */
2419 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2421 return s_ParameterRef_SetAttr(self, key, Qundef);
2426 * keys(idx) -> array of valid parameter attributes
2428 * Returns an array of valid parameter attributes (as Symbols).
2431 s_ParameterRef_Keys(VALUE self)
2434 Data_Get_Struct(self, ParameterRef, pref);
2435 switch (pref->parType) {
2437 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2439 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2440 case kDihedralParType:
2441 case kImproperParType:
2442 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2444 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);
2445 case kVdwPairParType:
2446 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2447 case kVdwCutoffParType:
2448 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2449 case kElementParType:
2450 return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_VdwRadiusSym, s_CommentSym, s_SourceSym);
2452 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2454 return Qnil; /* Not reached */
2459 * to_hash(idx) -> Hash
2461 * Returns a hash containing valid parameter names and values
2464 s_ParameterRef_ToHash(VALUE self)
2466 VALUE keys = s_ParameterRef_Keys(self);
2471 retval = rb_hash_new();
2472 for (i = 0; i < RARRAY_LEN(keys); i++) {
2473 VALUE key = RARRAY_PTR(keys)[i];
2474 VALUE val = s_ParameterRef_GetAttr(self, key);
2475 rb_hash_aset(retval, key, val);
2482 * parameter.to_s(idx) -> String
2484 * Returns a string representation of the given parameter
2487 s_ParameterRef_ToString(VALUE self)
2490 char buf[1024], types[4][8];
2491 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2494 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);
2497 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);
2499 case kDihedralParType:
2500 case kImproperParType:
2501 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]));
2503 for (i = 0; i < up->torsion.mult; i++) {
2504 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);
2509 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);
2511 case kVdwPairParType:
2512 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);
2514 case kVdwCutoffParType:
2515 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);
2517 case kElementParType:
2518 snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s %6.3f", up->atom.name, up->atom.number, up->atom.radius, up->atom.red / 65535.0, up->atom.green / 65535.0, up->atom.blue / 65535.0, up->atom.weight, up->atom.fullname, up->atom.vdw_radius);
2521 return rb_str_new2(buf);
2526 * self == parameterRef -> boolean
2528 * True if the parameters point to the same parameter record.
2531 s_ParameterRef_Equal(VALUE self, VALUE val)
2534 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2535 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2536 } else return Qfalse;
2539 #pragma mark ====== Parameter Class ======
2541 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2542 * is NULL, then the global parameters are looked for. */
2544 /* Rebuild the MD parameter record if necessary: may throw an exception */
2545 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2547 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2550 Data_Get_Struct(val, Molecule, mol);
2552 rb_raise(rb_eMolbyError, "the molecule is empty");
2553 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2554 /* Do self.md_arena.prepare */
2555 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2557 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2562 s_NewParameterValueFromValue(VALUE val)
2565 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2566 Data_Get_Struct(val, Molecule, mol);
2567 s_RebuildMDParameterIfNecessary(val, Qtrue);
2568 MoleculeRetain(mol);
2569 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2572 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2577 s_MoleculeFromParameterValue(VALUE val)
2580 Data_Get_Struct(val, Molecule, mol);
2585 s_ParameterFromParameterValue(VALUE val)
2588 Data_Get_Struct(val, Molecule, mol);
2591 return gBuiltinParameters;
2594 /* Forward declarations */
2595 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2596 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2599 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2601 if (val == rb_cParameter) {
2602 return NULL; /* Parameter class method: builtin parameters */
2603 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2604 return s_MoleculeFromParameterValue(val);
2605 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2606 return s_MoleculeFromParEnumerableValue(val);
2612 * builtin -> Parameter
2614 * Returns a parameter value that points to the global (builtin) parameters.
2615 * Equivalent to Parameter::Builtin (constant).
2618 s_Parameter_Builtin(VALUE self)
2620 static ID s_builtin_id = 0;
2621 if (s_builtin_id == 0)
2622 s_builtin_id = rb_intern("Builtin");
2623 return rb_const_get(rb_cParameter, s_builtin_id);
2628 * bond(idx) -> ParameterRef
2630 * The index-th bond parameter record is returned.
2633 s_Parameter_Bond(VALUE self, VALUE ival)
2637 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2638 idx = NUM2INT(rb_Integer(ival));
2640 n = gBuiltinParameters->nbondPars;
2641 else if (mol->par != NULL)
2642 n = mol->par->nbondPars;
2644 if (idx < -n || idx >= n)
2645 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2648 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2653 * angle(idx) -> ParameterRef
2655 * The index-th angle parameter record is returned.
2658 s_Parameter_Angle(VALUE self, VALUE ival)
2662 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2663 idx = NUM2INT(rb_Integer(ival));
2665 n = gBuiltinParameters->nanglePars;
2666 else if (mol->par != NULL)
2667 n = mol->par->nanglePars;
2669 if (idx < -n || idx >= n)
2670 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2673 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2678 * dihedral(idx) -> ParameterRef
2680 * The index-th dihedral parameter record is returned.
2683 s_Parameter_Dihedral(VALUE self, VALUE ival)
2687 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2688 idx = NUM2INT(rb_Integer(ival));
2690 n = gBuiltinParameters->ndihedralPars;
2691 else if (mol->par != NULL)
2692 n = mol->par->ndihedralPars;
2694 if (idx < -n || idx >= n)
2695 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2698 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2703 * improper(idx) -> ParameterRef
2705 * The index-th improper parameter record is returned.
2708 s_Parameter_Improper(VALUE self, VALUE ival)
2712 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2713 idx = NUM2INT(rb_Integer(ival));
2715 n = gBuiltinParameters->nimproperPars;
2716 else if (mol->par != NULL)
2717 n = mol->par->nimproperPars;
2719 if (idx < -n || idx >= n)
2720 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2723 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2728 * vdw(idx) -> ParameterRef
2730 * The index-th vdw parameter record is returned.
2733 s_Parameter_Vdw(VALUE self, VALUE ival)
2737 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2738 idx = NUM2INT(rb_Integer(ival));
2740 n = gBuiltinParameters->nvdwPars;
2741 else if (mol->par != NULL)
2742 n = mol->par->nvdwPars;
2744 if (idx < -n || idx >= n)
2745 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2748 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2753 * vdw_pair(idx) -> ParameterRef
2755 * The index-th vdw pair parameter record is returned.
2758 s_Parameter_VdwPair(VALUE self, VALUE ival)
2762 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2763 idx = NUM2INT(rb_Integer(ival));
2765 n = gBuiltinParameters->nvdwpPars;
2766 else if (mol->par != NULL)
2767 n = mol->par->nvdwpPars;
2769 if (idx < -n || idx >= n)
2770 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2773 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2778 * vdw_cutoff(idx) -> ParameterRef
2780 * The index-th vdw cutoff parameter record is returned.
2783 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2787 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2788 idx = NUM2INT(rb_Integer(ival));
2790 n = gBuiltinParameters->nvdwCutoffPars;
2791 else if (mol->par != NULL)
2792 n = mol->par->nvdwCutoffPars;
2794 if (idx < -n || idx >= n)
2795 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2798 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2803 * element(idx) -> ParameterRef
2804 * element(t1) -> ParameterRef
2806 * In the first form, the index-th element parameter record is returned. In the second
2807 * form, the element parameter for t1 is looked up (the last index first). t1
2808 * is the element name string (up to 4 characters).
2809 * Unlike other Parameter methods, this is used only for the global parameter.
2812 s_Parameter_Element(VALUE self, VALUE ival)
2815 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2816 int n = gCountElementParameters;
2817 idx1 = NUM2INT(rb_Integer(ival));
2818 if (idx1 < -n || idx1 >= n)
2819 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2822 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2827 strncpy(name, StringValuePtr(ival), 4);
2829 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2830 if (strncmp(ep->name, name, 4) == 0)
2831 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2841 * Returns the number of bond parameters.
2844 s_Parameter_Nbonds(VALUE self)
2847 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2849 n = gBuiltinParameters->nbondPars;
2850 else if (mol->par != NULL)
2851 n = mol->par->nbondPars;
2858 * nangles -> Integer
2860 * Returns the number of angle parameters.
2863 s_Parameter_Nangles(VALUE self)
2866 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2868 n = gBuiltinParameters->nanglePars;
2869 else if (mol->par != NULL)
2870 n = mol->par->nanglePars;
2877 * ndihedrals -> Integer
2879 * Returns the number of dihedral parameters.
2882 s_Parameter_Ndihedrals(VALUE self)
2885 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2887 n = gBuiltinParameters->ndihedralPars;
2888 else if (mol->par != NULL)
2889 n = mol->par->ndihedralPars;
2896 * nimpropers -> Integer
2898 * Returns the number of improper parameters.
2901 s_Parameter_Nimpropers(VALUE self)
2904 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2906 n = gBuiltinParameters->nimproperPars;
2907 else if (mol->par != NULL)
2908 n = mol->par->nimproperPars;
2917 * Returns the number of vdw parameters.
2920 s_Parameter_Nvdws(VALUE self)
2923 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2925 n = gBuiltinParameters->nvdwPars;
2926 else if (mol->par != NULL)
2927 n = mol->par->nvdwPars;
2934 * nvdw_pairs -> Integer
2936 * Returns the number of vdw pair parameters.
2939 s_Parameter_NvdwPairs(VALUE self)
2942 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2944 n = gBuiltinParameters->nvdwpPars;
2945 else if (mol->par != NULL)
2946 n = mol->par->nvdwpPars;
2953 * nvdw_cutoffs -> Integer
2955 * Returns the number of vdw cutoff parameters.
2958 s_Parameter_NvdwCutoffs(VALUE self)
2961 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2963 n = gBuiltinParameters->nvdwCutoffPars;
2964 else if (mol->par != NULL)
2965 n = mol->par->nvdwCutoffPars;
2972 * nelements -> Integer
2974 * Returns the number of element parameters.
2977 s_Parameter_Nelements(VALUE self)
2979 return INT2NUM(gCountElementParameters);
2984 * bonds -> ParEnumerable
2986 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2987 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2988 * useful when all accessible parameters should be examined by use of 'each' method.
2991 s_Parameter_Bonds(VALUE self)
2993 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2994 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2999 * angles -> ParEnumerable
3001 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3002 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3003 * useful when all accessible parameters should be examined by use of 'each' method.
3006 s_Parameter_Angles(VALUE self)
3008 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3009 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3014 * dihedrals -> ParEnumerable
3016 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3017 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3018 * useful when all accessible parameters should be examined by use of 'each' method.
3021 s_Parameter_Dihedrals(VALUE self)
3023 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3024 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3029 * impropers -> ParEnumerable
3031 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3032 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3033 * useful when all accessible parameters should be examined by use of 'each' method.
3036 s_Parameter_Impropers(VALUE self)
3038 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3039 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3044 * vdws -> ParEnumerable
3046 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3047 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3048 * useful when all accessible parameters should be examined by use of 'each' method.
3051 s_Parameter_Vdws(VALUE self)
3053 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3054 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3059 * vdw_pairs -> ParEnumerable
3061 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3062 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3063 * useful when all accessible parameters should be examined by use of 'each' method.
3066 s_Parameter_VdwPairs(VALUE self)
3068 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3069 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3074 * vdw_cutoffs -> ParEnumerable
3076 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3077 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3078 * useful when all accessible parameters should be examined by use of 'each' method.
3081 s_Parameter_VdwCutoffs(VALUE self)
3083 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3084 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3089 * elements -> ParEnumerable
3091 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3092 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3093 * useful when all accessible parameters should be examined by use of 'each' method.
3096 s_Parameter_Elements(VALUE self)
3098 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3099 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3103 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3105 VALUE atval, optval;
3108 int i, n, idx, flags, is_global;
3110 rb_scan_args(argc, argv, "1*", &atval, &optval);
3112 /* Get the atom types */
3114 case kBondParType: n = 2; break;
3115 case kAngleParType: n = 3; break;
3116 case kDihedralParType: n = 4; break;
3117 case kImproperParType: n = 4; break;
3118 case kVdwParType: n = 1; break;
3119 case kVdwPairParType: n = 2; break;
3120 default: return Qnil;
3122 s_ScanAtomTypes(atval, n, t);
3123 for (i = 0; i < n; i++) {
3124 if (t[i] < kAtomTypeMinimum) {
3125 /* Explicit atom index */
3127 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3128 if (t[i] >= mol->natoms)
3129 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3131 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3135 /* Analyze options */
3137 n = RARRAY_LEN(optval);
3138 for (i = 0; i < n; i++) {
3139 VALUE oval = RARRAY_PTR(optval)[i];
3140 if (oval == ID2SYM(rb_intern("global")))
3141 flags |= kParameterLookupGlobal;
3142 else if (oval == ID2SYM(rb_intern("local")))
3143 flags |= kParameterLookupLocal;
3144 else if (oval == ID2SYM(rb_intern("missing")))
3145 flags |= kParameterLookupMissing;
3146 else if (oval == ID2SYM(rb_intern("nowildcard")))
3147 flags |= kParameterLookupNoWildcard;
3148 else if (oval == ID2SYM(rb_intern("nobasetype")))
3149 flags |= kParameterLookupNoBaseAtomType;
3150 else if (oval == ID2SYM(rb_intern("create")))
3154 flags = kParameterLookupGlobal | kParameterLookupLocal;
3159 case kBondParType: {
3162 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3164 idx = bp - mol->par->bondPars;
3168 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3170 idx = bp - gBuiltinParameters->bondPars;
3175 case kAngleParType: {
3178 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3180 idx = ap - mol->par->anglePars;
3184 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3186 idx = ap - gBuiltinParameters->anglePars;
3191 case kDihedralParType: {
3194 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3196 idx = tp - mol->par->dihedralPars;
3200 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3202 idx = tp - gBuiltinParameters->dihedralPars;
3207 case kImproperParType: {
3210 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3212 idx = tp - mol->par->improperPars;
3216 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3218 idx = tp - gBuiltinParameters->improperPars;
3226 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3228 idx = vp - mol->par->vdwPars;
3232 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3234 idx = vp - gBuiltinParameters->vdwPars;
3239 case kVdwPairParType: {
3242 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3244 idx = vp - mol->par->vdwpPars;
3248 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3250 idx = vp - gBuiltinParameters->vdwpPars;
3259 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3262 /* Insert a new parameter record */
3264 Int count = ParameterGetCountForType(mol->par, parType);
3265 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3266 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3267 IntGroupRelease(ig);
3270 /* Set atom types */
3271 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3276 up->bond.type1 = t[0];
3277 up->bond.type2 = t[1];
3280 up->angle.type1 = t[0];
3281 up->angle.type2 = t[1];
3282 up->angle.type3 = t[2];
3284 case kDihedralParType:
3285 case kImproperParType:
3286 up->torsion.type1 = t[0];
3287 up->torsion.type2 = t[1];
3288 up->torsion.type3 = t[2];
3289 up->torsion.type4 = t[3];
3292 up->vdw.type1 = t[0];
3294 case kVdwPairParType:
3295 up->vdwp.type1 = t[0];
3296 up->vdwp.type2 = t[1];
3303 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3308 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3309 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3311 * Find the parameter record that matches the given atom types. The atom types are given
3312 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3313 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3314 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3315 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3318 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3321 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3323 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3324 parType = s_ParTypeFromValue(argv[0]);
3325 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3330 * self == parameter -> boolean
3332 * True if the parameters point to the same parameter table.
3335 s_Parameter_Equal(VALUE self, VALUE val)
3337 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3338 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3339 } else return Qfalse;
3342 #pragma mark ====== ParEnumerable Class ======
3344 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3345 and the parameter type. If the Molecule is NULL, then it refers to the
3346 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3347 the global parameters are always accessible. */
3349 typedef struct ParEnumerable {
3351 Int parType; /* Same as parType in ParameterRef */
3354 static ParEnumerable *
3355 s_ParEnumerableNew(Molecule *mol, Int parType)
3357 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3361 MoleculeRetain(mol);
3362 pen->parType = parType;
3368 s_ParEnumerableRelease(ParEnumerable *pen)
3371 if (pen->mol != NULL)
3372 MoleculeRelease(pen->mol);
3378 s_MoleculeFromParEnumerableValue(VALUE val)
3381 Data_Get_Struct(val, ParEnumerable, pen);
3386 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3388 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3390 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3391 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3396 * par_type -> String
3398 * Get the parameter type, like "bond", "angle", etc.
3401 s_ParEnumerable_ParType(VALUE self) {
3404 Data_Get_Struct(self, ParEnumerable, pen);
3406 if (tp == kElementParType)
3407 return rb_str_new2("element");
3408 tp -= kFirstParType;
3409 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3410 return rb_str_new2(s_ParameterTypeNames[tp]);
3411 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3416 * self[idx] -> ParameterRef
3418 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3419 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3420 * parent Parameter object of self.
3422 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3423 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3426 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3429 Data_Get_Struct(self, ParEnumerable, pen);
3430 switch (pen->parType) {
3431 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3432 case kBondParType: return s_Parameter_Bond(self, ival);
3433 case kAngleParType: return s_Parameter_Angle(self, ival);
3434 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3435 case kImproperParType: return s_Parameter_Improper(self, ival);
3436 case kVdwParType: return s_Parameter_Vdw(self, ival);
3437 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3438 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3439 case kElementParType: return s_Parameter_Element(self, ival);
3441 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3443 return Qnil; /* Not reached */
3450 * Returns the number of parameters included in this enumerable.
3453 s_ParEnumerable_Length(VALUE self)
3456 Data_Get_Struct(self, ParEnumerable, pen);
3457 switch (pen->parType) {
3458 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3459 case kBondParType: return s_Parameter_Nbonds(self);
3460 case kAngleParType: return s_Parameter_Nangles(self);
3461 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3462 case kImproperParType: return s_Parameter_Nimpropers(self);
3463 case kVdwParType: return s_Parameter_Nvdws(self);
3464 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3465 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3466 case kElementParType: return s_Parameter_Nelements(self);
3468 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3470 return Qnil; /* Not reached */
3477 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3480 s_ParEnumerable_Each(VALUE self)
3486 Data_Get_Struct(self, ParEnumerable, pen);
3487 if (pen->parType == kElementParType)
3488 n = gCountElementParameters;
3490 switch (pen->parType) {
3491 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3492 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3493 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3494 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3495 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3496 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3497 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3499 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3501 if (pen->mol == NULL)
3502 n = *((Int *)((char *)gBuiltinParameters + ofs));
3503 else if (pen->mol->par != NULL)
3504 n = *((Int *)((char *)(pen->mol->par) + ofs));
3507 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3508 Data_Get_Struct(aval, ParameterRef, pref);
3509 for (i = 0; i < n; i++) {
3518 * reverse_each {|pref| ...}
3520 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3523 s_ParEnumerable_ReverseEach(VALUE self)
3529 Data_Get_Struct(self, ParEnumerable, pen);
3530 if (pen->parType == kElementParType)
3531 n = gCountElementParameters;
3533 switch (pen->parType) {
3534 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3535 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3536 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3537 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3538 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3539 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3540 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3542 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3544 if (pen->mol == NULL)
3545 n = *((Int *)((char *)gBuiltinParameters + ofs));
3546 else if (pen->mol->par != NULL)
3547 n = *((Int *)((char *)(pen->mol->par) + ofs));
3550 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3551 Data_Get_Struct(aval, ParameterRef, pref);
3552 for (i = n - 1; i >= 0; i--) {
3561 * insert(idx = nil, pref = nil) -> ParameterRef
3563 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3564 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3565 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3566 * parameter is left undefined.
3567 * Throws an exception if ParEnumerable points to the global parameter.
3570 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3578 Data_Get_Struct(self, ParEnumerable, pen);
3579 if (pen->mol == NULL)
3580 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3581 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3582 rb_scan_args(argc, argv, "02", &ival, &pval);
3584 i = NUM2INT(rb_Integer(ival));
3586 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3591 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3592 if (up == NULL || type != pen->parType)
3593 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3594 ParameterCopyOneWithType(&u, up, pen->parType);
3597 memset(&u, 0, sizeof(u));
3600 ig = IntGroupNewWithPoints(n, 1, -1);
3601 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3603 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3604 MolActionCallback_registerUndo(pen->mol, act);
3605 MolActionRelease(act);
3607 IntGroupRelease(ig);
3608 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3616 * Delete the parameter(s) specified by the argument.
3619 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3624 Data_Get_Struct(self, ParEnumerable, pen);
3625 if (pen->mol == NULL)
3626 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3627 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3628 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3629 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3632 ig = IntGroupFromValue(ival);
3633 if ((i = IntGroupGetCount(ig)) == 0) {
3634 IntGroupRelease(ig);
3638 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3639 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3642 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3643 IntGroupRelease(ig);
3649 * lookup(atom_types, options, ...) -> ParameterRef
3650 * lookup(atom_type_string, options, ...) -> ParameterRef
3652 * Find the parameter record that matches the given atom types. The arguments are
3653 * the same as Parameter#lookup, except for the parameter type which is implicitly
3657 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3660 Data_Get_Struct(self, ParEnumerable, pen);
3661 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3666 * self == parEnumerable -> boolean
3668 * True if the arguments point to the same parameter table and type.
3671 s_ParEnumerable_Equal(VALUE self, VALUE val)
3673 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3674 ParEnumerable *pen1, *pen2;
3675 Data_Get_Struct(self, ParEnumerable, pen1);
3676 Data_Get_Struct(val, ParEnumerable, pen2);
3677 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3678 } else return Qfalse;
3681 #pragma mark ====== AtomRef Class ======
3683 /* Forward declaration for register undo */
3684 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3686 /* Ruby string "set_atom_attr" */
3687 static VALUE s_SetAtomAttrString;
3690 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3694 Data_Get_Struct(self, AtomRef, aref);
3695 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3696 if (idx < 0 || idx >= aref->mol->natoms)
3697 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3699 *app = aref->mol->atoms + idx;
3706 s_AtomFromValue(VALUE self)
3709 s_AtomIndexFromValue(self, &ap, NULL);
3714 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3717 s_AtomIndexFromValue(self, &ap, mpp);
3722 s_NotifyModificationForAtomRef(VALUE self)
3725 Data_Get_Struct(self, AtomRef, aref);
3726 MoleculeIncrementModifyCount(aref->mol);
3730 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3733 Data_Get_Struct(self, AtomRef, aref);
3734 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3737 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3738 MolActionCallback_registerUndo(aref->mol, act);
3739 MoleculeCallback_notifyModification(aref->mol, 0);
3740 /* Request MD rebuilt if necessary */
3741 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3742 aref->mol->needsMDRebuild = 1;
3747 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3750 aref = AtomRefNew(mol, idx);
3751 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3755 s_AtomRef_GetMolecule(VALUE self)
3758 s_AtomIndexFromValue(self, NULL, &mpp);
3759 return ValueFromMolecule(mpp);
3762 static VALUE s_AtomRef_GetIndex(VALUE self) {
3763 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3766 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3767 return INT2NUM(s_AtomFromValue(self)->segSeq);
3770 static VALUE s_AtomRef_GetSegName(VALUE self) {
3771 char *p = s_AtomFromValue(self)->segName;
3772 return rb_str_new(p, strlen_limit(p, 4));
3775 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3776 return INT2NUM(s_AtomFromValue(self)->resSeq);
3779 static VALUE s_AtomRef_GetResName(VALUE self) {
3780 char *p = s_AtomFromValue(self)->resName;
3781 return rb_str_new(p, strlen_limit(p, 4));
3784 static VALUE s_AtomRef_GetName(VALUE self) {
3785 char *p = s_AtomFromValue(self)->aname;
3786 return rb_str_new(p, strlen_limit(p, 4));
3789 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3790 int type = s_AtomFromValue(self)->type;
3791 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3792 return rb_str_new(p, strlen_limit(p, 6));
3795 static VALUE s_AtomRef_GetCharge(VALUE self) {
3796 return rb_float_new(s_AtomFromValue(self)->charge);
3799 static VALUE s_AtomRef_GetWeight(VALUE self) {
3800 return rb_float_new(s_AtomFromValue(self)->weight);
3803 static VALUE s_AtomRef_GetElement(VALUE self) {
3804 char *p = s_AtomFromValue(self)->element;
3805 return rb_str_new(p, strlen_limit(p, 4));
3808 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3809 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3812 static VALUE s_AtomRef_GetConnects(VALUE self) {
3815 Atom *ap = s_AtomFromValue(self);
3816 retval = rb_ary_new();
3817 cp = AtomConnectData(&ap->connect);
3818 for (i = 0; i < ap->connect.count; i++)
3819 rb_ary_push(retval, INT2NUM(cp[i]));
3823 static VALUE s_AtomRef_GetR(VALUE self) {
3824 return ValueFromVector(&(s_AtomFromValue(self)->r));
3827 static VALUE s_AtomRef_GetX(VALUE self) {
3828 return rb_float_new(s_AtomFromValue(self)->r.x);
3831 static VALUE s_AtomRef_GetY(VALUE self) {
3832 return rb_float_new(s_AtomFromValue(self)->r.y);
3835 static VALUE s_AtomRef_GetZ(VALUE self) {
3836 return rb_float_new(s_AtomFromValue(self)->r.z);
3839 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3843 s_AtomIndexFromValue(self, &ap, &mp);
3845 if (mp->cell != NULL)
3846 TransformVec(&r1, mp->cell->rtr, &r1);
3850 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3851 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3852 return ValueFromVector(&r1);
3855 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3856 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3859 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3860 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3863 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3864 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3867 static VALUE s_AtomRef_GetSigma(VALUE self) {
3868 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3871 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3872 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3875 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3876 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3879 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3880 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3883 static VALUE s_AtomRef_GetV(VALUE self) {
3884 return ValueFromVector(&(s_AtomFromValue(self)->v));
3887 static VALUE s_AtomRef_GetF(VALUE self) {
3888 Vector v = s_AtomFromValue(self)->f;
3889 VecScaleSelf(v, INTERNAL2KCAL);
3890 return ValueFromVector(&v);
3893 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3894 return rb_float_new(s_AtomFromValue(self)->occupancy);
3897 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3898 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3901 static VALUE s_AtomRef_GetAniso(VALUE self) {
3904 Atom *ap = s_AtomFromValue(self);
3905 if (ap->aniso == NULL)
3907 retval = rb_ary_new();
3908 for (i = 0; i < 6; i++)
3909 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3910 if (ap->aniso->has_bsig) {
3911 rb_ary_push(retval, INT2NUM(0));
3912 for (i = 0; i < 6; i++)
3913 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3918 static VALUE s_AtomRef_GetSymop(VALUE self) {
3920 Atom *ap = s_AtomFromValue(self);
3921 if (!ap->symop.alive)
3923 retval = rb_ary_new();
3924 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3925 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3926 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3927 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3928 rb_ary_push(retval, INT2NUM(ap->symbase));
3932 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3933 return INT2NUM(s_AtomFromValue(self)->intCharge);
3936 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3937 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3940 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3941 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3944 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3948 MDExclusion *exinfo;
3951 idx = s_AtomIndexFromValue(self, &ap, &mol);
3952 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3953 VALUE mval = ValueFromMolecule(mol);
3954 s_RebuildMDParameterIfNecessary(mval, Qnil);
3956 if (mol->arena->exinfo == NULL)
3958 exinfo = mol->arena->exinfo + idx;
3959 exlist = mol->arena->exlist;
3960 retval = rb_ary_new();
3961 aval = rb_ary_new();
3962 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3963 rb_ary_push(aval, INT2NUM(exlist[i]));
3964 rb_ary_push(retval, aval);
3965 aval = rb_ary_new();
3966 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3967 rb_ary_push(aval, INT2NUM(exlist[i]));
3968 rb_ary_push(retval, aval);
3969 aval = rb_ary_new();
3970 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3971 rb_ary_push(aval, INT2NUM(exlist[i]));
3972 rb_ary_push(retval, aval);
3976 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3977 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3980 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3981 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3984 static VALUE s_AtomRef_GetHidden(VALUE self) {
3985 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3988 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3991 Atom *ap = s_AtomFromValue(self);
3992 if (ap->anchor == NULL)
3994 count = ap->anchor->connect.count;
3995 retval = rb_ary_new2(count * 2);
3996 cp = AtomConnectData(&ap->anchor->connect);
3997 for (i = 0; i < count; i++) {
3998 rb_ary_store(retval, i, INT2NUM(cp[i]));
3999 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4004 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4005 char *p = s_AtomFromValue(self)->uff_type;
4006 return rb_str_new(p, strlen_limit(p, 5));
4009 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4010 rb_raise(rb_eMolbyError, "index cannot be directly set");
4014 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4015 VALUE oval = s_AtomRef_GetSegSeq(self);
4016 val = rb_Integer(val);
4017 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4018 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4022 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4023 char *p = StringValuePtr(val);
4024 VALUE oval = s_AtomRef_GetSegName(self);
4025 strncpy(s_AtomFromValue(self)->segName, p, 4);
4026 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4030 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4031 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4032 return val; /* Not reached */
4035 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4036 Atom *ap = s_AtomFromValue(self);
4037 char *p = StringValuePtr(val);
4038 VALUE oval = s_AtomRef_GetName(self);
4039 if (ap->anchor != NULL && p[0] == '_')
4040 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4041 strncpy(ap->aname, p, 4);
4042 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4046 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4048 char *p = StringValuePtr(val);
4049 VALUE oval = s_AtomRef_GetAtomType(self);
4050 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4051 if (type != 0 && type < kAtomTypeMinimum)
4052 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4053 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4054 mp->needsMDRebuild = 1;
4055 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4059 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4061 VALUE oval = s_AtomRef_GetCharge(self);
4062 val = rb_Float(val);
4063 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4064 mp->needsMDRebuild = 1;
4065 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4069 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4071 VALUE oval = s_AtomRef_GetWeight(self);
4072 val = rb_Float(val);
4073 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4074 mp->needsMDRebuild = 1;
4075 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4079 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4082 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4083 char *p = StringValuePtr(val);
4084 VALUE oval = s_AtomRef_GetElement(self);
4085 ap->atomicNumber = ElementToInt(p);
4086 ElementToString(ap->atomicNumber, ap->element);
4087 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4089 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4090 mp->needsMDRebuild = 1;
4094 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4097 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4098 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4099 val = rb_Integer(val);
4100 ap->atomicNumber = NUM2INT(val);
4101 ElementToString(ap->atomicNumber, ap->element);
4102 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4104 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4105 mp->needsMDRebuild = 1;
4109 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4110 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4111 return val; /* Not reached */
4114 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4117 VALUE oval = s_AtomRef_GetR(self);
4118 VectorFromValue(val, &v);
4119 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4120 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4121 mp->needsMDCopyCoordinates = 1;
4125 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4128 VALUE oval = s_AtomRef_GetX(self);
4129 val = rb_Float(val);
4131 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4132 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4133 mp->needsMDCopyCoordinates = 1;
4137 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4140 VALUE oval = s_AtomRef_GetY(self);
4141 val = rb_Float(val);
4143 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4144 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4145 mp->needsMDCopyCoordinates = 1;
4149 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4152 VALUE oval = s_AtomRef_GetZ(self);
4153 val = rb_Float(val);
4155 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4156 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4157 mp->needsMDCopyCoordinates = 1;
4161 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4165 s_AtomIndexFromValue(self, &ap, &mp);
4167 VectorFromValue(val, &v);
4168 if (mp->cell != NULL)
4169 TransformVec(&v, mp->cell->tr, &v);
4171 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4172 mp->needsMDCopyCoordinates = 1;
4176 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4181 s_AtomIndexFromValue(self, &ap, &mp);
4183 val = rb_Float(val);
4185 if (mp->cell != NULL) {
4186 TransformVec(&v, mp->cell->rtr, &v);
4188 TransformVec(&v, mp->cell->tr, &v);
4191 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4192 mp->needsMDCopyCoordinates = 1;
4196 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4201 s_AtomIndexFromValue(self, &ap, &mp);
4203 val = rb_Float(val);
4205 if (mp->cell != NULL) {
4206 TransformVec(&v, mp->cell->rtr, &v);
4208 TransformVec(&v, mp->cell->tr, &v);
4211 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4212 mp->needsMDCopyCoordinates = 1;
4216 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4221 s_AtomIndexFromValue(self, &ap, &mp);
4223 val = rb_Float(val);
4225 if (mp->cell != NULL) {
4226 TransformVec(&v, mp->cell->rtr, &v);
4228 TransformVec(&v, mp->cell->tr, &v);
4231 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4232 mp->needsMDCopyCoordinates = 1;
4236 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4239 VALUE oval = s_AtomRef_GetSigma(self);
4240 VectorFromValue(val, &v);
4241 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4242 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4243 mp->needsMDCopyCoordinates = 1;
4247 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4250 VALUE oval = s_AtomRef_GetSigmaX(self);
4251 val = rb_Float(val);
4253 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4254 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4255 mp->needsMDCopyCoordinates = 1;
4259 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4262 VALUE oval = s_AtomRef_GetSigmaY(self);
4263 val = rb_Float(val);
4265 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4266 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4267 mp->needsMDCopyCoordinates = 1;
4271 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4274 VALUE oval = s_AtomRef_GetSigmaZ(self);
4275 val = rb_Float(val);
4277 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4278 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4279 mp->needsMDCopyCoordinates = 1;
4283 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4287 VALUE oval = s_AtomRef_GetV(self);
4288 VectorFromValue(val, &v);
4289 s_AtomIndexFromValue(self, &ap, &mp);
4291 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4292 mp->needsMDCopyCoordinates = 1;
4296 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4299 VALUE oval = s_AtomRef_GetF(self);
4300 VectorFromValue(val, &v);
4301 VecScaleSelf(v, KCAL2INTERNAL);
4302 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4303 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4304 mp->needsMDCopyCoordinates = 1;
4308 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4309 VALUE oval = s_AtomRef_GetOccupancy(self);
4311 val = rb_Float(val);
4312 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4313 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4314 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4318 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4319 VALUE oval = s_AtomRef_GetTempFactor(self);
4320 val = rb_Float(val);
4321 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4322 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4326 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4331 VALUE oval = s_AtomRef_GetAniso(self);
4332 Data_Get_Struct(self, AtomRef, aref);
4333 val = rb_funcall(val, rb_intern("to_a"), 0);
4334 n = RARRAY_LEN(val);
4335 valp = RARRAY_PTR(val);
4336 for (i = 0; i < 6; i++) {
4338 f[i] = NUM2DBL(rb_Float(valp[i]));
4342 type = NUM2INT(rb_Integer(valp[6]));
4345 for (i = 0; i < 6; i++)
4346 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4348 for (i = 0; i < 6; i++)
4351 i = s_AtomIndexFromValue(self, NULL, NULL);
4352 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4353 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4357 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4363 VALUE oval = s_AtomRef_GetSymop(self);
4364 i = s_AtomIndexFromValue(self, &ap, &mol);
4366 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4368 val = rb_funcall(val, rb_intern("to_a"), 0);
4369 n = RARRAY_LEN(val);
4370 valp = RARRAY_PTR(val);
4371 for (i = 0; i < 5; i++) {
4373 if (valp[i] == Qnil)
4376 ival[i] = NUM2INT(rb_Integer(valp[i]));
4377 } else ival[i] = -100000;
4380 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4381 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));
4382 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4383 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4384 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4385 return val; /* No need to change */
4386 if (ival[0] != -100000)
4387 ap->symop.sym = ival[0];
4388 if (ival[1] != -100000)
4389 ap->symop.dx = ival[1];
4390 if (ival[2] != -100000)
4391 ap->symop.dy = ival[2];
4392 if (ival[3] != -100000)
4393 ap->symop.dz = ival[3];
4394 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4395 if (ival[4] != -100000)
4396 ap->symbase = ival[4];
4397 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4398 /* The anisotropic parameters should be recalculated */
4399 VALUE oaval = s_AtomRef_GetAniso(self);
4400 MoleculeSetAnisoBySymop(mol, i);
4401 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4403 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4407 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4408 VALUE oval = s_AtomRef_GetIntCharge(self);
4409 val = rb_Integer(val);
4410 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4411 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4415 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4417 VALUE oval = s_AtomRef_GetFixForce(self);
4418 val = rb_Float(val);
4419 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4420 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4421 mp->needsMDRebuild = 1;
4425 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4428 VALUE oval = s_AtomRef_GetFixPos(self);
4429 VectorFromValue(val, &v);
4430 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4431 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4432 mp->needsMDRebuild = 1;
4436 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4437 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4438 return val; /* Not reached */
4441 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4442 VALUE oval = s_AtomRef_GetIntCharge(self);
4443 val = rb_Integer(val);
4444 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4445 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4449 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4450 VALUE oval = s_AtomRef_GetIntCharge(self);
4451 val = rb_Integer(val);
4452 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4453 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4457 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4458 Atom *ap = s_AtomFromValue(self);
4459 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4461 ap->exflags |= kAtomHiddenFlag;
4463 ap->exflags &= ~kAtomHiddenFlag;
4465 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4469 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4470 Int idx, i, j, k, n, *ip;
4477 MolAction **undoActions;
4478 memset(&ac, 0, sizeof(ac));
4479 idx = s_AtomIndexFromValue(self, &ap, &mol);
4480 oval = s_AtomRef_GetAnchorList(self);
4482 val = rb_ary_to_ary(val);
4483 n = RARRAY_LEN(val);
4486 if (ap->anchor != NULL) {
4487 AtomConnectResize(&ap->anchor->connect, 0);
4488 free(ap->anchor->coeffs);
4491 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4496 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4497 if (ap->aname[0] == '_')
4498 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4499 ip = (Int *)malloc(sizeof(Int) * n);
4501 for (i = 0; i < n; i++) {
4502 v = RARRAY_PTR(val)[i];
4503 if (rb_obj_is_kind_of(v, rb_cFloat))
4505 j = NUM2INT(rb_Integer(v));
4506 if (j < 0 || j >= mol->natoms)
4507 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4508 for (k = 0; k < i; k++) {
4510 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4516 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4517 else if (i * 2 != n)
4518 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4519 dp = (Double *)malloc(sizeof(Double) * n / 2);
4520 for (i = 0; i < n / 2; i++) {
4521 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4523 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4529 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4533 rb_raise(rb_eMolbyError, "invalid argument");
4534 if (nUndoActions > 0) {
4535 for (i = 0; i < nUndoActions; i++) {
4536 MolActionCallback_registerUndo(mol, undoActions[i]);
4537 MolActionRelease(undoActions[i]);
4541 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4545 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4546 Atom *ap = s_AtomFromValue(self);
4547 char *p = StringValuePtr(val);
4548 VALUE oval = s_AtomRef_GetUFFType(self);
4549 strncpy(ap->uff_type, p, 5);
4550 ap->uff_type[5] = 0;
4551 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4555 static struct s_AtomAttrDef {
4557 VALUE *symref; /* Address of s_IndexSymbol etc. */
4558 ID id; /* Will be set within InitMolby() */
4559 VALUE (*getter)(VALUE);
4560 VALUE (*setter)(VALUE, VALUE);
4561 } s_AtomAttrDefTable[] = {
4562 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4563 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4564 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4565 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4566 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4567 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4568 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4569 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4570 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4571 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4572 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4573 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4574 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4575 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4576 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4577 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4578 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4579 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4580 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4581 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4582 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4583 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4584 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4585 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4586 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4587 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4588 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4589 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4590 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4591 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4592 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4593 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4594 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4595 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4596 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4597 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4598 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4599 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4600 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4601 {NULL} /* Sentinel */
4605 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4609 if (TYPE(key) != T_SYMBOL) {
4610 kid = rb_intern(StringValuePtr(key));
4612 } else kid = SYM2ID(key);
4613 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4614 if (s_AtomAttrDefTable[i].id == kid) {
4615 if (value == Qundef)
4616 return (*(s_AtomAttrDefTable[i].getter))(self);
4618 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4621 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4622 return Qnil; /* not reached */
4626 s_AtomRef_GetAttr(VALUE self, VALUE key)
4628 return s_AtomRef_SetAttr(self, key, Qundef);
4633 * self == atomRef -> boolean
4635 * True if the two references point to the same atom.
4638 s_AtomRef_Equal(VALUE self, VALUE val)
4640 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4641 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4642 } else return Qfalse;
4645 #pragma mark ====== MolEnumerable Class ======
4647 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4651 * self[idx] -> AtomRef or Array of Integers
4653 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4654 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4655 * value is a String. Otherwise, the return value is an Array of Integers.
4658 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4660 MolEnumerable *mseq;
4663 Data_Get_Struct(self, MolEnumerable, mseq);
4665 if (mseq->kind == kAtomKind) {
4666 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4668 idx1 = NUM2INT(arg1);
4669 switch (mseq->kind) {
4671 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4672 if (idx2 < 0 || idx2 >= mol->nbonds)
4673 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4674 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4677 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4678 if (idx2 < 0 || idx2 >= mol->nangles)
4679 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4680 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4682 case kDihedralKind: {
4683 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4684 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4685 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4686 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]));
4688 case kImproperKind: {
4689 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4690 if (idx2 < 0 || idx2 >= mol->nimpropers)
4691 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4692 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]));
4694 case kResidueKind: {
4696 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4697 if (idx2 < 0 || idx2 >= mol->nresidues)
4698 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4699 p = mol->residues[idx2];
4700 return rb_str_new(p, strlen_limit(p, 4));
4710 * Returns the number of objects included in this enumerable.
4713 s_MolEnumerable_Length(VALUE self)
4715 MolEnumerable *mseq;
4716 Data_Get_Struct(self, MolEnumerable, mseq);
4717 switch (mseq->kind) {
4719 return INT2NUM(mseq->mol->natoms);
4721 return INT2NUM(mseq->mol->nbonds);
4723 return INT2NUM(mseq->mol->nangles);
4725 return INT2NUM(mseq->mol->ndihedrals);
4727 return INT2NUM(mseq->mol->nimpropers);
4729 return INT2NUM(mseq->mol->nresidues);
4738 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4739 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4740 * For the atoms, a same AtomRef object is passed (with different internal information)
4741 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4742 * for each iteration.
4745 s_MolEnumerable_Each(VALUE self)
4747 MolEnumerable *mseq;
4749 int len = NUM2INT(s_MolEnumerable_Length(self));
4750 Data_Get_Struct(self, MolEnumerable, mseq);
4751 if (mseq->kind == kAtomKind) {
4752 /* The same AtomRef object will be used during the loop */
4753 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4754 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4755 for (i = 0; i < len; i++) {
4760 /* A new ruby object will be created at each iteration (not very efficient) */
4761 for (i = 0; i < len; i++) {
4762 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4770 * self == molEnumerable -> boolean
4772 * True if the two arguments point to the same molecule and enumerable type.
4775 s_MolEnumerable_Equal(VALUE self, VALUE val)
4777 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4778 MolEnumerable *mseq1, *mseq2;
4779 Data_Get_Struct(self, MolEnumerable, mseq1);
4780 Data_Get_Struct(val, MolEnumerable, mseq2);
4781 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4782 } else return Qfalse;
4786 #pragma mark ====== Molecule Class ======
4788 #pragma mark ------ Allocate/Release/Accessor ------
4790 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4791 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4792 char *gLoadSaveErrorMessage = NULL;
4794 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4797 MoleculeFromValue(VALUE val)
4800 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4801 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4802 Data_Get_Struct(val, Molecule, mol);
4806 static VALUE sMoleculeRetainArray = Qnil;
4808 /* The function is called from MoleculeRelease() */
4809 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4810 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4811 /* object is always returned for the same Molecule. */
4812 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4813 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4814 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4815 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4816 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4818 /* Register/unregister the exmolobj Ruby object */
4820 MoleculeReleaseExternalObj(Molecule *mol)
4822 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4823 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4824 mol->exmolobjProtected = 0;
4829 MoleculeRetainExternalObj(Molecule *mol)
4831 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4832 if (sMoleculeRetainArray == Qnil) {
4833 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4834 sMoleculeRetainArray = rb_ary_new();
4837 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4838 mol->exmolobjProtected = 1;
4842 /* Release hook function for Ruby */
4844 MoleculeReleaseHook(Molecule *mol)
4846 if (mol->exmolobj != NULL) {
4847 /* No need to remove from sMoleculeRetainArray */
4848 mol->exmolobj = NULL;
4849 mol->exmolobjProtected = 0;
4851 MoleculeRelease(mol);
4855 ValueFromMolecule(Molecule *mol)
4859 if (mol->exmolobj != NULL)
4860 return (VALUE)mol->exmolobj;
4861 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4862 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4863 return (VALUE)mol->exmolobj;
4868 s_Molecule_Alloc(VALUE klass)
4871 Molecule *mol = MoleculeNew();
4872 val = ValueFromMolecule(mol);
4873 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4878 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4882 if (FIXNUM_P(val)) {
4884 if (n >= 0 && n < mol->natoms)
4886 n = -1; /* No such atom */
4887 val = rb_inspect(val);
4889 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4891 if (n >= 0 && n < mol->natoms)
4893 p = StringValuePtr(val);
4895 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4897 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4899 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4900 return 0; /* Not reached */
4904 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4907 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4908 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4909 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4910 Data_Get_Struct(val, IntGroup, ig);
4919 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4920 * created object does not affect the old object in any sense.
4923 s_Molecule_InitCopy(VALUE self, VALUE arg)
4925 Molecule *mp1, *mp2;
4926 Data_Get_Struct(self, Molecule, mp1);
4927 mp2 = MoleculeFromValue(arg);
4928 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4929 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4935 * atom_index(val) -> Integer
4937 * Returns the atom index represented by val. val can be either a non-negative integer
4938 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4939 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
4940 * where resname, resid, name are the residue name, residue id, and atom name respectively.
4941 * If val is a string and multiple atoms match the description, the atom with the lowest index
4945 s_Molecule_AtomIndex(VALUE self, VALUE val)
4948 Data_Get_Struct(self, Molecule, mol);
4949 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4954 * self == Molecule -> boolean
4956 * True if the two arguments point to the same molecule.
4959 s_Molecule_Equal(VALUE self, VALUE val)
4961 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4962 Molecule *mol1, *mol2;
4963 Data_Get_Struct(self, Molecule, mol1);
4964 Data_Get_Struct(val, Molecule, mol2);
4965 return (mol1 == mol2 ? Qtrue : Qfalse);
4966 } else return Qfalse;
4969 #pragma mark ------ Load/Save ------
4972 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4974 if (gLoadSaveErrorMessage != NULL) {
4975 MyAppCallback_setConsoleColor(1);
4976 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4977 MyAppCallback_setConsoleColor(0);
4980 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4985 * loadmbsf(file) -> bool
4987 * Read a structure from a mbsf file.
4988 * Return true if successful.
4991 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4997 MoleculeClearLoadSaveErrorMessage();
4998 Data_Get_Struct(self, Molecule, mol);
4999 rb_scan_args(argc, argv, "1", &fname);
5000 fstr = FileStringValuePtr(fname);
5001 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5002 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5008 * loadpsf(file, pdbfile = nil) -> bool
5010 * Read a structure from a psf file. molecule must be empty. The psf may be
5011 * an "extended" version, which also contains coordinates. If pdbfile
5012 * is given, then atomic coordinates are read from that file.
5013 * Return true if successful.
5016 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5018 VALUE fname, pdbname;
5019 char *fstr, *pdbstr;
5022 Data_Get_Struct(self, Molecule, mol);
5023 if (mol->natoms > 0)
5024 return Qnil; /* Must be a new molecule */
5025 MoleculeClearLoadSaveErrorMessage();
5026 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5027 fstr = FileStringValuePtr(fname);
5028 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5029 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5031 if (!NIL_P(pdbname)) {
5032 pdbstr = strdup(FileStringValuePtr(pdbname));
5033 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5035 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5042 * loadpdb(file) -> bool
5044 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5045 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5046 * Return true if successful.
5049 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5055 Data_Get_Struct(self, Molecule, mol);
5056 rb_scan_args(argc, argv, "1", &fname);
5057 MoleculeClearLoadSaveErrorMessage();
5058 fstr = FileStringValuePtr(fname);
5059 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5060 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5066 * loaddcd(file) -> bool
5068 * Read coordinates from a dcd file. The molecule should not empty.
5069 * Return true if successful.
5072 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5078 Data_Get_Struct(self, Molecule, mol);
5079 rb_scan_args(argc, argv, "1", &fname);
5080 MoleculeClearLoadSaveErrorMessage();
5081 fstr = FileStringValuePtr(fname);
5082 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5083 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5089 * loadtep(file) -> bool
5091 * Read coordinates from an ortep .tep file.
5092 * Return true if successful.
5095 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5101 Data_Get_Struct(self, Molecule, mol);
5102 rb_scan_args(argc, argv, "1", &fname);
5103 MoleculeClearLoadSaveErrorMessage();
5104 fstr = FileStringValuePtr(fname);
5105 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5106 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5112 * loadres(file) -> bool
5114 * Read coordinates from a shelx .res file.
5115 * Return true if successful.
5118 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5124 Data_Get_Struct(self, Molecule, mol);
5125 rb_scan_args(argc, argv, "1", &fname);
5126 MoleculeClearLoadSaveErrorMessage();
5127 fstr = FileStringValuePtr(fname);
5128 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5129 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5135 * loadfchk(file) -> bool
5137 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5138 * Return true if successful.
5141 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5147 Data_Get_Struct(self, Molecule, mol);
5148 rb_scan_args(argc, argv, "1", &fname);
5149 MoleculeClearLoadSaveErrorMessage();
5150 fstr = FileStringValuePtr(fname);
5151 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5152 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5158 * loaddat(file) -> bool
5160 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5161 * Return true if successful.
5164 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5170 Data_Get_Struct(self, Molecule, mol);
5171 rb_scan_args(argc, argv, "1", &fname);
5172 MoleculeClearLoadSaveErrorMessage();
5173 fstr = FileStringValuePtr(fname);
5174 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5175 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5176 MyAppCallback_hideProgressPanel();
5177 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5183 * savembsf(file) -> bool
5185 * Write structure as a mbsf file. Returns true if successful.
5188 s_Molecule_Savembsf(VALUE self, VALUE fname)
5193 Data_Get_Struct(self, Molecule, mol);
5194 MoleculeClearLoadSaveErrorMessage();
5195 fstr = FileStringValuePtr(fname);
5196 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5197 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5203 * savepsf(file) -> bool
5205 * Write structure as a psf file. Returns true if successful.
5208 s_Molecule_Savepsf(VALUE self, VALUE fname)
5213 Data_Get_Struct(self, Molecule, mol);
5214 MoleculeClearLoadSaveErrorMessage();
5215 fstr = FileStringValuePtr(fname);
5216 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5217 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5223 * savepdb(file) -> bool
5225 * Write coordinates as a pdb file. Returns true if successful.
5228 s_Molecule_Savepdb(VALUE self, VALUE fname)
5233 Data_Get_Struct(self, Molecule, mol);
5234 MoleculeClearLoadSaveErrorMessage();
5235 fstr = FileStringValuePtr(fname);
5236 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5237 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5243 * savedcd(file) -> bool
5245 * Write coordinates as a dcd file. Returns true if successful.
5248 s_Molecule_Savedcd(VALUE self, VALUE fname)
5253 Data_Get_Struct(self, Molecule, mol);
5254 MoleculeClearLoadSaveErrorMessage();
5255 fstr = FileStringValuePtr(fname);
5256 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5257 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5261 /* load([ftype, ] fname, ...) */
5263 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5266 char *argstr, *methname, *p, *type = "";
5269 const char *ls = (loadFlag ? "load" : "save");
5270 int lslen = strlen(ls);
5275 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5276 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5277 if (argstr[0] == ':') {
5278 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5279 methname = ALLOC_N(char, lslen + strlen(argstr));
5280 strcpy(methname, ls);
5281 strcat(methname, argstr + 1);
5283 for (i = lslen; methname[i] != 0; i++)
5284 methname[i] = tolower(methname[i]);
5285 mid = rb_intern(methname);
5289 rval = rb_funcall2(self, mid, argc, argv);
5295 /* Guess file type from extension */
5296 p = strrchr(argstr, '.');
5300 for (methname = p; *methname != 0; methname++) {
5301 if (!isalpha(*methname))
5304 if (*methname == 0) {
5305 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5306 if (methname == NULL)
5307 rb_raise(rb_eMolbyError, "Low memory");
5308 strcpy(methname, ls);
5309 strcat(methname, p);
5310 for (i = lslen; methname[i] != 0; i++)
5311 methname[i] = tolower(methname[i]);
5312 mid = rb_intern(methname);
5315 if (rb_respond_to(self, mid)) {
5316 /* Load: try to call the load procedure only if it is available */
5317 rval = rb_funcall2(self, mid, argc, argv);
5322 /* Save: call the save procedure, and if not found then call 'method_missing' */
5323 rval = rb_funcall2(self, mid, argc, argv);
5330 rval = rb_str_to_str(argv[0]);
5331 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5332 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5333 return Qnil; /* Does not reach here */
5337 /* Register the path */
5340 Data_Get_Struct(self, Molecule, mol);
5341 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5343 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5344 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5345 if (ap->occupancy != 0.0)
5348 if (i == mol->natoms) {
5349 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5350 ap->occupancy = 1.0;
5359 * molload(file, *args) -> bool
5361 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5362 * file type given by the extension). If this method fails, then all defined (public)
5363 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5366 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5368 return s_Molecule_LoadSave(argc, argv, self, 1);
5373 * molsave(file, *args) -> bool
5375 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5376 * (XXX is the file type given by the extension).
5379 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5381 return s_Molecule_LoadSave(argc, argv, self, 0);
5387 * open(file) -> Molecule
5389 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5392 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5398 rb_scan_args(argc, argv, "01", &fname);
5402 p = FileStringValuePtr(fname);
5403 iflag = Ruby_SetInterruptFlag(Qfalse);
5404 mp = MoleculeCallback_openNewMolecule(p);
5405 Ruby_SetInterruptFlag(iflag);
5408 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5410 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5412 return ValueFromMolecule(mp);
5418 * new(file, *args) -> Molecule
5420 * Create a new molecule and call "load" method with the same arguments.
5423 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5426 return s_Molecule_Load(argc, argv, self);
5427 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5432 * error_message -> String
5434 * Get the error_message from the last load/save method. If no error, returns nil.
5437 s_Molecule_ErrorMessage(VALUE klass)
5439 if (gLoadSaveErrorMessage == NULL)
5441 else return rb_str_new2(gLoadSaveErrorMessage);
5446 * set_error_message(String)
5447 * Molecule.error_message = String
5449 * Set the error_message for the present load/save method.
5452 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5454 if (gLoadSaveErrorMessage != NULL) {
5455 free(gLoadSaveErrorMessage);
5456 gLoadSaveErrorMessage = NULL;
5459 sval = rb_str_to_str(sval);
5460 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5467 * set_molecule(Molecule)
5469 * Duplicate the given molecule and set to self. The present molecule must be empty.
5470 * This method is exclusively used for associating a new document with an existing molecule.
5473 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5475 Molecule *mp1, *mp2;
5476 Data_Get_Struct(self, Molecule, mp1);
5477 mp2 = MoleculeFromValue(mval);
5478 MoleculeInitWithMolecule(mp1, mp2);
5479 MoleculeCallback_notifyModification(mp1, 1);
5483 #pragma mark ------ Name attributes ------
5489 * Returns the display name of the molecule. If the molecule has no associated
5490 * document, then returns nil.
5493 s_Molecule_Name(VALUE self)
5497 Data_Get_Struct(self, Molecule, mol);
5498 MoleculeCallback_displayName(mol, buf, sizeof buf);
5502 return rb_str_new2(buf);
5507 * set_name(string) -> self
5509 * Set the name of an untitled molecule. If the molecule is not associated with window
5510 * or it already has an associated file, then exception is thrown.
5513 s_Molecule_SetName(VALUE self, VALUE nval)
5516 Data_Get_Struct(self, Molecule, mol);
5517 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5518 rb_raise(rb_eMolbyError, "Cannot change the window title");
5527 * Returns the full path name of the molecule, if it is associated with a file.
5528 * If the molecule has no associated file, then returns nil.
5531 s_Molecule_Path(VALUE self)
5535 Data_Get_Struct(self, Molecule, mol);
5536 MoleculeCallback_pathName(mol, buf, sizeof buf);
5540 return Ruby_NewFileStringValue(buf);
5547 * Returns the full path name of the directory in which the file associated with the
5548 * molecule is located. If the molecule has no associated file, then returns nil.
5551 s_Molecule_Dir(VALUE self)
5555 Data_Get_Struct(self, Molecule, mol);
5556 MoleculeCallback_pathName(mol, buf, sizeof buf);
5558 translate_char(buf, '\\', '/');
5563 p = strrchr(buf, '/');
5566 return rb_str_new2(buf);
5574 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5575 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5576 * the Molecule structure) is returned.
5579 s_Molecule_Inspect(VALUE self)
5583 Data_Get_Struct(self, Molecule, mol);
5584 MoleculeCallback_displayName(mol, buf, sizeof buf);
5586 /* No associated document */
5587 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5588 return rb_str_new2(buf);
5590 /* Check whether the document name is duplicate */
5594 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5595 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5596 if (strcmp(buf, buf2) == 0) {
5603 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5605 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5607 return rb_str_new2(buf2);
5611 #pragma mark ------ MolEnumerables ------
5614 s_Molecule_MolEnumerable(VALUE self, int kind)
5617 MolEnumerable *mseq;
5618 Data_Get_Struct(self, Molecule, mol);
5619 mseq = MolEnumerableNew(mol, kind);
5620 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5625 * atoms -> MolEnumerable
5627 * Returns a MolEnumerable object representing the array of atoms.
5630 s_Molecule_Atoms(VALUE self)
5632 return s_Molecule_MolEnumerable(self, kAtomKind);
5637 * bonds -> MolEnumerable
5639 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5640 * by an array of two atom indices.
5643 s_Molecule_Bonds(VALUE self)
5645 return s_Molecule_MolEnumerable(self, kBondKind);
5650 * angles -> MolEnumerable
5652 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5653 * by an array of three atom indices.
5656 s_Molecule_Angles(VALUE self)
5658 return s_Molecule_MolEnumerable(self, kAngleKind);
5663 * dihedrals -> MolEnumerable
5665 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5666 * by an array of four atom indices.
5669 s_Molecule_Dihedrals(VALUE self)
5671 return s_Molecule_MolEnumerable(self, kDihedralKind);
5676 * impropers -> MolEnumerable
5678 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5679 * by an array of four atom indices.
5682 s_Molecule_Impropers(VALUE self)
5684 return s_Molecule_MolEnumerable(self, kImproperKind);
5689 * residues -> MolEnumerable
5691 * Returns a MolEnumerable object representing the array of residue names.
5694 s_Molecule_Residues(VALUE self)
5696 return s_Molecule_MolEnumerable(self, kResidueKind);
5703 * Returns the number of atoms.
5706 s_Molecule_Natoms(VALUE self)
5709 Data_Get_Struct(self, Molecule, mol);
5710 return INT2NUM(mol->natoms);
5717 * Returns the number of bonds.
5720 s_Molecule_Nbonds(VALUE self)
5723 Data_Get_Struct(self, Molecule, mol);
5724 return INT2NUM(mol->nbonds);
5729 * nangles -> Integer
5731 * Returns the number of angles.
5734 s_Molecule_Nangles(VALUE self)
5737 Data_Get_Struct(self, Molecule, mol);
5738 return INT2NUM(mol->nangles);
5743 * ndihedrals -> Integer
5745 * Returns the number of dihedrals.
5748 s_Molecule_Ndihedrals(VALUE self)
5751 Data_Get_Struct(self, Molecule, mol);
5752 return INT2NUM(mol->ndihedrals);
5757 * nimpropers -> Integer
5759 * Returns the number of impropers.
5762 s_Molecule_Nimpropers(VALUE self)
5765 Data_Get_Struct(self, Molecule, mol);
5766 return INT2NUM(mol->nimpropers);
5771 * nresidues -> Integer
5773 * Returns the number of residues.
5776 s_Molecule_Nresidues(VALUE self)
5779 Data_Get_Struct(self, Molecule, mol);
5780 return INT2NUM(mol->nresidues);
5785 * nresidues = Integer
5787 * Change the number of residues.
5790 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5793 int ival = NUM2INT(val);
5794 Data_Get_Struct(self, Molecule, mol);
5795 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5796 if (ival != mol->nresidues)
5797 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5803 * max_residue_number(atom_group = nil) -> Integer
5805 * Returns the maximum residue number actually used. If an atom group is given, only
5806 * these atoms are examined. If no atom is present, nil is returned.
5809 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5815 Data_Get_Struct(self, Molecule, mol);
5816 rb_scan_args(argc, argv, "01", &gval);
5817 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5818 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5819 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5824 * min_residue_number(atom_group = nil) -> Integer
5826 * Returns the minimum residue number actually used. If an atom group is given, only
5827 * these atoms are examined. If no atom is present, nil is returned.
5830 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5836 Data_Get_Struct(self, Molecule, mol);
5837 rb_scan_args(argc, argv, "01", &gval);
5838 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5839 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5840 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5845 * each_atom(atom_group = nil) {|aref| ...}
5847 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5848 * group is given, only these atoms are processed.
5849 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5850 * is self (a Molecule object).
5853 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5861 Data_Get_Struct(self, Molecule, mol);
5862 rb_scan_args(argc, argv, "01", &gval);
5863 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5864 arval = ValueFromMoleculeAndIndex(mol, 0);
5865 Data_Get_Struct(arval, AtomRef, aref);
5866 for (i = 0; i < mol->natoms; i++) {
5868 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5872 IntGroupRelease(ig);
5876 #pragma mark ------ Atom Group ------
5879 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5881 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5882 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5883 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5884 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5891 * atom_group {|aref| ...}
5892 * atom_group(arg1, arg2, ...)
5893 * atom_group(arg1, arg2, ...) {|aref| ...}
5895 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5896 * If arguments are given, then the atoms reprensented by the arguments are added to the
5897 * group. For a conversion of a string to an atom index, see the description
5898 * of Molecule#atom_index.
5899 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5900 * representing each atom, and the atoms are removed from the result if the block returns false.
5904 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5906 IntGroup *ig1, *ig2;
5908 Int i, startPt, interval;
5909 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5910 Data_Get_Struct(retval, IntGroup, ig1);
5911 Data_Get_Struct(self, Molecule, mol);
5913 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5916 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5917 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5918 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5919 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5920 ig2 = IntGroupFromValue(*argv);
5921 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5922 interval = IntGroupGetInterval(ig2, i);
5923 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5925 IntGroupRelease(ig2);
5926 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5928 values[0] = (VALUE)mol;
5929 values[1] = (VALUE)ig1;
5930 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5932 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5937 if (rb_block_given_p()) {
5938 /* Evaluate the given block with an AtomRef as the argument, and delete
5939 the index if the block returns false */
5940 AtomRef *aref = AtomRefNew(mol, 0);
5941 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5942 ig2 = IntGroupNew();
5943 IntGroupCopy(ig2, ig1);
5944 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5946 if (startPt >= mol->natoms)
5948 aref->idx = startPt;
5949 resval = rb_yield(arval);
5951 IntGroupRemove(ig1, startPt, 1);
5953 IntGroupRelease(ig2);
5956 /* Remove points that are out of bounds */
5957 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5964 * selection -> IntGroup
5966 * Returns the current selection.
5969 s_Molecule_Selection(VALUE self)
5974 Data_Get_Struct(self, Molecule, mol);
5975 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5976 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
5977 val = ValueFromIntGroup(ig);
5978 IntGroupRelease(ig);
5980 val = IntGroup_Alloc(rb_cIntGroup);
5986 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5990 Data_Get_Struct(self, Molecule, mol);
5994 ig = s_Molecule_AtomGroupFromValue(self, val);
5996 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
5998 MoleculeSetSelection(mol, ig);
6000 IntGroupRelease(ig);
6006 * selection = IntGroup
6008 * Set the current selection. The right-hand operand may be nil.
6009 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6012 s_Molecule_SetSelection(VALUE self, VALUE val)
6014 return s_Molecule_SetSelectionSub(self, val, 0);
6019 * set_undoable_selection(IntGroup)
6021 * Set the current selection with undo registration. The right-hand operand may be nil.
6022 * This operation is undoable.
6025 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6027 return s_Molecule_SetSelectionSub(self, val, 1);
6030 #pragma mark ------ Editing ------
6034 * extract(group, dummy_flag = nil) -> Molecule
6036 * Extract the atoms given by group and return as a new molecule object.
6037 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6038 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6039 * names beginning with an underscore) and included in the new molecule object.
6042 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6044 Molecule *mol1, *mol2;
6046 VALUE group, dummy_flag, retval;
6047 Data_Get_Struct(self, Molecule, mol1);
6048 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6049 ig = s_Molecule_AtomGroupFromValue(self, group);
6050 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6053 retval = ValueFromMolecule(mol2);
6055 IntGroupRelease(ig);
6061 * add(molecule2) -> self
6063 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6065 This operation is undoable.
6068 s_Molecule_Add(VALUE self, VALUE val)
6070 Molecule *mol1, *mol2;
6071 Data_Get_Struct(self, Molecule, mol1);
6072 mol2 = MoleculeFromValue(val);
6073 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6079 * remove(group) -> Molecule
6081 * The atoms designated by the given group are removed from the molecule.
6082 * This operation is undoable.
6085 s_Molecule_Remove(VALUE self, VALUE group)
6090 IntGroupIterator iter;
6092 Data_Get_Struct(self, Molecule, mol1);
6093 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6094 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6095 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6096 Data_Get_Struct(group, IntGroup, ig);
6098 /* Remove the bonds between the two fragments */
6099 /* (This is necessary for undo to work correctly) */
6100 IntGroupIteratorInit(ig, &iter);
6102 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6103 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6105 cp = AtomConnectData(&ap->connect);
6106 for (j = 0; j < ap->connect.count; j++) {
6108 if (!IntGroupLookup(ig, n, NULL)) {
6109 /* bond i-n, i is in ig and n is not */
6110 int k = MoleculeLookupBond(mol1, i, n);
6114 IntGroupAdd(bg, k, 1);
6119 IntGroupIteratorRelease(&iter);
6122 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6123 IntGroupRelease(bg);
6126 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6133 * create_atom(name, pos = -1) -> AtomRef
6135 * Create a new atom with the specified name (may contain residue
6136 * information) and position (if position is out of range, the atom is appended at
6137 * the end). Returns the reference to the new atom.
6138 * This operation is undoable.
6141 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6148 char *p, resName[6], atomName[6];
6150 Data_Get_Struct(self, Molecule, mol);
6151 rb_scan_args(argc, argv, "02", &name, &ival);
6153 pos = NUM2INT(rb_Integer(ival));
6156 p = StringValuePtr(name);
6158 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6159 if (atomName[0] == 0)
6160 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6163 if (p == NULL || p[0] == 0) {
6164 memset(atomName, 0, 4);
6167 memset(&arec, 0, sizeof(arec));
6168 strncpy(arec.aname, atomName, 4);
6170 strncpy(arec.resName, resName, 4);
6171 arec.resSeq = resSeq;
6173 arec.occupancy = 1.0;
6174 // i = MoleculeCreateAnAtom(mol, &arec);
6175 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6177 aref = AtomRefNew(mol, pos);
6178 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6183 * duplicate_atom(atomref, pos = -1) -> AtomRef
6185 * Create a new atom with the same attributes (but no bonding information)
6186 * with the specified atom. Returns the reference to the new atom.
6187 * This operation is undoable.
6190 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6196 VALUE retval, aval, ival;
6198 Data_Get_Struct(self, Molecule, mol);
6199 rb_scan_args(argc, argv, "11", &aval, &ival);
6200 if (FIXNUM_P(aval)) {
6201 int idx = NUM2INT(aval);
6202 if (idx < 0 || idx >= mol->natoms)
6203 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6204 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6206 apsrc = s_AtomFromValue(aval);
6209 rb_raise(rb_eMolbyError, "bad atom specification");
6211 pos = NUM2INT(rb_Integer(ival));
6213 AtomDuplicate(&arec, apsrc);
6214 arec.connect.count = 0;
6215 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6218 aref = AtomRefNew(mol, pos);
6219 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6227 * create_bond(n1, n2, ...) -> Integer
6229 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6230 * do nothing for that pair. Returns the number of bonds actually created.
6231 * This operation is undoable.
6234 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6237 Int i, j, k, *ip, old_nbonds;
6239 rb_raise(rb_eMolbyError, "missing arguments");
6241 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6242 Data_Get_Struct(self, Molecule, mol);
6243 ip = ALLOC_N(Int, argc + 1);
6244 for (i = j = 0; i < argc; i++, j++) {
6245 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6247 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6248 j -= 2; /* This bond is already present: skip it */
6250 for (k = 0; k < j - 1; k += 2) {
6251 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6252 j -= 2; /* The same entry is already in the argument */
6259 old_nbonds = mol->nbonds;
6261 ip[j] = kInvalidIndex;
6262 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6266 rb_raise(rb_eMolbyError, "atom index out of range");
6268 rb_raise(rb_eMolbyError, "too many bonds");
6270 rb_raise(rb_eMolbyError, "duplicate bonds");
6272 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6274 rb_raise(rb_eMolbyError, "error in creating bonds");
6275 return INT2NUM(mol->nbonds - old_nbonds);
6280 * molecule.remove_bonds(n1, n2, ...) -> Integer
6282 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6283 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6284 * This operation is undoable.
6287 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6293 rb_raise(rb_eMolbyError, "missing arguments");
6295 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6296 Data_Get_Struct(self, Molecule, mol);
6298 for (i = j = 0; i < argc; i++, j = 1 - j) {
6299 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6301 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6305 IntGroupAdd(bg, k, 1);
6310 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6311 i = IntGroupGetCount(bg);
6312 IntGroupRelease(bg);
6319 * assign_bond_order(idx, d1)
6320 * assign_bond_orders(group, [d1, d2, ...])
6322 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6323 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6324 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6325 * (This may change in the future)
6326 * This operation is undoable.
6329 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6333 Data_Get_Struct(self, Molecule, mol);
6334 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6335 /* The first form */
6336 Int idx = NUM2INT(rb_Integer(idxval));
6337 Double d1 = NUM2DBL(rb_Float(dval));
6338 if (idx < 0 || idx >= mol->nbonds)
6339 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6340 ig = IntGroupNewWithPoints(idx, 1, -1);
6341 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6342 IntGroupRelease(ig);
6346 ig = IntGroupFromValue(idxval);
6347 n = IntGroupGetCount(ig);
6349 rb_raise(rb_eMolbyError, "the bond index is empty");
6350 dval = rb_ary_to_ary(dval);
6351 dp = (Double *)calloc(sizeof(Double), n);
6352 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6353 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6355 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6357 IntGroupRelease(ig);
6364 * get_bond_order(idx) -> Float
6365 * get_bond_orders(group) -> Array
6367 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6368 * In the second form, the bond orders at the indices in the group are returned as an array.
6369 * If no bond order information have been assigned, returns nil (the first form)
6370 * or an empty array (the second form).
6373 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6379 Int i, n, numericArg;
6380 Data_Get_Struct(self, Molecule, mol);
6381 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6382 /* The first form */
6383 Int idx = NUM2INT(rb_Integer(idxval));
6384 if (idx < 0 || idx >= mol->nbonds)
6385 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6386 if (mol->bondOrders == NULL)
6388 ig = IntGroupNewWithPoints(idx, 1, -1);
6392 if (mol->bondOrders == NULL)
6393 return rb_ary_new();
6394 ig = IntGroupFromValue(idxval);
6395 n = IntGroupGetCount(ig);
6397 rb_raise(rb_eMolbyError, "the bond index is empty");
6400 dp = (Double *)calloc(sizeof(Double), n);
6401 MoleculeGetBondOrders(mol, dp, ig);
6403 retval = rb_float_new(dp[0]);
6405 retval = rb_ary_new();
6406 for (i = 0; i < n; i++)
6407 rb_ary_push(retval, rb_float_new(dp[i]));
6410 IntGroupRelease(ig);
6416 * bond_exist?(idx1, idx2) -> bool
6418 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6419 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6422 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6428 Data_Get_Struct(self, Molecule, mol);
6429 idx1 = NUM2INT(rb_Integer(ival1));
6430 idx2 = NUM2INT(rb_Integer(ival2));
6431 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6432 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6433 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6434 cp = AtomConnectData(&ap->connect);
6435 for (i = 0; i < ap->connect.count; i++) {
6444 * add_angle(n1, n2, n3) -> Molecule
6446 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6447 * when a bond is created, so it is rarely necessary to use this method explicitly.
6448 * This operation is undoable.
6451 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6455 Data_Get_Struct(self, Molecule, mol);
6456 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6457 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6458 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6459 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6460 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6461 n[3] = kInvalidIndex;
6462 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6468 * remove_angle(n1, n2, n3) -> Molecule
6470 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6471 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6472 * This operation is undoable.
6475 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6480 Data_Get_Struct(self, Molecule, mol);
6481 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6482 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6483 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6484 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6485 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6486 ig = IntGroupNewWithPoints(n[3], 1, -1);
6487 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6488 IntGroupRelease(ig);
6494 * add_dihedral(n1, n2, n3, n4) -> Molecule
6496 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6497 * when a bond is created, so it is rarely necessary to use this method explicitly.
6498 * This operation is undoable.
6501 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6505 Data_Get_Struct(self, Molecule, mol);
6506 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6507 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6508 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6509 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6510 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6511 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6512 n[4] = kInvalidIndex;
6513 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6519 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6521 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6522 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6523 * This operation is undoable.
6526 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6531 Data_Get_Struct(self, Molecule, mol);
6532 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6533 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6534 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6535 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6536 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6537 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6538 ig = IntGroupNewWithPoints(n[4], 1, -1);
6539 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6540 IntGroupRelease(ig);
6546 * add_improper(n1, n2, n3, n4) -> Molecule
6548 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6549 * not automatically added when a new bond is created, so this method is more useful than
6550 * the angle/dihedral counterpart.
6551 * This operation is undoable.
6554 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6558 Data_Get_Struct(self, Molecule, mol);
6559 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6560 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6561 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6562 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6563 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6564 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6565 n[4] = kInvalidIndex;
6566 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6572 * remove_improper(n1, n2, n3, n4) -> Molecule
6573 * remove_improper(intgroup) -> Molecule
6575 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6576 * Returns self. Unlike angles and dihedrals, impropers are
6577 * not automatically added when a new bond is created, so this method is more useful than
6578 * the angle/dihedral counterpart.
6579 * This operation is undoable.
6582 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6585 VALUE v1, v2, v3, v4;
6588 Data_Get_Struct(self, Molecule, mol);
6590 ig = IntGroupFromValue(argv[0]);
6592 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6593 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6594 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6595 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6596 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6597 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6598 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6599 ig = IntGroupNewWithPoints(n[4], 1, -1);
6601 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6602 IntGroupRelease(ig);
6608 * assign_residue(group, res) -> Molecule
6610 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6611 * or "resname.resno". When the residue number is not specified, the residue number of
6612 * the first atom in the group is used.
6613 * This operation is undoable.
6616 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6620 char *p, *pp, buf[16];
6623 Data_Get_Struct(self, Molecule, mol);
6625 /* Parse the argument res */
6626 if (FIXNUM_P(res)) {
6627 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6628 resid = NUM2INT(res);
6631 p = StringValuePtr(res);
6632 pp = strchr(p, '.');
6634 resid = atoi(pp + 1);
6640 if (n > sizeof buf - 1)
6645 ig = s_Molecule_AtomGroupFromValue(self, range);
6646 if (ig == NULL || IntGroupGetCount(ig) == 0)
6650 /* Use the residue number of the first specified atom */
6651 n = IntGroupGetNthPoint(ig, 0);
6652 if (n >= mol->natoms)
6653 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6654 ap = ATOM_AT_INDEX(mol->atoms, n);
6657 /* Change the residue number */
6658 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6659 /* Change the residue name if necessary */
6663 seqs[1] = kInvalidIndex; */
6664 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6666 IntGroupRelease(ig);
6672 * offset_residue(group, offset) -> Molecule
6674 * Offset the residue number of the specified atoms. If any of the residue number gets
6675 * negative, then exception is thrown.
6676 * This operation is undoable.
6679 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6684 Data_Get_Struct(self, Molecule, mol);
6685 ig = s_Molecule_AtomGroupFromValue(self, range);
6686 ofs = NUM2INT(offset);
6687 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6689 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6690 IntGroupRelease(ig);
6696 * renumber_atoms(array) -> IntGroup
6698 * Change the order of atoms so that the atoms specified in the array argument appear
6699 * in this order from the top of the molecule. The atoms that are not included in array
6700 * are placed after these atoms, and these atoms are returned as an intGroup.
6701 * This operation is undoable.
6704 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6710 VALUE *valp, retval;
6711 Data_Get_Struct(self, Molecule, mol);
6712 if (TYPE(array) != T_ARRAY)
6713 array = rb_funcall(array, rb_intern("to_a"), 0);
6714 n = RARRAY_LEN(array);
6715 valp = RARRAY_PTR(array);
6716 new2old = ALLOC_N(Int, n + 1);
6717 for (i = 0; i < n; i++)
6718 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6719 new2old[i] = kInvalidIndex;
6720 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6722 rb_raise(rb_eMolbyError, "Atom index out of range");
6724 rb_raise(rb_eMolbyError, "Duplicate entry");
6726 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6727 retval = IntGroup_Alloc(rb_cIntGroup);
6728 Data_Get_Struct(retval, IntGroup, ig);
6729 if (mol->natoms > n)
6730 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6737 * set_atom_attr(index, key, value)
6739 * Set the atom attribute for the specified atom.
6740 * This operation is undoable.
6743 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6747 Data_Get_Struct(self, Molecule, mol);
6748 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6749 oldval = s_AtomRef_GetAttr(aref, key);
6752 s_AtomRef_SetAttr(aref, key, val);
6758 * get_atom_attr(index, key)
6760 * Get the atom attribute for the specified atom.
6763 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6765 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6768 #pragma mark ------ Undo Support ------
6772 * register_undo(script, *args)
6774 * Register an undo operation with the current molecule.
6777 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6782 Data_Get_Struct(self, Molecule, mol);
6783 rb_scan_args(argc, argv, "1*", &script, &args);
6784 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6785 MolActionCallback_registerUndo(mol, act);
6791 * undo_enabled? -> bool
6793 * Returns true if undo is enabled for this molecule; otherwise no.
6796 s_Molecule_UndoEnabled(VALUE self)
6799 Data_Get_Struct(self, Molecule, mol);
6800 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6807 * undo_enabled = bool
6809 * Enable or disable undo.
6812 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6815 Data_Get_Struct(self, Molecule, mol);
6816 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6820 #pragma mark ------ Measure ------
6823 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6825 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6826 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6827 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6829 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6835 * center_of_mass(group = nil) -> Vector3D
6837 * Calculate the center of mass for the given set of atoms. The argument
6838 * group is null, then all atoms are considered.
6841 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6847 Data_Get_Struct(self, Molecule, mol);
6848 rb_scan_args(argc, argv, "01", &group);
6849 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6850 s_Molecule_DoCenterOfMass(mol, &v, ig);
6852 IntGroupRelease(ig);
6853 return ValueFromVector(&v);
6858 * centralize(group = nil) -> self
6860 * Translate the molecule so that the center of mass of the given group is located
6861 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6864 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6870 Data_Get_Struct(self, Molecule, mol);
6871 rb_scan_args(argc, argv, "01", &group);
6872 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6873 s_Molecule_DoCenterOfMass(mol, &v, ig);
6875 IntGroupRelease(ig);
6879 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6885 * bounds(group = nil) -> [min, max]
6887 * Calculate the boundary. The return value is an array of two Vector3D objects.
6890 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6898 Data_Get_Struct(self, Molecule, mol);
6899 rb_scan_args(argc, argv, "01", &group);
6900 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6901 if (ig != NULL && IntGroupGetCount(ig) == 0)
6902 rb_raise(rb_eMolbyError, "atom group is empty");
6903 vmin.x = vmin.y = vmin.z = 1e30;
6904 vmax.x = vmax.y = vmax.z = -1e30;
6905 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6907 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6923 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6926 /* Get atom position or a vector */
6928 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6930 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6931 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6932 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6934 VectorFromValue(val, vp);
6940 * measure_bond(n1, n2) -> Float
6942 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
6943 * or Vector3D values.
6944 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6947 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6951 Data_Get_Struct(self, Molecule, mol);
6952 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6953 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6954 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6959 * measure_angle(n1, n2, n3) -> Float
6961 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
6962 * or Vector3D values. The return value is in degree.
6963 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6966 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6971 Data_Get_Struct(self, Molecule, mol);
6972 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6973 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6974 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6975 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6977 return Qnil; /* Cannot define */
6978 else return rb_float_new(d);
6983 * measure_dihedral(n1, n2, n3, n4) -> Float
6985 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
6986 * or Vector3D values. The return value is in degree.
6987 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6990 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6993 Vector v1, v2, v3, v4;
6995 Data_Get_Struct(self, Molecule, mol);
6996 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6997 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6998 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6999 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7000 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7002 return Qnil; /* Cannot define */
7003 else return rb_float_new(d);
7008 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7010 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7011 * first and second atom in the pair should belong to group1 and group2, respectively.
7012 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7015 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7018 VALUE limval, gval1, gval2, rval, igval;
7019 IntGroup *ig1, *ig2;
7020 IntGroupIterator iter1, iter2;
7026 MDExclusion *exinfo;
7029 Data_Get_Struct(self, Molecule, mol);
7030 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7031 lim = NUM2DBL(rb_Float(limval));
7033 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7035 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7037 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7039 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7041 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7043 if (!RTEST(igval)) {
7044 /* Use the exclusion table in MDArena */
7045 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7046 VALUE mval = ValueFromMolecule(mol);
7047 s_RebuildMDParameterIfNecessary(mval, Qnil);
7049 exinfo = mol->arena->exinfo; /* May be NULL */
7050 exlist = mol->arena->exlist;
7055 IntGroupIteratorInit(ig1, &iter1);
7056 IntGroupIteratorInit(ig2, &iter2);
7059 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7061 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7063 if (exinfo != NULL) {
7064 exn1 = exinfo[n[0]].index1;
7065 exn2 = exinfo[n[0] + 1].index1;
7066 } else exn1 = exn2 = -1;
7067 IntGroupIteratorReset(&iter2);
7068 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7069 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7071 continue; /* Same atom */
7072 if (exinfo != NULL) {
7073 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7074 for (i = exn1; i < exn2; i++) {
7075 if (exlist[i] == n[1])
7079 continue; /* Should be excluded */
7081 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7082 /* Is this pair already registered? */
7084 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7085 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7089 /* Not registered yet */
7090 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7095 IntGroupIteratorRelease(&iter2);
7096 IntGroupIteratorRelease(&iter1);
7097 IntGroupRelease(ig2);
7098 IntGroupRelease(ig1);
7099 rval = rb_ary_new2(npairs);
7100 if (pairs != NULL) {
7101 for (i = 0; i < npairs; i++) {
7102 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7111 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7113 * Find atoms that are within the threshold distance from the given atom.
7114 * (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.)
7115 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7116 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7117 * If limit is not given, a default value of 1.2 is used.
7118 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7121 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7124 VALUE aval, limval, radval;
7125 double limit, radius;
7126 Int n1, nbonds, *bonds, an;
7128 Data_Get_Struct(self, Molecule, mol);
7129 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7130 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)) {
7131 VectorFromValue(aval, &v);
7133 radius = gElementParameters[6].radius;
7135 radius = NUM2DBL(rb_Float(radval));
7138 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7139 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7140 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7141 if (an >= 0 && an < gCountElementParameters)
7142 radius = gElementParameters[an].radius;
7143 else radius = gElementParameters[6].radius;
7148 limit = NUM2DBL(rb_Float(limval));
7149 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7151 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7152 aval = rb_ary_new();
7154 for (n1 = 0; n1 < nbonds; n1++)
7155 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7163 * guess_bonds(limit = 1.2) -> Integer
7165 * Create bonds between atoms that are within the threshold distance.
7166 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7167 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7168 * If limit is not given, a default value of 1.2 is used.
7169 * The number of the newly created bonds is returned.
7170 * This operation is undoable.
7173 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7179 Data_Get_Struct(self, Molecule, mol);
7180 rb_scan_args(argc, argv, "01", &limval);
7184 limit = NUM2DBL(rb_Float(limval));
7185 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7187 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7190 return INT2NUM(nbonds);
7193 #pragma mark ------ Cell and Symmetry ------
7197 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7199 * Returns the unit cell parameters. If cell is not set, returns nil.
7202 s_Molecule_Cell(VALUE self)
7207 Data_Get_Struct(self, Molecule, mol);
7208 if (mol->cell == NULL)
7210 val = rb_ary_new2(6);
7211 for (i = 0; i < 6; i++)
7212 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7213 if (mol->cell->has_sigma) {
7214 for (i = 0; i < 6; i++) {
7215 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7223 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7224 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7226 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7227 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7228 This operation is undoable.
7229 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7232 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7236 int i, convert_coord, n;
7238 Data_Get_Struct(self, Molecule, mol);
7239 rb_scan_args(argc, argv, "11", &val, &cval);
7244 val = rb_ary_to_ary(val);
7245 len = RARRAY_LEN(val);
7248 } else if (len >= 6) {
7250 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7251 for (i = 0; i < n; i++)
7252 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7254 convert_coord = (RTEST(cval) ? 1 : 0);
7255 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7261 * box -> [avec, bvec, cvec, origin, flags]
7263 * Get the unit cell information in the form of a periodic bounding box.
7264 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7265 * Integers which define whether the system is periodic along the axis.
7266 * If no unit cell is defined, nil is returned.
7269 s_Molecule_Box(VALUE self)
7273 Data_Get_Struct(self, Molecule, mol);
7274 if (mol == NULL || mol->cell == NULL)
7276 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7277 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7278 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7279 v[3] = ValueFromVector(&(mol->cell->origin));
7280 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7281 val = rb_ary_new4(5, v);
7287 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7288 * set_box(d, origin = [0, 0, 0])
7291 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7292 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7294 Flags, if present, is a 3-member array of Integers defining whether the system is
7295 periodic along the axis.
7296 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7297 In the second form, an isotropic box with cell-length d is set.
7298 In the third form, the existing box is cleared.
7299 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7302 s_Molecule_SetBox(VALUE self, VALUE aval)
7306 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7308 Vector origin = {0, 0, 0};
7311 int i, convertCoordinates = 0;
7312 Data_Get_Struct(self, Molecule, mol);
7314 MolActionCreateAndPerform(mol, gMolActionClearBox);
7317 aval = rb_ary_to_ary(aval);
7318 for (i = 0; i < 6; i++) {
7319 if (i < RARRAY_LEN(aval))
7320 v[i] = (RARRAY_PTR(aval))[i];
7324 MolActionCreateAndPerform(mol, gMolActionClearBox);
7327 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7328 d = NUM2DBL(rb_Float(v[0]));
7329 for (i = 0; i < 3; i++)
7330 VecScale(vv[i], ax[i], d);
7332 VectorFromValue(v[1], &origin);
7333 flags[0] = flags[1] = flags[2] = 1;
7335 for (i = 0; i < 3; i++) {
7338 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7339 d = NUM2DBL(rb_Float(v[i]));
7340 VecScale(vv[i], ax[i], d);
7342 VectorFromValue(v[i], &vv[i]);
7344 flags[i] = (VecLength2(vv[i]) > 0.0);
7347 VectorFromValue(v[3], &origin);
7349 for (i = 0; i < 3; i++) {
7350 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7351 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7355 convertCoordinates = 1;
7357 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7363 * cell_periodicity -> [n1, n2, n3]
7365 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7369 s_Molecule_CellPeriodicity(VALUE self)
7372 Data_Get_Struct(self, Molecule, mol);
7373 if (mol->cell == NULL)
7375 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7380 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7381 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7383 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7384 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7385 * If cell is not defined, exception is raised.
7386 * This operation is undoable.
7389 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7393 Data_Get_Struct(self, Molecule, mol);
7394 if (mol->cell == NULL)
7395 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7398 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7399 flag = NUM2INT(rb_Integer(arg));
7403 arg = rb_ary_to_ary(arg);
7405 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7406 arg0 = RARRAY_PTR(arg)[i];
7407 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7408 flag |= (1 << (2 - i));
7411 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7417 * cell_flexibility -> bool
7419 * Returns the unit cell is flexible or not
7422 s_Molecule_CellFlexibility(VALUE self)
7424 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7427 Data_Get_Struct(self, Molecule, mol);
7428 if (mol->cell == NULL)
7430 if (mol->useFlexibleCell)
7432 else return Qfalse; */
7437 * self.cell_flexibility = bool
7438 * set_cell_flexibility(bool)
7440 * Change the unit cell is flexible or not
7443 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7445 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7448 Data_Get_Struct(self, Molecule, mol);
7449 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7455 * cell_transform -> Transform
7457 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7458 * If cell is not defined, nil is returned.
7461 s_Molecule_CellTransform(VALUE self)
7464 Data_Get_Struct(self, Molecule, mol);
7465 if (mol == NULL || mol->cell == NULL)
7467 return ValueFromTransform(&(mol->cell->tr));
7472 * symmetry -> Array of Transforms
7473 * symmetries -> Array of Transforms
7475 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7476 * returns an empty array.
7479 s_Molecule_Symmetry(VALUE self)
7484 Data_Get_Struct(self, Molecule, mol);
7485 if (mol->nsyms <= 0)
7486 return rb_ary_new();
7487 val = rb_ary_new2(mol->nsyms);
7488 for (i = 0; i < mol->nsyms; i++) {
7489 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7496 * nsymmetries -> Integer
7498 * Get the number of currently defined symmetry operations.
7501 s_Molecule_Nsymmetries(VALUE self)
7504 Data_Get_Struct(self, Molecule, mol);
7505 return INT2NUM(mol->nsyms);
7510 * add_symmetry(Transform) -> Integer
7512 * Add a new symmetry operation. If no symmetry operation is defined and the
7513 * given argument is not an identity transform, then also add an identity
7514 * transform at the index 0.
7515 * Returns the total number of symmetries after operation.
7518 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7522 Data_Get_Struct(self, Molecule, mol);
7523 TransformFromValue(trans, &tr);
7524 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7525 return INT2NUM(mol->nsyms);
7530 * remove_symmetry(count = nil) -> Integer
7531 * remove_symmetries(count = nil) -> Integer
7533 * Remove the specified number of symmetry operations. The last added ones are removed
7534 * first. If count is nil, then all symmetry operations are removed. Returns the
7535 * number of leftover symmetries.
7538 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7543 Data_Get_Struct(self, Molecule, mol);
7544 rb_scan_args(argc, argv, "01", &cval);
7548 n = NUM2INT(rb_Integer(cval));
7549 if (n < 0 || n > mol->nsyms)
7550 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7551 if (n == mol->nsyms)
7554 for (i = 0; i < n; i++)
7555 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7556 return INT2NUM(mol->nsyms);
7561 * wrap_unit_cell(group) -> Vector3D
7563 * Move the specified group so that the center of mass of the group is within the
7564 * unit cell. The offset vector is returned. If no periodic box is defined,
7565 * exception is raised.
7568 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7573 Data_Get_Struct(self, Molecule, mol);
7574 if (mol->cell == NULL)
7575 rb_raise(rb_eMolbyError, "no unit cell is defined");
7576 ig = s_Molecule_AtomGroupFromValue(self, gval);
7577 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7578 TransformVec(&v, mol->cell->rtr, &cv);
7579 if (mol->cell->flags[0])
7581 if (mol->cell->flags[1])
7583 if (mol->cell->flags[2])
7585 TransformVec(&dv, mol->cell->tr, &v);
7587 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7588 IntGroupRelease(ig);
7589 return ValueFromVector(&dv);
7594 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7596 * Expand the specified part of the molecule by the given symmetry operation.
7597 * Returns the array of atom indices corresponding to the expanded atoms.
7598 * If allow_overlap is true, then new atoms are created even when the
7599 * coordinates coincide with the some other atom (special position) of the
7600 * same element; otherwise, such atom will not be created and the index of the
7601 * existing atom is given in the returned array.
7604 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7607 VALUE gval, sval, xval, yval, zval, rval, oval;
7609 Int n[4], allow_overlap;
7613 Data_Get_Struct(self, Molecule, mol);
7614 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7615 n[0] = NUM2INT(rb_Integer(sval));
7616 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7617 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7618 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7619 allow_overlap = (RTEST(oval) ? 1 : 0);
7620 ig = s_Molecule_AtomGroupFromValue(self, gval);
7621 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7622 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7623 natoms = mol->natoms;
7625 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7627 rval = rb_ary_new2(nidx);
7628 while (--nidx >= 0) {
7629 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7631 /* if (natoms == mol->natoms)
7634 rval = IntGroup_Alloc(rb_cIntGroup);
7635 Data_Get_Struct(rval, IntGroup, ig);
7636 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7643 * amend_by_symmetry(group = nil) -> IntGroup
7645 * Expand the specified part of the molecule by the given symmetry operation.
7646 * Returns an IntGroup containing the added atoms.
7649 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7654 Data_Get_Struct(self, Molecule, mol);
7655 rb_scan_args(argc, argv, "01", &gval);
7657 ig = s_Molecule_AtomGroupFromValue(self, gval);
7659 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7660 rval = ValueFromIntGroup(ig2);
7661 IntGroupRelease(ig2);
7665 #pragma mark ------ Transforms ------
7669 * translate(vec, group = nil) -> Molecule
7671 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7672 * This operation is undoable.
7675 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7681 Data_Get_Struct(self, Molecule, mol);
7682 rb_scan_args(argc, argv, "11", &vec, &group);
7683 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7684 VectorFromValue(vec, &v);
7685 // MoleculeTranslate(mol, &v, ig);
7686 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7688 IntGroupRelease(ig);
7694 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7696 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7697 * If group is given, only atoms in the group are moved.
7698 * This operation is undoable.
7701 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7704 volatile VALUE aval, anval, cval, gval;
7709 Data_Get_Struct(self, Molecule, mol);
7710 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7711 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7712 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7713 VectorFromValue(aval, &av);
7715 cv.x = cv.y = cv.z = 0.0;
7717 VectorFromValue(cval, &cv);
7718 if (TransformForRotation(tr, &av, angle, &cv))
7719 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7720 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7722 IntGroupRelease(ig);
7728 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7730 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7731 * axis must not be a zero vector.
7732 * If group is given, only atoms in the group are moved.
7733 * This operation is undoable.
7736 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7739 volatile VALUE aval, cval, gval;
7743 Data_Get_Struct(self, Molecule, mol);
7744 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7745 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7746 VectorFromValue(aval, &av);
7748 cv.x = cv.y = cv.z = 0.0;
7750 VectorFromValue(cval, &cv);
7751 if (TransformForReflection(tr, &av, &cv))
7752 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7753 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7755 IntGroupRelease(ig);
7761 * invert(center = [0,0,0], group = nil) -> Molecule
7763 * Invert the molecule with the given center.
7764 * If group is given, only atoms in the group are moved.
7765 * This operation is undoable.
7768 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7771 volatile VALUE cval, gval;
7775 Data_Get_Struct(self, Molecule, mol);
7776 rb_scan_args(argc, argv, "02", &cval, &gval);
7777 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7779 cv.x = cv.y = cv.z = 0.0;
7781 VectorFromValue(cval, &cv);
7782 TransformForInversion(tr, &cv);
7783 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7785 IntGroupRelease(ig);
7791 * transform(transform, group = nil) -> Molecule
7793 * Transform the molecule by the given Transform object.
7794 * If group is given, only atoms in the group are moved.
7795 * This operation is undoable.
7798 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7804 Data_Get_Struct(self, Molecule, mol);
7805 rb_scan_args(argc, argv, "11", &trans, &group);
7806 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7807 TransformFromValue(trans, &tr);
7808 /* MoleculeTransform(mol, tr, ig); */
7809 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7811 IntGroupRelease(ig);
7817 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7819 * Get the transform corresponding to the symmetry operation. The symop can either be
7820 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7821 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7822 * Otherwise, the returned transform is for fractional coordinates.
7823 * Raises exception when no cell or no transform are defined.
7826 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7832 Data_Get_Struct(self, Molecule, mol);
7833 if (mol->cell == NULL)
7834 rb_raise(rb_eMolbyError, "no unit cell is defined");
7835 if (mol->nsyms == 0)
7836 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7837 rb_scan_args(argc, argv, "11", &sval, &fval);
7838 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7839 symop.sym = NUM2INT(rb_Integer(sval));
7840 symop.dx = symop.dy = symop.dz = 0;
7842 sval = rb_ary_to_ary(sval);
7843 if (RARRAY_LEN(sval) < 4)
7844 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7845 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7846 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7847 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7848 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7850 if (symop.sym >= mol->nsyms)
7851 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7852 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7853 return ValueFromTransform(&tr);
7858 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7860 * Get the symmetry operation corresponding to the given transform.
7861 * If is_cartesian is true, the given transform is for cartesian coordinates.
7862 * Otherwise, the given transform is for fractional coordinates.
7863 * Raises exception when no cell or no transform are defined.
7866 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7873 Data_Get_Struct(self, Molecule, mol);
7874 if (mol->cell == NULL)
7875 rb_raise(rb_eMolbyError, "no unit cell is defined");
7876 if (mol->nsyms == 0)
7877 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7878 rb_scan_args(argc, argv, "11", &tval, &fval);
7879 TransformFromValue(tval, &tr);
7880 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7882 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7884 return Qnil; /* Not found */
7888 #pragma mark ------ Frames ------
7892 * select_frame(index)
7895 * Select the specified frame. If successful, returns true, otherwise returns false.
7898 s_Molecule_SelectFrame(VALUE self, VALUE val)
7901 int ival = NUM2INT(val);
7902 Data_Get_Struct(self, Molecule, mol);
7903 ival = MoleculeSelectFrame(mol, ival, 1);
7913 * Get the current frame.
7916 s_Molecule_Frame(VALUE self)
7919 Data_Get_Struct(self, Molecule, mol);
7920 return INT2NUM(mol->cframe);
7925 * nframes -> Integer
7927 * Get the number of frames.
7930 s_Molecule_Nframes(VALUE self)
7933 Data_Get_Struct(self, Molecule, mol);
7934 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7939 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7940 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7942 * Insert new frames at the indices specified by the intGroup. If the first argument is
7943 * an integer, a single new frame is inserted at that index. If the first argument is
7944 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7945 * should be an array of arrays of Vector3Ds, then those coordinates are set
7946 * to the new frame. Otherwise, the coordinates of current molecule are copied
7948 * Returns an intGroup representing the inserted frames if successful, nil if not.
7951 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7953 VALUE val, coords, cells;
7956 int count, ival, i, j, len, len_c, len2, nframes;
7959 Data_Get_Struct(self, Molecule, mol);
7960 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7961 if (coords != Qnil) {
7962 if (TYPE(coords) != T_ARRAY)
7963 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7964 len = RARRAY_LEN(coords);
7966 if (cells != Qnil) {
7967 if (mol->cell == NULL)
7968 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7969 if (TYPE(cells) != T_ARRAY)
7970 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7971 len_c = RARRAY_LEN(cells);
7973 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7974 nframes = MoleculeGetNumberOfFrames(mol);
7976 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7977 val = ValueFromIntGroup(ig);
7979 ig = IntGroupFromValue(val);
7981 count = IntGroupGetCount(ig); /* Count is updated here */
7982 vp = ALLOC_N(Vector, mol->natoms * count);
7984 vp2 = ALLOC_N(Vector, 4 * count);
7988 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7989 ptr = RARRAY_PTR(coords);
7990 for (i = 0; i < count; i++) {
7991 if (TYPE(ptr[i]) != T_ARRAY)
7992 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7993 len2 = RARRAY_LEN(ptr[i]);
7994 if (len2 < mol->natoms)
7995 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7996 ptr2 = RARRAY_PTR(ptr[i]);
7997 for (j = 0; j < mol->natoms; j++)
7998 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8002 for (i = 0; i < count; i++) {
8003 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8004 vp[i * mol->natoms + j] = ap->r;
8010 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8011 ptr = RARRAY_PTR(cells);
8012 for (i = 0; i < count; i++) {
8013 if (TYPE(ptr[i]) != T_ARRAY)
8014 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8015 len2 = RARRAY_LEN(ptr[i]);
8017 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8018 ptr2 = RARRAY_PTR(ptr[i]);
8019 for (j = 0; j < 4; j++)
8020 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8023 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8024 IntGroupRelease(ig);
8028 return (ival >= 0 ? val : Qnil);
8033 * create_frame(coordinates = nil) -> Integer
8034 * create_frames(coordinates = nil) -> Integer
8036 * Same as molecule.insert_frames(nil, coordinates).
8039 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8042 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8044 return s_Molecule_InsertFrames(3, vals, self);
8049 * remove_frames(IntGroup, wantCoordinates = false)
8051 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8052 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8053 * removed frames is returned if operation is successful.
8056 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8063 Data_Get_Struct(self, Molecule, mol);
8064 rb_scan_args(argc, argv, "11", &val, &flag);
8065 ig = IntGroupFromValue(val);
8066 count = IntGroupGetCount(ig);
8068 /* Create return value before removing frames */
8073 retval = rb_ary_new2(count);
8074 for (i = 0; i < count; i++) {
8075 n = IntGroupGetNthPoint(ig, i);
8076 coords = rb_ary_new2(mol->natoms);
8077 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8078 if (n < ap->nframes && n != mol->cframe)
8081 rb_ary_push(coords, ValueFromVector(&v));
8083 rb_ary_push(retval, coords);
8085 } else retval = Qtrue;
8086 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8093 * each_frame {|n| ...}
8095 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8096 * the frame number. After completion, the original frame number is restored.
8099 s_Molecule_EachFrame(VALUE self)
8101 int i, cframe, nframes;
8103 Data_Get_Struct(self, Molecule, mol);
8104 cframe = mol->cframe;
8105 nframes = MoleculeGetNumberOfFrames(mol);
8107 for (i = 0; i < nframes; i++) {
8108 MoleculeSelectFrame(mol, i, 1);
8109 rb_yield(INT2NUM(i));
8111 MoleculeSelectFrame(mol, cframe, 1);
8118 * get_coord_from_frame(index, group = nil)
8120 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8121 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8122 * copied; now they are always copied)
8125 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8128 VALUE ival, gval, cval;
8129 Int index, i, j, n, nn;
8131 IntGroupIterator iter;
8134 Data_Get_Struct(self, Molecule, mol);
8135 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8137 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8138 index = NUM2INT(rb_Integer(ival));
8139 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8141 rb_raise(rb_eMolbyError, "No frame is present");
8143 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8146 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8148 ig = s_Molecule_AtomGroupFromValue(self, gval);
8150 n = IntGroupGetCount(ig);
8152 vp = (Vector *)calloc(sizeof(Vector), n);
8153 IntGroupIteratorInit(ig, &iter);
8156 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8157 ap = ATOM_AT_INDEX(mol->atoms, i);
8158 if (index < ap->nframes) {
8159 vp[j] = ap->frames[index];
8167 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8169 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8170 vp = mol->frame_cells + index * 4;
8171 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8173 IntGroupIteratorRelease(&iter);
8175 /* Copy the extra properties */
8176 IntGroupRelease(ig);
8177 for (i = 0; i < mol->nmolprops; i++) {
8178 Double *dp = (Double *)malloc(sizeof(Double));
8180 IntGroupAdd(ig, mol->cframe, 1);
8181 *dp = mol->molprops[i].propvals[index];
8182 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8184 IntGroupRelease(ig);
8192 * reorder_frames(old_indices)
8194 * Reorder the frames. The argument is an array of integers that specify the 'old'
8195 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8196 * same as the old frames 2/0/1, respectively.
8197 * The argument must have the same number of integers as the number of frames.
8200 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8203 Int *ip, *ip2, i, n, nframes;
8204 Data_Get_Struct(self, Molecule, mol);
8205 aval = rb_ary_to_ary(aval);
8206 nframes = MoleculeGetNumberOfFrames(mol);
8207 if (RARRAY_LEN(aval) != nframes)
8208 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8209 ip2 = (Int *)calloc(sizeof(Int), nframes);
8210 ip = (Int *)calloc(sizeof(Int), nframes);
8211 for (i = 0; i < nframes; i++) {
8212 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8213 if (n < 0 || n >= nframes || ip2[n] != 0) {
8216 if (n < 0 || n >= nframes)
8217 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8219 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8225 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8230 #pragma mark ------ Fragments ------
8234 * fragment(n1, *exatoms) -> IntGroup
8235 * fragment(group, *exatoms) -> IntGroup
8237 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8238 * those atoms will not be counted during the search.
8241 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8244 IntGroup *baseg, *ig, *exatoms;
8246 volatile VALUE nval, exval;
8247 Data_Get_Struct(self, Molecule, mol);
8248 rb_scan_args(argc, argv, "1*", &nval, &exval);
8249 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8251 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8253 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8255 if (RARRAY_LEN(exval) == 0) {
8258 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8259 Data_Get_Struct(exval, IntGroup, exatoms);
8261 if (baseg == NULL) {
8262 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8264 IntGroupIterator iter;
8265 IntGroupIteratorInit(baseg, &iter);
8266 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8269 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8271 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8273 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8275 IntGroupAddIntGroup(ig, subg);
8276 IntGroupRelease(subg);
8281 IntGroupIteratorRelease(&iter);
8282 IntGroupRelease(baseg);
8285 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8286 nval = ValueFromIntGroup(ig);
8287 IntGroupRelease(ig);
8293 * fragments(exclude = nil)
8295 * Returns the fragments as an array of IntGroups.
8296 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8297 * in defining the fragment.
8300 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8303 IntGroup *ag, *fg, *eg;
8304 VALUE gval, exval, retval;
8305 Data_Get_Struct(self, Molecule, mol);
8308 if (mol->natoms == 0)
8309 return rb_ary_new();
8310 rb_scan_args(argc, argv, "01", &exval);
8314 eg = IntGroupFromValue(exval);
8315 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8317 IntGroupRemoveIntGroup(ag, eg);
8318 retval = rb_ary_new();
8319 while (IntGroupGetCount(ag) > 0) {
8320 int n = IntGroupGetNthPoint(ag, 0);
8321 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8323 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8324 gval = ValueFromIntGroup(fg);
8325 rb_ary_push(retval, gval);
8326 IntGroupRemoveIntGroup(ag, fg);
8327 IntGroupRelease(fg);
8329 IntGroupRelease(ag);
8331 IntGroupRelease(eg);
8337 * each_fragment(exclude = nil) {|group| ...}
8339 * Execute the block, with the IntGroup object for each fragment as the argument.
8340 * Atoms or bonds should not be added or removed during the execution of the block.
8341 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8342 * in defining the fragment.
8345 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8348 IntGroup *ag, *fg, *eg;
8350 Data_Get_Struct(self, Molecule, mol);
8351 if (mol == NULL || mol->natoms == 0)
8353 rb_scan_args(argc, argv, "01", &exval);
8357 eg = IntGroupFromValue(exval);
8358 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8360 IntGroupRemoveIntGroup(ag, eg);
8361 while (IntGroupGetCount(ag) > 0) {
8362 int n = IntGroupGetNthPoint(ag, 0);
8363 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8365 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8366 gval = ValueFromIntGroup(fg);
8368 IntGroupRemoveIntGroup(ag, fg);
8369 IntGroupRelease(fg);
8371 IntGroupRelease(ag);
8373 IntGroupRelease(eg);
8379 * detachable?(group) -> [n1, n2]
8381 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8382 * of the molecule via only one bond. If it is, then the indices of the atoms
8383 * belonging to the bond is returned, the first element being the atom included
8384 * in the fragment. Otherwise, Qnil is returned.
8387 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8393 Data_Get_Struct(self, Molecule, mol);
8394 ig = s_Molecule_AtomGroupFromValue(self, gval);
8395 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8396 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8397 } else retval = Qnil;
8398 IntGroupRelease(ig);
8404 * bonds_on_border(group = selection) -> Array of Array of two Integers
8406 * Returns an array of bonds that connect an atom in the group and an atom out
8407 * of the group. The first atom in the bond always belongs to the group. If no
8408 * such bonds are present, an empty array is returned.
8411 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8416 Data_Get_Struct(self, Molecule, mol);
8417 rb_scan_args(argc, argv, "01", &gval);
8419 ig = MoleculeGetSelection(mol);
8423 ig = s_Molecule_AtomGroupFromValue(self, gval);
8425 retval = rb_ary_new();
8428 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8430 IntGroupIterator iter;
8432 IntGroupIteratorInit(bg, &iter);
8433 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8434 /* The atoms at the border */
8436 n1 = mol->bonds[i * 2];
8437 n2 = mol->bonds[i * 2 + 1];
8438 if (IntGroupLookupPoint(ig, n1) < 0) {
8442 if (IntGroupLookupPoint(ig, n1) < 0)
8443 continue; /* Actually this is an internal error */
8445 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8447 IntGroupIteratorRelease(&iter);
8449 IntGroupRelease(bg);
8450 IntGroupRelease(ig);
8454 /* Calculate the transform that moves the current coordinates to the reference
8455 coordinates with least displacements. */
8457 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8465 Double eigen_val[3];
8466 Vector eigen_vec[3];
8468 IntGroupIterator iter;
8470 natoms = mol->natoms;
8472 IntGroupIteratorInit(ig, &iter);
8474 /* Calculate the weighted center */
8478 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8479 ap1 = ATOM_AT_INDEX(ap, in);
8480 w1 = (weights != NULL ? weights[i] : ap1->weight);
8481 VecScaleInc(org1, ap1->r, w1);
8482 VecScaleInc(org2, ref[i], w1);
8486 VecScaleSelf(org1, w);
8487 VecScaleSelf(org2, w);
8489 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8490 /* Matrix to diagonalize = R * tR */
8491 memset(r, 0, sizeof(Mat33));
8492 memset(q, 0, sizeof(Mat33));
8493 memset(u, 0, sizeof(Mat33));
8495 IntGroupIteratorReset(&iter);
8496 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8498 ap1 = ATOM_AT_INDEX(ap, in);
8499 w1 = (weights != NULL ? weights[i] : ap1->weight);
8501 VecSub(v1, ap1->r, org1);
8502 VecSub(v2, ref[i], org2);
8503 r[0] += w1 * v1.x * v2.x;
8504 r[1] += w1 * v1.y * v2.x;
8505 r[2] += w1 * v1.z * v2.x;
8506 r[3] += w1 * v1.x * v2.y;
8507 r[4] += w1 * v1.y * v2.y;
8508 r[5] += w1 * v1.z * v2.y;
8509 r[6] += w1 * v1.x * v2.z;
8510 r[7] += w1 * v1.y * v2.z;
8511 r[8] += w1 * v1.z * v2.z;
8514 for (i = 0; i < 9; i++)
8516 for (i = 0; i < 3; i++) {
8517 for (j = 0; j < 3; j++) {
8518 for (k = 0; k < 3; k++) {
8519 q[i+j*3] += r[i+k*3] * r[j+k*3];
8524 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8525 IntGroupIteratorRelease(&iter);
8526 return -1.0; /* Cannot determine the eigenvector */
8529 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8530 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8531 MatrixTranspose(r, r);
8532 for (i = 0; i < 3; i++) {
8533 MatrixVec(&s[i], r, &eigen_vec[i]);
8534 w1 = 1.0 / sqrt(eigen_val[i]);
8535 VecScaleSelf(s[i], w1);
8537 for (k = 0; k < 3; k++) {
8538 u[0] += s[k].x * eigen_vec[k].x;
8539 u[1] += s[k].y * eigen_vec[k].x;
8540 u[2] += s[k].z * eigen_vec[k].x;
8541 u[3] += s[k].x * eigen_vec[k].y;
8542 u[4] += s[k].y * eigen_vec[k].y;
8543 u[5] += s[k].z * eigen_vec[k].y;
8544 u[6] += s[k].x * eigen_vec[k].z;
8545 u[7] += s[k].y * eigen_vec[k].z;
8546 u[8] += s[k].z * eigen_vec[k].z;
8549 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8550 MatrixVec(&org1, u, &org1);
8552 for (i = 0; i < 9; i++)
8558 /* Calculate rmsd */
8559 IntGroupIteratorReset(&iter);
8561 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8563 ap1 = ATOM_AT_INDEX(ap, in);
8564 TransformVec(&tv, trans, &ap1->r);
8566 w += VecLength2(tv);
8569 IntGroupIteratorRelease(&iter);
8575 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8577 * Calculate the transform to fit the given group to the set of reference coordinates.
8578 * The reference coordinates ref is given as either a frame number, an array of
8579 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8580 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8581 * Return values are the transform (that converts the present coordinates to the
8582 * target coordinates) and root mean square deviation (without weight).
8585 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8589 VALUE gval, rval, wval;
8591 IntGroupIterator iter;
8592 int nn, errno, i, j, in, status;
8594 Double *weights, dval[3];
8597 Data_Get_Struct(self, Molecule, mol);
8598 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8600 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8602 ig = IntGroupFromValue(gval);
8603 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8604 IntGroupRelease(ig);
8605 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8607 ref = (Vector *)calloc(sizeof(Vector), nn);
8608 weights = (Double *)calloc(sizeof(Double), nn);
8609 IntGroupIteratorInit(ig, &iter);
8610 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8611 int fn = NUM2INT(rb_Integer(rval));
8612 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8617 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8618 ap = ATOM_AT_INDEX(mol->atoms, in);
8619 if (fn < ap->nframes)
8620 ref[i] = ap->frames[fn];
8621 else ref[i] = ap->r;
8623 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8624 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8625 if (m->row * m->column < nn * 3) {
8629 for (i = 0; i < nn; i++) {
8630 ref[i].x = m->data[i * 3];
8631 ref[i].y = m->data[i * 3 + 1];
8632 ref[i].z = m->data[i * 3 + 2];
8636 rval = rb_protect(rb_ary_to_ary, rval, &status);
8641 if (RARRAY_LEN(rval) < nn) {
8645 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8646 /* Array of 3*nn numbers */
8647 if (RARRAY_LEN(rval) < nn * 3) {
8651 for (i = 0; i < nn; i++) {
8652 for (j = 0; j < 3; j++) {
8653 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8658 dval[j] = NUM2DBL(aval);
8665 /* Array of nn Vector3Ds or Arrays */
8666 for (i = 0; i < nn; i++) {
8667 aval = (RARRAY_PTR(rval))[i];
8668 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8669 VectorFromValue(aval, &ref[i]);
8671 aval = rb_protect(rb_ary_to_ary, aval, &status);
8676 if (RARRAY_LEN(aval) < 3) {
8681 for (j = 0; j < 3; j++) {
8682 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8687 dval[j] = NUM2DBL(aaval);
8697 /* Use atomic weights */
8698 IntGroupIteratorReset(&iter);
8699 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8700 ap = ATOM_AT_INDEX(mol->atoms, in);
8701 weights[i] = ap->weight;
8704 wval = rb_protect(rb_ary_to_ary, wval, &status);
8709 if (RARRAY_LEN(wval) < nn) {
8713 for (i = 0; i < nn; i++) {
8714 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8719 weights[i] = NUM2DBL(wwval);
8722 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8729 IntGroupIteratorRelease(&iter);
8733 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8734 } else if (errno == 1) {
8735 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8736 } else if (errno == 2) {
8737 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8738 } else if (errno == 3) {
8739 rb_jump_tag(status);
8740 } else if (errno == 4) {
8741 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8742 } else if (errno == 5) {
8743 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8744 } else if (errno == 6) {
8745 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8747 return Qnil; /* Not reached */
8750 #pragma mark ------ Screen Display ------
8756 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8759 s_Molecule_Display(VALUE self)
8762 Data_Get_Struct(self, Molecule, mol);
8763 if (mol->mview != NULL)
8764 MainViewCallback_display(mol->mview);
8772 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8775 s_Molecule_MakeFront(VALUE self)
8778 Data_Get_Struct(self, Molecule, mol);
8779 if (mol->mview != NULL)
8780 MainViewCallback_makeFront(mol->mview);
8786 * update_enabled? -> bool
8788 * Returns true if screen update is enabled; otherwise no.
8791 s_Molecule_UpdateEnabled(VALUE self)
8794 Data_Get_Struct(self, Molecule, mol);
8795 if (mol->mview != NULL && !mol->mview->freezeScreen)
8802 * update_enabled = bool
8804 * Enable or disable screen update. This is effective for automatic update on modification.
8805 * Explicit call to molecule.display() always updates the screen.
8808 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8811 Data_Get_Struct(self, Molecule, mol);
8812 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8813 if (mol->mview != NULL)
8814 mol->mview->freezeScreen = (val == Qfalse);
8822 * show_unitcell(bool)
8823 * show_unitcell = bool
8825 * Set the flag whether to show the unit cell. If no argument is given, the
8826 * current flag is returned.
8829 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8832 Data_Get_Struct(self, Molecule, mol);
8833 if (mol->mview == NULL)
8836 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8837 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8839 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8845 * show_hydrogens(bool)
8846 * show_hydrogens = bool
8848 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8849 * current flag is returned.
8852 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8855 Data_Get_Struct(self, Molecule, mol);
8856 if (mol->mview == NULL)
8859 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8860 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8862 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8868 * show_dummy_atoms(bool)
8869 * show_dummy_atoms = bool
8871 * Set the flag whether to show the dummy atoms. If no argument is given, the
8872 * current flag is returned.
8875 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8878 Data_Get_Struct(self, Molecule, mol);
8879 if (mol->mview == NULL)
8882 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8883 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8885 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8891 * show_expanded(bool)
8892 * show_expanded = bool
8894 * Set the flag whether to show the expanded atoms. If no argument is given, the
8895 * current flag is returned.
8898 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8901 Data_Get_Struct(self, Molecule, mol);
8902 if (mol->mview == NULL)
8905 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8906 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8908 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8914 * show_ellipsoids(bool)
8915 * show_ellipsoids = bool
8917 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8918 * current flag is returned.
8921 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8924 Data_Get_Struct(self, Molecule, mol);
8925 if (mol->mview == NULL)
8928 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8929 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8931 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8936 * is_atom_visible(index) -> Boolean
8938 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8939 * as well as the molecule attributes (showHydrogens, etc.)
8942 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8947 Data_Get_Struct(self, Molecule, mol);
8948 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8949 if (idx < 0 || idx >= mol->natoms)
8951 ap = ATOM_AT_INDEX(mol->atoms, idx);
8952 if (mol->mview != NULL) {
8953 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8955 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8957 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8960 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8965 * hidden_atoms -> IntGroup
8967 * Returns the currently hidden atoms.
8970 s_Molecule_HiddenAtoms(VALUE self)
8972 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8973 return Qnil; /* Not reached */
8978 * set_hidden_atoms(IntGroup)
8979 * self.hidden_atoms = IntGroup
8981 * Hide the specified atoms. This operation is _not_ undoable.
8984 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8986 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8987 return Qnil; /* Not reached */
8992 * show_graphite -> Integer
8993 * show_graphite = Integer
8994 * show_graphite = boolean
8996 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
8997 * number of rings to display for each direction.
8998 * If the argument is boolean, only the show/hide flag is set.
9001 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9004 Data_Get_Struct(self, Molecule, mol);
9005 if (mol->mview == NULL)
9008 if (argv[0] == Qnil || argv[0] == Qfalse)
9009 mol->mview->showGraphiteFlag = 0;
9010 else if (argv[0] == Qtrue)
9011 mol->mview->showGraphiteFlag = 1;
9013 int n = NUM2INT(rb_Integer(argv[0]));
9015 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9016 mol->mview->showGraphite = n;
9018 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9020 return INT2NUM(mol->mview->showGraphite);
9025 * show_graphite? -> boolean
9027 * Return whether the graphite is set visible or not.
9030 s_Molecule_ShowGraphiteFlag(VALUE self)
9033 Data_Get_Struct(self, Molecule, mol);
9034 if (mol->mview == NULL)
9036 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9041 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9042 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9043 * show_periodic_image = boolean
9045 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9046 * set but no visual effects are observed.
9047 * If the argument is boolean, only the show/hide flag is modified.
9050 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9056 Data_Get_Struct(self, Molecule, mol);
9057 if (mol->mview == NULL)
9059 rb_scan_args(argc, argv, "01", &val);
9061 /* Change current settings */
9062 if (val == Qnil || val == Qfalse)
9063 mol->mview->showPeriodicImageFlag = 0;
9064 else if (val == Qtrue)
9065 mol->mview->showPeriodicImageFlag = 1;
9067 val = rb_ary_to_ary(val);
9068 for (i = 0; i < 6; i++) {
9069 if (i < RARRAY_LEN(val))
9070 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9072 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9073 rb_raise(rb_eMolbyError, "bad arguments");
9074 for (i = 0; i < 6; i++)
9075 mol->mview->showPeriodicImage[i] = ival[i];
9077 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9080 for (i = 0; i < 6; i++)
9081 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9087 * show_periodic_image? -> boolean
9089 * Return whether the periodic images are set to visible or not. This flag is
9090 * independent from the show_periodic_image settings.
9093 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9096 Data_Get_Struct(self, Molecule, mol);
9097 if (mol->mview == NULL)
9099 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9104 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9105 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9106 * show_rotation_center = boolean
9108 * Set to show the rotation center of the screen.
9109 * If the argument is boolean, only the show/hide flag is modified.
9112 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9115 Data_Get_Struct(self, Molecule, mol);
9116 if (mol->mview == NULL)
9119 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9120 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9122 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9131 * Set the flag whether to draw the model in line mode. If no argument is given, the
9132 * current flag is returned.
9135 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9138 Data_Get_Struct(self, Molecule, mol);
9139 if (mol->mview == NULL)
9142 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9143 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9145 return (mol->mview->lineMode ? Qtrue : Qfalse);
9150 * atom_radius = float
9153 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9155 * If no argument is given, the current value is returned.
9158 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9161 Data_Get_Struct(self, Molecule, mol);
9162 if (mol->mview == NULL)
9165 double rad = NUM2DBL(rb_Float(argv[0]));
9167 mol->mview->atomRadius = rad;
9168 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9172 return rb_float_new(mol->mview->atomRadius);
9177 * bond_radius = float
9180 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9181 * If no argument is given, the current value is returned.
9184 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9187 Data_Get_Struct(self, Molecule, mol);
9188 if (mol->mview == NULL)
9191 double rad = NUM2DBL(rb_Float(argv[0]));
9193 mol->mview->bondRadius = rad;
9194 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9198 return rb_float_new(mol->mview->bondRadius);
9203 * atom_resolution = integer
9206 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9207 * (Default = 12; minimum = 6)
9208 * If no argument is given, the current value is returned.
9211 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9214 Data_Get_Struct(self, Molecule, mol);
9215 if (mol->mview == NULL)
9218 int res = NUM2INT(rb_Integer(argv[0]));
9220 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9221 mol->mview->atomResolution = res;
9222 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9223 return INT2NUM(res);
9225 return INT2NUM(mol->mview->atomResolution);
9230 * bond_resolution = integer
9233 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9234 * (Default = 8; minimum = 4)
9235 * If no argument is given, the current value is returned.
9238 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9241 Data_Get_Struct(self, Molecule, mol);
9242 if (mol->mview == NULL)
9245 int res = NUM2INT(rb_Integer(argv[0]));
9247 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9248 mol->mview->bondResolution = res;
9249 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9250 return INT2NUM(res);
9252 return INT2NUM(mol->mview->bondResolution);
9259 * Resize the model drawing to fit in the window.
9262 s_Molecule_ResizeToFit(VALUE self)
9265 Data_Get_Struct(self, Molecule, mol);
9266 if (mol->mview != NULL)
9267 MainView_resizeToFit(mol->mview);
9273 * get_view_rotation -> [[ax, ay, az], angle]
9275 * Get the current rotation for the view. Angle is in degree, not radian.
9278 s_Molecule_GetViewRotation(VALUE self)
9283 Data_Get_Struct(self, Molecule, mol);
9284 if (mol->mview == NULL)
9286 TrackballGetRotate(mol->mview->track, f);
9287 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9291 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9296 * get_view_scale -> float
9298 * Get the current scale for the view.
9301 s_Molecule_GetViewScale(VALUE self)
9304 Data_Get_Struct(self, Molecule, mol);
9305 if (mol->mview == NULL)
9307 return rb_float_new(TrackballGetScale(mol->mview->track));
9312 * get_view_center -> Vector
9314 * Get the current center point of the view.
9317 s_Molecule_GetViewCenter(VALUE self)
9322 Data_Get_Struct(self, Molecule, mol);
9323 if (mol->mview == NULL)
9325 TrackballGetTranslate(mol->mview->track, f);
9326 v.x = -f[0] * mol->mview->dimension;
9327 v.y = -f[1] * mol->mview->dimension;
9328 v.z = -f[2] * mol->mview->dimension;
9329 return ValueFromVector(&v);
9334 * set_view_rotation([ax, ay, az], angle) -> self
9336 * Set the current rotation for the view. Angle is in degree, not radian.
9339 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9344 Data_Get_Struct(self, Molecule, mol);
9345 if (mol->mview == NULL)
9347 VectorFromValue(aval, &v);
9348 if (NormalizeVec(&v, &v))
9349 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9353 f[0] = -NUM2DBL(rb_Float(angval));
9354 TrackballSetRotate(mol->mview->track, f);
9355 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9361 * set_view_scale(scale) -> self
9363 * Set the current scale for the view.
9366 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9369 Data_Get_Struct(self, Molecule, mol);
9370 if (mol->mview == NULL)
9372 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9373 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9379 * set_view_center(vec) -> self
9381 * Set the current center point of the view.
9384 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9389 Data_Get_Struct(self, Molecule, mol);
9390 if (mol->mview == NULL)
9392 VectorFromValue(aval, &v);
9393 f[0] = -v.x / mol->mview->dimension;
9394 f[1] = -v.y / mol->mview->dimension;
9395 f[2] = -v.z / mol->mview->dimension;
9396 TrackballSetTranslate(mol->mview->track, f);
9397 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9403 * set_background_color(red, green, blue)
9405 * Set the background color of the model window.
9408 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9411 Data_Get_Struct(self, Molecule, mol);
9412 if (mol->mview != NULL) {
9413 VALUE rval, gval, bval;
9414 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9415 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9422 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9424 * Export the current graphic to a PNG or TIF file (determined by the extension).
9425 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9426 * If either width or height is not specified, then the screen width/height is used instead.
9429 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9432 VALUE fval, sval, bval, wval, hval;
9435 int bg_color, width, height;
9436 Data_Get_Struct(self, Molecule, mol);
9437 if (mol->mview == NULL)
9438 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9439 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9440 fname = FileStringValuePtr(fval);
9443 else scale = NUM2DBL(rb_Float(sval));
9446 else bg_color = NUM2INT(rb_Integer(bval));
9449 else width = NUM2INT(rb_Integer(wval));
9452 else height = NUM2INT(rb_Integer(hval));
9453 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9458 #pragma mark ------ Graphics ------
9461 s_CalculateGraphicNormals(MainViewGraphic *gp)
9465 if (gp == NULL || gp->npoints < 3)
9467 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9468 v1.x = gp->points[3] - gp->points[0];
9469 v1.y = gp->points[4] - gp->points[1];
9470 v1.z = gp->points[5] - gp->points[2];
9471 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9472 for (i = 2; i < gp->npoints; i++) {
9473 v2.x = gp->points[i * 3] - gp->points[0];
9474 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9475 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9476 VecCross(v3, v1, v2);
9477 NormalizeVec(&v3, &v3);
9478 gp->normals[i * 3] = v3.x;
9479 gp->normals[i * 3 + 1] = v3.y;
9480 gp->normals[i * 3 + 2] = v3.z;
9483 /* normals[0] = average of all nv[i] (i=2..n-1) */
9485 for (i = 2; i < gp->npoints; i++) {
9486 v1.x += gp->normals[i * 3];
9487 v1.y += gp->normals[i * 3 + 1];
9488 v1.z += gp->normals[i * 3 + 2];
9490 NormalizeVec(&v1, &v1);
9491 gp->normals[0] = v1.x;
9492 gp->normals[1] = v1.y;
9493 gp->normals[2] = v1.z;
9494 /* normals[1] = nv[2].normalize */
9495 v2.x = gp->normals[6];
9496 v2.y = gp->normals[7];
9497 v2.z = gp->normals[8];
9498 NormalizeVec(&v1, &v2);
9499 gp->normals[3] = v1.x;
9500 gp->normals[4] = v1.y;
9501 gp->normals[5] = v1.z;
9502 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9503 for (i = 2; i < gp->npoints; i++) {
9504 if (i == gp->npoints - 1)
9507 v3.x = gp->normals[i * 3 + 3];
9508 v3.y = gp->normals[i * 3 + 4];
9509 v3.z = gp->normals[i * 3 + 5];
9512 NormalizeVec(&v1, &v2);
9513 gp->normals[i * 3] = v1.x;
9514 gp->normals[i * 3 + 1] = v1.y;
9515 gp->normals[i * 3 + 2] = v1.z;
9522 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9524 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9525 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9526 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9527 * points: an array of Vectors
9531 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9537 VALUE kval, cval, pval, fval, ival;
9538 Data_Get_Struct(self, Molecule, mol);
9539 if (mol->mview == NULL)
9540 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9541 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9542 idx = NUM2INT(rb_Integer(ival));
9544 idx = mol->mview->ngraphics;
9545 else if (idx < 0 || idx > mol->mview->ngraphics)
9546 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9547 memset(&g, 0, sizeof(g));
9549 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9550 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9552 kval = rb_obj_as_string(kval);
9553 p = StringValuePtr(kval);
9554 if (strcmp(p, "line") == 0)
9555 g.kind = kMainViewGraphicLine;
9556 else if (strcmp(p, "poly") == 0)
9557 g.kind = kMainViewGraphicPoly;
9558 else if (strcmp(p, "cylinder") == 0)
9559 g.kind = kMainViewGraphicCylinder;
9560 else if (strcmp(p, "cone") == 0)
9561 g.kind = kMainViewGraphicCone;
9562 else if (strcmp(p, "ellipsoid") == 0)
9563 g.kind = kMainViewGraphicEllipsoid;
9564 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9566 g.closed = (RTEST(fval) ? 1 : 0);
9567 cval = rb_ary_to_ary(cval);
9568 n = RARRAY_LEN(cval);
9569 if (n < 3 || n >= 5)
9570 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9573 for (i = 0; i < n; i++)
9574 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9575 pval = rb_ary_to_ary(pval);
9576 n = RARRAY_LEN(pval);
9577 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9579 rb_raise(rb_eArgError, "no control points are given");
9581 case kMainViewGraphicLine:
9583 rb_raise(rb_eArgError, "the line object must have at least two control points");
9585 case kMainViewGraphicPoly:
9587 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9589 case kMainViewGraphicCylinder:
9590 case kMainViewGraphicCone:
9592 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9595 case kMainViewGraphicEllipsoid:
9599 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9602 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9603 for (i = 0; i < n; i++) {
9605 VALUE rval = RARRAY_PTR(pval)[i];
9607 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9608 /* The float argument can also be given as a vector (for simplify undo registration) */
9609 VectorFromValue(rval, &v);
9611 v.x = NUM2DBL(rb_Float(rval));
9615 VectorFromValue(rval, &v);
9617 g.points[i * 3] = v.x;
9618 g.points[i * 3 + 1] = v.y;
9619 g.points[i * 3 + 2] = v.z;
9621 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9623 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9624 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9625 g.points[7] = g.points[11] = g.points[3];
9627 if (g.kind == kMainViewGraphicPoly) {
9628 /* Calculate normals */
9629 s_CalculateGraphicNormals(&g);
9631 MainView_insertGraphic(mol->mview, idx, &g);
9636 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9637 MolActionCallback_registerUndo(mol, act);
9638 MolActionRelease(act);
9641 return INT2NUM(idx);
9646 * create_graphic(kind, color, points, fill = nil) -> integer
9648 * Create a new graphic object. The arguments are similar as insert_graphic.
9651 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9654 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9655 args[0] = INT2NUM(-1);
9656 return s_Molecule_InsertGraphic(argc + 1, args, self);
9661 * remove_graphic(index) -> integer
9663 * Remove a graphic object.
9666 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9670 Data_Get_Struct(self, Molecule, mol);
9671 if (mol->mview == NULL)
9672 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9673 i = NUM2INT(rb_Integer(ival));
9674 if (i < 0 || i >= mol->mview->ngraphics)
9675 rb_raise(rb_eArgError, "graphic index is out of range");
9677 /* Prepare data for undo */
9678 MainViewGraphic *gp;
9683 gp = mol->mview->graphics + i;
9684 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9685 for (n = 0; n < gp->npoints; n++) {
9686 vp[n].x = gp->points[n * 3];
9687 vp[n].y = gp->points[n * 3 + 1];
9688 vp[n].z = gp->points[n * 3 + 2];
9690 col[0] = gp->rgba[0];
9691 col[1] = gp->rgba[1];
9692 col[2] = gp->rgba[2];
9693 col[3] = gp->rgba[3];
9694 if (gp->visible == 0) {
9695 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9696 MolActionCallback_registerUndo(mol, act);
9697 MolActionRelease(act);
9699 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9700 MolActionCallback_registerUndo(mol, act);
9702 MolActionRelease(act);
9704 MainView_removeGraphic(mol->mview, i);
9710 * ngraphics -> integer
9712 * Get the number of graphic objects.
9715 s_Molecule_NGraphics(VALUE self)
9718 Data_Get_Struct(self, Molecule, mol);
9719 if (mol->mview == NULL)
9720 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9721 return INT2NUM(mol->mview->ngraphics);
9726 * get_graphic_point(graphic_index, point_index) -> value
9727 * get_graphic_points(graphic_index) -> values
9729 * Get the point_index-th control point of graphic_index-th graphic object.
9730 * Get an array of all control points with the given values.
9734 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9736 MainViewGraphic *gp;
9741 Data_Get_Struct(self, Molecule, mol);
9742 if (mol->mview == NULL)
9743 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9744 rb_scan_args(argc, argv, "11", &gval, &pval);
9745 index = NUM2INT(rb_Integer(gval));
9746 if (index < 0 || index >= mol->mview->ngraphics)
9747 rb_raise(rb_eArgError, "the graphic index is out of range");
9748 gp = mol->mview->graphics + index;
9750 pindex = NUM2INT(rb_Integer(pval));
9751 if (pindex < 0 || pindex >= gp->npoints)
9752 rb_raise(rb_eArgError, "the point index is out of range");
9753 v.x = gp->points[pindex * 3];
9754 v.y = gp->points[pindex * 3 + 1];
9755 v.z = gp->points[pindex * 3 + 2];
9756 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9757 return rb_float_new(v.x);
9759 return ValueFromVector(&v);
9762 pval = rb_ary_new();
9763 for (pindex = 0; pindex < gp->npoints; pindex++) {
9764 v.x = gp->points[pindex * 3];
9765 v.y = gp->points[pindex * 3 + 1];
9766 v.z = gp->points[pindex * 3 + 2];
9767 rb_ary_push(pval, ValueFromVector(&v));
9775 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9776 * set_graphic_points(graphic_index, new_values) -> new_values
9778 * Change the point_index-th control point of graphic_index-th graphic object.
9779 * Replace the control points with the given values.
9783 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9785 MainViewGraphic *gp;
9789 VALUE gval, pval, nval;
9791 Data_Get_Struct(self, Molecule, mol);
9792 if (mol->mview == NULL)
9793 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9794 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9795 index = NUM2INT(rb_Integer(gval));
9796 if (index < 0 || index >= mol->mview->ngraphics)
9797 rb_raise(rb_eArgError, "the graphic index is out of range");
9798 gp = mol->mview->graphics + index;
9800 pindex = NUM2INT(rb_Integer(pval));
9801 if (pindex < 0 || pindex >= gp->npoints)
9802 rb_raise(rb_eArgError, "the point index is out of range");
9803 v0.x = gp->points[pindex * 3];
9804 v0.y = gp->points[pindex * 3 + 1];
9805 v0.z = gp->points[pindex * 3 + 2];
9806 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9807 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9808 v.x = NUM2DBL(rb_Float(nval));
9810 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9811 v.x = NUM2DBL(rb_Float(nval));
9813 gp->points[7] = gp->points[11] = v.x;
9814 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9815 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9818 v.x = kInvalidFloat;
9820 } else VectorFromValue(nval, &v);
9822 gp->points[pindex * 3] = v.x;
9823 gp->points[pindex * 3 + 1] = v.y;
9824 gp->points[pindex * 3 + 2] = v.z;
9825 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9829 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9830 for (pindex = 0; pindex < gp->npoints; pindex++) {
9831 vp[pindex].x = gp->points[pindex * 3];
9832 vp[pindex].y = gp->points[pindex * 3 + 1];
9833 vp[pindex].z = gp->points[pindex * 3 + 2];
9835 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9837 pval = rb_ary_to_ary(pval);
9838 len = RARRAY_LEN(pval);
9839 if (gp->npoints < len) {
9840 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9842 } else if (gp->npoints > len) {
9845 case kMainViewGraphicLine: len2 = 2; break;
9846 case kMainViewGraphicPoly: len2 = 3; break;
9847 case kMainViewGraphicCylinder: len2 = 3; break;
9848 case kMainViewGraphicCone: len2 = 3; break;
9849 case kMainViewGraphicEllipsoid: len2 = 4; break;
9855 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9856 aval = RARRAY_PTR(pval)[pindex];
9857 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9858 v.x = NUM2DBL(rb_Float(aval));
9860 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9861 v.x = NUM2DBL(rb_Float(aval));
9863 gp->points[7] = gp->points[11] = v.x;
9864 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9866 } else VectorFromValue(aval, &v);
9867 gp->points[pindex * 3] = v.x;
9868 gp->points[pindex * 3 + 1] = v.y;
9869 gp->points[pindex * 3 + 2] = v.z;
9872 if (gp->kind == kMainViewGraphicPoly) {
9873 /* Calculate normals */
9874 s_CalculateGraphicNormals(gp);
9876 MolActionCallback_registerUndo(mol, act);
9877 MolActionRelease(act);
9878 MoleculeCallback_notifyModification(mol, 0);
9884 * get_graphic_color(graphic_index) -> value
9886 * Get the color of graphic_index-th graphic object
9889 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9891 MainViewGraphic *gp;
9894 Data_Get_Struct(self, Molecule, mol);
9895 if (mol->mview == NULL)
9896 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9897 index = NUM2INT(rb_Integer(gval));
9898 if (index < 0 || index >= mol->mview->ngraphics)
9899 rb_raise(rb_eArgError, "the graphic index is out of range");
9900 gp = mol->mview->graphics + index;
9901 return rb_ary_new3(4, rb_float_new(gp->rgba[0]), rb_float_new(gp->rgba[1]), rb_float_new(gp->rgba[2]), rb_float_new(gp->rgba[3]));
9906 * set_graphic_color(graphic_index, new_value) -> new_value
9908 * Change the color of graphic_index-th graphic object
9912 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9914 MainViewGraphic *gp;
9919 Data_Get_Struct(self, Molecule, mol);
9920 if (mol->mview == NULL)
9921 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9922 index = NUM2INT(rb_Integer(gval));
9923 if (index < 0 || index >= mol->mview->ngraphics)
9924 rb_raise(rb_eArgError, "the graphic index is out of range");
9925 gp = mol->mview->graphics + index;
9926 for (i = 0; i < 4; i++)
9928 cval = rb_ary_to_ary(cval);
9929 n = RARRAY_LEN(cval);
9930 if (n != 3 && n != 4)
9931 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9933 for (i = 0; i < n; i++) {
9934 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9938 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9939 MolActionCallback_registerUndo(mol, act);
9940 MolActionRelease(act);
9941 MoleculeCallback_notifyModification(mol, 0);
9947 * show_graphic(graphic_index) -> self
9949 * Enable the visible flag of the graphic_index-th graphic object
9953 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9955 MainViewGraphic *gp;
9958 Data_Get_Struct(self, Molecule, mol);
9959 if (mol->mview == NULL)
9960 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9961 index = NUM2INT(rb_Integer(gval));
9962 if (index < 0 || index >= mol->mview->ngraphics)
9963 rb_raise(rb_eArgError, "the graphic index is out of range");
9964 gp = mol->mview->graphics + index;
9966 MoleculeCallback_notifyModification(mol, 0);
9972 * hide_graphic(graphic_index) -> self
9974 * Disable the visible flag of the graphic_index-th graphic object
9978 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9980 MainViewGraphic *gp;
9983 Data_Get_Struct(self, Molecule, mol);
9984 if (mol->mview == NULL)
9985 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9986 index = NUM2INT(rb_Integer(gval));
9987 if (index < 0 || index >= mol->mview->ngraphics)
9988 rb_raise(rb_eArgError, "the graphic index is out of range");
9989 gp = mol->mview->graphics + index;
9991 MoleculeCallback_notifyModification(mol, 0);
9999 * Show the string in the info text box.
10002 s_Molecule_ShowText(VALUE self, VALUE arg)
10005 Data_Get_Struct(self, Molecule, mol);
10006 if (mol->mview != NULL)
10007 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10011 #pragma mark ------ MD Support ------
10015 * md_arena -> MDArena
10017 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10018 * this molecule, a new arena is created.
10021 s_Molecule_MDArena(VALUE self)
10025 Data_Get_Struct(self, Molecule, mol);
10026 if (mol->arena == NULL)
10028 retval = ValueFromMDArena(mol->arena);
10034 * set_parameter_attr(type, index, key, value, src) -> value
10036 * This method is used only internally.
10039 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10041 /* This method is called from MolAction to change a MM parameter attribute. */
10044 ParameterRef *pref;
10046 Data_Get_Struct(self, Molecule, mol);
10047 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10048 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10050 /* This is the special part of this method; it allows modification of the src field. */
10051 /* (ParameterRef#set_attr sets 0 to the src field) */
10052 Data_Get_Struct(pval, ParameterRef, pref);
10053 up = ParameterRefGetPar(pref);
10054 up->bond.src = FIX2INT(sval);
10061 * parameter -> Parameter
10063 * Get the local parameter of this molecule. If not defined, returns nil.
10066 s_Molecule_Parameter(VALUE self)
10069 Data_Get_Struct(self, Molecule, mol);
10070 /* if (mol->par == NULL)
10072 return s_NewParameterValueFromValue(self);
10077 * start_step -> Integer
10079 * Returns the start step (defined by dcd format).
10082 s_Molecule_StartStep(VALUE self)
10085 Data_Get_Struct(self, Molecule, mol);
10086 return INT2NUM(mol->startStep);
10091 * start_step = Integer
10093 * Set the start step (defined by dcd format).
10096 s_Molecule_SetStartStep(VALUE self, VALUE val)
10099 Data_Get_Struct(self, Molecule, mol);
10100 mol->startStep = NUM2INT(rb_Integer(val));
10106 * steps_per_frame -> Integer
10108 * Returns the number of steps between frames (defined by dcd format).
10111 s_Molecule_StepsPerFrame(VALUE self)
10114 Data_Get_Struct(self, Molecule, mol);
10115 return INT2NUM(mol->stepsPerFrame);
10120 * steps_per_frame = Integer
10122 * Set the number of steps between frames (defined by dcd format).
10125 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10128 Data_Get_Struct(self, Molecule, mol);
10129 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10135 * ps_per_step -> Float
10137 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10140 s_Molecule_PsPerStep(VALUE self)
10143 Data_Get_Struct(self, Molecule, mol);
10144 return rb_float_new(mol->psPerStep);
10149 * ps_per_step = Float
10151 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10154 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10157 Data_Get_Struct(self, Molecule, mol);
10158 mol->psPerStep = NUM2DBL(rb_Float(val));
10163 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10165 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.");
10168 #pragma mark ------ MO Handling ------
10172 * selectedMO -> IntGroup
10174 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10175 * is not selected, returns nil. If the MO info table is selected but no MOs
10176 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10179 s_Molecule_SelectedMO(VALUE self)
10184 Data_Get_Struct(self, Molecule, mol);
10185 if (mol->mview == NULL)
10187 ig = MainView_selectedMO(mol->mview);
10190 IntGroupOffset(ig, 1);
10191 val = ValueFromIntGroup(ig);
10192 IntGroupRelease(ig);
10198 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10200 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10201 * If the molecule does not contain a basis set information, then returns nil.
10204 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10207 Vector o, dx, dy, dz;
10210 Int npoints = 80 * 80 * 80;
10211 Data_Get_Struct(self, Molecule, mol);
10212 if (mol->bset == NULL)
10214 rb_scan_args(argc, argv, "01", &nval);
10216 npoints = NUM2INT(rb_Integer(nval));
10217 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10219 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));
10223 s_Cubegen_callback(double progress, void *ref)
10225 MyAppCallback_setProgressValue(progress);
10226 if (MyAppCallback_checkInterrupt())
10233 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10234 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10236 * Calculate the molecular orbital with number mo and create a 'cube' file.
10237 * In the first form, the cube size is estimated from the atomic coordinates. In the
10238 * second form, the cube dimension is explicitly given.
10239 * Returns fname when successful, nil otherwise.
10240 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10241 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10242 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10245 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10247 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10249 Int mono, nx, ny, nz, npoints;
10250 Vector o, dx, dy, dz;
10253 Data_Get_Struct(self, Molecule, mol);
10254 if (mol->bset == NULL)
10255 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10256 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10258 /* Set up parameters */
10259 mono = NUM2INT(rb_Integer(mval));
10260 if (mono < 0 || mono > mol->bset->ncomps)
10261 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d, or 0 as 'arbitrary vector')", mono, mol->bset->ncomps);
10263 if (mol->bset->rflag != 0)
10264 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10265 mono += mol->bset->ncomps;
10268 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10269 /* Automatic grid formation */
10271 npoints = NUM2INT(rb_Integer(oval));
10275 else if (npoints < 8)
10276 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10277 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10278 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10282 VectorFromValue(oval, &o);
10283 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10284 VectorFromValue(dxval, &dx);
10286 dx.x = NUM2DBL(rb_Float(dxval));
10289 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10290 VectorFromValue(dyval, &dy);
10292 dy.y = NUM2DBL(rb_Float(dyval));
10295 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10296 VectorFromValue(dzval, &dz);
10298 dz.z = NUM2DBL(rb_Float(dzval));
10301 nx = NUM2INT(rb_Integer(nxval));
10302 ny = NUM2INT(rb_Integer(nyval));
10303 nz = NUM2INT(rb_Integer(nzval));
10304 if (nx <= 0 || ny <= 0 || nz <= 0)
10305 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10306 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10307 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);
10311 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10314 else if (index < 0)
10315 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10317 /* Output to file */
10318 MoleculeCallback_displayName(mol, buf, sizeof buf);
10319 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10321 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10323 /* Discard the cube */
10324 MoleculeClearCubeAtIndex(mol, index);
10332 * Clear the MO surface if present.
10335 s_Molecule_ClearSurface(VALUE self)
10338 Data_Get_Struct(self, Molecule, mol);
10339 if (mol->mcube != NULL)
10340 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10348 * Hide the MO surface if present.
10351 s_Molecule_HideSurface(VALUE self)
10354 Data_Get_Struct(self, Molecule, mol);
10355 if (mol->mcube != NULL) {
10356 mol->mcube->hidden = 1;
10357 MoleculeCallback_notifyModification(mol, 0);
10366 * Show the MO surface if present.
10369 s_Molecule_ShowSurface(VALUE self)
10372 Data_Get_Struct(self, Molecule, mol);
10373 if (mol->mcube != NULL) {
10374 mol->mcube->hidden = 0;
10375 MoleculeCallback_notifyModification(mol, 0);
10382 * create_surface(mo, attr = nil)
10384 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10385 * then it denotes the beta orbital.
10386 * If mo is nil, then the attributes of the current surface are modified.
10388 * :npoints : the approximate number of grid points
10389 * :expand : the scale factor to expand/shrink the display box size for each atom,
10390 * :thres : the threshold for the isovalue surface
10391 * If the molecule does not contain MO information, raises exception.
10394 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10397 Vector o, dx, dy, dz;
10398 Int nmo, nx, ny, nz, i;
10399 Int need_recalc = 0;
10400 VALUE nval, hval, aval;
10405 Data_Get_Struct(self, Molecule, mol);
10406 rb_scan_args(argc, argv, "11", &nval, &hval);
10407 if (mol->bset == NULL)
10408 rb_raise(rb_eMolbyError, "No MO information is given");
10409 if (nval == Qnil) {
10411 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10412 nmo = mol->bset->nmos + 1;
10414 nmo = NUM2INT(rb_Integer(nval));
10415 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10416 rb_raise(rb_eMolbyError, "MO index (%d) is out of range; should be 1..%d (or -1..-%d for beta orbitals); (0 is acceptable as arbitrary vector)", nmo, mol->bset->nmos, mol->bset->ncomps);
10418 nmo = -nmo + mol->bset->ncomps;
10420 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10421 npoints = NUM2INT(rb_Integer(aval));
10423 } else if (mol->mcube != NULL) {
10424 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10425 } else npoints = 80 * 80 * 80;
10426 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10427 expand = NUM2DBL(rb_Float(aval));
10428 } else if (mol->mcube != NULL) {
10429 expand = mol->mcube->expand;
10430 } else expand = 1.0;
10431 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10432 thres = NUM2DBL(rb_Float(aval));
10433 } else if (mol->mcube != NULL) {
10434 thres = mol->mcube->thres;
10435 } else thres = 0.05;
10436 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10437 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10438 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10439 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10440 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10442 for (nx = 0; nx < 2; nx++) {
10443 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10444 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10445 aval = rb_ary_to_ary(aval);
10446 if (RARRAY_LEN(aval) < 3) {
10448 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10450 for (i = 0; i < 4; i++)
10451 d[i] = mol->mcube->c[nx].rgba[i];
10452 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10453 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10454 if (d[i] < 0.0 && d[i] > 1.0)
10457 for (i = 0; i < 4; i++)
10458 mol->mcube->c[nx].rgba[i] = d[i];
10461 if (mol->mcube->expand != expand)
10463 mol->mcube->thres = thres;
10464 mol->mcube->expand = expand;
10466 if (mol->mcube->idn < 0)
10467 return self; /* Only set attributes for now */
10469 nmo = mol->mcube->idn; /* Force recalculation */
10471 if (MoleculeUpdateMCube(mol, nmo) != 0)
10472 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10478 * set_surface_attr(attr = nil)
10480 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10483 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10488 return s_Molecule_CreateSurface(2, args, self);
10495 * Get the number of electrostatic potential info.
10498 s_Molecule_NElpots(VALUE self)
10501 Data_Get_Struct(self, Molecule, mol);
10502 return INT2NUM(mol->nelpots);
10509 * Get the electrostatic potential info at the given index. If present, then the
10510 * return value is [Vector, Float] (position and potential). If not present, then
10514 s_Molecule_Elpot(VALUE self, VALUE ival)
10518 Data_Get_Struct(self, Molecule, mol);
10519 idx = NUM2INT(rb_Integer(ival));
10520 if (idx < 0 || idx >= mol->nelpots)
10522 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10529 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10530 * cube and marching cube information are discarded. This operation is _not_ undoable!
10533 s_Molecule_ClearBasisSet(VALUE self)
10536 Data_Get_Struct(self, Molecule, mol);
10538 if (mol->bset != NULL) {
10539 BasisSetRelease(mol->bset);
10542 if (mol->mcube != NULL) {
10543 MoleculeDeallocateMCube(mol->mcube);
10552 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10554 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10555 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10559 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10562 int sym, nprims, a_idx, n;
10563 Data_Get_Struct(self, Molecule, mol);
10564 a_idx = NUM2INT(rb_Integer(aval));
10565 sym = NUM2INT(rb_Integer(symval));
10566 nprims = NUM2INT(rb_Integer(npval));
10567 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10569 rb_raise(rb_eMolbyError, "Molecule is emptry");
10571 rb_raise(rb_eMolbyError, "Low memory");
10573 rb_raise(rb_eMolbyError, "Unknown orbital type");
10575 rb_raise(rb_eMolbyError, "Unknown error");
10581 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10583 * To be used internally. Add a gaussian primitive coefficients.
10586 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10590 Double exponent, contraction, contraction_sp;
10591 Data_Get_Struct(self, Molecule, mol);
10592 exponent = NUM2DBL(rb_Float(expval));
10593 contraction = NUM2DBL(rb_Float(cval));
10594 contraction_sp = NUM2DBL(rb_Float(cspval));
10595 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10597 rb_raise(rb_eMolbyError, "Molecule is emptry");
10599 rb_raise(rb_eMolbyError, "Low memory");
10601 rb_raise(rb_eMolbyError, "Unknown error");
10607 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10609 * Get the Gaussian shell information for the given MO coefficient index.
10610 * The symmetry code is the same as in add_gaussian_orbital_shell.
10611 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10612 * is the number of MO component belonging to this shell.
10615 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10620 Data_Get_Struct(self, Molecule, mol);
10621 if (mol->bset == NULL)
10622 rb_raise(rb_eMolbyError, "No basis set information is defined");
10623 s_idx = NUM2INT(rb_Integer(sval));
10624 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10626 sp = mol->bset->shells + s_idx;
10629 case kGTOType_S: sym = 0; break;
10630 case kGTOType_SP: sym = -1; break;
10631 case kGTOType_P: sym = 1; break;
10632 case kGTOType_D: sym = 2; break;
10633 case kGTOType_D5: sym = -2; break;
10634 case kGTOType_F: sym = 3; break;
10635 case kGTOType_F7: sym = -3; break;
10636 case kGTOType_G: sym = 4; break;
10637 case kGTOType_G9: sym = -4; break;
10639 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10641 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10646 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10648 * Get the Gaussian primitive coefficients for the given MO component.
10651 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10657 VALUE retval, aval;
10658 Data_Get_Struct(self, Molecule, mol);
10659 if (mol->bset == NULL)
10660 rb_raise(rb_eMolbyError, "No basis set information is defined");
10661 s_idx = NUM2INT(rb_Integer(sval));
10662 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10664 sp = mol->bset->shells + s_idx;
10665 pp = mol->bset->priminfos + sp->p_idx;
10666 retval = rb_ary_new2(sp->nprim);
10667 for (i = 0; i < sp->nprim; i++) {
10668 if (sp->sym == kGTOType_SP) {
10669 /* With P contraction coefficient */
10670 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10672 /* Without P contraction coefficient */
10673 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10675 rb_ary_store(retval, i, aval);
10682 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10684 * Get the Gaussian shell information for the given MO coefficient index.
10687 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10690 Int n, c, atom_idx, shell_idx;
10692 Data_Get_Struct(self, Molecule, mol);
10693 if (mol->bset == NULL)
10694 rb_raise(rb_eMolbyError, "No basis set information is defined");
10695 c = NUM2INT(rb_Integer(cval));
10696 if (c < 0 || c >= mol->bset->ncomps)
10698 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10700 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10701 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10706 * clear_mo_coefficients
10708 * Clear the existing MO coefficients.
10711 s_Molecule_ClearMOCoefficients(VALUE self)
10714 Data_Get_Struct(self, Molecule, mol);
10715 if (mol->bset != NULL) {
10716 if (mol->bset->moenergies != NULL) {
10717 free(mol->bset->moenergies);
10718 mol->bset->moenergies = NULL;
10720 if (mol->bset->mo != NULL) {
10721 free(mol->bset->mo);
10722 mol->bset->mo = NULL;
10724 mol->bset->nmos = 0;
10731 * set_mo_coefficients(idx, energy, coefficients)
10733 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10734 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10735 * Energy is the MO energy, and coefficients is an array
10736 * of MO coefficients.
10739 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10742 Int idx, ncomps, i;
10745 Data_Get_Struct(self, Molecule, mol);
10746 idx = NUM2INT(rb_Integer(ival));
10747 energy = NUM2DBL(rb_Float(eval));
10748 aval = rb_ary_to_ary(aval);
10749 ncomps = RARRAY_LEN(aval);
10750 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10751 if (coeffs == NULL) {
10755 for (i = 0; i < ncomps; i++)
10756 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10757 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10760 rb_raise(rb_eMolbyError, "Molecule is emptry");
10762 rb_raise(rb_eMolbyError, "Low memory");
10764 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10766 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10768 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10770 rb_raise(rb_eMolbyError, "Unknown error");
10776 * get_mo_coefficients(idx)
10778 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10781 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10784 Int idx, ncomps, n;
10788 Data_Get_Struct(self, Molecule, mol);
10789 idx = NUM2INT(rb_Integer(ival));
10792 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10794 rb_raise(rb_eMolbyError, "Molecule is emptry");
10796 rb_raise(rb_eMolbyError, "No basis set information is present");
10798 return Qnil; /* Silently returns nil */
10799 retval = rb_ary_new2(ncomps);
10800 for (n = 0; n < ncomps; n++)
10801 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10808 * get_mo_energy(idx)
10810 * To be used internally. Get the MO energy for the given MO index (1-based).
10813 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10818 Data_Get_Struct(self, Molecule, mol);
10819 idx = NUM2INT(rb_Integer(ival));
10820 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10822 rb_raise(rb_eMolbyError, "Molecule is emptry");
10824 rb_raise(rb_eMolbyError, "No basis set information is present");
10827 return rb_float_new(energy);
10830 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10833 s_InitMOInfoKeys(void)
10835 if (sTypeSym == 0) {
10836 sTypeSym = ID2SYM(rb_intern("type"));
10837 sAlphaSym = ID2SYM(rb_intern("alpha"));
10838 sBetaSym = ID2SYM(rb_intern("beta"));
10839 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10840 sNshellsSym = ID2SYM(rb_intern("nshells"));
10846 * set_mo_info(hash)
10848 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10849 * :alpha=>integer, :beta=>integer
10852 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10856 Int rflag, na, nb, n;
10858 Data_Get_Struct(self, Molecule, mol);
10859 if (mol->bset != NULL) {
10860 rflag = mol->bset->rflag;
10861 na = mol->bset->ne_alpha;
10862 nb = mol->bset->ne_beta;
10868 if (hval != Qnil) {
10869 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10870 s = StringValuePtr(aval);
10871 if (strcasecmp(s, "RHF") == 0)
10873 else if (strcasecmp(s, "UHF") == 0)
10875 else if (strcasecmp(s, "ROHF") == 0)
10878 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10879 n = NUM2INT(rb_Integer(aval));
10883 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10884 n = NUM2INT(rb_Integer(aval));
10888 MoleculeSetMOInfo(mol, rflag, na, nb);
10897 * Get the MO info. The key is as described in set_mo_info.
10898 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10901 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10904 Data_Get_Struct(self, Molecule, mol);
10905 if (mol->bset == NULL)
10907 if (kval == sTypeSym) {
10908 switch (mol->bset->rflag) {
10909 case 0: return rb_str_new2("UHF");
10910 case 1: return rb_str_new2("RHF");
10911 case 2: return rb_str_new2("ROHF");
10912 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10914 } else if (kval == sAlphaSym) {
10915 return INT2NUM(mol->bset->ne_alpha);
10916 } else if (kval == sBetaSym) {
10917 return INT2NUM(mol->bset->ne_beta);
10918 } else if (kval == sNcompsSym) {
10919 return INT2NUM(mol->bset->ncomps);
10920 } else if (kval == sNshellsSym) {
10921 return INT2NUM(mol->bset->nshells);
10923 kval = rb_inspect(kval);
10924 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10925 return Qnil; /* Does not reach here */
10933 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10936 s_Molecule_MOType(VALUE self)
10938 return s_Molecule_GetMOInfo(self, sTypeSym);
10941 #pragma mark ------ Molecular Topology ------
10945 * search_equivalent_atoms(ig = nil)
10947 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10950 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10956 Data_Get_Struct(self, Molecule, mol);
10957 if (mol->natoms == 0)
10959 rb_scan_args(argc, argv, "01", &val);
10961 ig = IntGroupFromValue(val);
10963 result = MoleculeSearchEquivalentAtoms(mol, ig);
10964 if (result == NULL)
10965 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10967 IntGroupRelease(ig);
10968 val = rb_ary_new2(mol->natoms);
10969 for (i = 0; i < mol->natoms; i++)
10970 rb_ary_push(val, INT2NUM(result[i]));
10977 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10979 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10980 * Name is the name of the new pi anchor, and group is the atoms that define
10981 * the pi system. Type (a String) is an atom type for MM implementation.
10982 * Weights represent the relative significance of the component atoms; if omitted, then
10983 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10984 * The weight values will be normalized so that the sum of the weights is 1.0.
10985 * The weight values must be positive.
10986 * Index is the atom index where the created pi-anchor is inserted in the
10987 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10988 * having the largest index.
10989 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10990 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10993 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10998 Int i, n, idx, last_component;
11002 if (argc < 2 || argc >= 6)
11003 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11007 Data_Get_Struct(self, Molecule, mol);
11008 ig = IntGroupFromValue(gval);
11009 memset(&a, 0, sizeof(a));
11010 memset(&an, 0, sizeof(an));
11011 strncpy(a.aname, StringValuePtr(nval), 4);
11012 if (a.aname[0] == '_')
11013 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11014 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11015 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11016 if (n >= mol->natoms) {
11017 AtomConnectResize(&an.connect, 0);
11018 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11020 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11021 last_component = n;
11023 if (an.connect.count == 0)
11024 rb_raise(rb_eMolbyError, "no atoms are specified");
11025 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11026 for (i = 0; i < an.connect.count; i++) {
11027 an.coeffs[i] = 1.0 / an.connect.count;
11029 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11031 if (argv[0] != Qnil)
11032 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11036 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11037 if (argv[0] != Qnil) {
11038 VALUE aval = rb_ary_to_ary(argv[0]);
11040 if (RARRAY_LEN(aval) != an.connect.count)
11041 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11042 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11043 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11045 rb_raise(rb_eMolbyError, "the weight value must be positive");
11049 for (i = 0; i < an.connect.count; i++)
11050 an.coeffs[i] /= sum;
11055 if (argc > 0 && argv[0] != Qnil) {
11057 idx = NUM2INT(rb_Integer(argv[0]));
11059 if (idx < 0 || idx > mol->natoms) {
11060 /* Immediately after the last specified atom */
11061 idx = last_component + 1;
11063 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11064 memmove(a.anchor, &an, sizeof(PiAnchor));
11065 /* Use residue information of the last specified atom */
11066 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11067 a.resSeq = ap->resSeq;
11068 strncpy(a.resName, ap->resName, 4);
11069 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11071 MoleculeCalculatePiAnchorPosition(mol, idx);
11072 aref = AtomRefNew(mol, idx);
11073 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11076 #pragma mark ------ Molecular Properties ------
11080 * set_property(name, value[, index]) -> value
11081 * set_property(name, values, group) -> values
11083 * Set molecular property. A property is a floating-point number with a specified name,
11084 * and can be set for each frame separately. The name of the property is given as a String.
11085 * The value can be a single floating point number, which is set to the current frame.
11089 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11092 VALUE nval, vval, ival;
11095 Int i, n, idx, fidx;
11097 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11098 Data_Get_Struct(self, Molecule, mol);
11099 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11100 idx = NUM2INT(rb_Integer(nval));
11101 if (idx < 0 || idx >= mol->nmolprops)
11102 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11104 name = StringValuePtr(nval);
11105 idx = MoleculeLookUpProperty(mol, name);
11107 idx = MoleculeCreateProperty(mol, name);
11109 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11112 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11114 fidx = mol->cframe;
11116 fidx = NUM2INT(rb_Integer(ival));
11117 n = MoleculeGetNumberOfFrames(mol);
11118 if (fidx < 0 || fidx >= n)
11119 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11121 ig = IntGroupNewWithPoints(fidx, 1, -1);
11122 dp = (Double *)malloc(sizeof(Double));
11123 *dp = NUM2DBL(rb_Float(vval));
11126 vval = rb_ary_to_ary(vval);
11127 ig = IntGroupFromValue(ival);
11128 n = IntGroupGetCount(ig);
11130 rb_raise(rb_eMolbyError, "No frames are specified");
11131 if (RARRAY_LEN(vval) < n)
11132 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11133 dp = (Double *)calloc(sizeof(Double), n);
11134 for (i = 0; i < n; i++)
11135 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11138 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11140 IntGroupRelease(ig);
11146 * get_property(name[, index]) -> value
11147 * get_property(name, group) -> values
11149 * Get molecular property. In the first form, a property value for a single frame is returned.
11150 * (If index is omitted, then the value for the current frame is given)
11151 * In the second form, an array of property values for the given frames is returned.
11152 * If name is not one of known properties or a valid index integer, exception is raised.
11155 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11161 Int i, n, idx, fidx;
11163 rb_scan_args(argc, argv, "11", &nval, &ival);
11164 Data_Get_Struct(self, Molecule, mol);
11165 if (mol->nmolprops == 0)
11166 rb_raise(rb_eMolbyError, "The molecule has no properties");
11167 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11168 idx = NUM2INT(rb_Integer(nval));
11169 if (idx < 0 || idx >= mol->nmolprops)
11170 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11172 name = StringValuePtr(nval);
11173 idx = MoleculeLookUpProperty(mol, name);
11175 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11177 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11179 fidx = mol->cframe;
11181 fidx = NUM2INT(rb_Integer(ival));
11182 n = MoleculeGetNumberOfFrames(mol);
11183 if (fidx < 0 || fidx >= n)
11184 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11186 ig = IntGroupNewWithPoints(fidx, 1, -1);
11187 ival = INT2FIX(fidx);
11190 ig = IntGroupFromValue(ival);
11191 n = IntGroupGetCount(ig);
11193 return rb_ary_new();
11195 dp = (Double *)calloc(sizeof(Double), n);
11196 MoleculeGetProperty(mol, idx, ig, dp);
11197 if (FIXNUM_P(ival))
11198 ival = rb_float_new(dp[0]);
11200 ival = rb_ary_new();
11201 for (i = n - 1; i >= 0; i--) {
11202 nval = rb_float_new(dp[i]);
11203 rb_ary_store(ival, i, nval);
11207 IntGroupRelease(ig);
11213 * property_names -> Array
11215 * Get an array of property names.
11218 s_Molecule_PropertyNames(VALUE self)
11223 Data_Get_Struct(self, Molecule, mol);
11224 rval = rb_ary_new();
11225 for (i = mol->nmolprops - 1; i >= 0; i--) {
11226 nval = rb_str_new2(mol->molprops[i].propname);
11227 rb_ary_store(rval, i, nval);
11232 #pragma mark ------ Class methods ------
11236 * current -> Molecule
11238 * Get the currently "active" molecule.
11241 s_Molecule_Current(VALUE klass)
11243 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11248 * Molecule[] -> Molecule
11249 * Molecule[n] -> Molecule
11250 * Molecule[name] -> Molecule
11251 * Molecule[name, k] -> Molecule
11252 * Molecule[regex] -> Molecule
11253 * Molecule[regex, k] -> Molecule
11255 * Molecule[] is equivalent to Molecule.current.
11256 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11257 * Molecule[name] gives the first document (in the order of creation time) that has
11258 * the given name. If a second argument (k) is given, the k-th document that has the
11259 * given name is returned.
11260 * Molecule[regex] gives the first document (in the order of creation time) that
11261 * has a name matching the regular expression. If a second argument (k) is given,
11262 * the k-th document that has a name matching the re is returned.
11265 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11271 rb_scan_args(argc, argv, "02", &val, &kval);
11273 return s_Molecule_Current(klass);
11274 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11275 idx = NUM2INT(val);
11276 mol = MoleculeCallback_moleculeAtIndex(idx);
11277 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11278 char *p = StringValuePtr(val);
11279 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11280 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11281 MoleculeCallback_displayName(mol, buf, sizeof buf);
11282 if (strcmp(buf, p) == 0 && --k == 0)
11285 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11286 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11287 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11289 MoleculeCallback_displayName(mol, buf, sizeof buf);
11290 name = rb_str_new2(buf);
11291 if (rb_reg_match(val, name) != Qnil && --k == 0)
11294 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11298 else return ValueFromMolecule(mol);
11303 * list -> array of Molecules
11305 * Get the list of molecules associated to the documents, in the order of creation
11306 * time of the document. If no document is open, returns an empry array.
11309 s_Molecule_List(VALUE klass)
11315 ary = rb_ary_new();
11316 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11317 rb_ary_push(ary, ValueFromMolecule(mol));
11325 * ordered_list -> array of Molecules
11327 * Get the list of molecules associated to the documents, in the order of front-to-back
11328 * ordering of the associated window. If no document is open, returns an empry array.
11331 s_Molecule_OrderedList(VALUE klass)
11337 ary = rb_ary_new();
11338 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11339 rb_ary_push(ary, ValueFromMolecule(mol));
11345 #pragma mark ------ Call Subprocess ------
11347 /* The callback functions for call_subprocess_async */
11349 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11352 VALUE procval, retval, args[2];
11353 args[0] = ValueFromMolecule(mol);
11354 args[1] = INT2NUM(status);
11355 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11356 if (procval != Qnil) {
11357 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11358 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11365 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11368 VALUE procval, retval, args[2];
11369 args[0] = ValueFromMolecule(mol);
11370 args[1] = INT2NUM(tcount);
11371 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11372 if (procval != Qnil) {
11373 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11374 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11382 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11384 * Call subprocess asynchronically.
11385 * If end_callback is given, it will be called (with two arguments self and termination status)
11386 * when the subprocess terminated.
11387 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11388 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11389 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11390 * filename begins with ">>", then the message will be appended to the file.
11391 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11392 * If the argument is nil, then the message will be sent to the Ruby console.
11393 * Returns the process ID as an integer.
11396 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11398 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11402 FILE *fpout, *fperr;
11403 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11404 Data_Get_Struct(self, Molecule, mol);
11406 if (stdout_val == Qnil) {
11409 sout = StringValuePtr(stdout_val);
11410 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11413 if (strncmp(sout, ">>", 2) == 0) {
11415 fpout = fopen(sout, "a");
11419 fpout = fopen(sout, "w");
11422 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11425 if (stderr_val == Qnil) {
11428 serr = StringValuePtr(stderr_val);
11429 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11432 if (strncmp(serr, ">>", 2) == 0) {
11434 fpout = fopen(serr, "a");
11438 fperr = fopen(serr, "w");
11441 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11445 /* Register procs as instance variables */
11446 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11447 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11448 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11449 if (fpout != NULL && fpout != (FILE *)1)
11451 if (fperr != NULL && fperr != (FILE *)1)
11456 #pragma mark ====== Define Molby Classes ======
11463 /* Define module Molby */
11464 rb_mMolby = rb_define_module("Molby");
11466 /* Define Vector3D, Transform, IntGroup */
11469 /* Define MDArena */
11470 Init_MolbyMDTypes();
11472 /* class Molecule */
11473 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11475 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11476 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11477 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11478 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11479 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11481 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11482 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11483 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11484 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11485 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11486 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11487 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11488 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11489 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11490 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11491 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11492 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11493 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11494 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11495 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11496 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11497 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11498 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11499 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11500 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11501 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11502 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11504 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11505 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11506 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11507 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11508 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11510 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11511 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11512 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11513 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11514 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11515 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11516 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11517 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11518 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11519 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11520 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11521 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11522 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11523 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11524 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11526 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11527 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11528 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11529 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11530 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11532 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11533 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11534 rb_define_alias(rb_cMolecule, "+", "add");
11535 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11536 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11537 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11538 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11539 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11540 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11541 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11542 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11543 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11544 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11545 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11546 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11547 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11548 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11549 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11550 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11551 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11552 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11553 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11554 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11555 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11557 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11558 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11559 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11560 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11561 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11563 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11564 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11565 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11566 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11567 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11568 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11569 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11570 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11571 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11573 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11574 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11575 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11576 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11577 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11578 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11579 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11580 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11581 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11582 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11583 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11584 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11585 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11586 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11587 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11588 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11589 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11590 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11591 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11592 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11593 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11594 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11596 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11597 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11598 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11599 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11600 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11601 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11602 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11604 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11605 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11606 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11607 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11608 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11609 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11610 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11611 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11612 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11613 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11614 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11615 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11616 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11618 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11619 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11620 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11621 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11622 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11623 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11625 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11626 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11627 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11628 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11629 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11630 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11631 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11632 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11633 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11634 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11635 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11636 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11637 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11638 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11639 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11640 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11641 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11642 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11643 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11644 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11645 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11646 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11647 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11648 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11649 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11650 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11651 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11652 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11653 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11654 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11655 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11656 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11657 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11658 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11659 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11660 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11661 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11662 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11663 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11664 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11665 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11666 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11667 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11668 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11669 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11670 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11671 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11672 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11673 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11674 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11675 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11676 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11677 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11678 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11680 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11681 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11682 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11683 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11684 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11685 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11686 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11687 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11688 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11689 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11690 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11691 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11692 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11694 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11695 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11696 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11697 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11698 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11699 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11700 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11701 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11702 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11703 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11704 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11705 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11706 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11707 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11709 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11710 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11711 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11712 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11713 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11714 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11715 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11716 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11717 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11718 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11719 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11720 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11721 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11722 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11723 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11724 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11725 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11726 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11727 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11728 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11729 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11730 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11731 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11733 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11734 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11736 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11737 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11738 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11740 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11741 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11742 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11743 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11745 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11747 /* class MolEnumerable */
11748 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11749 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11750 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11751 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11752 rb_define_alias(rb_cMolEnumerable, "size", "length");
11753 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11754 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11756 /* class AtomRef */
11757 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11758 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11760 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11761 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11762 s_AtomAttrDefTable[i].id = rb_intern(buf);
11763 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11765 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11767 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11768 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11769 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11770 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11771 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11772 rb_global_variable(&s_SetAtomAttrString);
11773 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11774 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11776 /* class Parameter */
11777 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11778 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11779 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11780 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11781 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11782 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11783 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11784 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11785 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11786 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11787 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11788 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11789 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11790 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11791 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11792 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11793 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11794 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11795 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11796 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11797 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11798 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11799 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11800 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11801 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11802 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11803 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11804 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11805 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11806 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11807 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11808 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11809 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11810 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11811 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11812 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11813 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11814 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11815 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11816 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11817 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11818 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11819 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11820 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11821 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11822 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11823 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11824 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11825 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11826 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11827 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11828 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11829 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11830 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11832 /* class ParEnumerable */
11833 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11834 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11835 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11836 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11837 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11838 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11839 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11840 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11841 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11842 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11843 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11845 /* class ParameterRef */
11846 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11847 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11849 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11850 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11851 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11852 if (s_ParameterAttrDefTable[i].symref != NULL)
11853 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11854 if (s_ParameterAttrDefTable[i].setter != NULL) {
11856 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11859 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11860 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11861 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11862 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11863 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11864 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11865 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11866 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11868 /* class MolbyError */
11869 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11871 /* module Kernel */
11872 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11873 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11874 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11875 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11876 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11877 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11878 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11879 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11880 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11881 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11882 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11883 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11884 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11885 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11886 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11887 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11888 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11889 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11890 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11891 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11892 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11893 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11894 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11895 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11898 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11900 s_ID_equal = rb_intern("==");
11901 g_RubyID_call = rb_intern("call");
11903 s_InitMOInfoKeys();
11905 /* Symbols for graphics */
11906 s_LineSym = ID2SYM(rb_intern("line"));
11907 s_PolySym = ID2SYM(rb_intern("poly"));
11908 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11909 s_ConeSym = ID2SYM(rb_intern("cone"));
11910 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11913 #pragma mark ====== Interface with RubyDialog class ======
11916 RubyDialogCallback_parentModule(void)
11918 return (RubyValue)rb_mMolby;
11921 #pragma mark ====== External functions ======
11923 static VALUE s_ruby_top_self = Qfalse;
11924 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11925 static VALUE s_ruby_export_local_variables = Qfalse;
11928 s_evalRubyScriptOnMoleculeSub(VALUE val)
11930 void **ptr = (void **)val;
11931 Molecule *mol = (Molecule *)ptr[1];
11932 VALUE sval, fnval, lnval, retval;
11935 /* Clear the error information (store in the history array if necessary) */
11936 sval = rb_errinfo();
11937 if (sval != Qnil) {
11938 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11939 rb_set_errinfo(Qnil);
11942 if (s_ruby_top_self == Qfalse) {
11943 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11945 if (s_ruby_get_binding_for_molecule == Qfalse) {
11947 "lambda { |_mol_, _bind_| \n"
11948 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11949 " _proc_.call(_mol_) } ";
11950 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11951 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11953 if (s_ruby_export_local_variables == Qfalse) {
11955 "lambda { |_bind_| \n"
11956 " # find local variables newly defined in _bind_ \n"
11957 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11958 " _a_.each { |_vsym_| \n"
11959 " _vname_ = _vsym_.to_s \n"
11960 " _vval_ = _bind_.eval(_vname_) \n"
11961 " # Define local variable \n"
11962 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11963 " # Then set value \n"
11964 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11967 s_ruby_export_local_variables = rb_eval_string(s2);
11968 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11970 if (ptr[2] == NULL) {
11972 /* String literal: we need to specify string encoding */
11973 #if defined(__WXMSW__)
11974 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11976 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11978 sval = rb_str_new2(scr);
11980 fnval = rb_str_new2("(eval)");
11981 lnval = INT2FIX(0);
11983 sval = rb_str_new2((char *)ptr[0]);
11984 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11985 lnval = INT2FIX(1);
11987 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11989 VALUE mval = ValueFromMolecule(mol);
11990 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11992 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11994 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12000 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12004 VALUE save_interrupt_flag;
12005 /* char *save_ruby_sourcefile;
12006 int save_ruby_sourceline; */
12007 if (gMolbyIsCheckingInterrupt) {
12008 MolActionAlertRubyIsRunning();
12010 return (RubyValue)Qnil;
12013 args[0] = (void *)script;
12014 args[1] = (void *)mol;
12015 args[2] = (void *)fname;
12016 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12017 /* save_ruby_sourcefile = ruby_sourcefile;
12018 save_ruby_sourceline = ruby_sourceline; */
12019 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12020 if (*status != 0) {
12021 /* Is this 'exit' exception? */
12022 VALUE last_exception = rb_gv_get("$!");
12023 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12024 /* Capture exit and return the status value */
12025 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12027 rb_set_errinfo(Qnil);
12030 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12031 /* ruby_sourcefile = save_ruby_sourcefile;
12032 ruby_sourceline = save_ruby_sourceline; */
12038 Ruby_showValue(RubyValue value, char **outValueString)
12040 VALUE val = (VALUE)value;
12041 if (gMolbyIsCheckingInterrupt) {
12042 MolActionAlertRubyIsRunning();
12049 val = rb_protect(rb_inspect, val, &status);
12053 str = StringValuePtr(val);
12054 if (outValueString != NULL)
12055 *outValueString = strdup(str);
12056 MyAppCallback_showScriptMessage("%s", str);
12058 if (outValueString != NULL)
12059 *outValueString = NULL;
12065 Ruby_showError(int status)
12067 static const int tag_raise = 6;
12068 char *msg = NULL, *msg2;
12069 VALUE val, backtrace;
12070 int interrupted = 0;
12071 if (status == tag_raise) {
12072 VALUE errinfo = rb_errinfo();
12073 VALUE eclass = CLASS_OF(errinfo);
12074 if (eclass == rb_eInterrupt) {
12080 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12082 val = rb_eval_string_protect("$!.to_s", &status);
12084 msg = RSTRING_PTR(val);
12085 else msg = "(message not available)";
12087 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12088 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
12094 Molby_getDescription(void)
12096 extern const char *gVersionString, *gCopyrightString;
12097 extern int gRevisionNumber;
12098 extern char *gLastBuildString;
12100 char *revisionString;
12101 if (gRevisionNumber > 0) {
12102 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12103 } else revisionString = "";
12105 "Molby %s%s\n%s\nLast compile: %s\n"
12106 #if !defined(__CMDMAC__)
12112 "ruby %s, http://www.ruby-lang.org/\n"
12114 "FFTW 3.3.2, http://www.fftw.org/\n"
12115 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
12116 " and Massachusetts Institute of Technology",
12117 gVersionString, revisionString, gCopyrightString, gLastBuildString,
12118 #if !defined(__CMDMAC__)
12119 MyAppCallback_getGUIDescriptionString(),
12121 gRubyVersion, gRubyCopyright);
12122 if (revisionString[0] != 0)
12123 free(revisionString);
12128 Molby_startup(const char *script, const char *dir)
12133 char *respath, *p, *wbuf;
12135 /* Get version/copyright string from Ruby interpreter */
12137 gRubyVersion = strdup(ruby_version);
12138 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12139 #if defined(__CMDMAC__)
12142 " ", /* Indent for displaying in About dialog */
12144 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12147 /* Read build and revision information for Molby */
12150 extern int gRevisionNumber;
12151 extern char *gLastBuildString;
12152 FILE *fp = fopen("../buildInfo.txt", "r");
12153 gLastBuildString = "";
12155 if (fgets(buf, sizeof(buf), fp) != NULL) {
12156 char *p1 = strchr(buf, '\"');
12157 char *p2 = strrchr(buf, '\"');
12158 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12159 memmove(buf, p1 + 1, p2 - p1 - 1);
12160 buf[p2 - p1 - 1] = 0;
12161 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12166 fp = fopen("../revisionInfo.txt", "r");
12167 gRevisionNumber = 0;
12169 if (fgets(buf, sizeof(buf), fp) != NULL) {
12170 gRevisionNumber = strtol(buf, NULL, 0);
12176 #if defined(__CMDMAC__)
12177 wbuf = Molby_getDescription();
12178 printf("%s\n", wbuf);
12182 /* Read atom display parameters */
12183 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12184 #if defined(__CMDMAC__)
12185 fprintf(stderr, "%s\n", wbuf);
12187 MyAppCallback_setConsoleColor(1);
12188 MyAppCallback_showScriptMessage("%s", wbuf);
12189 MyAppCallback_setConsoleColor(0);
12194 /* Read default parameters */
12195 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12196 if (wbuf != NULL) {
12197 #if defined(__CMDMAC__)
12198 fprintf(stderr, "%s\n", wbuf);
12200 MyAppCallback_setConsoleColor(1);
12201 MyAppCallback_showScriptMessage("%s", wbuf);
12202 MyAppCallback_setConsoleColor(0);
12207 /* Initialize Ruby interpreter */
12210 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12211 it causes rb_bug() (= fatal error) during ruby_init().
12212 As a workaround, these standard streams are reopend as
12214 freopen("NUL", "r", stdin);
12215 freopen("NUL", "w", stdout);
12216 freopen("NUL", "w", stderr);
12222 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12224 Init_trans_japanese_sjis();
12227 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12229 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12230 ruby_incpush(libpath);
12234 ruby_script("Molby");
12236 /* Find the resource path (the parent directory of the given directory) */
12237 respath = strdup(dir);
12238 p = strrchr(respath, '/');
12239 if (p == NULL && PATH_SEPARATOR != '/')
12240 p = strrchr(respath, PATH_SEPARATOR);
12243 val = Ruby_NewFileStringValue(respath);
12244 rb_define_global_const("MolbyResourcePath", val);
12247 /* Define Molby classes */
12249 RubyDialogInitClass();
12251 rb_define_const(rb_mMolby, "ResourcePath", val);
12252 val = Ruby_NewFileStringValue(dir);
12253 rb_define_const(rb_mMolby, "ScriptPath", val);
12254 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12255 val = Ruby_NewFileStringValue(p);
12256 rb_define_const(rb_mMolby, "MbsfPath", val);
12259 p = MyAppCallback_getHomeDir();
12260 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12261 rb_define_const(rb_mMolby, "HomeDirectory", val);
12263 p = MyAppCallback_getDocumentHomeDir();
12264 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12265 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12268 #if defined(__CMDMAC__)
12269 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12271 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12276 /* Create objects for stdout and stderr */
12277 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12278 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12279 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12280 rb_gv_set("$stdout", val);
12281 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12282 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12283 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12284 rb_gv_set("$stderr", val);
12286 /* Create objects for stdin */
12287 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12288 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12289 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12290 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12291 rb_gv_set("$stdin", val);
12295 /* Global variable to hold error information */
12296 rb_define_variable("$backtrace", &gMolbyBacktrace);
12297 rb_define_variable("$error_history", &gMolbyErrorHistory);
12298 gMolbyErrorHistory = rb_ary_new();
12300 /* Global variables for script menus */
12301 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12302 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12303 gScriptMenuCommands = rb_ary_new();
12304 gScriptMenuEnablers = rb_ary_new();
12307 /* Register interrupt check code */
12308 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12312 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12313 s_SetIntervalTimer(0, 50);
12316 /* Read the startup script */
12317 if (script != NULL && script[0] != 0) {
12318 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12320 rb_load_protect(rb_str_new2(script), 0, &status);
12323 Ruby_showError(status);
12325 MyAppCallback_showScriptMessage("Done.\n");
12330 Molby_buildARGV(int argc, const char **argv)
12333 rb_ary_clear(rb_argv);
12334 for (i = 0; i < argc; i++) {
12335 VALUE arg = rb_tainted_str_new2(argv[i]);
12337 rb_ary_push(rb_argv, arg);