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;
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);
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)
997 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
999 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
1001 val = Ruby_NewEncodedStringValue(buf, 0);
1004 val = Ruby_NewEncodedStringValue("", 0);
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 ======
1048 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1051 s_IO_gets_any_eol(VALUE self)
1056 static ID id_getbyte = 0, id_ungetbyte;
1057 if (id_getbyte == 0) {
1058 id_getbyte = rb_intern("getbyte");
1059 id_ungetbyte = rb_intern("ungetbyte");
1063 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1064 c = NUM2INT(rb_Integer(cval));
1066 cval = rb_funcall(self, id_getbyte, 0);
1068 c = NUM2INT(rb_Integer(cval));
1070 rb_funcall(self, id_ungetbyte, 1, cval);
1073 } else if (c != 0x0a) {
1078 val = rb_str_new(buf, i);
1080 rb_str_append(val, rb_str_new(buf, i));
1085 if (cval == Qnil && i == 0 && val == Qnil)
1086 return Qnil; /* End of file */
1089 val = rb_str_new(buf, i);
1091 rb_str_append(val, rb_str_new(buf, i));
1092 val = rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
1094 /* Needs a end-of-line mark */
1095 cval = rb_gv_get("$/");
1096 rb_str_append(val, cval);
1098 rb_gv_set("$_", val);
1102 #pragma mark ====== Utility functions (protected funcall) ======
1104 struct Ruby_funcall2_record {
1112 s_Ruby_funcall2_sub(VALUE data)
1114 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1115 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1119 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1121 struct Ruby_funcall2_record rec;
1126 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1130 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1132 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1135 #pragma mark ====== ParameterRef Class ======
1138 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1142 Data_Get_Struct(self, ParameterRef, pref);
1144 *typep = pref->parType;
1145 if (pref->parType == kElementParType) {
1146 up = (UnionPar *)&gElementParameters[pref->idx];
1148 up = ParameterRefGetPar(pref);
1149 if (checkEditable) {
1151 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1152 if (up->bond.src != 0 && up->bond.src != -1)
1153 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1160 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1164 Data_Get_Struct(self, ParameterRef, pref);
1165 if (pref->mol == NULL)
1167 up = ParameterRefGetPar(pref);
1168 if (key != s_SourceSym)
1169 up->bond.src = 0; /* Becomes automatically molecule-local */
1170 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1173 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1174 MolActionCallback_registerUndo(pref->mol, act);
1175 MoleculeCallback_notifyModification(pref->mol, 0);
1176 pref->mol->needsMDRebuild = 1;
1181 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1183 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1185 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1187 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1191 s_AtomTypeIndexFromValue(VALUE val)
1193 if (rb_obj_is_kind_of(val, rb_cNumeric))
1194 return NUM2INT(val);
1196 return AtomTypeEncodeToUInt(StringValuePtr(val));
1199 static const char *s_ParameterTypeNames[] = {
1200 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1202 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1205 s_ParTypeFromValue(VALUE val)
1209 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1210 if (s_ParameterTypeIDs[0] == 0) {
1211 for (i = 0; i < n; i++)
1212 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1214 valid = rb_to_id(val);
1215 for (i = 0; i < n; i++) {
1216 if (valid == s_ParameterTypeIDs[i]) {
1218 return kElementParType;
1219 else return kFirstParType + i;
1222 return kInvalidParType;
1229 * Get the index in the parameter list.
1231 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1233 Data_Get_Struct(self, ParameterRef, pref);
1234 return INT2NUM(pref->idx);
1239 * par_type -> String
1241 * Get the parameter type, like "bond", "angle", etc.
1243 static VALUE s_ParameterRef_GetParType(VALUE self) {
1245 s_UnionParFromValue(self, &tp, 0);
1246 if (tp == kElementParType)
1247 return rb_str_new2("element");
1248 tp -= kFirstParType;
1249 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1250 return rb_str_new2(s_ParameterTypeNames[tp]);
1251 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1256 * atom_type -> String or Array of String
1257 * atom_types -> String or Array of String
1259 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1260 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1261 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1262 * The atom type may be "X", which is a wildcard that matches any atom type.
1264 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1269 up = s_UnionParFromValue(self, &tp, 0);
1270 n = ParameterGetAtomTypes(tp, up, types);
1272 rb_raise(rb_eMolbyError, "invalid member atom_types");
1273 for (i = 0; i < n; i++) {
1274 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1275 vals[i] = INT2NUM(types[i]);
1277 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1282 return rb_ary_new4(n, vals);
1289 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1291 static VALUE s_ParameterRef_GetK(VALUE self) {
1295 up = s_UnionParFromValue(self, &tp, 0);
1298 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1300 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1301 case kDihedralParType:
1302 case kImproperParType:
1303 if (up->torsion.mult == 1)
1304 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1305 n = up->torsion.mult;
1308 for (i = 0; i < n; i++)
1309 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1310 return rb_ary_new4(n, vals);
1312 rb_raise(rb_eMolbyError, "invalid member k");
1320 * Get the equilibrium bond length. Only available for bond parameters.
1322 static VALUE s_ParameterRef_GetR0(VALUE self) {
1325 up = s_UnionParFromValue(self, &tp, 0);
1326 if (tp == kBondParType)
1327 return rb_float_new(up->bond.r0);
1328 else rb_raise(rb_eMolbyError, "invalid member r0");
1335 * Get the equilibrium angle (in degree). Only available for angle parameters.
1337 static VALUE s_ParameterRef_GetA0(VALUE self) {
1340 up = s_UnionParFromValue(self, &tp, 0);
1341 if (tp == kAngleParType)
1342 return rb_float_new(up->angle.a0 * kRad2Deg);
1343 else rb_raise(rb_eMolbyError, "invalid member a0");
1350 * Get the multiplicity. Only available for dihedral and improper parameters.
1351 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1353 static VALUE s_ParameterRef_GetMult(VALUE self) {
1356 up = s_UnionParFromValue(self, &tp, 0);
1357 if (tp == kDihedralParType || tp == kImproperParType)
1358 return rb_float_new(up->torsion.mult);
1359 else rb_raise(rb_eMolbyError, "invalid member mult");
1364 * period -> Integer or Array of Integers
1366 * Get the periodicity. Only available for dihedral and improper parameters.
1367 * If the multiplicity is larger than 1, then an array of integers is returned.
1368 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1370 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1374 up = s_UnionParFromValue(self, &tp, 0);
1375 if (tp == kDihedralParType || tp == kImproperParType) {
1376 if (up->torsion.mult == 1)
1377 return INT2NUM(up->torsion.period[0]);
1378 n = up->torsion.mult;
1381 for (i = 0; i < n; i++)
1382 vals[i] = INT2NUM(up->torsion.period[i]);
1383 return rb_ary_new4(n, vals);
1384 } else rb_raise(rb_eMolbyError, "invalid member period");
1389 * phi0 -> Float or Array of Floats
1391 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1392 * If the multiplicity is larger than 1, then an array of floats is returned.
1393 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1395 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1399 up = s_UnionParFromValue(self, &tp, 0);
1400 if (tp == kDihedralParType || tp == kImproperParType) {
1401 if (up->torsion.mult == 1)
1402 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1403 n = up->torsion.mult;
1406 for (i = 0; i < n; i++)
1407 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1408 return rb_ary_new4(n, vals);
1409 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1416 * Get the "A" value for the van der Waals parameter.
1419 static VALUE s_ParameterRef_GetA(VALUE self) {
1422 up = s_UnionParFromValue(self, &tp, 0);
1423 if (tp == kVdwParType)
1424 return rb_float_new(up->vdw.A);
1425 else if (tp == kVdwPairParType)
1426 return rb_float_new(up->vdwp.A);
1427 else rb_raise(rb_eMolbyError, "invalid member A");
1435 * Get the "B" value for the van der Waals parameter.
1438 static VALUE s_ParameterRef_GetB(VALUE self) {
1441 up = s_UnionParFromValue(self, &tp, 0);
1442 if (tp == kVdwParType)
1443 return rb_float_new(up->vdw.B);
1444 else if (tp == kVdwPairParType)
1445 return rb_float_new(up->vdwp.B);
1446 else rb_raise(rb_eMolbyError, "invalid member B");
1454 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1456 static VALUE s_ParameterRef_GetReq(VALUE self) {
1459 /* Double a, b, r; */
1461 up = s_UnionParFromValue(self, &tp, 0);
1462 if (tp == kVdwParType) {
1466 } else if (tp == kVdwPairParType) {
1470 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1471 /* if (a == 0.0 || b == 0.0) */
1472 return rb_float_new(r);
1473 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1480 * Get the minimum energy for the van der Waals parameter.
1482 static VALUE s_ParameterRef_GetEps(VALUE self) {
1487 up = s_UnionParFromValue(self, &tp, 0);
1488 if (tp == kVdwParType) {
1492 } else if (tp == kVdwPairParType) {
1496 } else rb_raise(rb_eMolbyError, "invalid member eps");
1497 /* if (a == 0.0 || b == 0.0) */
1498 return rb_float_new(eps * INTERNAL2KCAL);
1499 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1506 * Get the "A" value for the 1-4 van der Waals parameter.
1509 static VALUE s_ParameterRef_GetA14(VALUE self) {
1512 up = s_UnionParFromValue(self, &tp, 0);
1513 if (tp == kVdwParType)
1514 return rb_float_new(up->vdw.A14);
1515 else if (tp == kVdwPairParType)
1516 return rb_float_new(up->vdwp.A14);
1517 else rb_raise(rb_eMolbyError, "invalid member A14");
1525 * Get the "B" value for the 1-4 van der Waals parameter.
1528 static VALUE s_ParameterRef_GetB14(VALUE self) {
1531 up = s_UnionParFromValue(self, &tp, 0);
1532 if (tp == kVdwParType)
1533 return rb_float_new(up->vdw.B14);
1534 else if (tp == kVdwPairParType)
1535 return rb_float_new(up->vdwp.B14);
1536 else rb_raise(rb_eMolbyError, "invalid member B14");
1544 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1546 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1549 /* Double a, b, r; */
1551 up = s_UnionParFromValue(self, &tp, 0);
1552 if (tp == kVdwParType) {
1556 } else if (tp == kVdwPairParType) {
1557 /* a = up->vdwp.A14;
1558 b = up->vdwp.B14; */
1559 r = up->vdwp.r_eq14;
1560 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1561 /* if (a == 0.0 || b == 0.0) */
1562 return rb_float_new(r);
1563 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1570 * Get the minimum energy for the 1-4 van der Waals parameter.
1572 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1577 up = s_UnionParFromValue(self, &tp, 0);
1578 if (tp == kVdwParType) {
1581 eps = up->vdw.eps14;
1582 } else if (tp == kVdwPairParType) {
1583 /* a = up->vdwp.A14;
1584 b = up->vdwp.B14; */
1585 eps = up->vdwp.eps14;
1586 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1587 /* if (a == 0.0 || b == 0.0) */
1588 return rb_float_new(eps * INTERNAL2KCAL);
1589 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1596 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1598 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1601 up = s_UnionParFromValue(self, &tp, 0);
1602 if (tp == kVdwCutoffParType)
1603 return rb_float_new(up->vdwcutoff.cutoff);
1604 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1611 * Get the atomic (covalent) radius for the element parameter.
1613 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1616 up = s_UnionParFromValue(self, &tp, 0);
1617 if (tp == kElementParType)
1618 return rb_float_new(up->atom.radius);
1619 else rb_raise(rb_eMolbyError, "invalid member radius");
1624 * vdw_radius -> Float
1626 * Get the van der Waals radius for the element parameter. (0 if not given)
1628 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1631 up = s_UnionParFromValue(self, &tp, 0);
1632 if (tp == kElementParType)
1633 return rb_float_new(up->atom.vdw_radius);
1634 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1639 * color -> [Float, Float, Float]
1641 * Get the rgb color for the element parameter.
1643 static VALUE s_ParameterRef_GetColor(VALUE self) {
1646 up = s_UnionParFromValue(self, &tp, 0);
1647 if (tp == kElementParType)
1648 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));
1649 else rb_raise(rb_eMolbyError, "invalid member color");
1654 * atomic_number -> Integer
1656 * Get the atomic number for the vdw or element parameter.
1658 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1661 up = s_UnionParFromValue(self, &tp, 0);
1662 if (tp == kElementParType)
1663 return INT2NUM(up->atom.number);
1664 else if (tp == kVdwParType)
1665 return INT2NUM(up->vdw.atomicNumber);
1666 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1673 * Get the name for the element parameter.
1675 static VALUE s_ParameterRef_GetName(VALUE self) {
1678 up = s_UnionParFromValue(self, &tp, 0);
1679 if (tp == kElementParType) {
1681 strncpy(name, up->atom.name, 4);
1683 return rb_str_new2(name);
1684 } else rb_raise(rb_eMolbyError, "invalid member name");
1691 * Get the atomic weight for the element parameter.
1693 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1696 up = s_UnionParFromValue(self, &tp, 0);
1697 if (tp == kElementParType)
1698 return rb_float_new(up->atom.weight);
1699 else if (tp == kVdwParType)
1700 return rb_float_new(up->vdw.weight);
1701 else rb_raise(rb_eMolbyError, "invalid member weight");
1706 * fullname -> String
1708 * Get the full name for the element parameter.
1710 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1713 up = s_UnionParFromValue(self, &tp, 0);
1714 if (tp == kElementParType) {
1716 strncpy(fullname, up->atom.fullname, 15);
1718 return rb_str_new2(fullname);
1719 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1726 * Get the comment for the parameter.
1728 static VALUE s_ParameterRef_GetComment(VALUE self) {
1731 up = s_UnionParFromValue(self, &tp, 0);
1735 else return rb_str_new2(ParameterGetComment(com));
1742 * Get the source string for the parameter. Returns false for undefined parameter,
1743 * and nil for "local" parameter that is specific for the molecule.
1745 static VALUE s_ParameterRef_GetSource(VALUE self) {
1748 up = s_UnionParFromValue(self, &tp, 0);
1751 return Qfalse; /* undefined */
1753 return Qnil; /* local */
1754 else return rb_str_new2(ParameterGetComment(src));
1758 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1765 if (rb_obj_is_kind_of(val, rb_cString)) {
1766 char *s = StringValuePtr(val);
1768 for (i = 0; i < n; i++) {
1771 /* Skip leading separaters */
1772 while (*s == '-' || *s == ' ' || *s == '\t')
1774 for (p = s; *p != 0; p++) {
1775 if (*p == '-' || *p == ' ' || *p == '\t')
1779 if (len >= sizeof(buf))
1780 len = sizeof(buf) - 1;
1781 strncpy(buf, s, len);
1783 /* Skip trailing blanks */
1784 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1787 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1788 if (buf[0] >= '0' && buf[0] <= '9')
1789 types[i] = atoi(buf);
1791 types[i] = AtomTypeEncodeToUInt(buf);
1792 if (p == NULL || *p == 0) {
1798 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1801 val = rb_ary_to_ary(val);
1802 if (RARRAY_LEN(val) != n)
1803 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1804 valp = RARRAY_PTR(val);
1806 for (i = 0; i < n; i++) {
1807 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1808 types[i] = NUM2INT(rb_Integer(valp[i]));
1810 VALUE sval = valp[i];
1811 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1816 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1821 up = s_UnionParFromValue(self, &tp, 1);
1822 oldval = s_ParameterRef_GetAtomTypes(self);
1823 oldsrc = up->bond.src;
1826 s_ScanAtomTypes(val, 2, types);
1827 up->bond.type1 = types[0];
1828 up->bond.type2 = types[1];
1831 s_ScanAtomTypes(val, 3, types);
1832 up->angle.type1 = types[0];
1833 up->angle.type2 = types[1];
1834 up->angle.type3 = types[2];
1836 case kDihedralParType:
1837 case kImproperParType:
1838 s_ScanAtomTypes(val, 4, types);
1839 up->torsion.type1 = types[0];
1840 up->torsion.type2 = types[1];
1841 up->torsion.type3 = types[2];
1842 up->torsion.type4 = types[3];
1845 s_ScanAtomTypes(val, 1, types);
1846 up->vdw.type1 = types[0];
1848 case kVdwPairParType:
1849 s_ScanAtomTypes(val, 2, types);
1850 up->vdwp.type1 = types[0];
1851 up->vdwp.type2 = types[1];
1853 case kVdwCutoffParType:
1854 s_ScanAtomTypes(val, 2, types);
1855 up->vdwcutoff.type1 = types[0];
1856 up->vdwcutoff.type2 = types[1];
1861 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1865 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1867 Int tp, i, n, oldsrc;
1868 VALUE *valp, oldval;
1869 up = s_UnionParFromValue(self, &tp, 1);
1870 oldval = s_ParameterRef_GetK(self);
1871 oldsrc = up->bond.src;
1874 val = rb_Float(val);
1875 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1878 val = rb_Float(val);
1879 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1881 case kDihedralParType:
1882 case kImproperParType:
1883 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1884 up->torsion.mult = 1;
1885 val = rb_Float(val);
1886 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1889 n = up->torsion.mult;
1892 val = rb_ary_to_ary(val);
1893 if (RARRAY_LEN(val) != n)
1894 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1895 valp = RARRAY_PTR(val);
1896 for (i = 0; i < n; i++) {
1897 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1901 rb_raise(rb_eMolbyError, "invalid member k");
1903 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1907 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1911 up = s_UnionParFromValue(self, &tp, 1);
1912 oldval = s_ParameterRef_GetR0(self);
1913 oldsrc = up->bond.src;
1914 if (tp == kBondParType) {
1915 val = rb_Float(val);
1916 up->bond.r0 = NUM2DBL(val);
1917 } else rb_raise(rb_eMolbyError, "invalid member r0");
1918 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1922 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1926 up = s_UnionParFromValue(self, &tp, 1);
1927 oldval = s_ParameterRef_GetA0(self);
1928 oldsrc = up->bond.src;
1929 if (tp == kAngleParType) {
1930 val = rb_Float(val);
1931 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1932 } else rb_raise(rb_eMolbyError, "invalid member a0");
1933 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1937 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1941 up = s_UnionParFromValue(self, &tp, 1);
1942 oldval = s_ParameterRef_GetMult(self);
1943 oldsrc = up->bond.src;
1944 if (tp == kDihedralParType || tp == kImproperParType) {
1946 val = rb_Integer(val);
1949 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1950 up->torsion.mult = i;
1951 } else rb_raise(rb_eMolbyError, "invalid member mult");
1952 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1956 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1958 Int tp, i, n, oldsrc;
1959 VALUE *valp, oldval;
1960 up = s_UnionParFromValue(self, &tp, 1);
1961 oldval = s_ParameterRef_GetPeriod(self);
1962 oldsrc = up->bond.src;
1963 if (tp == kDihedralParType || tp == kImproperParType) {
1964 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1965 up->torsion.mult = 1;
1966 val = rb_Integer(val);
1967 up->torsion.period[0] = NUM2INT(val);
1969 n = up->torsion.mult;
1972 val = rb_ary_to_ary(val);
1973 if (RARRAY_LEN(val) != n)
1974 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1975 valp = RARRAY_PTR(val);
1976 for (i = 0; i < n; i++) {
1977 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1980 } else rb_raise(rb_eMolbyError, "invalid member period");
1981 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1985 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1987 Int tp, i, n, oldsrc;
1988 VALUE *valp, oldval;
1989 up = s_UnionParFromValue(self, &tp, 1);
1990 oldval = s_ParameterRef_GetPhi0(self);
1991 oldsrc = up->bond.src;
1992 if (tp == kDihedralParType || tp == kImproperParType) {
1993 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1994 up->torsion.mult = 1;
1995 val = rb_Float(val);
1996 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1998 n = up->torsion.mult;
2001 val = rb_ary_to_ary(val);
2002 if (RARRAY_LEN(val) != n)
2003 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2004 valp = RARRAY_PTR(val);
2005 for (i = 0; i < n; i++)
2006 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2008 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2009 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2014 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2019 up = s_UnionParFromValue(self, &tp, 1);
2020 oldval = s_ParameterRef_GetA(self);
2021 oldsrc = up->bond.src;
2022 val = rb_Float(val);
2024 if (tp == kVdwParType)
2026 else if (tp == kVdwPairParType)
2028 else rb_raise(rb_eMolbyError, "invalid member A");
2029 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2033 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2038 up = s_UnionParFromValue(self, &tp, 1);
2039 oldval = s_ParameterRef_GetB(self);
2040 oldsrc = up->bond.src;
2041 val = rb_Float(val);
2043 if (tp == kVdwParType)
2045 else if (tp == kVdwPairParType)
2047 else rb_raise(rb_eMolbyError, "invalid member B");
2048 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2053 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2058 up = s_UnionParFromValue(self, &tp, 1);
2059 oldval = s_ParameterRef_GetReq(self);
2060 oldsrc = up->bond.src;
2061 val = rb_Float(val);
2063 if (tp == kVdwParType) {
2065 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2066 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2067 } else if (tp == kVdwPairParType) {
2069 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2070 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2071 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2072 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2076 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2081 up = s_UnionParFromValue(self, &tp, 1);
2082 oldval = s_ParameterRef_GetEps(self);
2083 oldsrc = up->bond.src;
2084 val = rb_Float(val);
2085 e = NUM2DBL(val) * KCAL2INTERNAL;
2086 if (tp == kVdwParType) {
2088 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2089 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2090 } else if (tp == kVdwPairParType) {
2092 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2093 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2094 } else rb_raise(rb_eMolbyError, "invalid member eps");
2095 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2100 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2105 up = s_UnionParFromValue(self, &tp, 1);
2106 oldval = s_ParameterRef_GetA14(self);
2107 oldsrc = up->bond.src;
2108 val = rb_Float(val);
2110 if (tp == kVdwParType)
2112 else if (tp == kVdwPairParType)
2114 else rb_raise(rb_eMolbyError, "invalid member A14");
2115 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2119 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2124 up = s_UnionParFromValue(self, &tp, 1);
2125 oldval = s_ParameterRef_GetB14(self);
2126 oldsrc = up->bond.src;
2127 val = rb_Float(val);
2129 if (tp == kVdwParType)
2131 else if (tp == kVdwPairParType)
2133 else rb_raise(rb_eMolbyError, "invalid member B14");
2134 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2139 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2144 up = s_UnionParFromValue(self, &tp, 1);
2145 oldval = s_ParameterRef_GetReq14(self);
2146 oldsrc = up->bond.src;
2147 val = rb_Float(val);
2149 if (tp == kVdwParType) {
2151 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2152 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2153 } else if (tp == kVdwPairParType) {
2154 up->vdwp.r_eq14 = r;
2155 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2156 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2157 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2158 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2162 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2167 up = s_UnionParFromValue(self, &tp, 1);
2168 oldval = s_ParameterRef_GetEps14(self);
2169 oldsrc = up->bond.src;
2170 val = rb_Float(val);
2171 e = NUM2DBL(val) * KCAL2INTERNAL;
2172 if (tp == kVdwParType) {
2174 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2175 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2176 } else if (tp == kVdwPairParType) {
2178 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2179 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2180 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2181 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2185 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2189 oldval = s_ParameterRef_GetCutoff(self);
2190 oldsrc = up->bond.src;
2191 up = s_UnionParFromValue(self, &tp, 1);
2192 val = rb_Float(val);
2193 if (tp == kVdwCutoffParType) {
2194 up->vdwcutoff.cutoff = NUM2DBL(val);
2195 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2196 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2200 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2204 up = s_UnionParFromValue(self, &tp, 1);
2205 oldval = s_ParameterRef_GetRadius(self);
2206 oldsrc = up->bond.src;
2207 val = rb_Float(val);
2208 if (tp == kElementParType) {
2209 up->atom.radius = NUM2DBL(val);
2210 } else rb_raise(rb_eMolbyError, "invalid member radius");
2211 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2215 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2219 up = s_UnionParFromValue(self, &tp, 1);
2220 oldval = s_ParameterRef_GetVdwRadius(self);
2221 oldsrc = up->bond.src;
2222 val = rb_Float(val);
2223 if (tp == kElementParType) {
2224 up->atom.vdw_radius = NUM2DBL(val);
2225 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2226 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2230 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2233 VALUE *valp, oldval;
2234 up = s_UnionParFromValue(self, &tp, 1);
2235 oldval = s_ParameterRef_GetColor(self);
2236 oldsrc = up->bond.src;
2237 val = rb_ary_to_ary(val);
2238 if (RARRAY_LEN(val) != 3)
2239 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2240 valp = RARRAY_PTR(val);
2241 if (tp == kElementParType) {
2242 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2243 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2244 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2245 } else rb_raise(rb_eMolbyError, "invalid member color");
2246 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2250 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2254 up = s_UnionParFromValue(self, &tp, 1);
2255 oldval = s_ParameterRef_GetAtomicNumber(self);
2256 oldsrc = up->bond.src;
2257 val = rb_Integer(val);
2258 if (tp == kElementParType)
2259 up->atom.number = NUM2INT(val);
2260 else if (tp == kVdwParType) {
2261 up->vdw.atomicNumber = NUM2INT(val);
2262 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2263 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2264 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2268 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2272 up = s_UnionParFromValue(self, &tp, 1);
2273 oldval = s_ParameterRef_GetName(self);
2274 oldsrc = up->bond.src;
2275 if (tp == kElementParType) {
2276 strncpy(up->atom.name, StringValuePtr(val), 4);
2277 } else rb_raise(rb_eMolbyError, "invalid member name");
2278 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2282 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2286 val = rb_Float(val);
2287 oldval = s_ParameterRef_GetWeight(self);
2288 up = s_UnionParFromValue(self, &tp, 1);
2289 oldsrc = up->bond.src;
2290 if (tp == kElementParType)
2291 up->atom.weight = NUM2DBL(val);
2292 else if (tp == kVdwParType)
2293 up->vdw.weight = NUM2DBL(val);
2294 else rb_raise(rb_eMolbyError, "invalid member weight");
2295 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2299 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2303 up = s_UnionParFromValue(self, &tp, 1);
2304 oldval = s_ParameterRef_GetFullName(self);
2305 oldsrc = up->bond.src;
2306 if (tp == kElementParType) {
2307 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2308 up->atom.fullname[15] = 0;
2309 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2310 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2314 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2316 Int tp, com, oldsrc;
2318 up = s_UnionParFromValue(self, &tp, 1);
2319 oldval = s_ParameterRef_GetComment(self);
2320 oldsrc = up->bond.src;
2324 com = ParameterCommentIndex(StringValuePtr(val));
2327 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2331 /* Only false (undefined) and nil (local) can be set */
2332 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2336 up = s_UnionParFromValue(self, &tp, 1);
2337 if (val != Qfalse && val != Qnil)
2338 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2339 oldval = s_ParameterRef_GetSource(self);
2340 oldsrc = up->bond.src;
2341 if (oldsrc != 0 && oldsrc != -1)
2342 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2343 up->bond.src = (val == Qfalse ? -1 : 0);
2344 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2348 static struct s_ParameterAttrDef {
2350 VALUE *symref; /* Address of s_IndexSymbol etc. */
2351 ID id; /* Will be set within InitMolby() */
2352 VALUE (*getter)(VALUE);
2353 VALUE (*setter)(VALUE, VALUE);
2354 } s_ParameterAttrDefTable[] = {
2355 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2356 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2357 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2358 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2359 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2360 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2361 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2362 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2363 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2364 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2365 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2366 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2367 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2368 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2369 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2370 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2371 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2372 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2373 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2374 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2375 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2376 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2377 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2378 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2379 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2380 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2381 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2382 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2383 {NULL} /* Sentinel */
2387 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2391 if (TYPE(key) != T_SYMBOL) {
2392 kid = rb_intern(StringValuePtr(key));
2394 } else kid = SYM2ID(key);
2395 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2396 if (s_ParameterAttrDefTable[i].id == kid) {
2397 if (value == Qundef)
2398 return (*(s_ParameterAttrDefTable[i].getter))(self);
2399 else if (s_ParameterAttrDefTable[i].setter == NULL)
2400 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2402 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2405 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2406 return Qnil; /* not reached */
2410 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2412 return s_ParameterRef_SetAttr(self, key, Qundef);
2417 * keys(idx) -> array of valid parameter attributes
2419 * Returns an array of valid parameter attributes (as Symbols).
2422 s_ParameterRef_Keys(VALUE self)
2425 Data_Get_Struct(self, ParameterRef, pref);
2426 switch (pref->parType) {
2428 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2430 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2431 case kDihedralParType:
2432 case kImproperParType:
2433 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2435 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);
2436 case kVdwPairParType:
2437 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2438 case kVdwCutoffParType:
2439 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2440 case kElementParType:
2441 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);
2443 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2445 return Qnil; /* Not reached */
2450 * to_hash(idx) -> Hash
2452 * Returns a hash containing valid parameter names and values
2455 s_ParameterRef_ToHash(VALUE self)
2457 VALUE keys = s_ParameterRef_Keys(self);
2462 retval = rb_hash_new();
2463 for (i = 0; i < RARRAY_LEN(keys); i++) {
2464 VALUE key = RARRAY_PTR(keys)[i];
2465 VALUE val = s_ParameterRef_GetAttr(self, key);
2466 rb_hash_aset(retval, key, val);
2473 * parameter.to_s(idx) -> String
2475 * Returns a string representation of the given parameter
2478 s_ParameterRef_ToString(VALUE self)
2481 char buf[1024], types[4][8];
2482 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2485 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);
2488 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);
2490 case kDihedralParType:
2491 case kImproperParType:
2492 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]));
2494 for (i = 0; i < up->torsion.mult; i++) {
2495 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);
2500 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);
2502 case kVdwPairParType:
2503 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);
2505 case kVdwCutoffParType:
2506 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);
2508 case kElementParType:
2509 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);
2512 return rb_str_new2(buf);
2517 * self == parameterRef -> boolean
2519 * True if the parameters point to the same parameter record.
2522 s_ParameterRef_Equal(VALUE self, VALUE val)
2525 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2526 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2527 } else return Qfalse;
2530 #pragma mark ====== Parameter Class ======
2532 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2533 * is NULL, then the global parameters are looked for. */
2535 /* Rebuild the MD parameter record if necessary: may throw an exception */
2536 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2538 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2541 Data_Get_Struct(val, Molecule, mol);
2543 rb_raise(rb_eMolbyError, "the molecule is empty");
2544 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2545 /* Do self.md_arena.prepare */
2546 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2548 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2553 s_NewParameterValueFromValue(VALUE val)
2556 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2557 Data_Get_Struct(val, Molecule, mol);
2558 s_RebuildMDParameterIfNecessary(val, Qtrue);
2559 MoleculeRetain(mol);
2560 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2563 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2568 s_MoleculeFromParameterValue(VALUE val)
2571 Data_Get_Struct(val, Molecule, mol);
2576 s_ParameterFromParameterValue(VALUE val)
2579 Data_Get_Struct(val, Molecule, mol);
2582 return gBuiltinParameters;
2585 /* Forward declarations */
2586 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2587 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2590 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2592 if (val == rb_cParameter) {
2593 return NULL; /* Parameter class method: builtin parameters */
2594 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2595 return s_MoleculeFromParameterValue(val);
2596 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2597 return s_MoleculeFromParEnumerableValue(val);
2603 * builtin -> Parameter
2605 * Returns a parameter value that points to the global (builtin) parameters.
2606 * Equivalent to Parameter::Builtin (constant).
2609 s_Parameter_Builtin(VALUE self)
2611 static ID s_builtin_id = 0;
2612 if (s_builtin_id == 0)
2613 s_builtin_id = rb_intern("Builtin");
2614 return rb_const_get(rb_cParameter, s_builtin_id);
2619 * bond(idx) -> ParameterRef
2621 * The index-th bond parameter record is returned.
2624 s_Parameter_Bond(VALUE self, VALUE ival)
2628 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2629 idx = NUM2INT(rb_Integer(ival));
2631 n = gBuiltinParameters->nbondPars;
2632 else if (mol->par != NULL)
2633 n = mol->par->nbondPars;
2635 if (idx < -n || idx >= n)
2636 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2639 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2644 * angle(idx) -> ParameterRef
2646 * The index-th angle parameter record is returned.
2649 s_Parameter_Angle(VALUE self, VALUE ival)
2653 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2654 idx = NUM2INT(rb_Integer(ival));
2656 n = gBuiltinParameters->nanglePars;
2657 else if (mol->par != NULL)
2658 n = mol->par->nanglePars;
2660 if (idx < -n || idx >= n)
2661 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2664 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2669 * dihedral(idx) -> ParameterRef
2671 * The index-th dihedral parameter record is returned.
2674 s_Parameter_Dihedral(VALUE self, VALUE ival)
2678 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2679 idx = NUM2INT(rb_Integer(ival));
2681 n = gBuiltinParameters->ndihedralPars;
2682 else if (mol->par != NULL)
2683 n = mol->par->ndihedralPars;
2685 if (idx < -n || idx >= n)
2686 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2689 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2694 * improper(idx) -> ParameterRef
2696 * The index-th improper parameter record is returned.
2699 s_Parameter_Improper(VALUE self, VALUE ival)
2703 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2704 idx = NUM2INT(rb_Integer(ival));
2706 n = gBuiltinParameters->nimproperPars;
2707 else if (mol->par != NULL)
2708 n = mol->par->nimproperPars;
2710 if (idx < -n || idx >= n)
2711 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2714 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2719 * vdw(idx) -> ParameterRef
2721 * The index-th vdw parameter record is returned.
2724 s_Parameter_Vdw(VALUE self, VALUE ival)
2728 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2729 idx = NUM2INT(rb_Integer(ival));
2731 n = gBuiltinParameters->nvdwPars;
2732 else if (mol->par != NULL)
2733 n = mol->par->nvdwPars;
2735 if (idx < -n || idx >= n)
2736 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2739 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2744 * vdw_pair(idx) -> ParameterRef
2746 * The index-th vdw pair parameter record is returned.
2749 s_Parameter_VdwPair(VALUE self, VALUE ival)
2753 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2754 idx = NUM2INT(rb_Integer(ival));
2756 n = gBuiltinParameters->nvdwpPars;
2757 else if (mol->par != NULL)
2758 n = mol->par->nvdwpPars;
2760 if (idx < -n || idx >= n)
2761 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2764 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2769 * vdw_cutoff(idx) -> ParameterRef
2771 * The index-th vdw cutoff parameter record is returned.
2774 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2778 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2779 idx = NUM2INT(rb_Integer(ival));
2781 n = gBuiltinParameters->nvdwCutoffPars;
2782 else if (mol->par != NULL)
2783 n = mol->par->nvdwCutoffPars;
2785 if (idx < -n || idx >= n)
2786 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2789 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2794 * element(idx) -> ParameterRef
2795 * element(t1) -> ParameterRef
2797 * In the first form, the index-th element parameter record is returned. In the second
2798 * form, the element parameter for t1 is looked up (the last index first). t1
2799 * is the element name string (up to 4 characters).
2800 * Unlike other Parameter methods, this is used only for the global parameter.
2803 s_Parameter_Element(VALUE self, VALUE ival)
2806 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2807 int n = gCountElementParameters;
2808 idx1 = NUM2INT(rb_Integer(ival));
2809 if (idx1 < -n || idx1 >= n)
2810 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2813 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2818 strncpy(name, StringValuePtr(ival), 4);
2820 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2821 if (strncmp(ep->name, name, 4) == 0)
2822 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2832 * Returns the number of bond parameters.
2835 s_Parameter_Nbonds(VALUE self)
2838 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2840 n = gBuiltinParameters->nbondPars;
2841 else if (mol->par != NULL)
2842 n = mol->par->nbondPars;
2849 * nangles -> Integer
2851 * Returns the number of angle parameters.
2854 s_Parameter_Nangles(VALUE self)
2857 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2859 n = gBuiltinParameters->nanglePars;
2860 else if (mol->par != NULL)
2861 n = mol->par->nanglePars;
2868 * ndihedrals -> Integer
2870 * Returns the number of dihedral parameters.
2873 s_Parameter_Ndihedrals(VALUE self)
2876 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2878 n = gBuiltinParameters->ndihedralPars;
2879 else if (mol->par != NULL)
2880 n = mol->par->ndihedralPars;
2887 * nimpropers -> Integer
2889 * Returns the number of improper parameters.
2892 s_Parameter_Nimpropers(VALUE self)
2895 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2897 n = gBuiltinParameters->nimproperPars;
2898 else if (mol->par != NULL)
2899 n = mol->par->nimproperPars;
2908 * Returns the number of vdw parameters.
2911 s_Parameter_Nvdws(VALUE self)
2914 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2916 n = gBuiltinParameters->nvdwPars;
2917 else if (mol->par != NULL)
2918 n = mol->par->nvdwPars;
2925 * nvdw_pairs -> Integer
2927 * Returns the number of vdw pair parameters.
2930 s_Parameter_NvdwPairs(VALUE self)
2933 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2935 n = gBuiltinParameters->nvdwpPars;
2936 else if (mol->par != NULL)
2937 n = mol->par->nvdwpPars;
2944 * nvdw_cutoffs -> Integer
2946 * Returns the number of vdw cutoff parameters.
2949 s_Parameter_NvdwCutoffs(VALUE self)
2952 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2954 n = gBuiltinParameters->nvdwCutoffPars;
2955 else if (mol->par != NULL)
2956 n = mol->par->nvdwCutoffPars;
2963 * nelements -> Integer
2965 * Returns the number of element parameters.
2968 s_Parameter_Nelements(VALUE self)
2970 return INT2NUM(gCountElementParameters);
2975 * bonds -> ParEnumerable
2977 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2978 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2979 * useful when all accessible parameters should be examined by use of 'each' method.
2982 s_Parameter_Bonds(VALUE self)
2984 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2985 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2990 * angles -> ParEnumerable
2992 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2993 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2994 * useful when all accessible parameters should be examined by use of 'each' method.
2997 s_Parameter_Angles(VALUE self)
2999 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3000 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3005 * dihedrals -> ParEnumerable
3007 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3008 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3009 * useful when all accessible parameters should be examined by use of 'each' method.
3012 s_Parameter_Dihedrals(VALUE self)
3014 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3015 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3020 * impropers -> ParEnumerable
3022 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3023 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3024 * useful when all accessible parameters should be examined by use of 'each' method.
3027 s_Parameter_Impropers(VALUE self)
3029 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3030 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3035 * vdws -> ParEnumerable
3037 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3038 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3039 * useful when all accessible parameters should be examined by use of 'each' method.
3042 s_Parameter_Vdws(VALUE self)
3044 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3045 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3050 * vdw_pairs -> ParEnumerable
3052 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3053 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3054 * useful when all accessible parameters should be examined by use of 'each' method.
3057 s_Parameter_VdwPairs(VALUE self)
3059 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3060 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3065 * vdw_cutoffs -> ParEnumerable
3067 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3068 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3069 * useful when all accessible parameters should be examined by use of 'each' method.
3072 s_Parameter_VdwCutoffs(VALUE self)
3074 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3075 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3080 * elements -> ParEnumerable
3082 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3083 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3084 * useful when all accessible parameters should be examined by use of 'each' method.
3087 s_Parameter_Elements(VALUE self)
3089 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3090 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3094 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3096 VALUE atval, optval;
3099 int i, n, idx, flags, is_global;
3101 rb_scan_args(argc, argv, "1*", &atval, &optval);
3103 /* Get the atom types */
3105 case kBondParType: n = 2; break;
3106 case kAngleParType: n = 3; break;
3107 case kDihedralParType: n = 4; break;
3108 case kImproperParType: n = 4; break;
3109 case kVdwParType: n = 1; break;
3110 case kVdwPairParType: n = 2; break;
3111 default: return Qnil;
3113 s_ScanAtomTypes(atval, n, t);
3114 for (i = 0; i < n; i++) {
3115 if (t[i] < kAtomTypeMinimum) {
3116 /* Explicit atom index */
3118 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3119 if (t[i] >= mol->natoms)
3120 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3122 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3126 /* Analyze options */
3128 n = RARRAY_LEN(optval);
3129 for (i = 0; i < n; i++) {
3130 VALUE oval = RARRAY_PTR(optval)[i];
3131 if (oval == ID2SYM(rb_intern("global")))
3132 flags |= kParameterLookupGlobal;
3133 else if (oval == ID2SYM(rb_intern("local")))
3134 flags |= kParameterLookupLocal;
3135 else if (oval == ID2SYM(rb_intern("missing")))
3136 flags |= kParameterLookupMissing;
3137 else if (oval == ID2SYM(rb_intern("nowildcard")))
3138 flags |= kParameterLookupNoWildcard;
3139 else if (oval == ID2SYM(rb_intern("nobasetype")))
3140 flags |= kParameterLookupNoBaseAtomType;
3141 else if (oval == ID2SYM(rb_intern("create")))
3145 flags = kParameterLookupGlobal | kParameterLookupLocal;
3150 case kBondParType: {
3153 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3155 idx = bp - mol->par->bondPars;
3159 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3161 idx = bp - gBuiltinParameters->bondPars;
3166 case kAngleParType: {
3169 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3171 idx = ap - mol->par->anglePars;
3175 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3177 idx = ap - gBuiltinParameters->anglePars;
3182 case kDihedralParType: {
3185 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3187 idx = tp - mol->par->dihedralPars;
3191 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3193 idx = tp - gBuiltinParameters->dihedralPars;
3198 case kImproperParType: {
3201 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3203 idx = tp - mol->par->improperPars;
3207 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3209 idx = tp - gBuiltinParameters->improperPars;
3217 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3219 idx = vp - mol->par->vdwPars;
3223 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3225 idx = vp - gBuiltinParameters->vdwPars;
3230 case kVdwPairParType: {
3233 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3235 idx = vp - mol->par->vdwpPars;
3239 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3241 idx = vp - gBuiltinParameters->vdwpPars;
3250 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3253 /* Insert a new parameter record */
3255 Int count = ParameterGetCountForType(mol->par, parType);
3256 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3257 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3258 IntGroupRelease(ig);
3261 /* Set atom types */
3262 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3267 up->bond.type1 = t[0];
3268 up->bond.type2 = t[1];
3271 up->angle.type1 = t[0];
3272 up->angle.type2 = t[1];
3273 up->angle.type3 = t[2];
3275 case kDihedralParType:
3276 case kImproperParType:
3277 up->torsion.type1 = t[0];
3278 up->torsion.type2 = t[1];
3279 up->torsion.type3 = t[2];
3280 up->torsion.type4 = t[3];
3283 up->vdw.type1 = t[0];
3285 case kVdwPairParType:
3286 up->vdwp.type1 = t[0];
3287 up->vdwp.type2 = t[1];
3294 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3299 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3300 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3302 * Find the parameter record that matches the given atom types. The atom types are given
3303 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3304 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3305 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3306 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3309 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3312 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3314 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3315 parType = s_ParTypeFromValue(argv[0]);
3316 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3321 * self == parameter -> boolean
3323 * True if the parameters point to the same parameter table.
3326 s_Parameter_Equal(VALUE self, VALUE val)
3328 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3329 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3330 } else return Qfalse;
3333 #pragma mark ====== ParEnumerable Class ======
3335 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3336 and the parameter type. If the Molecule is NULL, then it refers to the
3337 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3338 the global parameters are always accessible. */
3340 typedef struct ParEnumerable {
3342 Int parType; /* Same as parType in ParameterRef */
3345 static ParEnumerable *
3346 s_ParEnumerableNew(Molecule *mol, Int parType)
3348 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3352 MoleculeRetain(mol);
3353 pen->parType = parType;
3359 s_ParEnumerableRelease(ParEnumerable *pen)
3362 if (pen->mol != NULL)
3363 MoleculeRelease(pen->mol);
3369 s_MoleculeFromParEnumerableValue(VALUE val)
3372 Data_Get_Struct(val, ParEnumerable, pen);
3377 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3379 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3381 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3382 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3387 * par_type -> String
3389 * Get the parameter type, like "bond", "angle", etc.
3392 s_ParEnumerable_ParType(VALUE self) {
3395 Data_Get_Struct(self, ParEnumerable, pen);
3397 if (tp == kElementParType)
3398 return rb_str_new2("element");
3399 tp -= kFirstParType;
3400 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3401 return rb_str_new2(s_ParameterTypeNames[tp]);
3402 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3407 * self[idx] -> ParameterRef
3409 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3410 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3411 * parent Parameter object of self.
3413 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3414 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3417 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3420 Data_Get_Struct(self, ParEnumerable, pen);
3421 switch (pen->parType) {
3422 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3423 case kBondParType: return s_Parameter_Bond(self, ival);
3424 case kAngleParType: return s_Parameter_Angle(self, ival);
3425 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3426 case kImproperParType: return s_Parameter_Improper(self, ival);
3427 case kVdwParType: return s_Parameter_Vdw(self, ival);
3428 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3429 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3430 case kElementParType: return s_Parameter_Element(self, ival);
3432 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3434 return Qnil; /* Not reached */
3441 * Returns the number of parameters included in this enumerable.
3444 s_ParEnumerable_Length(VALUE self)
3447 Data_Get_Struct(self, ParEnumerable, pen);
3448 switch (pen->parType) {
3449 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3450 case kBondParType: return s_Parameter_Nbonds(self);
3451 case kAngleParType: return s_Parameter_Nangles(self);
3452 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3453 case kImproperParType: return s_Parameter_Nimpropers(self);
3454 case kVdwParType: return s_Parameter_Nvdws(self);
3455 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3456 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3457 case kElementParType: return s_Parameter_Nelements(self);
3459 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3461 return Qnil; /* Not reached */
3468 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3471 s_ParEnumerable_Each(VALUE self)
3477 Data_Get_Struct(self, ParEnumerable, pen);
3478 if (pen->parType == kElementParType)
3479 n = gCountElementParameters;
3481 switch (pen->parType) {
3482 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3483 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3484 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3485 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3486 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3487 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3488 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3490 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3492 if (pen->mol == NULL)
3493 n = *((Int *)((char *)gBuiltinParameters + ofs));
3494 else if (pen->mol->par != NULL)
3495 n = *((Int *)((char *)(pen->mol->par) + ofs));
3498 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3499 Data_Get_Struct(aval, ParameterRef, pref);
3500 for (i = 0; i < n; i++) {
3509 * reverse_each {|pref| ...}
3511 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3514 s_ParEnumerable_ReverseEach(VALUE self)
3520 Data_Get_Struct(self, ParEnumerable, pen);
3521 if (pen->parType == kElementParType)
3522 n = gCountElementParameters;
3524 switch (pen->parType) {
3525 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3526 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3527 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3528 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3529 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3530 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3531 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3533 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3535 if (pen->mol == NULL)
3536 n = *((Int *)((char *)gBuiltinParameters + ofs));
3537 else if (pen->mol->par != NULL)
3538 n = *((Int *)((char *)(pen->mol->par) + ofs));
3541 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3542 Data_Get_Struct(aval, ParameterRef, pref);
3543 for (i = n - 1; i >= 0; i--) {
3552 * insert(idx = nil, pref = nil) -> ParameterRef
3554 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3555 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3556 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3557 * parameter is left undefined.
3558 * Throws an exception if ParEnumerable points to the global parameter.
3561 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3569 Data_Get_Struct(self, ParEnumerable, pen);
3570 if (pen->mol == NULL)
3571 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3572 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3573 rb_scan_args(argc, argv, "02", &ival, &pval);
3575 i = NUM2INT(rb_Integer(ival));
3577 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3582 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3583 if (up == NULL || type != pen->parType)
3584 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3585 ParameterCopyOneWithType(&u, up, pen->parType);
3588 memset(&u, 0, sizeof(u));
3591 ig = IntGroupNewWithPoints(n, 1, -1);
3592 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3594 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3595 MolActionCallback_registerUndo(pen->mol, act);
3596 MolActionRelease(act);
3598 IntGroupRelease(ig);
3599 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3607 * Delete the parameter(s) specified by the argument.
3610 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3615 Data_Get_Struct(self, ParEnumerable, pen);
3616 if (pen->mol == NULL)
3617 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3618 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3619 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3620 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3623 ig = IntGroupFromValue(ival);
3624 if ((i = IntGroupGetCount(ig)) == 0) {
3625 IntGroupRelease(ig);
3629 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3630 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3633 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3634 IntGroupRelease(ig);
3640 * lookup(atom_types, options, ...) -> ParameterRef
3641 * lookup(atom_type_string, options, ...) -> ParameterRef
3643 * Find the parameter record that matches the given atom types. The arguments are
3644 * the same as Parameter#lookup, except for the parameter type which is implicitly
3648 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3651 Data_Get_Struct(self, ParEnumerable, pen);
3652 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3657 * self == parEnumerable -> boolean
3659 * True if the arguments point to the same parameter table and type.
3662 s_ParEnumerable_Equal(VALUE self, VALUE val)
3664 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3665 ParEnumerable *pen1, *pen2;
3666 Data_Get_Struct(self, ParEnumerable, pen1);
3667 Data_Get_Struct(val, ParEnumerable, pen2);
3668 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3669 } else return Qfalse;
3672 #pragma mark ====== AtomRef Class ======
3674 /* Forward declaration for register undo */
3675 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3677 /* Ruby string "set_atom_attr" */
3678 static VALUE s_SetAtomAttrString;
3681 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3685 Data_Get_Struct(self, AtomRef, aref);
3686 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3687 if (idx < 0 || idx >= aref->mol->natoms)
3688 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3690 *app = aref->mol->atoms + idx;
3697 s_AtomFromValue(VALUE self)
3700 s_AtomIndexFromValue(self, &ap, NULL);
3705 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3708 s_AtomIndexFromValue(self, &ap, mpp);
3713 s_NotifyModificationForAtomRef(VALUE self)
3716 Data_Get_Struct(self, AtomRef, aref);
3717 MoleculeIncrementModifyCount(aref->mol);
3721 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3724 Data_Get_Struct(self, AtomRef, aref);
3725 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3728 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3729 MolActionCallback_registerUndo(aref->mol, act);
3730 MoleculeCallback_notifyModification(aref->mol, 0);
3731 /* Request MD rebuilt if necessary */
3732 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3733 aref->mol->needsMDRebuild = 1;
3738 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3741 aref = AtomRefNew(mol, idx);
3742 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3746 s_AtomRef_GetMolecule(VALUE self)
3749 s_AtomIndexFromValue(self, NULL, &mpp);
3750 return ValueFromMolecule(mpp);
3753 static VALUE s_AtomRef_GetIndex(VALUE self) {
3754 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3757 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3758 return INT2NUM(s_AtomFromValue(self)->segSeq);
3761 static VALUE s_AtomRef_GetSegName(VALUE self) {
3762 char *p = s_AtomFromValue(self)->segName;
3763 return rb_str_new(p, strlen_limit(p, 4));
3766 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3767 return INT2NUM(s_AtomFromValue(self)->resSeq);
3770 static VALUE s_AtomRef_GetResName(VALUE self) {
3771 char *p = s_AtomFromValue(self)->resName;
3772 return rb_str_new(p, strlen_limit(p, 4));
3775 static VALUE s_AtomRef_GetName(VALUE self) {
3776 char *p = s_AtomFromValue(self)->aname;
3777 return rb_str_new(p, strlen_limit(p, 4));
3780 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3781 int type = s_AtomFromValue(self)->type;
3782 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3783 return rb_str_new(p, strlen_limit(p, 6));
3786 static VALUE s_AtomRef_GetCharge(VALUE self) {
3787 return rb_float_new(s_AtomFromValue(self)->charge);
3790 static VALUE s_AtomRef_GetWeight(VALUE self) {
3791 return rb_float_new(s_AtomFromValue(self)->weight);
3794 static VALUE s_AtomRef_GetElement(VALUE self) {
3795 char *p = s_AtomFromValue(self)->element;
3796 return rb_str_new(p, strlen_limit(p, 4));
3799 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3800 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3803 static VALUE s_AtomRef_GetConnects(VALUE self) {
3806 Atom *ap = s_AtomFromValue(self);
3807 retval = rb_ary_new();
3808 cp = AtomConnectData(&ap->connect);
3809 for (i = 0; i < ap->connect.count; i++)
3810 rb_ary_push(retval, INT2NUM(cp[i]));
3814 static VALUE s_AtomRef_GetR(VALUE self) {
3815 return ValueFromVector(&(s_AtomFromValue(self)->r));
3818 static VALUE s_AtomRef_GetX(VALUE self) {
3819 return rb_float_new(s_AtomFromValue(self)->r.x);
3822 static VALUE s_AtomRef_GetY(VALUE self) {
3823 return rb_float_new(s_AtomFromValue(self)->r.y);
3826 static VALUE s_AtomRef_GetZ(VALUE self) {
3827 return rb_float_new(s_AtomFromValue(self)->r.z);
3830 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3834 s_AtomIndexFromValue(self, &ap, &mp);
3836 if (mp->cell != NULL)
3837 TransformVec(&r1, mp->cell->rtr, &r1);
3841 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3842 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3843 return ValueFromVector(&r1);
3846 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3847 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3850 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3851 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3854 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3855 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3858 static VALUE s_AtomRef_GetSigma(VALUE self) {
3859 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3862 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3863 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3866 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3867 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3870 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3871 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3874 static VALUE s_AtomRef_GetV(VALUE self) {
3875 return ValueFromVector(&(s_AtomFromValue(self)->v));
3878 static VALUE s_AtomRef_GetF(VALUE self) {
3879 Vector v = s_AtomFromValue(self)->f;
3880 VecScaleSelf(v, INTERNAL2KCAL);
3881 return ValueFromVector(&v);
3884 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3885 return rb_float_new(s_AtomFromValue(self)->occupancy);
3888 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3889 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3892 static VALUE s_AtomRef_GetAniso(VALUE self) {
3895 Atom *ap = s_AtomFromValue(self);
3896 if (ap->aniso == NULL)
3898 retval = rb_ary_new();
3899 for (i = 0; i < 6; i++)
3900 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3901 if (ap->aniso->has_bsig) {
3902 rb_ary_push(retval, INT2NUM(0));
3903 for (i = 0; i < 6; i++)
3904 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3909 static VALUE s_AtomRef_GetSymop(VALUE self) {
3911 Atom *ap = s_AtomFromValue(self);
3912 if (!ap->symop.alive)
3914 retval = rb_ary_new();
3915 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3916 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3917 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3918 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3919 rb_ary_push(retval, INT2NUM(ap->symbase));
3923 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3924 return INT2NUM(s_AtomFromValue(self)->intCharge);
3927 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3928 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3931 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3932 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3935 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3939 MDExclusion *exinfo;
3942 idx = s_AtomIndexFromValue(self, &ap, &mol);
3943 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3944 VALUE mval = ValueFromMolecule(mol);
3945 s_RebuildMDParameterIfNecessary(mval, Qnil);
3947 if (mol->arena->exinfo == NULL)
3949 exinfo = mol->arena->exinfo + idx;
3950 exlist = mol->arena->exlist;
3951 retval = rb_ary_new();
3952 aval = rb_ary_new();
3953 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3954 rb_ary_push(aval, INT2NUM(exlist[i]));
3955 rb_ary_push(retval, aval);
3956 aval = rb_ary_new();
3957 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3958 rb_ary_push(aval, INT2NUM(exlist[i]));
3959 rb_ary_push(retval, aval);
3960 aval = rb_ary_new();
3961 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3962 rb_ary_push(aval, INT2NUM(exlist[i]));
3963 rb_ary_push(retval, aval);
3967 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3968 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3971 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3972 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3975 static VALUE s_AtomRef_GetHidden(VALUE self) {
3976 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3979 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3982 Atom *ap = s_AtomFromValue(self);
3983 if (ap->anchor == NULL)
3985 count = ap->anchor->connect.count;
3986 retval = rb_ary_new2(count * 2);
3987 cp = AtomConnectData(&ap->anchor->connect);
3988 for (i = 0; i < count; i++) {
3989 rb_ary_store(retval, i, INT2NUM(cp[i]));
3990 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3995 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3996 char *p = s_AtomFromValue(self)->uff_type;
3997 return rb_str_new(p, strlen_limit(p, 5));
4000 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4001 rb_raise(rb_eMolbyError, "index cannot be directly set");
4005 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4006 VALUE oval = s_AtomRef_GetSegSeq(self);
4007 val = rb_Integer(val);
4008 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4009 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4013 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4014 char *p = StringValuePtr(val);
4015 VALUE oval = s_AtomRef_GetSegName(self);
4016 strncpy(s_AtomFromValue(self)->segName, p, 4);
4017 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4021 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4022 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4023 return val; /* Not reached */
4026 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4027 Atom *ap = s_AtomFromValue(self);
4028 char *p = StringValuePtr(val);
4029 VALUE oval = s_AtomRef_GetName(self);
4030 if (ap->anchor != NULL && p[0] == '_')
4031 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4032 strncpy(ap->aname, p, 4);
4033 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4037 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4039 char *p = StringValuePtr(val);
4040 VALUE oval = s_AtomRef_GetAtomType(self);
4041 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4042 if (type != 0 && type < kAtomTypeMinimum)
4043 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4044 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4045 mp->needsMDRebuild = 1;
4046 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4050 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4052 VALUE oval = s_AtomRef_GetCharge(self);
4053 val = rb_Float(val);
4054 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4055 mp->needsMDRebuild = 1;
4056 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4060 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4062 VALUE oval = s_AtomRef_GetWeight(self);
4063 val = rb_Float(val);
4064 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4065 mp->needsMDRebuild = 1;
4066 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4070 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4073 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4074 char *p = StringValuePtr(val);
4075 VALUE oval = s_AtomRef_GetElement(self);
4076 ap->atomicNumber = ElementToInt(p);
4077 ElementToString(ap->atomicNumber, ap->element);
4078 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4080 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4081 mp->needsMDRebuild = 1;
4085 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4088 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4089 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4090 val = rb_Integer(val);
4091 ap->atomicNumber = NUM2INT(val);
4092 ElementToString(ap->atomicNumber, ap->element);
4093 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4095 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4096 mp->needsMDRebuild = 1;
4100 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4101 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4102 return val; /* Not reached */
4105 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4108 VALUE oval = s_AtomRef_GetR(self);
4109 VectorFromValue(val, &v);
4110 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4111 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4112 mp->needsMDCopyCoordinates = 1;
4116 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4119 VALUE oval = s_AtomRef_GetX(self);
4120 val = rb_Float(val);
4122 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4123 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4124 mp->needsMDCopyCoordinates = 1;
4128 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4131 VALUE oval = s_AtomRef_GetY(self);
4132 val = rb_Float(val);
4134 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4135 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4136 mp->needsMDCopyCoordinates = 1;
4140 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4143 VALUE oval = s_AtomRef_GetZ(self);
4144 val = rb_Float(val);
4146 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4147 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4148 mp->needsMDCopyCoordinates = 1;
4152 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4156 s_AtomIndexFromValue(self, &ap, &mp);
4158 VectorFromValue(val, &v);
4159 if (mp->cell != NULL)
4160 TransformVec(&v, mp->cell->tr, &v);
4162 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4163 mp->needsMDCopyCoordinates = 1;
4167 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4172 s_AtomIndexFromValue(self, &ap, &mp);
4174 val = rb_Float(val);
4176 if (mp->cell != NULL) {
4177 TransformVec(&v, mp->cell->rtr, &v);
4179 TransformVec(&v, mp->cell->tr, &v);
4182 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4183 mp->needsMDCopyCoordinates = 1;
4187 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4192 s_AtomIndexFromValue(self, &ap, &mp);
4194 val = rb_Float(val);
4196 if (mp->cell != NULL) {
4197 TransformVec(&v, mp->cell->rtr, &v);
4199 TransformVec(&v, mp->cell->tr, &v);
4202 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4203 mp->needsMDCopyCoordinates = 1;
4207 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4212 s_AtomIndexFromValue(self, &ap, &mp);
4214 val = rb_Float(val);
4216 if (mp->cell != NULL) {
4217 TransformVec(&v, mp->cell->rtr, &v);
4219 TransformVec(&v, mp->cell->tr, &v);
4222 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4223 mp->needsMDCopyCoordinates = 1;
4227 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4230 VALUE oval = s_AtomRef_GetSigma(self);
4231 VectorFromValue(val, &v);
4232 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4233 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4234 mp->needsMDCopyCoordinates = 1;
4238 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4241 VALUE oval = s_AtomRef_GetSigmaX(self);
4242 val = rb_Float(val);
4244 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4245 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4246 mp->needsMDCopyCoordinates = 1;
4250 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4253 VALUE oval = s_AtomRef_GetSigmaY(self);
4254 val = rb_Float(val);
4256 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4257 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4258 mp->needsMDCopyCoordinates = 1;
4262 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4265 VALUE oval = s_AtomRef_GetSigmaZ(self);
4266 val = rb_Float(val);
4268 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4269 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4270 mp->needsMDCopyCoordinates = 1;
4274 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4278 VALUE oval = s_AtomRef_GetV(self);
4279 VectorFromValue(val, &v);
4280 s_AtomIndexFromValue(self, &ap, &mp);
4282 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4283 mp->needsMDCopyCoordinates = 1;
4287 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4290 VALUE oval = s_AtomRef_GetF(self);
4291 VectorFromValue(val, &v);
4292 VecScaleSelf(v, KCAL2INTERNAL);
4293 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4294 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4295 mp->needsMDCopyCoordinates = 1;
4299 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4300 VALUE oval = s_AtomRef_GetOccupancy(self);
4302 val = rb_Float(val);
4303 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4304 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4305 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4309 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4310 VALUE oval = s_AtomRef_GetTempFactor(self);
4311 val = rb_Float(val);
4312 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4313 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4317 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4322 VALUE oval = s_AtomRef_GetAniso(self);
4323 Data_Get_Struct(self, AtomRef, aref);
4324 val = rb_funcall(val, rb_intern("to_a"), 0);
4325 n = RARRAY_LEN(val);
4326 valp = RARRAY_PTR(val);
4327 for (i = 0; i < 6; i++) {
4329 f[i] = NUM2DBL(rb_Float(valp[i]));
4333 type = NUM2INT(rb_Integer(valp[6]));
4336 for (i = 0; i < 6; i++)
4337 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4339 for (i = 0; i < 6; i++)
4342 i = s_AtomIndexFromValue(self, NULL, NULL);
4343 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4344 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4348 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4354 VALUE oval = s_AtomRef_GetSymop(self);
4355 i = s_AtomIndexFromValue(self, &ap, &mol);
4357 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4359 val = rb_funcall(val, rb_intern("to_a"), 0);
4360 n = RARRAY_LEN(val);
4361 valp = RARRAY_PTR(val);
4362 for (i = 0; i < 5; i++) {
4364 if (valp[i] == Qnil)
4367 ival[i] = NUM2INT(rb_Integer(valp[i]));
4368 } else ival[i] = -100000;
4371 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4372 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));
4373 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4374 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4375 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4376 return val; /* No need to change */
4377 if (ival[0] != -100000)
4378 ap->symop.sym = ival[0];
4379 if (ival[1] != -100000)
4380 ap->symop.dx = ival[1];
4381 if (ival[2] != -100000)
4382 ap->symop.dy = ival[2];
4383 if (ival[3] != -100000)
4384 ap->symop.dz = ival[3];
4385 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4386 if (ival[4] != -100000)
4387 ap->symbase = ival[4];
4388 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4389 /* The anisotropic parameters should be recalculated */
4390 VALUE oaval = s_AtomRef_GetAniso(self);
4391 MoleculeSetAnisoBySymop(mol, i);
4392 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4394 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4398 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4399 VALUE oval = s_AtomRef_GetIntCharge(self);
4400 val = rb_Integer(val);
4401 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4402 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4406 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4408 VALUE oval = s_AtomRef_GetFixForce(self);
4409 val = rb_Float(val);
4410 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4411 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4412 mp->needsMDRebuild = 1;
4416 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4419 VALUE oval = s_AtomRef_GetFixPos(self);
4420 VectorFromValue(val, &v);
4421 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4422 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4423 mp->needsMDRebuild = 1;
4427 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4428 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4429 return val; /* Not reached */
4432 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4433 VALUE oval = s_AtomRef_GetIntCharge(self);
4434 val = rb_Integer(val);
4435 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4436 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4440 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4441 VALUE oval = s_AtomRef_GetIntCharge(self);
4442 val = rb_Integer(val);
4443 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4444 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4448 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4449 Atom *ap = s_AtomFromValue(self);
4450 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4452 ap->exflags |= kAtomHiddenFlag;
4454 ap->exflags &= ~kAtomHiddenFlag;
4456 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4460 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4461 Int idx, i, j, k, n, *ip;
4468 MolAction **undoActions;
4469 memset(&ac, 0, sizeof(ac));
4470 idx = s_AtomIndexFromValue(self, &ap, &mol);
4471 oval = s_AtomRef_GetAnchorList(self);
4473 val = rb_ary_to_ary(val);
4474 n = RARRAY_LEN(val);
4477 if (ap->anchor != NULL) {
4478 AtomConnectResize(&ap->anchor->connect, 0);
4479 free(ap->anchor->coeffs);
4482 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4487 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4488 if (ap->aname[0] == '_')
4489 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4490 ip = (Int *)malloc(sizeof(Int) * n);
4492 for (i = 0; i < n; i++) {
4493 v = RARRAY_PTR(val)[i];
4494 if (rb_obj_is_kind_of(v, rb_cFloat))
4496 j = NUM2INT(rb_Integer(v));
4497 if (j < 0 || j >= mol->natoms)
4498 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4499 for (k = 0; k < i; k++) {
4501 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4507 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4508 else if (i * 2 != n)
4509 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4510 dp = (Double *)malloc(sizeof(Double) * n / 2);
4511 for (i = 0; i < n / 2; i++) {
4512 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4514 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4520 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4524 rb_raise(rb_eMolbyError, "invalid argument");
4525 if (nUndoActions > 0) {
4526 for (i = 0; i < nUndoActions; i++) {
4527 MolActionCallback_registerUndo(mol, undoActions[i]);
4528 MolActionRelease(undoActions[i]);
4532 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4536 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4537 Atom *ap = s_AtomFromValue(self);
4538 char *p = StringValuePtr(val);
4539 VALUE oval = s_AtomRef_GetUFFType(self);
4540 strncpy(ap->uff_type, p, 5);
4541 ap->uff_type[5] = 0;
4542 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4546 static struct s_AtomAttrDef {
4548 VALUE *symref; /* Address of s_IndexSymbol etc. */
4549 ID id; /* Will be set within InitMolby() */
4550 VALUE (*getter)(VALUE);
4551 VALUE (*setter)(VALUE, VALUE);
4552 } s_AtomAttrDefTable[] = {
4553 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4554 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4555 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4556 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4557 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4558 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4559 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4560 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4561 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4562 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4563 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4564 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4565 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4566 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4567 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4568 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4569 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4570 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4571 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4572 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4573 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4574 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4575 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4576 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4577 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4578 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4579 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4580 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4581 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4582 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4583 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4584 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4585 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4586 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4587 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4588 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4589 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4590 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4591 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4592 {NULL} /* Sentinel */
4596 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4600 if (TYPE(key) != T_SYMBOL) {
4601 kid = rb_intern(StringValuePtr(key));
4603 } else kid = SYM2ID(key);
4604 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4605 if (s_AtomAttrDefTable[i].id == kid) {
4606 if (value == Qundef)
4607 return (*(s_AtomAttrDefTable[i].getter))(self);
4609 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4612 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4613 return Qnil; /* not reached */
4617 s_AtomRef_GetAttr(VALUE self, VALUE key)
4619 return s_AtomRef_SetAttr(self, key, Qundef);
4624 * self == atomRef -> boolean
4626 * True if the two references point to the same atom.
4629 s_AtomRef_Equal(VALUE self, VALUE val)
4631 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4632 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4633 } else return Qfalse;
4636 #pragma mark ====== MolEnumerable Class ======
4638 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4642 * self[idx] -> AtomRef or Array of Integers
4644 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4645 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4646 * value is a String. Otherwise, the return value is an Array of Integers.
4649 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4651 MolEnumerable *mseq;
4654 Data_Get_Struct(self, MolEnumerable, mseq);
4656 if (mseq->kind == kAtomKind) {
4657 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4659 idx1 = NUM2INT(arg1);
4660 switch (mseq->kind) {
4662 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4663 if (idx2 < 0 || idx2 >= mol->nbonds)
4664 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4665 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4668 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4669 if (idx2 < 0 || idx2 >= mol->nangles)
4670 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4671 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4673 case kDihedralKind: {
4674 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4675 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4676 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4677 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]));
4679 case kImproperKind: {
4680 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4681 if (idx2 < 0 || idx2 >= mol->nimpropers)
4682 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4683 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]));
4685 case kResidueKind: {
4687 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4688 if (idx2 < 0 || idx2 >= mol->nresidues)
4689 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4690 p = mol->residues[idx2];
4691 return rb_str_new(p, strlen_limit(p, 4));
4701 * Returns the number of objects included in this enumerable.
4704 s_MolEnumerable_Length(VALUE self)
4706 MolEnumerable *mseq;
4707 Data_Get_Struct(self, MolEnumerable, mseq);
4708 switch (mseq->kind) {
4710 return INT2NUM(mseq->mol->natoms);
4712 return INT2NUM(mseq->mol->nbonds);
4714 return INT2NUM(mseq->mol->nangles);
4716 return INT2NUM(mseq->mol->ndihedrals);
4718 return INT2NUM(mseq->mol->nimpropers);
4720 return INT2NUM(mseq->mol->nresidues);
4729 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4730 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4731 * For the atoms, a same AtomRef object is passed (with different internal information)
4732 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4733 * for each iteration.
4736 s_MolEnumerable_Each(VALUE self)
4738 MolEnumerable *mseq;
4740 int len = NUM2INT(s_MolEnumerable_Length(self));
4741 Data_Get_Struct(self, MolEnumerable, mseq);
4742 if (mseq->kind == kAtomKind) {
4743 /* The same AtomRef object will be used during the loop */
4744 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4745 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4746 for (i = 0; i < len; i++) {
4751 /* A new ruby object will be created at each iteration (not very efficient) */
4752 for (i = 0; i < len; i++) {
4753 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4761 * self == molEnumerable -> boolean
4763 * True if the two arguments point to the same molecule and enumerable type.
4766 s_MolEnumerable_Equal(VALUE self, VALUE val)
4768 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4769 MolEnumerable *mseq1, *mseq2;
4770 Data_Get_Struct(self, MolEnumerable, mseq1);
4771 Data_Get_Struct(val, MolEnumerable, mseq2);
4772 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4773 } else return Qfalse;
4777 #pragma mark ====== Molecule Class ======
4779 #pragma mark ------ Allocate/Release/Accessor ------
4781 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4782 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4783 char *gLoadSaveErrorMessage = NULL;
4785 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4788 MoleculeFromValue(VALUE val)
4791 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4792 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4793 Data_Get_Struct(val, Molecule, mol);
4797 static VALUE sMoleculeRetainArray = Qnil;
4799 /* The function is called from MoleculeRelease() */
4800 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4801 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4802 /* object is always returned for the same Molecule. */
4803 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4804 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4805 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4806 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4807 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4809 /* Register/unregister the exmolobj Ruby object */
4811 MoleculeReleaseExternalObj(Molecule *mol)
4813 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4814 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4815 mol->exmolobjProtected = 0;
4820 MoleculeRetainExternalObj(Molecule *mol)
4822 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4823 if (sMoleculeRetainArray == Qnil) {
4824 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4825 sMoleculeRetainArray = rb_ary_new();
4828 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4829 mol->exmolobjProtected = 1;
4833 /* Release hook function for Ruby */
4835 MoleculeReleaseHook(Molecule *mol)
4837 if (mol->exmolobj != NULL) {
4838 /* No need to remove from sMoleculeRetainArray */
4839 mol->exmolobj = NULL;
4840 mol->exmolobjProtected = 0;
4842 MoleculeRelease(mol);
4846 ValueFromMolecule(Molecule *mol)
4850 if (mol->exmolobj != NULL)
4851 return (VALUE)mol->exmolobj;
4852 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4853 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4854 return (VALUE)mol->exmolobj;
4859 s_Molecule_Alloc(VALUE klass)
4862 Molecule *mol = MoleculeNew();
4863 val = ValueFromMolecule(mol);
4864 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4869 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4873 if (FIXNUM_P(val)) {
4875 if (n >= 0 && n < mol->natoms)
4877 n = -1; /* No such atom */
4878 val = rb_inspect(val);
4880 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4882 if (n >= 0 && n < mol->natoms)
4884 p = StringValuePtr(val);
4886 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4888 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4890 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4891 return 0; /* Not reached */
4895 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4898 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4899 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4900 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4901 Data_Get_Struct(val, IntGroup, ig);
4910 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4911 * created object does not affect the old object in any sense.
4914 s_Molecule_InitCopy(VALUE self, VALUE arg)
4916 Molecule *mp1, *mp2;
4917 Data_Get_Struct(self, Molecule, mp1);
4918 mp2 = MoleculeFromValue(arg);
4919 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4920 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4926 * atom_index(val) -> Integer
4928 * Returns the atom index represented by val. val can be either a non-negative integer
4929 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4930 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
4931 * where resname, resid, name are the residue name, residue id, and atom name respectively.
4932 * If val is a string and multiple atoms match the description, the atom with the lowest index
4936 s_Molecule_AtomIndex(VALUE self, VALUE val)
4939 Data_Get_Struct(self, Molecule, mol);
4940 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4945 * self == Molecule -> boolean
4947 * True if the two arguments point to the same molecule.
4950 s_Molecule_Equal(VALUE self, VALUE val)
4952 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4953 Molecule *mol1, *mol2;
4954 Data_Get_Struct(self, Molecule, mol1);
4955 Data_Get_Struct(val, Molecule, mol2);
4956 return (mol1 == mol2 ? Qtrue : Qfalse);
4957 } else return Qfalse;
4960 #pragma mark ------ Load/Save ------
4963 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4965 if (gLoadSaveErrorMessage != NULL) {
4966 MyAppCallback_setConsoleColor(1);
4967 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4968 MyAppCallback_setConsoleColor(0);
4971 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4976 * loadmbsf(file) -> bool
4978 * Read a structure from a mbsf file.
4979 * Return true if successful.
4982 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4988 MoleculeClearLoadSaveErrorMessage();
4989 Data_Get_Struct(self, Molecule, mol);
4990 rb_scan_args(argc, argv, "1", &fname);
4991 fstr = FileStringValuePtr(fname);
4992 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4993 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
4999 * loadpsf(file, pdbfile = nil) -> bool
5001 * Read a structure from a psf file. molecule must be empty. The psf may be
5002 * an "extended" version, which also contains coordinates. If pdbfile
5003 * is given, then atomic coordinates are read from that file.
5004 * Return true if successful.
5007 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5009 VALUE fname, pdbname;
5010 char *fstr, *pdbstr;
5013 Data_Get_Struct(self, Molecule, mol);
5014 if (mol->natoms > 0)
5015 return Qnil; /* Must be a new molecule */
5016 MoleculeClearLoadSaveErrorMessage();
5017 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5018 fstr = FileStringValuePtr(fname);
5019 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5020 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5022 if (!NIL_P(pdbname)) {
5023 pdbstr = strdup(FileStringValuePtr(pdbname));
5024 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5026 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5033 * loadpdb(file) -> bool
5035 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5036 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5037 * Return true if successful.
5040 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5046 Data_Get_Struct(self, Molecule, mol);
5047 rb_scan_args(argc, argv, "1", &fname);
5048 MoleculeClearLoadSaveErrorMessage();
5049 fstr = FileStringValuePtr(fname);
5050 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5051 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5057 * loaddcd(file) -> bool
5059 * Read coordinates from a dcd file. The molecule should not empty.
5060 * Return true if successful.
5063 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5069 Data_Get_Struct(self, Molecule, mol);
5070 rb_scan_args(argc, argv, "1", &fname);
5071 MoleculeClearLoadSaveErrorMessage();
5072 fstr = FileStringValuePtr(fname);
5073 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5074 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5080 * loadtep(file) -> bool
5082 * Read coordinates from an ortep .tep file.
5083 * Return true if successful.
5086 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5092 Data_Get_Struct(self, Molecule, mol);
5093 rb_scan_args(argc, argv, "1", &fname);
5094 MoleculeClearLoadSaveErrorMessage();
5095 fstr = FileStringValuePtr(fname);
5096 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5097 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5103 * loadres(file) -> bool
5105 * Read coordinates from a shelx .res file.
5106 * Return true if successful.
5109 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5115 Data_Get_Struct(self, Molecule, mol);
5116 rb_scan_args(argc, argv, "1", &fname);
5117 MoleculeClearLoadSaveErrorMessage();
5118 fstr = FileStringValuePtr(fname);
5119 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5120 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5126 * loadfchk(file) -> bool
5128 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5129 * Return true if successful.
5132 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5138 Data_Get_Struct(self, Molecule, mol);
5139 rb_scan_args(argc, argv, "1", &fname);
5140 MoleculeClearLoadSaveErrorMessage();
5141 fstr = FileStringValuePtr(fname);
5142 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5143 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5149 * loaddat(file) -> bool
5151 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5152 * Return true if successful.
5155 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5161 Data_Get_Struct(self, Molecule, mol);
5162 rb_scan_args(argc, argv, "1", &fname);
5163 MoleculeClearLoadSaveErrorMessage();
5164 fstr = FileStringValuePtr(fname);
5165 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5166 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5167 MyAppCallback_hideProgressPanel();
5168 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5174 * savembsf(file) -> bool
5176 * Write structure as a mbsf file. Returns true if successful.
5179 s_Molecule_Savembsf(VALUE self, VALUE fname)
5184 Data_Get_Struct(self, Molecule, mol);
5185 MoleculeClearLoadSaveErrorMessage();
5186 fstr = FileStringValuePtr(fname);
5187 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5188 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5194 * savepsf(file) -> bool
5196 * Write structure as a psf file. Returns true if successful.
5199 s_Molecule_Savepsf(VALUE self, VALUE fname)
5204 Data_Get_Struct(self, Molecule, mol);
5205 MoleculeClearLoadSaveErrorMessage();
5206 fstr = FileStringValuePtr(fname);
5207 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5208 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5214 * savepdb(file) -> bool
5216 * Write coordinates as a pdb file. Returns true if successful.
5219 s_Molecule_Savepdb(VALUE self, VALUE fname)
5224 Data_Get_Struct(self, Molecule, mol);
5225 MoleculeClearLoadSaveErrorMessage();
5226 fstr = FileStringValuePtr(fname);
5227 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5228 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5234 * savedcd(file) -> bool
5236 * Write coordinates as a dcd file. Returns true if successful.
5239 s_Molecule_Savedcd(VALUE self, VALUE fname)
5244 Data_Get_Struct(self, Molecule, mol);
5245 MoleculeClearLoadSaveErrorMessage();
5246 fstr = FileStringValuePtr(fname);
5247 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5248 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5252 /* load([ftype, ] fname, ...) */
5254 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5257 char *argstr, *methname, *p, *type = "";
5260 const char *ls = (loadFlag ? "load" : "save");
5261 int lslen = strlen(ls);
5266 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5267 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5268 if (argstr[0] == ':') {
5269 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5270 methname = ALLOC_N(char, lslen + strlen(argstr));
5271 strcpy(methname, ls);
5272 strcat(methname, argstr + 1);
5274 for (i = lslen; methname[i] != 0; i++)
5275 methname[i] = tolower(methname[i]);
5276 mid = rb_intern(methname);
5280 rval = rb_funcall2(self, mid, argc, argv);
5286 /* Guess file type from extension */
5287 p = strrchr(argstr, '.');
5291 for (methname = p; *methname != 0; methname++) {
5292 if (!isalpha(*methname))
5295 if (*methname == 0) {
5296 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5297 if (methname == NULL)
5298 rb_raise(rb_eMolbyError, "Low memory");
5299 strcpy(methname, ls);
5300 strcat(methname, p);
5301 for (i = lslen; methname[i] != 0; i++)
5302 methname[i] = tolower(methname[i]);
5303 mid = rb_intern(methname);
5306 if (rb_respond_to(self, mid)) {
5307 /* Load: try to call the load procedure only if it is available */
5308 rval = rb_funcall2(self, mid, argc, argv);
5313 /* Save: call the save procedure, and if not found then call 'method_missing' */
5314 rval = rb_funcall2(self, mid, argc, argv);
5321 rval = rb_str_to_str(argv[0]);
5322 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5323 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5324 return Qnil; /* Does not reach here */
5328 /* Register the path */
5331 Data_Get_Struct(self, Molecule, mol);
5332 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5334 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5335 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5336 if (ap->occupancy != 0.0)
5339 if (i == mol->natoms) {
5340 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5341 ap->occupancy = 1.0;
5350 * molload(file, *args) -> bool
5352 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5353 * file type given by the extension). If this method fails, then all defined (public)
5354 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5357 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5359 return s_Molecule_LoadSave(argc, argv, self, 1);
5364 * molsave(file, *args) -> bool
5366 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5367 * (XXX is the file type given by the extension).
5370 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5372 return s_Molecule_LoadSave(argc, argv, self, 0);
5378 * open(file) -> Molecule
5380 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5383 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5389 rb_scan_args(argc, argv, "01", &fname);
5393 p = FileStringValuePtr(fname);
5394 iflag = Ruby_SetInterruptFlag(Qfalse);
5395 mp = MoleculeCallback_openNewMolecule(p);
5396 Ruby_SetInterruptFlag(iflag);
5399 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5401 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5403 return ValueFromMolecule(mp);
5409 * new(file, *args) -> Molecule
5411 * Create a new molecule and call "load" method with the same arguments.
5414 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5417 return s_Molecule_Load(argc, argv, self);
5418 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5423 * error_message -> String
5425 * Get the error_message from the last load/save method. If no error, returns nil.
5428 s_Molecule_ErrorMessage(VALUE klass)
5430 if (gLoadSaveErrorMessage == NULL)
5432 else return rb_str_new2(gLoadSaveErrorMessage);
5437 * set_error_message(String)
5438 * Molecule.error_message = String
5440 * Get the error_message from the last load/save method. If no error, returns nil.
5443 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5445 if (gLoadSaveErrorMessage != NULL) {
5446 free(gLoadSaveErrorMessage);
5447 gLoadSaveErrorMessage = NULL;
5450 sval = rb_str_to_str(sval);
5451 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5458 * set_molecule(Molecule)
5460 * Duplicate the given molecule and set to self. The present molecule must be empty.
5461 * This method is exclusively used for associating a new document with an existing molecule.
5464 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5466 Molecule *mp1, *mp2;
5467 Data_Get_Struct(self, Molecule, mp1);
5468 mp2 = MoleculeFromValue(mval);
5469 MoleculeInitWithMolecule(mp1, mp2);
5470 MoleculeCallback_notifyModification(mp1, 1);
5474 #pragma mark ------ Name attributes ------
5480 * Returns the display name of the molecule. If the molecule has no associated
5481 * document, then returns nil.
5484 s_Molecule_Name(VALUE self)
5488 Data_Get_Struct(self, Molecule, mol);
5489 MoleculeCallback_displayName(mol, buf, sizeof buf);
5493 return rb_str_new2(buf);
5498 * set_name(string) -> self
5500 * Set the name of an untitled molecule. If the molecule is not associated with window
5501 * or it already has an associated file, then exception is thrown.
5504 s_Molecule_SetName(VALUE self, VALUE nval)
5507 Data_Get_Struct(self, Molecule, mol);
5508 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5509 rb_raise(rb_eMolbyError, "Cannot change the window title");
5518 * Returns the full path name of the molecule, if it is associated with a file.
5519 * If the molecule has no associated file, then returns nil.
5522 s_Molecule_Path(VALUE self)
5526 Data_Get_Struct(self, Molecule, mol);
5527 MoleculeCallback_pathName(mol, buf, sizeof buf);
5531 return Ruby_NewFileStringValue(buf);
5538 * Returns the full path name of the directory in which the file associated with the
5539 * molecule is located. If the molecule has no associated file, then returns nil.
5542 s_Molecule_Dir(VALUE self)
5546 Data_Get_Struct(self, Molecule, mol);
5547 MoleculeCallback_pathName(mol, buf, sizeof buf);
5549 translate_char(buf, '\\', '/');
5554 p = strrchr(buf, '/');
5557 return rb_str_new2(buf);
5565 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5566 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5567 * the Molecule structure) is returned.
5570 s_Molecule_Inspect(VALUE self)
5574 Data_Get_Struct(self, Molecule, mol);
5575 MoleculeCallback_displayName(mol, buf, sizeof buf);
5577 /* No associated document */
5578 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5579 return rb_str_new2(buf);
5581 /* Check whether the document name is duplicate */
5585 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5586 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5587 if (strcmp(buf, buf2) == 0) {
5594 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5596 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5598 return rb_str_new2(buf2);
5602 #pragma mark ------ MolEnumerables ------
5605 s_Molecule_MolEnumerable(VALUE self, int kind)
5608 MolEnumerable *mseq;
5609 Data_Get_Struct(self, Molecule, mol);
5610 mseq = MolEnumerableNew(mol, kind);
5611 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5616 * atoms -> MolEnumerable
5618 * Returns a MolEnumerable object representing the array of atoms.
5621 s_Molecule_Atoms(VALUE self)
5623 return s_Molecule_MolEnumerable(self, kAtomKind);
5628 * bonds -> MolEnumerable
5630 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5631 * by an array of two atom indices.
5634 s_Molecule_Bonds(VALUE self)
5636 return s_Molecule_MolEnumerable(self, kBondKind);
5641 * angles -> MolEnumerable
5643 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5644 * by an array of three atom indices.
5647 s_Molecule_Angles(VALUE self)
5649 return s_Molecule_MolEnumerable(self, kAngleKind);
5654 * dihedrals -> MolEnumerable
5656 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5657 * by an array of four atom indices.
5660 s_Molecule_Dihedrals(VALUE self)
5662 return s_Molecule_MolEnumerable(self, kDihedralKind);
5667 * impropers -> MolEnumerable
5669 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5670 * by an array of four atom indices.
5673 s_Molecule_Impropers(VALUE self)
5675 return s_Molecule_MolEnumerable(self, kImproperKind);
5680 * residues -> MolEnumerable
5682 * Returns a MolEnumerable object representing the array of residue names.
5685 s_Molecule_Residues(VALUE self)
5687 return s_Molecule_MolEnumerable(self, kResidueKind);
5694 * Returns the number of atoms.
5697 s_Molecule_Natoms(VALUE self)
5700 Data_Get_Struct(self, Molecule, mol);
5701 return INT2NUM(mol->natoms);
5708 * Returns the number of bonds.
5711 s_Molecule_Nbonds(VALUE self)
5714 Data_Get_Struct(self, Molecule, mol);
5715 return INT2NUM(mol->nbonds);
5720 * nangles -> Integer
5722 * Returns the number of angles.
5725 s_Molecule_Nangles(VALUE self)
5728 Data_Get_Struct(self, Molecule, mol);
5729 return INT2NUM(mol->nangles);
5734 * ndihedrals -> Integer
5736 * Returns the number of dihedrals.
5739 s_Molecule_Ndihedrals(VALUE self)
5742 Data_Get_Struct(self, Molecule, mol);
5743 return INT2NUM(mol->ndihedrals);
5748 * nimpropers -> Integer
5750 * Returns the number of impropers.
5753 s_Molecule_Nimpropers(VALUE self)
5756 Data_Get_Struct(self, Molecule, mol);
5757 return INT2NUM(mol->nimpropers);
5762 * nresidues -> Integer
5764 * Returns the number of residues.
5767 s_Molecule_Nresidues(VALUE self)
5770 Data_Get_Struct(self, Molecule, mol);
5771 return INT2NUM(mol->nresidues);
5776 * nresidues = Integer
5778 * Change the number of residues.
5781 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5784 int ival = NUM2INT(val);
5785 Data_Get_Struct(self, Molecule, mol);
5786 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5787 if (ival != mol->nresidues)
5788 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5794 * max_residue_number(atom_group = nil) -> Integer
5796 * Returns the maximum residue number actually used. If an atom group is given, only
5797 * these atoms are examined. If no atom is present, nil is returned.
5800 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5806 Data_Get_Struct(self, Molecule, mol);
5807 rb_scan_args(argc, argv, "01", &gval);
5808 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5809 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5810 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5815 * min_residue_number(atom_group = nil) -> Integer
5817 * Returns the minimum residue number actually used. If an atom group is given, only
5818 * these atoms are examined. If no atom is present, nil is returned.
5821 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5827 Data_Get_Struct(self, Molecule, mol);
5828 rb_scan_args(argc, argv, "01", &gval);
5829 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5830 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5831 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5836 * each_atom(atom_group = nil) {|aref| ...}
5838 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5839 * group is given, only these atoms are processed.
5840 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5841 * is self (a Molecule object).
5844 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5852 Data_Get_Struct(self, Molecule, mol);
5853 rb_scan_args(argc, argv, "01", &gval);
5854 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5855 arval = ValueFromMoleculeAndIndex(mol, 0);
5856 Data_Get_Struct(arval, AtomRef, aref);
5857 for (i = 0; i < mol->natoms; i++) {
5859 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5863 IntGroupRelease(ig);
5867 #pragma mark ------ Atom Group ------
5870 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5872 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5873 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5874 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5875 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5882 * atom_group {|aref| ...}
5883 * atom_group(arg1, arg2, ...)
5884 * atom_group(arg1, arg2, ...) {|aref| ...}
5886 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5887 * If arguments are given, then the atoms reprensented by the arguments are added to the
5888 * group. For a conversion of a string to an atom index, see the description
5889 * of Molecule#atom_index.
5890 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5891 * representing each atom, and the atoms are removed from the result if the block returns false.
5895 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5897 IntGroup *ig1, *ig2;
5899 Int i, startPt, interval;
5900 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5901 Data_Get_Struct(retval, IntGroup, ig1);
5902 Data_Get_Struct(self, Molecule, mol);
5904 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5907 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5908 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5909 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5910 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5911 ig2 = IntGroupFromValue(*argv);
5912 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5913 interval = IntGroupGetInterval(ig2, i);
5914 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5916 IntGroupRelease(ig2);
5917 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5919 values[0] = (VALUE)mol;
5920 values[1] = (VALUE)ig1;
5921 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5923 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5928 if (rb_block_given_p()) {
5929 /* Evaluate the given block with an AtomRef as the argument, and delete
5930 the index if the block returns false */
5931 AtomRef *aref = AtomRefNew(mol, 0);
5932 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5933 ig2 = IntGroupNew();
5934 IntGroupCopy(ig2, ig1);
5935 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5937 if (startPt >= mol->natoms)
5939 aref->idx = startPt;
5940 resval = rb_yield(arval);
5942 IntGroupRemove(ig1, startPt, 1);
5944 IntGroupRelease(ig2);
5947 /* Remove points that are out of bounds */
5948 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5955 * selection -> IntGroup
5957 * Returns the current selection.
5960 s_Molecule_Selection(VALUE self)
5965 Data_Get_Struct(self, Molecule, mol);
5966 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5967 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
5968 val = ValueFromIntGroup(ig);
5969 IntGroupRelease(ig);
5971 val = IntGroup_Alloc(rb_cIntGroup);
5977 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5981 Data_Get_Struct(self, Molecule, mol);
5985 ig = s_Molecule_AtomGroupFromValue(self, val);
5987 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
5989 MoleculeSetSelection(mol, ig);
5991 IntGroupRelease(ig);
5997 * selection = IntGroup
5999 * Set the current selection. The right-hand operand may be nil.
6000 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6003 s_Molecule_SetSelection(VALUE self, VALUE val)
6005 return s_Molecule_SetSelectionSub(self, val, 0);
6010 * set_undoable_selection(IntGroup)
6012 * Set the current selection with undo registration. The right-hand operand may be nil.
6013 * This operation is undoable.
6016 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6018 return s_Molecule_SetSelectionSub(self, val, 1);
6021 #pragma mark ------ Editing ------
6025 * extract(group, dummy_flag = nil) -> Molecule
6027 * Extract the atoms given by group and return as a new molecule object.
6028 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6029 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6030 * names beginning with an underscore) and included in the new molecule object.
6033 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6035 Molecule *mol1, *mol2;
6037 VALUE group, dummy_flag, retval;
6038 Data_Get_Struct(self, Molecule, mol1);
6039 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6040 ig = s_Molecule_AtomGroupFromValue(self, group);
6041 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6044 retval = ValueFromMolecule(mol2);
6046 IntGroupRelease(ig);
6052 * add(molecule2) -> self
6054 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6056 This operation is undoable.
6059 s_Molecule_Add(VALUE self, VALUE val)
6061 Molecule *mol1, *mol2;
6062 Data_Get_Struct(self, Molecule, mol1);
6063 mol2 = MoleculeFromValue(val);
6064 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6070 * remove(group) -> Molecule
6072 * The atoms designated by the given group are removed from the molecule.
6073 * This operation is undoable.
6076 s_Molecule_Remove(VALUE self, VALUE group)
6081 IntGroupIterator iter;
6083 Data_Get_Struct(self, Molecule, mol1);
6084 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6085 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6086 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6087 Data_Get_Struct(group, IntGroup, ig);
6089 /* Remove the bonds between the two fragments */
6090 /* (This is necessary for undo to work correctly) */
6091 IntGroupIteratorInit(ig, &iter);
6093 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6094 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6096 cp = AtomConnectData(&ap->connect);
6097 for (j = 0; j < ap->connect.count; j++) {
6099 if (!IntGroupLookup(ig, n, NULL)) {
6100 /* bond i-n, i is in ig and n is not */
6101 int k = MoleculeLookupBond(mol1, i, n);
6105 IntGroupAdd(bg, k, 1);
6110 IntGroupIteratorRelease(&iter);
6113 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6114 IntGroupRelease(bg);
6117 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6124 * create_atom(name, pos = -1) -> AtomRef
6126 * Create a new atom with the specified name (may contain residue
6127 * information) and position (if position is out of range, the atom is appended at
6128 * the end). Returns the reference to the new atom.
6129 * This operation is undoable.
6132 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6139 char *p, resName[6], atomName[6];
6141 Data_Get_Struct(self, Molecule, mol);
6142 rb_scan_args(argc, argv, "02", &name, &ival);
6144 pos = NUM2INT(rb_Integer(ival));
6147 p = StringValuePtr(name);
6149 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6150 if (atomName[0] == 0)
6151 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6154 if (p == NULL || p[0] == 0) {
6155 memset(atomName, 0, 4);
6158 memset(&arec, 0, sizeof(arec));
6159 strncpy(arec.aname, atomName, 4);
6161 strncpy(arec.resName, resName, 4);
6162 arec.resSeq = resSeq;
6164 arec.occupancy = 1.0;
6165 // i = MoleculeCreateAnAtom(mol, &arec);
6166 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6168 aref = AtomRefNew(mol, pos);
6169 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6174 * duplicate_atom(atomref, pos = -1) -> AtomRef
6176 * Create a new atom with the same attributes (but no bonding information)
6177 * with the specified atom. Returns the reference to the new atom.
6178 * This operation is undoable.
6181 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6187 VALUE retval, aval, ival;
6189 Data_Get_Struct(self, Molecule, mol);
6190 rb_scan_args(argc, argv, "11", &aval, &ival);
6191 if (FIXNUM_P(aval)) {
6192 int idx = NUM2INT(aval);
6193 if (idx < 0 || idx >= mol->natoms)
6194 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6195 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6197 apsrc = s_AtomFromValue(aval);
6200 rb_raise(rb_eMolbyError, "bad atom specification");
6202 pos = NUM2INT(rb_Integer(ival));
6204 AtomDuplicate(&arec, apsrc);
6205 arec.connect.count = 0;
6206 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6209 aref = AtomRefNew(mol, pos);
6210 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6218 * create_bond(n1, n2, ...) -> Integer
6220 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6221 * do nothing for that pair. Returns the number of bonds actually created.
6222 * This operation is undoable.
6225 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6228 Int i, j, k, *ip, old_nbonds;
6230 rb_raise(rb_eMolbyError, "missing arguments");
6232 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6233 Data_Get_Struct(self, Molecule, mol);
6234 ip = ALLOC_N(Int, argc + 1);
6235 for (i = j = 0; i < argc; i++, j++) {
6236 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6238 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6239 j -= 2; /* This bond is already present: skip it */
6241 for (k = 0; k < j - 1; k += 2) {
6242 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6243 j -= 2; /* The same entry is already in the argument */
6250 old_nbonds = mol->nbonds;
6252 ip[j] = kInvalidIndex;
6253 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6257 rb_raise(rb_eMolbyError, "atom index out of range");
6259 rb_raise(rb_eMolbyError, "too many bonds");
6261 rb_raise(rb_eMolbyError, "duplicate bonds");
6263 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6265 rb_raise(rb_eMolbyError, "error in creating bonds");
6266 return INT2NUM(mol->nbonds - old_nbonds);
6271 * molecule.remove_bonds(n1, n2, ...) -> Integer
6273 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6274 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6275 * This operation is undoable.
6278 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6284 rb_raise(rb_eMolbyError, "missing arguments");
6286 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6287 Data_Get_Struct(self, Molecule, mol);
6289 for (i = j = 0; i < argc; i++, j = 1 - j) {
6290 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6292 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6296 IntGroupAdd(bg, k, 1);
6301 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6302 i = IntGroupGetCount(bg);
6303 IntGroupRelease(bg);
6310 * assign_bond_order(idx, d1)
6311 * assign_bond_orders(group, [d1, d2, ...])
6313 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6314 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6315 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6316 * (This may change in the future)
6317 * This operation is undoable.
6320 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6324 Data_Get_Struct(self, Molecule, mol);
6325 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6326 /* The first form */
6327 Int idx = NUM2INT(rb_Integer(idxval));
6328 Double d1 = NUM2DBL(rb_Float(dval));
6329 if (idx < 0 || idx >= mol->nbonds)
6330 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6331 ig = IntGroupNewWithPoints(idx, 1, -1);
6332 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6333 IntGroupRelease(ig);
6337 ig = IntGroupFromValue(idxval);
6338 n = IntGroupGetCount(ig);
6340 rb_raise(rb_eMolbyError, "the bond index is empty");
6341 dval = rb_ary_to_ary(dval);
6342 dp = (Double *)calloc(sizeof(Double), n);
6343 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6344 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6346 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6348 IntGroupRelease(ig);
6355 * get_bond_order(idx) -> Float
6356 * get_bond_orders(group) -> Array
6358 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6359 * In the second form, the bond orders at the indices in the group are returned as an array.
6360 * If no bond order information have been assigned, returns nil (the first form)
6361 * or an empty array (the second form).
6364 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6370 Int i, n, numericArg;
6371 Data_Get_Struct(self, Molecule, mol);
6372 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6373 /* The first form */
6374 Int idx = NUM2INT(rb_Integer(idxval));
6375 if (idx < 0 || idx >= mol->nbonds)
6376 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6377 if (mol->bondOrders == NULL)
6379 ig = IntGroupNewWithPoints(idx, 1, -1);
6383 if (mol->bondOrders == NULL)
6384 return rb_ary_new();
6385 ig = IntGroupFromValue(idxval);
6386 n = IntGroupGetCount(ig);
6388 rb_raise(rb_eMolbyError, "the bond index is empty");
6391 dp = (Double *)calloc(sizeof(Double), n);
6392 MoleculeGetBondOrders(mol, dp, ig);
6394 retval = rb_float_new(dp[0]);
6396 retval = rb_ary_new();
6397 for (i = 0; i < n; i++)
6398 rb_ary_push(retval, rb_float_new(dp[i]));
6401 IntGroupRelease(ig);
6407 * bond_exist?(idx1, idx2) -> bool
6409 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6410 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6413 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6419 Data_Get_Struct(self, Molecule, mol);
6420 idx1 = NUM2INT(rb_Integer(ival1));
6421 idx2 = NUM2INT(rb_Integer(ival2));
6422 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6423 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6424 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6425 cp = AtomConnectData(&ap->connect);
6426 for (i = 0; i < ap->connect.count; i++) {
6435 * add_angle(n1, n2, n3) -> Molecule
6437 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6438 * when a bond is created, so it is rarely necessary to use this method explicitly.
6439 * This operation is undoable.
6442 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6446 Data_Get_Struct(self, Molecule, mol);
6447 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6448 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6449 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6450 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6451 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6452 n[3] = kInvalidIndex;
6453 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6459 * remove_angle(n1, n2, n3) -> Molecule
6461 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6462 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6463 * This operation is undoable.
6466 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6471 Data_Get_Struct(self, Molecule, mol);
6472 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6473 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6474 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6475 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6476 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6477 ig = IntGroupNewWithPoints(n[3], 1, -1);
6478 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6479 IntGroupRelease(ig);
6485 * add_dihedral(n1, n2, n3, n4) -> Molecule
6487 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6488 * when a bond is created, so it is rarely necessary to use this method explicitly.
6489 * This operation is undoable.
6492 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6496 Data_Get_Struct(self, Molecule, mol);
6497 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6498 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6499 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6500 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6501 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6502 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6503 n[4] = kInvalidIndex;
6504 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6510 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6512 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6513 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6514 * This operation is undoable.
6517 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6522 Data_Get_Struct(self, Molecule, mol);
6523 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6524 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6525 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6526 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6527 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6528 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6529 ig = IntGroupNewWithPoints(n[4], 1, -1);
6530 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6531 IntGroupRelease(ig);
6537 * add_improper(n1, n2, n3, n4) -> Molecule
6539 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6540 * not automatically added when a new bond is created, so this method is more useful than
6541 * the angle/dihedral counterpart.
6542 * This operation is undoable.
6545 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6549 Data_Get_Struct(self, Molecule, mol);
6550 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6551 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6552 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6553 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6554 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6555 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6556 n[4] = kInvalidIndex;
6557 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6563 * remove_improper(n1, n2, n3, n4) -> Molecule
6564 * remove_improper(intgroup) -> Molecule
6566 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6567 * Returns self. Unlike angles and dihedrals, impropers are
6568 * not automatically added when a new bond is created, so this method is more useful than
6569 * the angle/dihedral counterpart.
6570 * This operation is undoable.
6573 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6576 VALUE v1, v2, v3, v4;
6579 Data_Get_Struct(self, Molecule, mol);
6581 ig = IntGroupFromValue(argv[0]);
6583 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6584 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6585 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6586 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6587 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6588 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6589 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6590 ig = IntGroupNewWithPoints(n[4], 1, -1);
6592 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6593 IntGroupRelease(ig);
6599 * assign_residue(group, res) -> Molecule
6601 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6602 * or "resname.resno". When the residue number is not specified, the residue number of
6603 * the first atom in the group is used.
6604 * This operation is undoable.
6607 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6611 char *p, *pp, buf[16];
6614 Data_Get_Struct(self, Molecule, mol);
6616 /* Parse the argument res */
6617 if (FIXNUM_P(res)) {
6618 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6619 resid = NUM2INT(res);
6622 p = StringValuePtr(res);
6623 pp = strchr(p, '.');
6625 resid = atoi(pp + 1);
6631 if (n > sizeof buf - 1)
6636 ig = s_Molecule_AtomGroupFromValue(self, range);
6637 if (ig == NULL || IntGroupGetCount(ig) == 0)
6641 /* Use the residue number of the first specified atom */
6642 n = IntGroupGetNthPoint(ig, 0);
6643 if (n >= mol->natoms)
6644 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6645 ap = ATOM_AT_INDEX(mol->atoms, n);
6648 /* Change the residue number */
6649 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6650 /* Change the residue name if necessary */
6654 seqs[1] = kInvalidIndex; */
6655 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6657 IntGroupRelease(ig);
6663 * offset_residue(group, offset) -> Molecule
6665 * Offset the residue number of the specified atoms. If any of the residue number gets
6666 * negative, then exception is thrown.
6667 * This operation is undoable.
6670 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6675 Data_Get_Struct(self, Molecule, mol);
6676 ig = s_Molecule_AtomGroupFromValue(self, range);
6677 ofs = NUM2INT(offset);
6678 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6680 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6681 IntGroupRelease(ig);
6687 * renumber_atoms(array) -> IntGroup
6689 * Change the order of atoms so that the atoms specified in the array argument appear
6690 * in this order from the top of the molecule. The atoms that are not included in array
6691 * are placed after these atoms, and these atoms are returned as an intGroup.
6692 * This operation is undoable.
6695 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6701 VALUE *valp, retval;
6702 Data_Get_Struct(self, Molecule, mol);
6703 if (TYPE(array) != T_ARRAY)
6704 array = rb_funcall(array, rb_intern("to_a"), 0);
6705 n = RARRAY_LEN(array);
6706 valp = RARRAY_PTR(array);
6707 new2old = ALLOC_N(Int, n + 1);
6708 for (i = 0; i < n; i++)
6709 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6710 new2old[i] = kInvalidIndex;
6711 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6713 rb_raise(rb_eMolbyError, "Atom index out of range");
6715 rb_raise(rb_eMolbyError, "Duplicate entry");
6717 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6718 retval = IntGroup_Alloc(rb_cIntGroup);
6719 Data_Get_Struct(retval, IntGroup, ig);
6720 if (mol->natoms > n)
6721 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6728 * set_atom_attr(index, key, value)
6730 * Set the atom attribute for the specified atom.
6731 * This operation is undoable.
6734 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6738 Data_Get_Struct(self, Molecule, mol);
6739 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6740 oldval = s_AtomRef_GetAttr(aref, key);
6743 s_AtomRef_SetAttr(aref, key, val);
6749 * get_atom_attr(index, key)
6751 * Get the atom attribute for the specified atom.
6754 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6756 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6759 #pragma mark ------ Undo Support ------
6763 * register_undo(script, *args)
6765 * Register an undo operation with the current molecule.
6768 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6773 Data_Get_Struct(self, Molecule, mol);
6774 rb_scan_args(argc, argv, "1*", &script, &args);
6775 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6776 MolActionCallback_registerUndo(mol, act);
6782 * undo_enabled? -> bool
6784 * Returns true if undo is enabled for this molecule; otherwise no.
6787 s_Molecule_UndoEnabled(VALUE self)
6790 Data_Get_Struct(self, Molecule, mol);
6791 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6798 * undo_enabled = bool
6800 * Enable or disable undo.
6803 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6806 Data_Get_Struct(self, Molecule, mol);
6807 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6811 #pragma mark ------ Measure ------
6814 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6816 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6817 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6818 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6820 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6826 * center_of_mass(group = nil) -> Vector3D
6828 * Calculate the center of mass for the given set of atoms. The argument
6829 * group is null, then all atoms are considered.
6832 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6838 Data_Get_Struct(self, Molecule, mol);
6839 rb_scan_args(argc, argv, "01", &group);
6840 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6841 s_Molecule_DoCenterOfMass(mol, &v, ig);
6843 IntGroupRelease(ig);
6844 return ValueFromVector(&v);
6849 * centralize(group = nil) -> self
6851 * Translate the molecule so that the center of mass of the given group is located
6852 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6855 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6861 Data_Get_Struct(self, Molecule, mol);
6862 rb_scan_args(argc, argv, "01", &group);
6863 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6864 s_Molecule_DoCenterOfMass(mol, &v, ig);
6866 IntGroupRelease(ig);
6870 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6876 * bounds(group = nil) -> [min, max]
6878 * Calculate the boundary. The return value is an array of two Vector3D objects.
6881 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6889 Data_Get_Struct(self, Molecule, mol);
6890 rb_scan_args(argc, argv, "01", &group);
6891 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6892 if (ig != NULL && IntGroupGetCount(ig) == 0)
6893 rb_raise(rb_eMolbyError, "atom group is empty");
6894 vmin.x = vmin.y = vmin.z = 1e30;
6895 vmax.x = vmax.y = vmax.z = -1e30;
6896 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6898 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6914 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6917 /* Get atom position or a vector */
6919 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6921 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6922 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6923 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6925 VectorFromValue(val, vp);
6931 * measure_bond(n1, n2) -> Float
6933 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
6934 * or Vector3D values.
6935 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6938 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6942 Data_Get_Struct(self, Molecule, mol);
6943 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6944 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6945 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6950 * measure_angle(n1, n2, n3) -> Float
6952 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
6953 * or Vector3D values. The return value is in degree.
6954 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6957 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6962 Data_Get_Struct(self, Molecule, mol);
6963 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6964 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6965 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6966 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6968 return Qnil; /* Cannot define */
6969 else return rb_float_new(d);
6974 * measure_dihedral(n1, n2, n3, n4) -> Float
6976 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
6977 * or Vector3D values. The return value is in degree.
6978 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6981 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6984 Vector v1, v2, v3, v4;
6986 Data_Get_Struct(self, Molecule, mol);
6987 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6988 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6989 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6990 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
6991 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
6993 return Qnil; /* Cannot define */
6994 else return rb_float_new(d);
6999 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7001 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7002 * first and second atom in the pair should belong to group1 and group2, respectively.
7003 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7006 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7009 VALUE limval, gval1, gval2, rval, igval;
7010 IntGroup *ig1, *ig2;
7011 IntGroupIterator iter1, iter2;
7017 MDExclusion *exinfo;
7020 Data_Get_Struct(self, Molecule, mol);
7021 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7022 lim = NUM2DBL(rb_Float(limval));
7024 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7026 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7028 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7030 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7032 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7034 if (!RTEST(igval)) {
7035 /* Use the exclusion table in MDArena */
7036 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7037 VALUE mval = ValueFromMolecule(mol);
7038 s_RebuildMDParameterIfNecessary(mval, Qnil);
7040 exinfo = mol->arena->exinfo; /* May be NULL */
7041 exlist = mol->arena->exlist;
7046 IntGroupIteratorInit(ig1, &iter1);
7047 IntGroupIteratorInit(ig2, &iter2);
7050 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7052 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7054 if (exinfo != NULL) {
7055 exn1 = exinfo[n[0]].index1;
7056 exn2 = exinfo[n[0] + 1].index1;
7057 } else exn1 = exn2 = -1;
7058 IntGroupIteratorReset(&iter2);
7059 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7060 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7062 continue; /* Same atom */
7063 if (exinfo != NULL) {
7064 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7065 for (i = exn1; i < exn2; i++) {
7066 if (exlist[i] == n[1])
7070 continue; /* Should be excluded */
7072 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7073 /* Is this pair already registered? */
7075 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7076 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7080 /* Not registered yet */
7081 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7086 IntGroupIteratorRelease(&iter2);
7087 IntGroupIteratorRelease(&iter1);
7088 IntGroupRelease(ig2);
7089 IntGroupRelease(ig1);
7090 rval = rb_ary_new2(npairs);
7091 if (pairs != NULL) {
7092 for (i = 0; i < npairs; i++) {
7093 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7102 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7104 * Find atoms that are within the threshold distance from the given atom.
7105 * (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.)
7106 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7107 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7108 * If limit is not given, a default value of 1.2 is used.
7109 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7112 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7115 VALUE aval, limval, radval;
7116 double limit, radius;
7117 Int n1, nbonds, *bonds, an;
7119 Data_Get_Struct(self, Molecule, mol);
7120 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7121 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)) {
7122 VectorFromValue(aval, &v);
7124 radius = gElementParameters[6].radius;
7126 radius = NUM2DBL(rb_Float(radval));
7129 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7130 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7131 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7132 if (an >= 0 && an < gCountElementParameters)
7133 radius = gElementParameters[an].radius;
7134 else radius = gElementParameters[6].radius;
7139 limit = NUM2DBL(rb_Float(limval));
7140 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7142 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7143 aval = rb_ary_new();
7145 for (n1 = 0; n1 < nbonds; n1++)
7146 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7154 * guess_bonds(limit = 1.2) -> Integer
7156 * Create bonds between atoms that are within the threshold distance.
7157 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7158 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7159 * If limit is not given, a default value of 1.2 is used.
7160 * The number of the newly created bonds is returned.
7161 * This operation is undoable.
7164 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7170 Data_Get_Struct(self, Molecule, mol);
7171 rb_scan_args(argc, argv, "01", &limval);
7175 limit = NUM2DBL(rb_Float(limval));
7176 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7178 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7181 return INT2NUM(nbonds);
7184 #pragma mark ------ Cell and Symmetry ------
7188 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7190 * Returns the unit cell parameters. If cell is not set, returns nil.
7193 s_Molecule_Cell(VALUE self)
7198 Data_Get_Struct(self, Molecule, mol);
7199 if (mol->cell == NULL)
7201 val = rb_ary_new2(6);
7202 for (i = 0; i < 6; i++)
7203 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7204 if (mol->cell->has_sigma) {
7205 for (i = 0; i < 6; i++) {
7206 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7214 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7215 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7217 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7218 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7219 This operation is undoable.
7220 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7223 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7227 int i, convert_coord, n;
7229 Data_Get_Struct(self, Molecule, mol);
7230 rb_scan_args(argc, argv, "11", &val, &cval);
7235 val = rb_ary_to_ary(val);
7236 len = RARRAY_LEN(val);
7239 } else if (len >= 6) {
7241 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7242 for (i = 0; i < n; i++)
7243 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7245 convert_coord = (RTEST(cval) ? 1 : 0);
7246 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7252 * box -> [avec, bvec, cvec, origin, flags]
7254 * Get the unit cell information in the form of a periodic bounding box.
7255 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7256 * Integers which define whether the system is periodic along the axis.
7257 * If no unit cell is defined, nil is returned.
7260 s_Molecule_Box(VALUE self)
7264 Data_Get_Struct(self, Molecule, mol);
7265 if (mol == NULL || mol->cell == NULL)
7267 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7268 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7269 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7270 v[3] = ValueFromVector(&(mol->cell->origin));
7271 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7272 val = rb_ary_new4(5, v);
7278 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7279 * set_box(d, origin = [0, 0, 0])
7282 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7283 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7285 Flags, if present, is a 3-member array of Integers defining whether the system is
7286 periodic along the axis.
7287 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7288 In the second form, an isotropic box with cell-length d is set.
7289 In the third form, the existing box is cleared.
7290 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7293 s_Molecule_SetBox(VALUE self, VALUE aval)
7297 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7299 Vector origin = {0, 0, 0};
7302 int i, convertCoordinates = 0;
7303 Data_Get_Struct(self, Molecule, mol);
7305 MolActionCreateAndPerform(mol, gMolActionClearBox);
7308 aval = rb_ary_to_ary(aval);
7309 for (i = 0; i < 6; i++) {
7310 if (i < RARRAY_LEN(aval))
7311 v[i] = (RARRAY_PTR(aval))[i];
7315 MolActionCreateAndPerform(mol, gMolActionClearBox);
7318 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7319 d = NUM2DBL(rb_Float(v[0]));
7320 for (i = 0; i < 3; i++)
7321 VecScale(vv[i], ax[i], d);
7323 VectorFromValue(v[1], &origin);
7324 flags[0] = flags[1] = flags[2] = 1;
7326 for (i = 0; i < 3; i++) {
7329 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7330 d = NUM2DBL(rb_Float(v[i]));
7331 VecScale(vv[i], ax[i], d);
7333 VectorFromValue(v[i], &vv[i]);
7335 flags[i] = (VecLength2(vv[i]) > 0.0);
7338 VectorFromValue(v[3], &origin);
7340 for (i = 0; i < 3; i++) {
7341 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7342 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7346 convertCoordinates = 1;
7348 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7354 * cell_periodicity -> [n1, n2, n3]
7356 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7360 s_Molecule_CellPeriodicity(VALUE self)
7363 Data_Get_Struct(self, Molecule, mol);
7364 if (mol->cell == NULL)
7366 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7371 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7372 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7374 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7375 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7376 * If cell is not defined, exception is raised.
7377 * This operation is undoable.
7380 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7384 Data_Get_Struct(self, Molecule, mol);
7385 if (mol->cell == NULL)
7386 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7389 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7390 flag = NUM2INT(rb_Integer(arg));
7394 arg = rb_ary_to_ary(arg);
7396 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7397 arg0 = RARRAY_PTR(arg)[i];
7398 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7399 flag |= (1 << (2 - i));
7402 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7408 * cell_flexibility -> bool
7410 * Returns the unit cell is flexible or not
7413 s_Molecule_CellFlexibility(VALUE self)
7415 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7418 Data_Get_Struct(self, Molecule, mol);
7419 if (mol->cell == NULL)
7421 if (mol->useFlexibleCell)
7423 else return Qfalse; */
7428 * self.cell_flexibility = bool
7429 * set_cell_flexibility(bool)
7431 * Change the unit cell is flexible or not
7434 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7436 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7439 Data_Get_Struct(self, Molecule, mol);
7440 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7446 * cell_transform -> Transform
7448 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7449 * If cell is not defined, nil is returned.
7452 s_Molecule_CellTransform(VALUE self)
7455 Data_Get_Struct(self, Molecule, mol);
7456 if (mol == NULL || mol->cell == NULL)
7458 return ValueFromTransform(&(mol->cell->tr));
7463 * symmetry -> Array of Transforms
7464 * symmetries -> Array of Transforms
7466 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7467 * returns an empty array.
7470 s_Molecule_Symmetry(VALUE self)
7475 Data_Get_Struct(self, Molecule, mol);
7476 if (mol->nsyms <= 0)
7477 return rb_ary_new();
7478 val = rb_ary_new2(mol->nsyms);
7479 for (i = 0; i < mol->nsyms; i++) {
7480 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7487 * nsymmetries -> Integer
7489 * Get the number of currently defined symmetry operations.
7492 s_Molecule_Nsymmetries(VALUE self)
7495 Data_Get_Struct(self, Molecule, mol);
7496 return INT2NUM(mol->nsyms);
7501 * add_symmetry(Transform) -> Integer
7503 * Add a new symmetry operation. If no symmetry operation is defined and the
7504 * given argument is not an identity transform, then also add an identity
7505 * transform at the index 0.
7506 * Returns the total number of symmetries after operation.
7509 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7513 Data_Get_Struct(self, Molecule, mol);
7514 TransformFromValue(trans, &tr);
7515 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7516 return INT2NUM(mol->nsyms);
7521 * remove_symmetry(count = nil) -> Integer
7522 * remove_symmetries(count = nil) -> Integer
7524 * Remove the specified number of symmetry operations. The last added ones are removed
7525 * first. If count is nil, then all symmetry operations are removed. Returns the
7526 * number of leftover symmetries.
7529 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7534 Data_Get_Struct(self, Molecule, mol);
7535 rb_scan_args(argc, argv, "01", &cval);
7539 n = NUM2INT(rb_Integer(cval));
7540 if (n < 0 || n > mol->nsyms)
7541 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7542 if (n == mol->nsyms)
7545 for (i = 0; i < n; i++)
7546 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7547 return INT2NUM(mol->nsyms);
7552 * wrap_unit_cell(group) -> Vector3D
7554 * Move the specified group so that the center of mass of the group is within the
7555 * unit cell. The offset vector is returned. If no periodic box is defined,
7556 * exception is raised.
7559 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7564 Data_Get_Struct(self, Molecule, mol);
7565 if (mol->cell == NULL)
7566 rb_raise(rb_eMolbyError, "no unit cell is defined");
7567 ig = s_Molecule_AtomGroupFromValue(self, gval);
7568 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7569 TransformVec(&v, mol->cell->rtr, &cv);
7570 if (mol->cell->flags[0])
7572 if (mol->cell->flags[1])
7574 if (mol->cell->flags[2])
7576 TransformVec(&dv, mol->cell->tr, &v);
7578 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7579 IntGroupRelease(ig);
7580 return ValueFromVector(&dv);
7585 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7587 * Expand the specified part of the molecule by the given symmetry operation.
7588 * Returns the array of atom indices corresponding to the expanded atoms.
7589 * If allow_overlap is true, then new atoms are created even when the
7590 * coordinates coincide with the some other atom (special position) of the
7591 * same element; otherwise, such atom will not be created and the index of the
7592 * existing atom is given in the returned array.
7595 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7598 VALUE gval, sval, xval, yval, zval, rval, oval;
7600 Int n[4], allow_overlap;
7604 Data_Get_Struct(self, Molecule, mol);
7605 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7606 n[0] = NUM2INT(rb_Integer(sval));
7607 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7608 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7609 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7610 allow_overlap = (RTEST(oval) ? 1 : 0);
7611 ig = s_Molecule_AtomGroupFromValue(self, gval);
7612 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7613 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7614 natoms = mol->natoms;
7616 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7618 rval = rb_ary_new2(nidx);
7619 while (--nidx >= 0) {
7620 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7622 /* if (natoms == mol->natoms)
7625 rval = IntGroup_Alloc(rb_cIntGroup);
7626 Data_Get_Struct(rval, IntGroup, ig);
7627 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7634 * amend_by_symmetry(group = nil) -> IntGroup
7636 * Expand the specified part of the molecule by the given symmetry operation.
7637 * Returns an IntGroup containing the added atoms.
7640 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7645 Data_Get_Struct(self, Molecule, mol);
7646 rb_scan_args(argc, argv, "01", &gval);
7648 ig = s_Molecule_AtomGroupFromValue(self, gval);
7650 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7651 rval = ValueFromIntGroup(ig2);
7652 IntGroupRelease(ig2);
7656 #pragma mark ------ Transforms ------
7660 * translate(vec, group = nil) -> Molecule
7662 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7663 * This operation is undoable.
7666 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7672 Data_Get_Struct(self, Molecule, mol);
7673 rb_scan_args(argc, argv, "11", &vec, &group);
7674 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7675 VectorFromValue(vec, &v);
7676 // MoleculeTranslate(mol, &v, ig);
7677 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7679 IntGroupRelease(ig);
7685 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7687 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7688 * If group is given, only atoms in the group are moved.
7689 * This operation is undoable.
7692 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7695 volatile VALUE aval, anval, cval, gval;
7700 Data_Get_Struct(self, Molecule, mol);
7701 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7702 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7703 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7704 VectorFromValue(aval, &av);
7706 cv.x = cv.y = cv.z = 0.0;
7708 VectorFromValue(cval, &cv);
7709 if (TransformForRotation(tr, &av, angle, &cv))
7710 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7711 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7713 IntGroupRelease(ig);
7719 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7721 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7722 * axis must not be a zero vector.
7723 * If group is given, only atoms in the group are moved.
7724 * This operation is undoable.
7727 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7730 volatile VALUE aval, cval, gval;
7734 Data_Get_Struct(self, Molecule, mol);
7735 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7736 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7737 VectorFromValue(aval, &av);
7739 cv.x = cv.y = cv.z = 0.0;
7741 VectorFromValue(cval, &cv);
7742 if (TransformForReflection(tr, &av, &cv))
7743 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7744 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7746 IntGroupRelease(ig);
7752 * invert(center = [0,0,0], group = nil) -> Molecule
7754 * Invert the molecule with the given center.
7755 * If group is given, only atoms in the group are moved.
7756 * This operation is undoable.
7759 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7762 volatile VALUE cval, gval;
7766 Data_Get_Struct(self, Molecule, mol);
7767 rb_scan_args(argc, argv, "02", &cval, &gval);
7768 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7770 cv.x = cv.y = cv.z = 0.0;
7772 VectorFromValue(cval, &cv);
7773 TransformForInversion(tr, &cv);
7774 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7776 IntGroupRelease(ig);
7782 * transform(transform, group = nil) -> Molecule
7784 * Transform the molecule by the given Transform object.
7785 * If group is given, only atoms in the group are moved.
7786 * This operation is undoable.
7789 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7795 Data_Get_Struct(self, Molecule, mol);
7796 rb_scan_args(argc, argv, "11", &trans, &group);
7797 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7798 TransformFromValue(trans, &tr);
7799 /* MoleculeTransform(mol, tr, ig); */
7800 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7802 IntGroupRelease(ig);
7808 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7810 * Get the transform corresponding to the symmetry operation. The symop can either be
7811 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7812 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7813 * Otherwise, the returned transform is for fractional coordinates.
7814 * Raises exception when no cell or no transform are defined.
7817 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7823 Data_Get_Struct(self, Molecule, mol);
7824 if (mol->cell == NULL)
7825 rb_raise(rb_eMolbyError, "no unit cell is defined");
7826 if (mol->nsyms == 0)
7827 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7828 rb_scan_args(argc, argv, "11", &sval, &fval);
7829 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7830 symop.sym = NUM2INT(rb_Integer(sval));
7831 symop.dx = symop.dy = symop.dz = 0;
7833 sval = rb_ary_to_ary(sval);
7834 if (RARRAY_LEN(sval) < 4)
7835 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7836 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7837 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7838 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7839 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7841 if (symop.sym >= mol->nsyms)
7842 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7843 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7844 return ValueFromTransform(&tr);
7849 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7851 * Get the symmetry operation corresponding to the given transform.
7852 * If is_cartesian is true, the given transform is for cartesian coordinates.
7853 * Otherwise, the given transform is for fractional coordinates.
7854 * Raises exception when no cell or no transform are defined.
7857 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7864 Data_Get_Struct(self, Molecule, mol);
7865 if (mol->cell == NULL)
7866 rb_raise(rb_eMolbyError, "no unit cell is defined");
7867 if (mol->nsyms == 0)
7868 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7869 rb_scan_args(argc, argv, "11", &tval, &fval);
7870 TransformFromValue(tval, &tr);
7871 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7873 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7875 return Qnil; /* Not found */
7879 #pragma mark ------ Frames ------
7883 * select_frame(index)
7886 * Select the specified frame. If successful, returns true, otherwise returns false.
7889 s_Molecule_SelectFrame(VALUE self, VALUE val)
7892 int ival = NUM2INT(val);
7893 Data_Get_Struct(self, Molecule, mol);
7894 ival = MoleculeSelectFrame(mol, ival, 1);
7904 * Get the current frame.
7907 s_Molecule_Frame(VALUE self)
7910 Data_Get_Struct(self, Molecule, mol);
7911 return INT2NUM(mol->cframe);
7916 * nframes -> Integer
7918 * Get the number of frames.
7921 s_Molecule_Nframes(VALUE self)
7924 Data_Get_Struct(self, Molecule, mol);
7925 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7930 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7931 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7933 * Insert new frames at the indices specified by the intGroup. If the first argument is
7934 * an integer, a single new frame is inserted at that index. If the first argument is
7935 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7936 * should be an array of arrays of Vector3Ds, then those coordinates are set
7937 * to the new frame. Otherwise, the coordinates of current molecule are copied
7939 * Returns an intGroup representing the inserted frames if successful, nil if not.
7942 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7944 VALUE val, coords, cells;
7947 int count, ival, i, j, len, len_c, len2, nframes;
7950 Data_Get_Struct(self, Molecule, mol);
7951 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7952 if (coords != Qnil) {
7953 if (TYPE(coords) != T_ARRAY)
7954 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7955 len = RARRAY_LEN(coords);
7957 if (cells != Qnil) {
7958 if (mol->cell == NULL)
7959 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7960 if (TYPE(cells) != T_ARRAY)
7961 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7962 len_c = RARRAY_LEN(cells);
7964 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7965 nframes = MoleculeGetNumberOfFrames(mol);
7967 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7968 val = ValueFromIntGroup(ig);
7970 ig = IntGroupFromValue(val);
7972 count = IntGroupGetCount(ig); /* Count is updated here */
7973 vp = ALLOC_N(Vector, mol->natoms * count);
7975 vp2 = ALLOC_N(Vector, 4 * count);
7979 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7980 ptr = RARRAY_PTR(coords);
7981 for (i = 0; i < count; i++) {
7982 if (TYPE(ptr[i]) != T_ARRAY)
7983 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7984 len2 = RARRAY_LEN(ptr[i]);
7985 if (len2 < mol->natoms)
7986 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7987 ptr2 = RARRAY_PTR(ptr[i]);
7988 for (j = 0; j < mol->natoms; j++)
7989 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7993 for (i = 0; i < count; i++) {
7994 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7995 vp[i * mol->natoms + j] = ap->r;
8001 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8002 ptr = RARRAY_PTR(cells);
8003 for (i = 0; i < count; i++) {
8004 if (TYPE(ptr[i]) != T_ARRAY)
8005 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8006 len2 = RARRAY_LEN(ptr[i]);
8008 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8009 ptr2 = RARRAY_PTR(ptr[i]);
8010 for (j = 0; j < 4; j++)
8011 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8014 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8015 IntGroupRelease(ig);
8019 return (ival >= 0 ? val : Qnil);
8024 * create_frame(coordinates = nil) -> Integer
8025 * create_frames(coordinates = nil) -> Integer
8027 * Same as molecule.insert_frames(nil, coordinates).
8030 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8033 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8035 return s_Molecule_InsertFrames(3, vals, self);
8040 * remove_frames(IntGroup, wantCoordinates = false)
8042 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8043 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8044 * removed frames is returned if operation is successful.
8047 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8054 Data_Get_Struct(self, Molecule, mol);
8055 rb_scan_args(argc, argv, "11", &val, &flag);
8056 ig = IntGroupFromValue(val);
8057 count = IntGroupGetCount(ig);
8059 /* Create return value before removing frames */
8064 retval = rb_ary_new2(count);
8065 for (i = 0; i < count; i++) {
8066 n = IntGroupGetNthPoint(ig, i);
8067 coords = rb_ary_new2(mol->natoms);
8068 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8069 if (n < ap->nframes && n != mol->cframe)
8072 rb_ary_push(coords, ValueFromVector(&v));
8074 rb_ary_push(retval, coords);
8076 } else retval = Qtrue;
8077 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8084 * each_frame {|n| ...}
8086 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8087 * the frame number. After completion, the original frame number is restored.
8090 s_Molecule_EachFrame(VALUE self)
8092 int i, cframe, nframes;
8094 Data_Get_Struct(self, Molecule, mol);
8095 cframe = mol->cframe;
8096 nframes = MoleculeGetNumberOfFrames(mol);
8098 for (i = 0; i < nframes; i++) {
8099 MoleculeSelectFrame(mol, i, 1);
8100 rb_yield(INT2NUM(i));
8102 MoleculeSelectFrame(mol, cframe, 1);
8109 * get_coord_from_frame(index, group = nil)
8111 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8112 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8113 * copied; now they are always copied)
8116 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8119 VALUE ival, gval, cval;
8120 Int index, i, j, n, nn;
8122 IntGroupIterator iter;
8125 Data_Get_Struct(self, Molecule, mol);
8126 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8128 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8129 index = NUM2INT(rb_Integer(ival));
8130 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8132 rb_raise(rb_eMolbyError, "No frame is present");
8134 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8137 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8139 ig = s_Molecule_AtomGroupFromValue(self, gval);
8141 n = IntGroupGetCount(ig);
8143 vp = (Vector *)calloc(sizeof(Vector), n);
8144 IntGroupIteratorInit(ig, &iter);
8147 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8148 ap = ATOM_AT_INDEX(mol->atoms, i);
8149 if (index < ap->nframes) {
8150 vp[j] = ap->frames[index];
8158 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8160 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8161 vp = mol->frame_cells + index * 4;
8162 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8164 IntGroupIteratorRelease(&iter);
8166 /* Copy the extra properties */
8167 IntGroupRelease(ig);
8168 for (i = 0; i < mol->nmolprops; i++) {
8169 Double *dp = (Double *)malloc(sizeof(Double));
8171 IntGroupAdd(ig, mol->cframe, 1);
8172 *dp = mol->molprops[i].propvals[index];
8173 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8175 IntGroupRelease(ig);
8183 * reorder_frames(old_indices)
8185 * Reorder the frames. The argument is an array of integers that specify the 'old'
8186 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8187 * same as the old frames 2/0/1, respectively.
8188 * The argument must have the same number of integers as the number of frames.
8191 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8194 Int *ip, *ip2, i, n, nframes;
8195 Data_Get_Struct(self, Molecule, mol);
8196 aval = rb_ary_to_ary(aval);
8197 nframes = MoleculeGetNumberOfFrames(mol);
8198 if (RARRAY_LEN(aval) != nframes)
8199 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8200 ip2 = (Int *)calloc(sizeof(Int), nframes);
8201 ip = (Int *)calloc(sizeof(Int), nframes);
8202 for (i = 0; i < nframes; i++) {
8203 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8204 if (n < 0 || n >= nframes || ip2[n] != 0) {
8207 if (n < 0 || n >= nframes)
8208 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8210 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8216 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8221 #pragma mark ------ Fragments ------
8225 * fragment(n1, *exatoms) -> IntGroup
8226 * fragment(group, *exatoms) -> IntGroup
8228 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8229 * those atoms will not be counted during the search.
8232 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8235 IntGroup *baseg, *ig, *exatoms;
8237 volatile VALUE nval, exval;
8238 Data_Get_Struct(self, Molecule, mol);
8239 rb_scan_args(argc, argv, "1*", &nval, &exval);
8240 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8242 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8244 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8246 if (RARRAY_LEN(exval) == 0) {
8249 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8250 Data_Get_Struct(exval, IntGroup, exatoms);
8252 if (baseg == NULL) {
8253 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8255 IntGroupIterator iter;
8256 IntGroupIteratorInit(baseg, &iter);
8257 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8260 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8262 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8264 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8266 IntGroupAddIntGroup(ig, subg);
8267 IntGroupRelease(subg);
8272 IntGroupIteratorRelease(&iter);
8273 IntGroupRelease(baseg);
8276 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8277 nval = ValueFromIntGroup(ig);
8278 IntGroupRelease(ig);
8284 * fragments(exclude = nil)
8286 * Returns the fragments as an array of IntGroups.
8287 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8288 * in defining the fragment.
8291 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8294 IntGroup *ag, *fg, *eg;
8295 VALUE gval, exval, retval;
8296 Data_Get_Struct(self, Molecule, mol);
8299 if (mol->natoms == 0)
8300 return rb_ary_new();
8301 rb_scan_args(argc, argv, "01", &exval);
8305 eg = IntGroupFromValue(exval);
8306 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8308 IntGroupRemoveIntGroup(ag, eg);
8309 retval = rb_ary_new();
8310 while (IntGroupGetCount(ag) > 0) {
8311 int n = IntGroupGetNthPoint(ag, 0);
8312 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8314 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8315 gval = ValueFromIntGroup(fg);
8316 rb_ary_push(retval, gval);
8317 IntGroupRemoveIntGroup(ag, fg);
8318 IntGroupRelease(fg);
8320 IntGroupRelease(ag);
8322 IntGroupRelease(eg);
8328 * each_fragment(exclude = nil) {|group| ...}
8330 * Execute the block, with the IntGroup object for each fragment as the argument.
8331 * Atoms or bonds should not be added or removed during the execution of the block.
8332 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8333 * in defining the fragment.
8336 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8339 IntGroup *ag, *fg, *eg;
8341 Data_Get_Struct(self, Molecule, mol);
8342 if (mol == NULL || mol->natoms == 0)
8344 rb_scan_args(argc, argv, "01", &exval);
8348 eg = IntGroupFromValue(exval);
8349 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8351 IntGroupRemoveIntGroup(ag, eg);
8352 while (IntGroupGetCount(ag) > 0) {
8353 int n = IntGroupGetNthPoint(ag, 0);
8354 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8356 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8357 gval = ValueFromIntGroup(fg);
8359 IntGroupRemoveIntGroup(ag, fg);
8360 IntGroupRelease(fg);
8362 IntGroupRelease(ag);
8364 IntGroupRelease(eg);
8370 * detachable?(group) -> [n1, n2]
8372 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8373 * of the molecule via only one bond. If it is, then the indices of the atoms
8374 * belonging to the bond is returned, the first element being the atom included
8375 * in the fragment. Otherwise, Qnil is returned.
8378 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8384 Data_Get_Struct(self, Molecule, mol);
8385 ig = s_Molecule_AtomGroupFromValue(self, gval);
8386 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8387 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8388 } else retval = Qnil;
8389 IntGroupRelease(ig);
8395 * bonds_on_border(group = selection) -> Array of Array of two Integers
8397 * Returns an array of bonds that connect an atom in the group and an atom out
8398 * of the group. The first atom in the bond always belongs to the group. If no
8399 * such bonds are present, an empty array is returned.
8402 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8407 Data_Get_Struct(self, Molecule, mol);
8408 rb_scan_args(argc, argv, "01", &gval);
8410 ig = MoleculeGetSelection(mol);
8414 ig = s_Molecule_AtomGroupFromValue(self, gval);
8416 retval = rb_ary_new();
8419 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8421 IntGroupIterator iter;
8423 IntGroupIteratorInit(bg, &iter);
8424 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8425 /* The atoms at the border */
8427 n1 = mol->bonds[i * 2];
8428 n2 = mol->bonds[i * 2 + 1];
8429 if (IntGroupLookupPoint(ig, n1) < 0) {
8433 if (IntGroupLookupPoint(ig, n1) < 0)
8434 continue; /* Actually this is an internal error */
8436 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8438 IntGroupIteratorRelease(&iter);
8440 IntGroupRelease(bg);
8441 IntGroupRelease(ig);
8445 /* Calculate the transform that moves the current coordinates to the reference
8446 coordinates with least displacements. */
8448 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8456 Double eigen_val[3];
8457 Vector eigen_vec[3];
8459 IntGroupIterator iter;
8461 natoms = mol->natoms;
8463 IntGroupIteratorInit(ig, &iter);
8465 /* Calculate the weighted center */
8469 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8470 ap1 = ATOM_AT_INDEX(ap, in);
8471 w1 = (weights != NULL ? weights[i] : ap1->weight);
8472 VecScaleInc(org1, ap1->r, w1);
8473 VecScaleInc(org2, ref[i], w1);
8477 VecScaleSelf(org1, w);
8478 VecScaleSelf(org2, w);
8480 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8481 /* Matrix to diagonalize = R * tR */
8482 memset(r, 0, sizeof(Mat33));
8483 memset(q, 0, sizeof(Mat33));
8484 memset(u, 0, sizeof(Mat33));
8486 IntGroupIteratorReset(&iter);
8487 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8489 ap1 = ATOM_AT_INDEX(ap, in);
8490 w1 = (weights != NULL ? weights[i] : ap1->weight);
8492 VecSub(v1, ap1->r, org1);
8493 VecSub(v2, ref[i], org2);
8494 r[0] += w1 * v1.x * v2.x;
8495 r[1] += w1 * v1.y * v2.x;
8496 r[2] += w1 * v1.z * v2.x;
8497 r[3] += w1 * v1.x * v2.y;
8498 r[4] += w1 * v1.y * v2.y;
8499 r[5] += w1 * v1.z * v2.y;
8500 r[6] += w1 * v1.x * v2.z;
8501 r[7] += w1 * v1.y * v2.z;
8502 r[8] += w1 * v1.z * v2.z;
8505 for (i = 0; i < 9; i++)
8507 for (i = 0; i < 3; i++) {
8508 for (j = 0; j < 3; j++) {
8509 for (k = 0; k < 3; k++) {
8510 q[i+j*3] += r[i+k*3] * r[j+k*3];
8515 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8516 IntGroupIteratorRelease(&iter);
8517 return -1.0; /* Cannot determine the eigenvector */
8520 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8521 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8522 MatrixTranspose(r, r);
8523 for (i = 0; i < 3; i++) {
8524 MatrixVec(&s[i], r, &eigen_vec[i]);
8525 w1 = 1.0 / sqrt(eigen_val[i]);
8526 VecScaleSelf(s[i], w1);
8528 for (k = 0; k < 3; k++) {
8529 u[0] += s[k].x * eigen_vec[k].x;
8530 u[1] += s[k].y * eigen_vec[k].x;
8531 u[2] += s[k].z * eigen_vec[k].x;
8532 u[3] += s[k].x * eigen_vec[k].y;
8533 u[4] += s[k].y * eigen_vec[k].y;
8534 u[5] += s[k].z * eigen_vec[k].y;
8535 u[6] += s[k].x * eigen_vec[k].z;
8536 u[7] += s[k].y * eigen_vec[k].z;
8537 u[8] += s[k].z * eigen_vec[k].z;
8540 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8541 MatrixVec(&org1, u, &org1);
8543 for (i = 0; i < 9; i++)
8549 /* Calculate rmsd */
8550 IntGroupIteratorReset(&iter);
8552 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8554 ap1 = ATOM_AT_INDEX(ap, in);
8555 TransformVec(&tv, trans, &ap1->r);
8557 w += VecLength2(tv);
8560 IntGroupIteratorRelease(&iter);
8566 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8568 * Calculate the transform to fit the given group to the set of reference coordinates.
8569 * The reference coordinates ref is given as either a frame number, an array of
8570 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8571 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8572 * Return values are the transform (that converts the present coordinates to the
8573 * target coordinates) and root mean square deviation (without weight).
8576 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8580 VALUE gval, rval, wval;
8582 IntGroupIterator iter;
8583 int nn, errno, i, j, in, status;
8585 Double *weights, dval[3];
8588 Data_Get_Struct(self, Molecule, mol);
8589 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8591 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8593 ig = IntGroupFromValue(gval);
8594 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8595 IntGroupRelease(ig);
8596 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8598 ref = (Vector *)calloc(sizeof(Vector), nn);
8599 weights = (Double *)calloc(sizeof(Double), nn);
8600 IntGroupIteratorInit(ig, &iter);
8601 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8602 int fn = NUM2INT(rb_Integer(rval));
8603 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8608 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8609 ap = ATOM_AT_INDEX(mol->atoms, in);
8610 if (fn < ap->nframes)
8611 ref[i] = ap->frames[fn];
8612 else ref[i] = ap->r;
8614 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8615 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8616 if (m->row * m->column < nn * 3) {
8620 for (i = 0; i < nn; i++) {
8621 ref[i].x = m->data[i * 3];
8622 ref[i].y = m->data[i * 3 + 1];
8623 ref[i].z = m->data[i * 3 + 2];
8627 rval = rb_protect(rb_ary_to_ary, rval, &status);
8632 if (RARRAY_LEN(rval) < nn) {
8636 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8637 /* Array of 3*nn numbers */
8638 if (RARRAY_LEN(rval) < nn * 3) {
8642 for (i = 0; i < nn; i++) {
8643 for (j = 0; j < 3; j++) {
8644 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8649 dval[j] = NUM2DBL(aval);
8656 /* Array of nn Vector3Ds or Arrays */
8657 for (i = 0; i < nn; i++) {
8658 aval = (RARRAY_PTR(rval))[i];
8659 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8660 VectorFromValue(aval, &ref[i]);
8662 aval = rb_protect(rb_ary_to_ary, aval, &status);
8667 if (RARRAY_LEN(aval) < 3) {
8672 for (j = 0; j < 3; j++) {
8673 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8678 dval[j] = NUM2DBL(aaval);
8688 /* Use atomic weights */
8689 IntGroupIteratorReset(&iter);
8690 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8691 ap = ATOM_AT_INDEX(mol->atoms, in);
8692 weights[i] = ap->weight;
8695 wval = rb_protect(rb_ary_to_ary, wval, &status);
8700 if (RARRAY_LEN(wval) < nn) {
8704 for (i = 0; i < nn; i++) {
8705 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8710 weights[i] = NUM2DBL(wwval);
8713 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8720 IntGroupIteratorRelease(&iter);
8724 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8725 } else if (errno == 1) {
8726 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8727 } else if (errno == 2) {
8728 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8729 } else if (errno == 3) {
8730 rb_jump_tag(status);
8731 } else if (errno == 4) {
8732 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8733 } else if (errno == 5) {
8734 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8735 } else if (errno == 6) {
8736 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8738 return Qnil; /* Not reached */
8741 #pragma mark ------ Screen Display ------
8747 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8750 s_Molecule_Display(VALUE self)
8753 Data_Get_Struct(self, Molecule, mol);
8754 if (mol->mview != NULL)
8755 MainViewCallback_display(mol->mview);
8763 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8766 s_Molecule_MakeFront(VALUE self)
8769 Data_Get_Struct(self, Molecule, mol);
8770 if (mol->mview != NULL)
8771 MainViewCallback_makeFront(mol->mview);
8777 * update_enabled? -> bool
8779 * Returns true if screen update is enabled; otherwise no.
8782 s_Molecule_UpdateEnabled(VALUE self)
8785 Data_Get_Struct(self, Molecule, mol);
8786 if (mol->mview != NULL && !mol->mview->freezeScreen)
8793 * update_enabled = bool
8795 * Enable or disable screen update. This is effective for automatic update on modification.
8796 * Explicit call to molecule.display() always updates the screen.
8799 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8802 Data_Get_Struct(self, Molecule, mol);
8803 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8804 if (mol->mview != NULL)
8805 mol->mview->freezeScreen = (val == Qfalse);
8813 * show_unitcell(bool)
8814 * show_unitcell = bool
8816 * Set the flag whether to show the unit cell. If no argument is given, the
8817 * current flag is returned.
8820 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8823 Data_Get_Struct(self, Molecule, mol);
8824 if (mol->mview == NULL)
8827 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8828 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8830 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8836 * show_hydrogens(bool)
8837 * show_hydrogens = bool
8839 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8840 * current flag is returned.
8843 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8846 Data_Get_Struct(self, Molecule, mol);
8847 if (mol->mview == NULL)
8850 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8851 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8853 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8859 * show_dummy_atoms(bool)
8860 * show_dummy_atoms = bool
8862 * Set the flag whether to show the dummy atoms. If no argument is given, the
8863 * current flag is returned.
8866 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8869 Data_Get_Struct(self, Molecule, mol);
8870 if (mol->mview == NULL)
8873 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8874 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8876 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8882 * show_expanded(bool)
8883 * show_expanded = bool
8885 * Set the flag whether to show the expanded atoms. If no argument is given, the
8886 * current flag is returned.
8889 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8892 Data_Get_Struct(self, Molecule, mol);
8893 if (mol->mview == NULL)
8896 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8897 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8899 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8905 * show_ellipsoids(bool)
8906 * show_ellipsoids = bool
8908 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8909 * current flag is returned.
8912 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8915 Data_Get_Struct(self, Molecule, mol);
8916 if (mol->mview == NULL)
8919 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8920 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8922 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8927 * is_atom_visible(index) -> Boolean
8929 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8930 * as well as the molecule attributes (showHydrogens, etc.)
8933 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8938 Data_Get_Struct(self, Molecule, mol);
8939 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8940 if (idx < 0 || idx >= mol->natoms)
8942 ap = ATOM_AT_INDEX(mol->atoms, idx);
8943 if (mol->mview != NULL) {
8944 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8946 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8948 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8951 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8956 * hidden_atoms -> IntGroup
8958 * Returns the currently hidden atoms.
8961 s_Molecule_HiddenAtoms(VALUE self)
8963 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8964 return Qnil; /* Not reached */
8969 * set_hidden_atoms(IntGroup)
8970 * self.hidden_atoms = IntGroup
8972 * Hide the specified atoms. This operation is _not_ undoable.
8975 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8977 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8978 return Qnil; /* Not reached */
8983 * show_graphite -> Integer
8984 * show_graphite = Integer
8985 * show_graphite = boolean
8987 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
8988 * number of rings to display for each direction.
8989 * If the argument is boolean, only the show/hide flag is set.
8992 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8995 Data_Get_Struct(self, Molecule, mol);
8996 if (mol->mview == NULL)
8999 if (argv[0] == Qnil || argv[0] == Qfalse)
9000 mol->mview->showGraphiteFlag = 0;
9001 else if (argv[0] == Qtrue)
9002 mol->mview->showGraphiteFlag = 1;
9004 int n = NUM2INT(rb_Integer(argv[0]));
9006 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9007 mol->mview->showGraphite = n;
9009 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9011 return INT2NUM(mol->mview->showGraphite);
9016 * show_graphite? -> boolean
9018 * Return whether the graphite is set visible or not.
9021 s_Molecule_ShowGraphiteFlag(VALUE self)
9024 Data_Get_Struct(self, Molecule, mol);
9025 if (mol->mview == NULL)
9027 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9032 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9033 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9034 * show_periodic_image = boolean
9036 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9037 * set but no visual effects are observed.
9038 * If the argument is boolean, only the show/hide flag is modified.
9041 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9047 Data_Get_Struct(self, Molecule, mol);
9048 if (mol->mview == NULL)
9050 rb_scan_args(argc, argv, "01", &val);
9052 /* Change current settings */
9053 if (val == Qnil || val == Qfalse)
9054 mol->mview->showPeriodicImageFlag = 0;
9055 else if (val == Qtrue)
9056 mol->mview->showPeriodicImageFlag = 1;
9058 val = rb_ary_to_ary(val);
9059 for (i = 0; i < 6; i++) {
9060 if (i < RARRAY_LEN(val))
9061 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9063 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9064 rb_raise(rb_eMolbyError, "bad arguments");
9065 for (i = 0; i < 6; i++)
9066 mol->mview->showPeriodicImage[i] = ival[i];
9068 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9071 for (i = 0; i < 6; i++)
9072 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9078 * show_periodic_image? -> boolean
9080 * Return whether the periodic images are set to visible or not. This flag is
9081 * independent from the show_periodic_image settings.
9084 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9087 Data_Get_Struct(self, Molecule, mol);
9088 if (mol->mview == NULL)
9090 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9095 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9096 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9097 * show_rotation_center = boolean
9099 * Set to show the rotation center of the screen.
9100 * If the argument is boolean, only the show/hide flag is modified.
9103 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9106 Data_Get_Struct(self, Molecule, mol);
9107 if (mol->mview == NULL)
9110 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9111 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9113 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9122 * Set the flag whether to draw the model in line mode. If no argument is given, the
9123 * current flag is returned.
9126 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9129 Data_Get_Struct(self, Molecule, mol);
9130 if (mol->mview == NULL)
9133 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9134 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9136 return (mol->mview->lineMode ? Qtrue : Qfalse);
9141 * atom_radius = float
9144 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9146 * If no argument is given, the current value is returned.
9149 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9152 Data_Get_Struct(self, Molecule, mol);
9153 if (mol->mview == NULL)
9156 double rad = NUM2DBL(rb_Float(argv[0]));
9158 mol->mview->atomRadius = rad;
9159 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9163 return rb_float_new(mol->mview->atomRadius);
9168 * bond_radius = float
9171 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9172 * If no argument is given, the current value is returned.
9175 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9178 Data_Get_Struct(self, Molecule, mol);
9179 if (mol->mview == NULL)
9182 double rad = NUM2DBL(rb_Float(argv[0]));
9184 mol->mview->bondRadius = rad;
9185 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9189 return rb_float_new(mol->mview->bondRadius);
9194 * atom_resolution = integer
9197 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9198 * (Default = 12; minimum = 6)
9199 * If no argument is given, the current value is returned.
9202 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9205 Data_Get_Struct(self, Molecule, mol);
9206 if (mol->mview == NULL)
9209 int res = NUM2INT(rb_Integer(argv[0]));
9211 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9212 mol->mview->atomResolution = res;
9213 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9214 return INT2NUM(res);
9216 return INT2NUM(mol->mview->atomResolution);
9221 * bond_resolution = integer
9224 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9225 * (Default = 8; minimum = 4)
9226 * If no argument is given, the current value is returned.
9229 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9232 Data_Get_Struct(self, Molecule, mol);
9233 if (mol->mview == NULL)
9236 int res = NUM2INT(rb_Integer(argv[0]));
9238 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9239 mol->mview->bondResolution = res;
9240 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9241 return INT2NUM(res);
9243 return INT2NUM(mol->mview->bondResolution);
9250 * Resize the model drawing to fit in the window.
9253 s_Molecule_ResizeToFit(VALUE self)
9256 Data_Get_Struct(self, Molecule, mol);
9257 if (mol->mview != NULL)
9258 MainView_resizeToFit(mol->mview);
9264 * get_view_rotation -> [[ax, ay, az], angle]
9266 * Get the current rotation for the view. Angle is in degree, not radian.
9269 s_Molecule_GetViewRotation(VALUE self)
9274 Data_Get_Struct(self, Molecule, mol);
9275 if (mol->mview == NULL)
9277 TrackballGetRotate(mol->mview->track, f);
9278 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9282 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9287 * get_view_scale -> float
9289 * Get the current scale for the view.
9292 s_Molecule_GetViewScale(VALUE self)
9295 Data_Get_Struct(self, Molecule, mol);
9296 if (mol->mview == NULL)
9298 return rb_float_new(TrackballGetScale(mol->mview->track));
9303 * get_view_center -> Vector
9305 * Get the current center point of the view.
9308 s_Molecule_GetViewCenter(VALUE self)
9313 Data_Get_Struct(self, Molecule, mol);
9314 if (mol->mview == NULL)
9316 TrackballGetTranslate(mol->mview->track, f);
9317 v.x = -f[0] * mol->mview->dimension;
9318 v.y = -f[1] * mol->mview->dimension;
9319 v.z = -f[2] * mol->mview->dimension;
9320 return ValueFromVector(&v);
9325 * set_view_rotation([ax, ay, az], angle) -> self
9327 * Set the current rotation for the view. Angle is in degree, not radian.
9330 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9335 Data_Get_Struct(self, Molecule, mol);
9336 if (mol->mview == NULL)
9338 VectorFromValue(aval, &v);
9339 if (NormalizeVec(&v, &v))
9340 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9344 f[0] = -NUM2DBL(rb_Float(angval));
9345 TrackballSetRotate(mol->mview->track, f);
9346 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9352 * set_view_scale(scale) -> self
9354 * Set the current scale for the view.
9357 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9360 Data_Get_Struct(self, Molecule, mol);
9361 if (mol->mview == NULL)
9363 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9364 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9370 * set_view_center(vec) -> self
9372 * Set the current center point of the view.
9375 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9380 Data_Get_Struct(self, Molecule, mol);
9381 if (mol->mview == NULL)
9383 VectorFromValue(aval, &v);
9384 f[0] = -v.x / mol->mview->dimension;
9385 f[1] = -v.y / mol->mview->dimension;
9386 f[2] = -v.z / mol->mview->dimension;
9387 TrackballSetTranslate(mol->mview->track, f);
9388 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9394 * set_background_color(red, green, blue)
9396 * Set the background color of the model window.
9399 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9402 Data_Get_Struct(self, Molecule, mol);
9403 if (mol->mview != NULL) {
9404 VALUE rval, gval, bval;
9405 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9406 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9413 * export_graphic(fname, scale = 1.0, bg_color = -1)
9415 * Export the current graphic to a PNG or TIF file (determined by the extension).
9416 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9420 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9423 VALUE fval, sval, bval;
9427 Data_Get_Struct(self, Molecule, mol);
9428 if (mol->mview == NULL)
9429 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9430 rb_scan_args(argc, argv, "12", &fval, &sval, &bval);
9431 fname = FileStringValuePtr(fval);
9434 else scale = NUM2DBL(rb_Float(sval));
9437 else bg_color = NUM2INT(rb_Integer(bval));
9438 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color) == 0)
9443 #pragma mark ------ Graphics ------
9446 s_CalculateGraphicNormals(MainViewGraphic *gp)
9450 if (gp == NULL || gp->npoints < 3)
9452 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9453 v1.x = gp->points[3] - gp->points[0];
9454 v1.y = gp->points[4] - gp->points[1];
9455 v1.z = gp->points[5] - gp->points[2];
9456 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9457 for (i = 2; i < gp->npoints; i++) {
9458 v2.x = gp->points[i * 3] - gp->points[0];
9459 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9460 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9461 VecCross(v3, v1, v2);
9462 NormalizeVec(&v3, &v3);
9463 gp->normals[i * 3] = v3.x;
9464 gp->normals[i * 3 + 1] = v3.y;
9465 gp->normals[i * 3 + 2] = v3.z;
9468 /* normals[0] = average of all nv[i] (i=2..n-1) */
9470 for (i = 2; i < gp->npoints; i++) {
9471 v1.x += gp->normals[i * 3];
9472 v1.y += gp->normals[i * 3 + 1];
9473 v1.z += gp->normals[i * 3 + 2];
9475 NormalizeVec(&v1, &v1);
9476 gp->normals[0] = v1.x;
9477 gp->normals[1] = v1.y;
9478 gp->normals[2] = v1.z;
9479 /* normals[1] = nv[2].normalize */
9480 v2.x = gp->normals[6];
9481 v2.y = gp->normals[7];
9482 v2.z = gp->normals[8];
9483 NormalizeVec(&v1, &v2);
9484 gp->normals[3] = v1.x;
9485 gp->normals[4] = v1.y;
9486 gp->normals[5] = v1.z;
9487 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9488 for (i = 2; i < gp->npoints; i++) {
9489 if (i == gp->npoints - 1)
9492 v3.x = gp->normals[i * 3 + 3];
9493 v3.y = gp->normals[i * 3 + 4];
9494 v3.z = gp->normals[i * 3 + 5];
9497 NormalizeVec(&v1, &v2);
9498 gp->normals[i * 3] = v1.x;
9499 gp->normals[i * 3 + 1] = v1.y;
9500 gp->normals[i * 3 + 2] = v1.z;
9507 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9509 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9510 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9511 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9512 * points: an array of Vectors
9516 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9522 VALUE kval, cval, pval, fval, ival;
9523 Data_Get_Struct(self, Molecule, mol);
9524 if (mol->mview == NULL)
9525 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9526 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9527 idx = NUM2INT(rb_Integer(ival));
9529 idx = mol->mview->ngraphics;
9530 else if (idx < 0 || idx > mol->mview->ngraphics)
9531 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9532 memset(&g, 0, sizeof(g));
9534 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9535 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9537 kval = rb_obj_as_string(kval);
9538 p = StringValuePtr(kval);
9539 if (strcmp(p, "line") == 0)
9540 g.kind = kMainViewGraphicLine;
9541 else if (strcmp(p, "poly") == 0)
9542 g.kind = kMainViewGraphicPoly;
9543 else if (strcmp(p, "cylinder") == 0)
9544 g.kind = kMainViewGraphicCylinder;
9545 else if (strcmp(p, "cone") == 0)
9546 g.kind = kMainViewGraphicCone;
9547 else if (strcmp(p, "ellipsoid") == 0)
9548 g.kind = kMainViewGraphicEllipsoid;
9549 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9551 g.closed = (RTEST(fval) ? 1 : 0);
9552 cval = rb_ary_to_ary(cval);
9553 n = RARRAY_LEN(cval);
9554 if (n < 3 || n >= 5)
9555 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9558 for (i = 0; i < n; i++)
9559 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9560 pval = rb_ary_to_ary(pval);
9561 n = RARRAY_LEN(pval);
9562 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9564 rb_raise(rb_eArgError, "no control points are given");
9566 case kMainViewGraphicLine:
9568 rb_raise(rb_eArgError, "the line object must have at least two control points");
9570 case kMainViewGraphicPoly:
9572 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9574 case kMainViewGraphicCylinder:
9575 case kMainViewGraphicCone:
9577 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9580 case kMainViewGraphicEllipsoid:
9584 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9587 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9588 for (i = 0; i < n; i++) {
9590 VALUE rval = RARRAY_PTR(pval)[i];
9592 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9593 /* The float argument can also be given as a vector (for simplify undo registration) */
9594 VectorFromValue(rval, &v);
9596 v.x = NUM2DBL(rb_Float(rval));
9600 VectorFromValue(rval, &v);
9602 g.points[i * 3] = v.x;
9603 g.points[i * 3 + 1] = v.y;
9604 g.points[i * 3 + 2] = v.z;
9606 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9608 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9609 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9610 g.points[7] = g.points[11] = g.points[3];
9612 if (g.kind == kMainViewGraphicPoly) {
9613 /* Calculate normals */
9614 s_CalculateGraphicNormals(&g);
9616 MainView_insertGraphic(mol->mview, idx, &g);
9621 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9622 MolActionCallback_registerUndo(mol, act);
9623 MolActionRelease(act);
9626 return INT2NUM(idx);
9631 * create_graphic(kind, color, points, fill = nil) -> integer
9633 * Create a new graphic object. The arguments are similar as insert_graphic.
9636 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9639 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9640 args[0] = INT2NUM(-1);
9641 return s_Molecule_InsertGraphic(argc + 1, args, self);
9646 * remove_graphic(index) -> integer
9648 * Remove a graphic object.
9651 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9655 Data_Get_Struct(self, Molecule, mol);
9656 if (mol->mview == NULL)
9657 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9658 i = NUM2INT(rb_Integer(ival));
9659 if (i < 0 || i >= mol->mview->ngraphics)
9660 rb_raise(rb_eArgError, "graphic index is out of range");
9662 /* Prepare data for undo */
9663 MainViewGraphic *gp;
9668 gp = mol->mview->graphics + i;
9669 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9670 for (n = 0; n < gp->npoints; n++) {
9671 vp[n].x = gp->points[n * 3];
9672 vp[n].y = gp->points[n * 3 + 1];
9673 vp[n].z = gp->points[n * 3 + 2];
9675 col[0] = gp->rgba[0];
9676 col[1] = gp->rgba[1];
9677 col[2] = gp->rgba[2];
9678 col[3] = gp->rgba[3];
9679 if (gp->visible == 0) {
9680 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9681 MolActionCallback_registerUndo(mol, act);
9682 MolActionRelease(act);
9684 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9685 MolActionCallback_registerUndo(mol, act);
9687 MolActionRelease(act);
9689 MainView_removeGraphic(mol->mview, i);
9695 * ngraphics -> integer
9697 * Get the number of graphic objects.
9700 s_Molecule_NGraphics(VALUE self)
9703 Data_Get_Struct(self, Molecule, mol);
9704 if (mol->mview == NULL)
9705 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9706 return INT2NUM(mol->mview->ngraphics);
9711 * get_graphic_point(graphic_index, point_index) -> value
9712 * get_graphic_points(graphic_index) -> values
9714 * Get the point_index-th control point of graphic_index-th graphic object.
9715 * Get an array of all control points with the given values.
9719 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9721 MainViewGraphic *gp;
9726 Data_Get_Struct(self, Molecule, mol);
9727 if (mol->mview == NULL)
9728 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9729 rb_scan_args(argc, argv, "11", &gval, &pval);
9730 index = NUM2INT(rb_Integer(gval));
9731 if (index < 0 || index >= mol->mview->ngraphics)
9732 rb_raise(rb_eArgError, "the graphic index is out of range");
9733 gp = mol->mview->graphics + index;
9735 pindex = NUM2INT(rb_Integer(pval));
9736 if (pindex < 0 || pindex >= gp->npoints)
9737 rb_raise(rb_eArgError, "the point index is out of range");
9738 v.x = gp->points[pindex * 3];
9739 v.y = gp->points[pindex * 3 + 1];
9740 v.z = gp->points[pindex * 3 + 2];
9741 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9742 return rb_float_new(v.x);
9744 return ValueFromVector(&v);
9747 pval = rb_ary_new();
9748 for (pindex = 0; pindex < gp->npoints; pindex++) {
9749 v.x = gp->points[pindex * 3];
9750 v.y = gp->points[pindex * 3 + 1];
9751 v.z = gp->points[pindex * 3 + 2];
9752 rb_ary_push(pval, ValueFromVector(&v));
9760 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9761 * set_graphic_points(graphic_index, new_values) -> new_values
9763 * Change the point_index-th control point of graphic_index-th graphic object.
9764 * Replace the control points with the given values.
9768 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9770 MainViewGraphic *gp;
9774 VALUE gval, pval, nval;
9776 Data_Get_Struct(self, Molecule, mol);
9777 if (mol->mview == NULL)
9778 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9779 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9780 index = NUM2INT(rb_Integer(gval));
9781 if (index < 0 || index >= mol->mview->ngraphics)
9782 rb_raise(rb_eArgError, "the graphic index is out of range");
9783 gp = mol->mview->graphics + index;
9785 pindex = NUM2INT(rb_Integer(pval));
9786 if (pindex < 0 || pindex >= gp->npoints)
9787 rb_raise(rb_eArgError, "the point index is out of range");
9788 v0.x = gp->points[pindex * 3];
9789 v0.y = gp->points[pindex * 3 + 1];
9790 v0.z = gp->points[pindex * 3 + 2];
9791 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9792 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9793 v.x = NUM2DBL(rb_Float(nval));
9795 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9796 v.x = NUM2DBL(rb_Float(nval));
9798 gp->points[7] = gp->points[11] = v.x;
9799 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9800 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9803 v.x = kInvalidFloat;
9805 } else VectorFromValue(nval, &v);
9807 gp->points[pindex * 3] = v.x;
9808 gp->points[pindex * 3 + 1] = v.y;
9809 gp->points[pindex * 3 + 2] = v.z;
9810 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9814 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9815 for (pindex = 0; pindex < gp->npoints; pindex++) {
9816 vp[pindex].x = gp->points[pindex * 3];
9817 vp[pindex].y = gp->points[pindex * 3 + 1];
9818 vp[pindex].z = gp->points[pindex * 3 + 2];
9820 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9822 pval = rb_ary_to_ary(pval);
9823 len = RARRAY_LEN(pval);
9824 if (gp->npoints < len) {
9825 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9827 } else if (gp->npoints > len) {
9830 case kMainViewGraphicLine: len2 = 2; break;
9831 case kMainViewGraphicPoly: len2 = 3; break;
9832 case kMainViewGraphicCylinder: len2 = 3; break;
9833 case kMainViewGraphicCone: len2 = 3; break;
9834 case kMainViewGraphicEllipsoid: len2 = 4; break;
9840 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9841 aval = RARRAY_PTR(pval)[pindex];
9842 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9843 v.x = NUM2DBL(rb_Float(aval));
9845 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9846 v.x = NUM2DBL(rb_Float(aval));
9848 gp->points[7] = gp->points[11] = v.x;
9849 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9851 } else VectorFromValue(aval, &v);
9852 gp->points[pindex * 3] = v.x;
9853 gp->points[pindex * 3 + 1] = v.y;
9854 gp->points[pindex * 3 + 2] = v.z;
9857 if (gp->kind == kMainViewGraphicPoly) {
9858 /* Calculate normals */
9859 s_CalculateGraphicNormals(gp);
9861 MolActionCallback_registerUndo(mol, act);
9862 MolActionRelease(act);
9863 MoleculeCallback_notifyModification(mol, 0);
9869 * get_graphic_color(graphic_index) -> value
9871 * Get the color of graphic_index-th graphic object
9874 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9876 MainViewGraphic *gp;
9879 Data_Get_Struct(self, Molecule, mol);
9880 if (mol->mview == NULL)
9881 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9882 index = NUM2INT(rb_Integer(gval));
9883 if (index < 0 || index >= mol->mview->ngraphics)
9884 rb_raise(rb_eArgError, "the graphic index is out of range");
9885 gp = mol->mview->graphics + index;
9886 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]));
9891 * set_graphic_color(graphic_index, new_value) -> new_value
9893 * Change the color of graphic_index-th graphic object
9897 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9899 MainViewGraphic *gp;
9904 Data_Get_Struct(self, Molecule, mol);
9905 if (mol->mview == NULL)
9906 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9907 index = NUM2INT(rb_Integer(gval));
9908 if (index < 0 || index >= mol->mview->ngraphics)
9909 rb_raise(rb_eArgError, "the graphic index is out of range");
9910 gp = mol->mview->graphics + index;
9911 for (i = 0; i < 4; i++)
9913 cval = rb_ary_to_ary(cval);
9914 n = RARRAY_LEN(cval);
9915 if (n != 3 && n != 4)
9916 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9918 for (i = 0; i < n; i++) {
9919 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9923 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9924 MolActionCallback_registerUndo(mol, act);
9925 MolActionRelease(act);
9926 MoleculeCallback_notifyModification(mol, 0);
9932 * show_graphic(graphic_index) -> self
9934 * Enable the visible flag of the graphic_index-th graphic object
9938 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9940 MainViewGraphic *gp;
9943 Data_Get_Struct(self, Molecule, mol);
9944 if (mol->mview == NULL)
9945 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9946 index = NUM2INT(rb_Integer(gval));
9947 if (index < 0 || index >= mol->mview->ngraphics)
9948 rb_raise(rb_eArgError, "the graphic index is out of range");
9949 gp = mol->mview->graphics + index;
9951 MoleculeCallback_notifyModification(mol, 0);
9957 * hide_graphic(graphic_index) -> self
9959 * Disable the visible flag of the graphic_index-th graphic object
9963 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9965 MainViewGraphic *gp;
9968 Data_Get_Struct(self, Molecule, mol);
9969 if (mol->mview == NULL)
9970 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9971 index = NUM2INT(rb_Integer(gval));
9972 if (index < 0 || index >= mol->mview->ngraphics)
9973 rb_raise(rb_eArgError, "the graphic index is out of range");
9974 gp = mol->mview->graphics + index;
9976 MoleculeCallback_notifyModification(mol, 0);
9984 * Show the string in the info text box.
9987 s_Molecule_ShowText(VALUE self, VALUE arg)
9990 Data_Get_Struct(self, Molecule, mol);
9991 if (mol->mview != NULL)
9992 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9996 #pragma mark ------ MD Support ------
10000 * md_arena -> MDArena
10002 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10003 * this molecule, a new arena is created.
10006 s_Molecule_MDArena(VALUE self)
10010 Data_Get_Struct(self, Molecule, mol);
10011 if (mol->arena == NULL)
10013 retval = ValueFromMDArena(mol->arena);
10019 * set_parameter_attr(type, index, key, value, src) -> value
10021 * This method is used only internally.
10024 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10026 /* This method is called from MolAction to change a MM parameter attribute. */
10029 ParameterRef *pref;
10031 Data_Get_Struct(self, Molecule, mol);
10032 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10033 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10035 /* This is the special part of this method; it allows modification of the src field. */
10036 /* (ParameterRef#set_attr sets 0 to the src field) */
10037 Data_Get_Struct(pval, ParameterRef, pref);
10038 up = ParameterRefGetPar(pref);
10039 up->bond.src = FIX2INT(sval);
10046 * parameter -> Parameter
10048 * Get the local parameter of this molecule. If not defined, returns nil.
10051 s_Molecule_Parameter(VALUE self)
10054 Data_Get_Struct(self, Molecule, mol);
10055 /* if (mol->par == NULL)
10057 return s_NewParameterValueFromValue(self);
10062 * start_step -> Integer
10064 * Returns the start step (defined by dcd format).
10067 s_Molecule_StartStep(VALUE self)
10070 Data_Get_Struct(self, Molecule, mol);
10071 return INT2NUM(mol->startStep);
10076 * start_step = Integer
10078 * Set the start step (defined by dcd format).
10081 s_Molecule_SetStartStep(VALUE self, VALUE val)
10084 Data_Get_Struct(self, Molecule, mol);
10085 mol->startStep = NUM2INT(rb_Integer(val));
10091 * steps_per_frame -> Integer
10093 * Returns the number of steps between frames (defined by dcd format).
10096 s_Molecule_StepsPerFrame(VALUE self)
10099 Data_Get_Struct(self, Molecule, mol);
10100 return INT2NUM(mol->stepsPerFrame);
10105 * steps_per_frame = Integer
10107 * Set the number of steps between frames (defined by dcd format).
10110 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10113 Data_Get_Struct(self, Molecule, mol);
10114 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10120 * ps_per_step -> Float
10122 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10125 s_Molecule_PsPerStep(VALUE self)
10128 Data_Get_Struct(self, Molecule, mol);
10129 return rb_float_new(mol->psPerStep);
10134 * ps_per_step = Float
10136 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10139 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10142 Data_Get_Struct(self, Molecule, mol);
10143 mol->psPerStep = NUM2DBL(rb_Float(val));
10148 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10150 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.");
10153 #pragma mark ------ MO Handling ------
10157 * selectedMO -> IntGroup
10159 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10160 * is not selected, returns nil. If the MO info table is selected but no MOs
10161 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10164 s_Molecule_SelectedMO(VALUE self)
10169 Data_Get_Struct(self, Molecule, mol);
10170 if (mol->mview == NULL)
10172 ig = MainView_selectedMO(mol->mview);
10175 IntGroupOffset(ig, 1);
10176 val = ValueFromIntGroup(ig);
10177 IntGroupRelease(ig);
10183 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10185 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10186 * If the molecule does not contain a basis set information, then returns nil.
10189 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10192 Vector o, dx, dy, dz;
10195 Int npoints = 80 * 80 * 80;
10196 Data_Get_Struct(self, Molecule, mol);
10197 if (mol->bset == NULL)
10199 rb_scan_args(argc, argv, "01", &nval);
10201 npoints = NUM2INT(rb_Integer(nval));
10202 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10204 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));
10208 s_Cubegen_callback(double progress, void *ref)
10210 MyAppCallback_setProgressValue(progress);
10211 if (MyAppCallback_checkInterrupt())
10218 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10219 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10221 * Calculate the molecular orbital with number mo and create a 'cube' file.
10222 * In the first form, the cube size is estimated from the atomic coordinates. In the
10223 * second form, the cube dimension is explicitly given.
10224 * Returns fname when successful, nil otherwise.
10225 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10226 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10227 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10230 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10232 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10234 Int mono, nx, ny, nz, npoints;
10235 Vector o, dx, dy, dz;
10238 Data_Get_Struct(self, Molecule, mol);
10239 if (mol->bset == NULL)
10240 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10241 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10243 /* Set up parameters */
10244 mono = NUM2INT(rb_Integer(mval));
10245 if (mono <= 0 || mono > mol->bset->ncomps)
10246 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
10248 if (mol->bset->rflag != 0)
10249 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10250 mono += mol->bset->ncomps;
10253 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10254 /* Automatic grid formation */
10256 npoints = NUM2INT(rb_Integer(oval));
10260 else if (npoints < 8)
10261 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10262 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10263 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10267 VectorFromValue(oval, &o);
10268 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10269 VectorFromValue(dxval, &dx);
10271 dx.x = NUM2DBL(rb_Float(dxval));
10274 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10275 VectorFromValue(dyval, &dy);
10277 dy.y = NUM2DBL(rb_Float(dyval));
10280 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10281 VectorFromValue(dzval, &dz);
10283 dz.z = NUM2DBL(rb_Float(dzval));
10286 nx = NUM2INT(rb_Integer(nxval));
10287 ny = NUM2INT(rb_Integer(nyval));
10288 nz = NUM2INT(rb_Integer(nzval));
10289 if (nx <= 0 || ny <= 0 || nz <= 0)
10290 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10291 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10292 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);
10296 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10299 else if (index < 0)
10300 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10302 /* Output to file */
10303 MoleculeCallback_displayName(mol, buf, sizeof buf);
10304 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10306 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10308 /* Discard the cube */
10309 MoleculeClearCubeAtIndex(mol, index);
10317 * Clear the MO surface if present.
10320 s_Molecule_ClearSurface(VALUE self)
10323 Data_Get_Struct(self, Molecule, mol);
10324 if (mol->mcube != NULL)
10325 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10333 * Hide the MO surface if present.
10336 s_Molecule_HideSurface(VALUE self)
10339 Data_Get_Struct(self, Molecule, mol);
10340 if (mol->mcube != NULL) {
10341 mol->mcube->hidden = 1;
10342 MoleculeCallback_notifyModification(mol, 0);
10351 * Show the MO surface if present.
10354 s_Molecule_ShowSurface(VALUE self)
10357 Data_Get_Struct(self, Molecule, mol);
10358 if (mol->mcube != NULL) {
10359 mol->mcube->hidden = 0;
10360 MoleculeCallback_notifyModification(mol, 0);
10367 * create_surface(mo, attr = nil)
10369 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10370 * then it denotes the beta orbital.
10371 * If mo is nil, then the attributes of the current surface are modified.
10373 * :npoints : the approximate number of grid points
10374 * :expand : the scale factor to expand/shrink the display box size for each atom,
10375 * :thres : the threshold for the isovalue surface
10376 * If the molecule does not contain MO information, raises exception.
10379 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10382 Vector o, dx, dy, dz;
10383 Int nmo, nx, ny, nz, i;
10384 Int need_recalc = 0;
10385 VALUE nval, hval, aval;
10390 Data_Get_Struct(self, Molecule, mol);
10391 rb_scan_args(argc, argv, "11", &nval, &hval);
10392 if (mol->bset == NULL)
10393 rb_raise(rb_eMolbyError, "No MO information is given");
10394 if (nval == Qnil) {
10396 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10397 nmo = mol->bset->nmos + 1;
10399 nmo = NUM2INT(rb_Integer(nval));
10400 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10401 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);
10403 nmo = -nmo + mol->bset->ncomps;
10405 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10406 npoints = NUM2INT(rb_Integer(aval));
10408 } else if (mol->mcube != NULL) {
10409 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10410 } else npoints = 80 * 80 * 80;
10411 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10412 expand = NUM2DBL(rb_Float(aval));
10413 } else if (mol->mcube != NULL) {
10414 expand = mol->mcube->expand;
10415 } else expand = 1.0;
10416 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10417 thres = NUM2DBL(rb_Float(aval));
10418 } else if (mol->mcube != NULL) {
10419 thres = mol->mcube->thres;
10420 } else thres = 0.05;
10421 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10422 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10423 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10424 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10425 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10427 for (nx = 0; nx < 2; nx++) {
10428 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10429 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10430 aval = rb_ary_to_ary(aval);
10431 if (RARRAY_LEN(aval) < 3) {
10433 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10435 for (i = 0; i < 4; i++)
10436 d[i] = mol->mcube->c[nx].rgba[i];
10437 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10438 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10439 if (d[i] < 0.0 && d[i] > 1.0)
10442 for (i = 0; i < 4; i++)
10443 mol->mcube->c[nx].rgba[i] = d[i];
10446 if (mol->mcube->expand != expand)
10448 mol->mcube->thres = thres;
10449 mol->mcube->expand = expand;
10451 if (mol->mcube->idn < 0)
10452 return self; /* Only set attributes for now */
10454 nmo = mol->mcube->idn; /* Force recalculation */
10456 if (MoleculeUpdateMCube(mol, nmo) != 0)
10457 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10463 * set_surface_attr(attr = nil)
10465 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10468 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10473 return s_Molecule_CreateSurface(2, args, self);
10480 * Get the number of electrostatic potential info.
10483 s_Molecule_NElpots(VALUE self)
10486 Data_Get_Struct(self, Molecule, mol);
10487 return INT2NUM(mol->nelpots);
10494 * Get the electrostatic potential info at the given index. If present, then the
10495 * return value is [Vector, Float] (position and potential). If not present, then
10499 s_Molecule_Elpot(VALUE self, VALUE ival)
10503 Data_Get_Struct(self, Molecule, mol);
10504 idx = NUM2INT(rb_Integer(ival));
10505 if (idx < 0 || idx >= mol->nelpots)
10507 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10514 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10515 * cube and marching cube information are discarded. This operation is _not_ undoable!
10518 s_Molecule_ClearBasisSet(VALUE self)
10521 Data_Get_Struct(self, Molecule, mol);
10523 if (mol->bset != NULL) {
10524 BasisSetRelease(mol->bset);
10527 if (mol->mcube != NULL) {
10528 MoleculeDeallocateMCube(mol->mcube);
10537 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10539 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10540 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10544 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10547 int sym, nprims, a_idx, n;
10548 Data_Get_Struct(self, Molecule, mol);
10549 a_idx = NUM2INT(rb_Integer(aval));
10550 sym = NUM2INT(rb_Integer(symval));
10551 nprims = NUM2INT(rb_Integer(npval));
10552 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10554 rb_raise(rb_eMolbyError, "Molecule is emptry");
10556 rb_raise(rb_eMolbyError, "Low memory");
10558 rb_raise(rb_eMolbyError, "Unknown orbital type");
10560 rb_raise(rb_eMolbyError, "Unknown error");
10566 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10568 * To be used internally. Add a gaussian primitive coefficients.
10571 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10575 Double exponent, contraction, contraction_sp;
10576 Data_Get_Struct(self, Molecule, mol);
10577 exponent = NUM2DBL(rb_Float(expval));
10578 contraction = NUM2DBL(rb_Float(cval));
10579 contraction_sp = NUM2DBL(rb_Float(cspval));
10580 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10582 rb_raise(rb_eMolbyError, "Molecule is emptry");
10584 rb_raise(rb_eMolbyError, "Low memory");
10586 rb_raise(rb_eMolbyError, "Unknown error");
10592 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10594 * Get the Gaussian shell information for the given MO coefficient index.
10595 * The symmetry code is the same as in add_gaussian_orbital_shell.
10596 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10597 * is the number of MO component belonging to this shell.
10600 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10605 Data_Get_Struct(self, Molecule, mol);
10606 if (mol->bset == NULL)
10607 rb_raise(rb_eMolbyError, "No basis set information is defined");
10608 s_idx = NUM2INT(rb_Integer(sval));
10609 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10611 sp = mol->bset->shells + s_idx;
10614 case kGTOType_S: sym = 0; break;
10615 case kGTOType_SP: sym = -1; break;
10616 case kGTOType_P: sym = 1; break;
10617 case kGTOType_D: sym = 2; break;
10618 case kGTOType_D5: sym = -2; break;
10619 case kGTOType_F: sym = 3; break;
10620 case kGTOType_F7: sym = -3; break;
10621 case kGTOType_G: sym = 4; break;
10622 case kGTOType_G9: sym = -4; break;
10624 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10626 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10631 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10633 * Get the Gaussian primitive coefficients for the given MO component.
10636 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10642 VALUE retval, aval;
10643 Data_Get_Struct(self, Molecule, mol);
10644 if (mol->bset == NULL)
10645 rb_raise(rb_eMolbyError, "No basis set information is defined");
10646 s_idx = NUM2INT(rb_Integer(sval));
10647 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10649 sp = mol->bset->shells + s_idx;
10650 pp = mol->bset->priminfos + sp->p_idx;
10651 retval = rb_ary_new2(sp->nprim);
10652 for (i = 0; i < sp->nprim; i++) {
10653 if (sp->sym == kGTOType_SP) {
10654 /* With P contraction coefficient */
10655 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10657 /* Without P contraction coefficient */
10658 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10660 rb_ary_store(retval, i, aval);
10667 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10669 * Get the Gaussian shell information for the given MO coefficient index.
10672 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10675 Int n, c, atom_idx, shell_idx;
10677 Data_Get_Struct(self, Molecule, mol);
10678 if (mol->bset == NULL)
10679 rb_raise(rb_eMolbyError, "No basis set information is defined");
10680 c = NUM2INT(rb_Integer(cval));
10681 if (c < 0 || c >= mol->bset->ncomps)
10683 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10685 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10686 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10691 * clear_mo_coefficients
10693 * Clear the existing MO coefficients.
10696 s_Molecule_ClearMOCoefficients(VALUE self)
10699 Data_Get_Struct(self, Molecule, mol);
10700 if (mol->bset != NULL) {
10701 if (mol->bset->moenergies != NULL) {
10702 free(mol->bset->moenergies);
10703 mol->bset->moenergies = NULL;
10705 if (mol->bset->mo != NULL) {
10706 free(mol->bset->mo);
10707 mol->bset->mo = NULL;
10709 mol->bset->nmos = 0;
10716 * set_mo_coefficients(idx, energy, coefficients)
10718 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10719 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10720 * Energy is the MO energy, and coefficients is an array
10721 * of MO coefficients.
10724 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10727 Int idx, ncomps, i;
10730 Data_Get_Struct(self, Molecule, mol);
10731 idx = NUM2INT(rb_Integer(ival));
10732 energy = NUM2DBL(rb_Float(eval));
10733 aval = rb_ary_to_ary(aval);
10734 ncomps = RARRAY_LEN(aval);
10735 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10736 if (coeffs == NULL) {
10740 for (i = 0; i < ncomps; i++)
10741 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10742 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10745 rb_raise(rb_eMolbyError, "Molecule is emptry");
10747 rb_raise(rb_eMolbyError, "Low memory");
10749 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10751 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10753 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10755 rb_raise(rb_eMolbyError, "Unknown error");
10761 * get_mo_coefficients(idx)
10763 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10766 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10769 Int idx, ncomps, n;
10773 Data_Get_Struct(self, Molecule, mol);
10774 idx = NUM2INT(rb_Integer(ival));
10777 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10779 rb_raise(rb_eMolbyError, "Molecule is emptry");
10781 rb_raise(rb_eMolbyError, "No basis set information is present");
10783 return Qnil; /* Silently returns nil */
10784 retval = rb_ary_new2(ncomps);
10785 for (n = 0; n < ncomps; n++)
10786 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10793 * get_mo_energy(idx)
10795 * To be used internally. Get the MO energy for the given MO index (1-based).
10798 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10803 Data_Get_Struct(self, Molecule, mol);
10804 idx = NUM2INT(rb_Integer(ival));
10805 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10807 rb_raise(rb_eMolbyError, "Molecule is emptry");
10809 rb_raise(rb_eMolbyError, "No basis set information is present");
10812 return rb_float_new(energy);
10815 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10818 s_InitMOInfoKeys(void)
10820 if (sTypeSym == 0) {
10821 sTypeSym = ID2SYM(rb_intern("type"));
10822 sAlphaSym = ID2SYM(rb_intern("alpha"));
10823 sBetaSym = ID2SYM(rb_intern("beta"));
10824 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10825 sNshellsSym = ID2SYM(rb_intern("nshells"));
10831 * set_mo_info(hash)
10833 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10834 * :alpha=>integer, :beta=>integer
10837 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10841 Int rflag, na, nb, n;
10843 Data_Get_Struct(self, Molecule, mol);
10844 if (mol->bset != NULL) {
10845 rflag = mol->bset->rflag;
10846 na = mol->bset->ne_alpha;
10847 nb = mol->bset->ne_beta;
10853 if (hval != Qnil) {
10854 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10855 s = StringValuePtr(aval);
10856 if (strcasecmp(s, "RHF") == 0)
10858 else if (strcasecmp(s, "UHF") == 0)
10860 else if (strcasecmp(s, "ROHF") == 0)
10863 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10864 n = NUM2INT(rb_Integer(aval));
10868 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10869 n = NUM2INT(rb_Integer(aval));
10873 MoleculeSetMOInfo(mol, rflag, na, nb);
10882 * Get the MO info. The key is as described in set_mo_info.
10883 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10886 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10889 Data_Get_Struct(self, Molecule, mol);
10890 if (mol->bset == NULL)
10892 if (kval == sTypeSym) {
10893 switch (mol->bset->rflag) {
10894 case 0: return rb_str_new2("UHF");
10895 case 1: return rb_str_new2("RHF");
10896 case 2: return rb_str_new2("ROHF");
10897 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10899 } else if (kval == sAlphaSym) {
10900 return INT2NUM(mol->bset->ne_alpha);
10901 } else if (kval == sBetaSym) {
10902 return INT2NUM(mol->bset->ne_beta);
10903 } else if (kval == sNcompsSym) {
10904 return INT2NUM(mol->bset->ncomps);
10905 } else if (kval == sNshellsSym) {
10906 return INT2NUM(mol->bset->nshells);
10908 kval = rb_inspect(kval);
10909 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10910 return Qnil; /* Does not reach here */
10918 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10921 s_Molecule_MOType(VALUE self)
10923 return s_Molecule_GetMOInfo(self, sTypeSym);
10926 #pragma mark ------ Molecular Topology ------
10930 * search_equivalent_atoms(ig = nil)
10932 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10935 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10941 Data_Get_Struct(self, Molecule, mol);
10942 if (mol->natoms == 0)
10944 rb_scan_args(argc, argv, "01", &val);
10946 ig = IntGroupFromValue(val);
10948 result = MoleculeSearchEquivalentAtoms(mol, ig);
10949 if (result == NULL)
10950 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10952 IntGroupRelease(ig);
10953 val = rb_ary_new2(mol->natoms);
10954 for (i = 0; i < mol->natoms; i++)
10955 rb_ary_push(val, INT2NUM(result[i]));
10962 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10964 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10965 * Name is the name of the new pi anchor, and group is the atoms that define
10966 * the pi system. Type (a String) is an atom type for MM implementation.
10967 * Weights represent the relative significance of the component atoms; if omitted, then
10968 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10969 * The weight values will be normalized so that the sum of the weights is 1.0.
10970 * The weight values must be positive.
10971 * Index is the atom index where the created pi-anchor is inserted in the
10972 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10973 * having the largest index.
10974 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10975 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10978 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10983 Int i, n, idx, last_component;
10987 if (argc < 2 || argc >= 6)
10988 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10992 Data_Get_Struct(self, Molecule, mol);
10993 ig = IntGroupFromValue(gval);
10994 memset(&a, 0, sizeof(a));
10995 memset(&an, 0, sizeof(an));
10996 strncpy(a.aname, StringValuePtr(nval), 4);
10997 if (a.aname[0] == '_')
10998 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10999 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11000 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11001 if (n >= mol->natoms) {
11002 AtomConnectResize(&an.connect, 0);
11003 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11005 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11006 last_component = n;
11008 if (an.connect.count == 0)
11009 rb_raise(rb_eMolbyError, "no atoms are specified");
11010 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11011 for (i = 0; i < an.connect.count; i++) {
11012 an.coeffs[i] = 1.0 / an.connect.count;
11014 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11016 if (argv[0] != Qnil)
11017 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11021 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11022 if (argv[0] != Qnil) {
11023 VALUE aval = rb_ary_to_ary(argv[0]);
11025 if (RARRAY_LEN(aval) != an.connect.count)
11026 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11027 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11028 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11030 rb_raise(rb_eMolbyError, "the weight value must be positive");
11034 for (i = 0; i < an.connect.count; i++)
11035 an.coeffs[i] /= sum;
11040 if (argc > 0 && argv[0] != Qnil) {
11042 idx = NUM2INT(rb_Integer(argv[0]));
11044 if (idx < 0 || idx > mol->natoms) {
11045 /* Immediately after the last specified atom */
11046 idx = last_component + 1;
11048 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11049 memmove(a.anchor, &an, sizeof(PiAnchor));
11050 /* Use residue information of the last specified atom */
11051 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11052 a.resSeq = ap->resSeq;
11053 strncpy(a.resName, ap->resName, 4);
11054 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11056 MoleculeCalculatePiAnchorPosition(mol, idx);
11057 aref = AtomRefNew(mol, idx);
11058 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11061 #pragma mark ------ Molecular Properties ------
11065 * set_property(name, value[, index]) -> value
11066 * set_property(name, values, group) -> values
11068 * Set molecular property. A property is a floating-point number with a specified name,
11069 * and can be set for each frame separately. The name of the property is given as a String.
11070 * The value can be a single floating point number, which is set to the current frame.
11074 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11077 VALUE nval, vval, ival;
11080 Int i, n, idx, fidx;
11082 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11083 Data_Get_Struct(self, Molecule, mol);
11084 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11085 idx = NUM2INT(rb_Integer(nval));
11086 if (idx < 0 || idx >= mol->nmolprops)
11087 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11089 name = StringValuePtr(nval);
11090 idx = MoleculeLookUpProperty(mol, name);
11092 idx = MoleculeCreateProperty(mol, name);
11094 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11097 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11099 fidx = mol->cframe;
11101 fidx = NUM2INT(rb_Integer(ival));
11102 n = MoleculeGetNumberOfFrames(mol);
11103 if (fidx < 0 || fidx >= n)
11104 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11106 ig = IntGroupNewWithPoints(fidx, 1, -1);
11107 dp = (Double *)malloc(sizeof(Double));
11108 *dp = NUM2DBL(rb_Float(vval));
11111 vval = rb_ary_to_ary(vval);
11112 ig = IntGroupFromValue(ival);
11113 n = IntGroupGetCount(ig);
11115 rb_raise(rb_eMolbyError, "No frames are specified");
11116 if (RARRAY_LEN(vval) < n)
11117 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11118 dp = (Double *)calloc(sizeof(Double), n);
11119 for (i = 0; i < n; i++)
11120 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11123 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11125 IntGroupRelease(ig);
11131 * get_property(name[, index]) -> value
11132 * get_property(name, group) -> values
11134 * Get molecular property. In the first form, a property value for a single frame is returned.
11135 * (If index is omitted, then the value for the current frame is given)
11136 * In the second form, an array of property values for the given frames is returned.
11137 * If name is not one of known properties or a valid index integer, exception is raised.
11140 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11146 Int i, n, idx, fidx;
11148 rb_scan_args(argc, argv, "11", &nval, &ival);
11149 Data_Get_Struct(self, Molecule, mol);
11150 if (mol->nmolprops == 0)
11151 rb_raise(rb_eMolbyError, "The molecule has no properties");
11152 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11153 idx = NUM2INT(rb_Integer(nval));
11154 if (idx < 0 || idx >= mol->nmolprops)
11155 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11157 name = StringValuePtr(nval);
11158 idx = MoleculeLookUpProperty(mol, name);
11160 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11162 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11164 fidx = mol->cframe;
11166 fidx = NUM2INT(rb_Integer(ival));
11167 n = MoleculeGetNumberOfFrames(mol);
11168 if (fidx < 0 || fidx >= n)
11169 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11171 ig = IntGroupNewWithPoints(fidx, 1, -1);
11172 ival = INT2FIX(fidx);
11175 ig = IntGroupFromValue(ival);
11176 n = IntGroupGetCount(ig);
11178 return rb_ary_new();
11180 dp = (Double *)calloc(sizeof(Double), n);
11181 MoleculeGetProperty(mol, idx, ig, dp);
11182 if (FIXNUM_P(ival))
11183 ival = rb_float_new(dp[0]);
11185 ival = rb_ary_new();
11186 for (i = n - 1; i >= 0; i--) {
11187 nval = rb_float_new(dp[i]);
11188 rb_ary_store(ival, i, nval);
11192 IntGroupRelease(ig);
11198 * property_names -> Array
11200 * Get an array of property names.
11203 s_Molecule_PropertyNames(VALUE self)
11208 Data_Get_Struct(self, Molecule, mol);
11209 rval = rb_ary_new();
11210 for (i = mol->nmolprops - 1; i >= 0; i--) {
11211 nval = rb_str_new2(mol->molprops[i].propname);
11212 rb_ary_store(rval, i, nval);
11217 #pragma mark ------ Class methods ------
11221 * current -> Molecule
11223 * Get the currently "active" molecule.
11226 s_Molecule_Current(VALUE klass)
11228 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11233 * Molecule[] -> Molecule
11234 * Molecule[n] -> Molecule
11235 * Molecule[name] -> Molecule
11236 * Molecule[name, k] -> Molecule
11237 * Molecule[regex] -> Molecule
11238 * Molecule[regex, k] -> Molecule
11240 * Molecule[] is equivalent to Molecule.current.
11241 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11242 * Molecule[name] gives the first document (in the order of creation time) that has
11243 * the given name. If a second argument (k) is given, the k-th document that has the
11244 * given name is returned.
11245 * Molecule[regex] gives the first document (in the order of creation time) that
11246 * has a name matching the regular expression. If a second argument (k) is given,
11247 * the k-th document that has a name matching the re is returned.
11250 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11256 rb_scan_args(argc, argv, "02", &val, &kval);
11258 return s_Molecule_Current(klass);
11259 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11260 idx = NUM2INT(val);
11261 mol = MoleculeCallback_moleculeAtIndex(idx);
11262 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11263 char *p = StringValuePtr(val);
11264 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11265 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11266 MoleculeCallback_displayName(mol, buf, sizeof buf);
11267 if (strcmp(buf, p) == 0 && --k == 0)
11270 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11271 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11272 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11274 MoleculeCallback_displayName(mol, buf, sizeof buf);
11275 name = rb_str_new2(buf);
11276 if (rb_reg_match(val, name) != Qnil && --k == 0)
11279 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11283 else return ValueFromMolecule(mol);
11288 * list -> array of Molecules
11290 * Get the list of molecules associated to the documents, in the order of creation
11291 * time of the document. If no document is open, returns an empry array.
11294 s_Molecule_List(VALUE klass)
11300 ary = rb_ary_new();
11301 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11302 rb_ary_push(ary, ValueFromMolecule(mol));
11310 * ordered_list -> array of Molecules
11312 * Get the list of molecules associated to the documents, in the order of front-to-back
11313 * ordering of the associated window. If no document is open, returns an empry array.
11316 s_Molecule_OrderedList(VALUE klass)
11322 ary = rb_ary_new();
11323 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11324 rb_ary_push(ary, ValueFromMolecule(mol));
11330 #pragma mark ------ Call Subprocess ------
11332 /* The callback functions for call_subprocess_async */
11334 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11337 VALUE procval, retval, args[2];
11338 args[0] = ValueFromMolecule(mol);
11339 args[1] = INT2NUM(status);
11340 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11341 if (procval != Qnil) {
11342 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11343 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11350 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11353 VALUE procval, retval, args[2];
11354 args[0] = ValueFromMolecule(mol);
11355 args[1] = INT2NUM(tcount);
11356 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11357 if (procval != Qnil) {
11358 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11359 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11367 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11369 * Call subprocess asynchronically.
11370 * If end_callback is given, it will be called (with two arguments self and termination status)
11371 * when the subprocess terminated.
11372 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11373 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11374 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11375 * filename begins with ">>", then the message will be appended to the file.
11376 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11377 * If the argument is nil, then the message will be sent to the Ruby console.
11378 * Returns the process ID as an integer.
11381 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11383 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11387 FILE *fpout, *fperr;
11388 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11389 Data_Get_Struct(self, Molecule, mol);
11391 if (stdout_val == Qnil) {
11394 sout = StringValuePtr(stdout_val);
11395 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11398 if (strncmp(sout, ">>", 2) == 0) {
11400 fpout = fopen(sout, "a");
11404 fpout = fopen(sout, "w");
11407 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11410 if (stderr_val == Qnil) {
11413 serr = StringValuePtr(stderr_val);
11414 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11417 if (strncmp(serr, ">>", 2) == 0) {
11419 fpout = fopen(serr, "a");
11423 fperr = fopen(serr, "w");
11426 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11430 /* Register procs as instance variables */
11431 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11432 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11433 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11434 if (fpout != NULL && fpout != (FILE *)1)
11436 if (fperr != NULL && fperr != (FILE *)1)
11441 #pragma mark ====== Define Molby Classes ======
11448 /* Define module Molby */
11449 rb_mMolby = rb_define_module("Molby");
11451 /* Define Vector3D, Transform, IntGroup */
11454 /* Define MDArena */
11455 Init_MolbyMDTypes();
11457 /* class Molecule */
11458 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11460 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11461 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11462 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11463 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11464 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11466 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11467 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11468 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11469 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11470 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11471 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11472 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11473 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11474 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11475 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11476 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11477 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11478 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11479 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11480 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11481 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11482 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11483 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11484 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11485 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11486 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11487 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11489 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11490 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11491 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11492 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11493 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11495 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11496 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11497 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11498 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11499 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11500 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11501 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11502 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11503 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11504 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11505 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11506 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11507 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11508 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11509 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11511 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11512 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11513 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11514 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11515 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11517 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11518 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11519 rb_define_alias(rb_cMolecule, "+", "add");
11520 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11521 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11522 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11523 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11524 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11525 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11526 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11527 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11528 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11529 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11530 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11531 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11532 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11533 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11534 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11535 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11536 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11537 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11538 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11539 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11540 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11542 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11543 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11544 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11545 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11546 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11548 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11549 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11550 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11551 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11552 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11553 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11554 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11555 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11556 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11558 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11559 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11560 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11561 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11562 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11563 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11564 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11565 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11566 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11567 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11568 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11569 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11570 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11571 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11572 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11573 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11574 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11575 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11576 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11577 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11578 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11579 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11581 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11582 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11583 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11584 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11585 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11586 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11587 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11589 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11590 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11591 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11592 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11593 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11594 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11595 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11596 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11597 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11598 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11599 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11600 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11601 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11603 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11604 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11605 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11606 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11607 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11608 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11610 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11611 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11612 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11613 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11614 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11615 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11616 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11617 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11618 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11619 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11620 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11621 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11622 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11623 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11624 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11625 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11626 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11627 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11628 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11629 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11630 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11631 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11632 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11633 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11634 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11635 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11636 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11637 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11638 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11639 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11640 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11641 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11642 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11643 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11644 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11645 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11646 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11647 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11648 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11649 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11650 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11651 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11652 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11653 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11654 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11655 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11656 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11657 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11658 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11659 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11660 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11661 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11662 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11663 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11665 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11666 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11667 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11668 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11669 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11670 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11671 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11672 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11673 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11674 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11675 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11676 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11677 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11679 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11680 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11681 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11682 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11683 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11684 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11685 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11686 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11687 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11688 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11689 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11690 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11691 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11692 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11694 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11695 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11696 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11697 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11698 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11699 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11700 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11701 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11702 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11703 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11704 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11705 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11706 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11707 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11708 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11709 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11710 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11711 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11712 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11713 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11714 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11715 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11716 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11718 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11719 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11721 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11722 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11723 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11725 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11726 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11727 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11728 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11730 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11732 /* class MolEnumerable */
11733 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11734 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11735 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11736 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11737 rb_define_alias(rb_cMolEnumerable, "size", "length");
11738 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11739 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11741 /* class AtomRef */
11742 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11743 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11745 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11746 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11747 s_AtomAttrDefTable[i].id = rb_intern(buf);
11748 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11750 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11752 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11753 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11754 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11755 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11756 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11757 rb_global_variable(&s_SetAtomAttrString);
11758 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11759 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11761 /* class Parameter */
11762 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11763 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11764 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11765 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11766 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11767 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11768 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11769 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11770 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11771 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11772 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11773 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11774 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11775 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11776 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11777 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11778 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11779 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11780 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11781 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11782 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11783 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11784 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11785 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11786 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11787 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11788 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11789 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11790 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11791 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11792 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11793 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11794 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11795 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11796 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11797 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11798 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11799 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11800 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11801 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11802 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11803 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11804 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11805 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11806 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11807 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11808 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11809 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11810 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11811 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11812 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11813 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11814 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11815 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11817 /* class ParEnumerable */
11818 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11819 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11820 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11821 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11822 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11823 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11824 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11825 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11826 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11827 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11828 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11830 /* class ParameterRef */
11831 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11832 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11834 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11835 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11836 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11837 if (s_ParameterAttrDefTable[i].symref != NULL)
11838 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11839 if (s_ParameterAttrDefTable[i].setter != NULL) {
11841 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11844 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11845 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11846 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11847 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11848 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11849 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11850 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11851 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11853 /* class MolbyError */
11854 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11856 /* module Kernel */
11857 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11858 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11859 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11860 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11861 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11862 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11863 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11864 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11865 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11866 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11867 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11868 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11869 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11870 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11871 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11872 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11873 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11874 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11875 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11876 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11877 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11878 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11879 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11880 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11883 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11885 s_ID_equal = rb_intern("==");
11886 g_RubyID_call = rb_intern("call");
11888 s_InitMOInfoKeys();
11890 /* Symbols for graphics */
11891 s_LineSym = ID2SYM(rb_intern("line"));
11892 s_PolySym = ID2SYM(rb_intern("poly"));
11893 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11894 s_ConeSym = ID2SYM(rb_intern("cone"));
11895 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11898 #pragma mark ====== Interface with RubyDialog class ======
11901 RubyDialogCallback_parentModule(void)
11903 return (RubyValue)rb_mMolby;
11906 #pragma mark ====== External functions ======
11908 static VALUE s_ruby_top_self = Qfalse;
11909 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11910 static VALUE s_ruby_export_local_variables = Qfalse;
11913 s_evalRubyScriptOnMoleculeSub(VALUE val)
11915 void **ptr = (void **)val;
11916 Molecule *mol = (Molecule *)ptr[1];
11917 VALUE sval, fnval, lnval, retval;
11920 /* Clear the error information (store in the history array if necessary) */
11921 sval = rb_errinfo();
11922 if (sval != Qnil) {
11923 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11924 rb_set_errinfo(Qnil);
11927 if (s_ruby_top_self == Qfalse) {
11928 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11930 if (s_ruby_get_binding_for_molecule == Qfalse) {
11932 "lambda { |_mol_, _bind_| \n"
11933 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11934 " _proc_.call(_mol_) } ";
11935 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11936 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11938 if (s_ruby_export_local_variables == Qfalse) {
11940 "lambda { |_bind_| \n"
11941 " # find local variables newly defined in _bind_ \n"
11942 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11943 " _a_.each { |_vsym_| \n"
11944 " _vname_ = _vsym_.to_s \n"
11945 " _vval_ = _bind_.eval(_vname_) \n"
11946 " # Define local variable \n"
11947 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11948 " # Then set value \n"
11949 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11952 s_ruby_export_local_variables = rb_eval_string(s2);
11953 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11955 if (ptr[2] == NULL) {
11957 /* String literal: we need to specify string encoding */
11958 #if defined(__WXMSW__)
11959 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11961 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11963 sval = rb_str_new2(scr);
11965 fnval = rb_str_new2("(eval)");
11966 lnval = INT2FIX(0);
11968 sval = rb_str_new2((char *)ptr[0]);
11969 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11970 lnval = INT2FIX(1);
11972 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11974 VALUE mval = ValueFromMolecule(mol);
11975 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11977 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11979 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11985 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11989 VALUE save_interrupt_flag;
11990 /* char *save_ruby_sourcefile;
11991 int save_ruby_sourceline; */
11992 if (gMolbyIsCheckingInterrupt) {
11993 MolActionAlertRubyIsRunning();
11995 return (RubyValue)Qnil;
11998 args[0] = (void *)script;
11999 args[1] = (void *)mol;
12000 args[2] = (void *)fname;
12001 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12002 /* save_ruby_sourcefile = ruby_sourcefile;
12003 save_ruby_sourceline = ruby_sourceline; */
12004 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12005 if (*status != 0) {
12006 /* Is this 'exit' exception? */
12007 VALUE last_exception = rb_gv_get("$!");
12008 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12009 /* Capture exit and return the status value */
12010 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12012 rb_set_errinfo(Qnil);
12015 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12016 /* ruby_sourcefile = save_ruby_sourcefile;
12017 ruby_sourceline = save_ruby_sourceline; */
12023 Ruby_showValue(RubyValue value, char **outValueString)
12025 VALUE val = (VALUE)value;
12026 if (gMolbyIsCheckingInterrupt) {
12027 MolActionAlertRubyIsRunning();
12034 val = rb_protect(rb_inspect, val, &status);
12038 str = StringValuePtr(val);
12039 if (outValueString != NULL)
12040 *outValueString = strdup(str);
12041 MyAppCallback_showScriptMessage("%s", str);
12043 if (outValueString != NULL)
12044 *outValueString = NULL;
12050 Ruby_showError(int status)
12052 static const int tag_raise = 6;
12053 char *msg = NULL, *msg2;
12054 VALUE val, backtrace;
12055 int interrupted = 0;
12056 if (status == tag_raise) {
12057 VALUE errinfo = rb_errinfo();
12058 VALUE eclass = CLASS_OF(errinfo);
12059 if (eclass == rb_eInterrupt) {
12065 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12067 val = rb_eval_string_protect("$!.to_s", &status);
12069 msg = RSTRING_PTR(val);
12070 else msg = "(message not available)";
12072 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12073 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
12079 Molby_getDescription(void)
12081 extern const char *gVersionString, *gCopyrightString;
12082 extern int gRevisionNumber;
12083 extern char *gLastBuildString;
12085 char *revisionString;
12086 if (gRevisionNumber > 0) {
12087 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12088 } else revisionString = "";
12090 "Molby %s%s\n%s\nLast compile: %s\n"
12091 #if !defined(__CMDMAC__)
12097 "ruby %s, http://www.ruby-lang.org/\n"
12099 "FFTW 3.3.2, http://www.fftw.org/\n"
12100 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
12101 " and Massachusetts Institute of Technology",
12102 gVersionString, revisionString, gCopyrightString, gLastBuildString,
12103 #if !defined(__CMDMAC__)
12104 MyAppCallback_getGUIDescriptionString(),
12106 gRubyVersion, gRubyCopyright);
12107 if (revisionString[0] != 0)
12108 free(revisionString);
12113 Molby_startup(const char *script, const char *dir)
12118 char *respath, *p, *wbuf;
12120 /* Get version/copyright string from Ruby interpreter */
12122 gRubyVersion = strdup(ruby_version);
12123 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12124 #if defined(__CMDMAC__)
12127 " ", /* Indent for displaying in About dialog */
12129 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12132 /* Read build and revision information for Molby */
12135 extern int gRevisionNumber;
12136 extern char *gLastBuildString;
12137 FILE *fp = fopen("../buildInfo.txt", "r");
12138 gLastBuildString = "";
12140 if (fgets(buf, sizeof(buf), fp) != NULL) {
12141 char *p1 = strchr(buf, '\"');
12142 char *p2 = strrchr(buf, '\"');
12143 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12144 memmove(buf, p1 + 1, p2 - p1 - 1);
12145 buf[p2 - p1 - 1] = 0;
12146 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12151 fp = fopen("../revisionInfo.txt", "r");
12152 gRevisionNumber = 0;
12154 if (fgets(buf, sizeof(buf), fp) != NULL) {
12155 gRevisionNumber = strtol(buf, NULL, 0);
12161 #if defined(__CMDMAC__)
12162 wbuf = Molby_getDescription();
12163 printf("%s\n", wbuf);
12167 /* Read atom display parameters */
12168 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12169 #if defined(__CMDMAC__)
12170 fprintf(stderr, "%s\n", wbuf);
12172 MyAppCallback_setConsoleColor(1);
12173 MyAppCallback_showScriptMessage("%s", wbuf);
12174 MyAppCallback_setConsoleColor(0);
12179 /* Read default parameters */
12180 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12181 if (wbuf != NULL) {
12182 #if defined(__CMDMAC__)
12183 fprintf(stderr, "%s\n", wbuf);
12185 MyAppCallback_setConsoleColor(1);
12186 MyAppCallback_showScriptMessage("%s", wbuf);
12187 MyAppCallback_setConsoleColor(0);
12192 /* Initialize Ruby interpreter */
12195 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12196 it causes rb_bug() (= fatal error) during ruby_init().
12197 As a workaround, these standard streams are reopend as
12199 freopen("NUL", "r", stdin);
12200 freopen("NUL", "w", stdout);
12201 freopen("NUL", "w", stderr);
12207 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12209 Init_trans_japanese_sjis();
12212 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12214 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12215 ruby_incpush(libpath);
12219 ruby_script("Molby");
12221 /* Find the resource path (the parent directory of the given directory) */
12222 respath = strdup(dir);
12223 p = strrchr(respath, '/');
12224 if (p == NULL && PATH_SEPARATOR != '/')
12225 p = strrchr(respath, PATH_SEPARATOR);
12228 val = Ruby_NewFileStringValue(respath);
12229 rb_define_global_const("MolbyResourcePath", val);
12232 /* Define Molby classes */
12234 RubyDialogInitClass();
12236 rb_define_const(rb_mMolby, "ResourcePath", val);
12237 val = Ruby_NewFileStringValue(dir);
12238 rb_define_const(rb_mMolby, "ScriptPath", val);
12239 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12240 val = Ruby_NewFileStringValue(p);
12241 rb_define_const(rb_mMolby, "MbsfPath", val);
12244 p = MyAppCallback_getHomeDir();
12245 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12246 rb_define_const(rb_mMolby, "HomeDirectory", val);
12248 p = MyAppCallback_getDocumentHomeDir();
12249 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12250 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12253 #if defined(__CMDMAC__)
12254 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12256 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12261 /* Create objects for stdout and stderr */
12262 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12263 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12264 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12265 rb_gv_set("$stdout", val);
12266 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12267 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12268 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12269 rb_gv_set("$stderr", val);
12271 /* Create objects for stdin */
12272 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12273 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12274 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12275 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12276 rb_gv_set("$stdin", val);
12280 /* Global variable to hold error information */
12281 rb_define_variable("$backtrace", &gMolbyBacktrace);
12282 rb_define_variable("$error_history", &gMolbyErrorHistory);
12283 gMolbyErrorHistory = rb_ary_new();
12285 /* Global variables for script menus */
12286 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12287 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12288 gScriptMenuCommands = rb_ary_new();
12289 gScriptMenuEnablers = rb_ary_new();
12292 /* Register interrupt check code */
12293 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12297 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12298 s_SetIntervalTimer(0, 50);
12301 /* Read the startup script */
12302 if (script != NULL && script[0] != 0) {
12303 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12305 rb_load_protect(rb_str_new2(script), 0, &status);
12308 Ruby_showError(status);
12310 MyAppCallback_showScriptMessage("Done.\n");
12315 Molby_buildARGV(int argc, const char **argv)
12318 rb_ary_clear(rb_argv);
12319 for (i = 0; i < argc; i++) {
12320 VALUE arg = rb_tainted_str_new2(argv[i]);
12322 rb_ary_push(rb_argv, arg);