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));
5456 #pragma mark ------ Name attributes ------
5462 * Returns the display name of the molecule. If the molecule has no associated
5463 * document, then returns nil.
5466 s_Molecule_Name(VALUE self)
5470 Data_Get_Struct(self, Molecule, mol);
5471 MoleculeCallback_displayName(mol, buf, sizeof buf);
5475 return rb_str_new2(buf);
5480 * set_name(string) -> self
5482 * Set the name of an untitled molecule. If the molecule is not associated with window
5483 * or it already has an associated file, then exception is thrown.
5486 s_Molecule_SetName(VALUE self, VALUE nval)
5489 Data_Get_Struct(self, Molecule, mol);
5490 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5491 rb_raise(rb_eMolbyError, "Cannot change the window title");
5500 * Returns the full path name of the molecule, if it is associated with a file.
5501 * If the molecule has no associated file, then returns nil.
5504 s_Molecule_Path(VALUE self)
5508 Data_Get_Struct(self, Molecule, mol);
5509 MoleculeCallback_pathName(mol, buf, sizeof buf);
5513 return Ruby_NewFileStringValue(buf);
5520 * Returns the full path name of the directory in which the file associated with the
5521 * molecule is located. If the molecule has no associated file, then returns nil.
5524 s_Molecule_Dir(VALUE self)
5528 Data_Get_Struct(self, Molecule, mol);
5529 MoleculeCallback_pathName(mol, buf, sizeof buf);
5531 translate_char(buf, '\\', '/');
5536 p = strrchr(buf, '/');
5539 return rb_str_new2(buf);
5547 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5548 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5549 * the Molecule structure) is returned.
5552 s_Molecule_Inspect(VALUE self)
5556 Data_Get_Struct(self, Molecule, mol);
5557 MoleculeCallback_displayName(mol, buf, sizeof buf);
5559 /* No associated document */
5560 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5561 return rb_str_new2(buf);
5563 /* Check whether the document name is duplicate */
5567 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5568 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5569 if (strcmp(buf, buf2) == 0) {
5576 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5578 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5580 return rb_str_new2(buf2);
5584 #pragma mark ------ MolEnumerables ------
5587 s_Molecule_MolEnumerable(VALUE self, int kind)
5590 MolEnumerable *mseq;
5591 Data_Get_Struct(self, Molecule, mol);
5592 mseq = MolEnumerableNew(mol, kind);
5593 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5598 * atoms -> MolEnumerable
5600 * Returns a MolEnumerable object representing the array of atoms.
5603 s_Molecule_Atoms(VALUE self)
5605 return s_Molecule_MolEnumerable(self, kAtomKind);
5610 * bonds -> MolEnumerable
5612 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5613 * by an array of two atom indices.
5616 s_Molecule_Bonds(VALUE self)
5618 return s_Molecule_MolEnumerable(self, kBondKind);
5623 * angles -> MolEnumerable
5625 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5626 * by an array of three atom indices.
5629 s_Molecule_Angles(VALUE self)
5631 return s_Molecule_MolEnumerable(self, kAngleKind);
5636 * dihedrals -> MolEnumerable
5638 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5639 * by an array of four atom indices.
5642 s_Molecule_Dihedrals(VALUE self)
5644 return s_Molecule_MolEnumerable(self, kDihedralKind);
5649 * impropers -> MolEnumerable
5651 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5652 * by an array of four atom indices.
5655 s_Molecule_Impropers(VALUE self)
5657 return s_Molecule_MolEnumerable(self, kImproperKind);
5662 * residues -> MolEnumerable
5664 * Returns a MolEnumerable object representing the array of residue names.
5667 s_Molecule_Residues(VALUE self)
5669 return s_Molecule_MolEnumerable(self, kResidueKind);
5676 * Returns the number of atoms.
5679 s_Molecule_Natoms(VALUE self)
5682 Data_Get_Struct(self, Molecule, mol);
5683 return INT2NUM(mol->natoms);
5690 * Returns the number of bonds.
5693 s_Molecule_Nbonds(VALUE self)
5696 Data_Get_Struct(self, Molecule, mol);
5697 return INT2NUM(mol->nbonds);
5702 * nangles -> Integer
5704 * Returns the number of angles.
5707 s_Molecule_Nangles(VALUE self)
5710 Data_Get_Struct(self, Molecule, mol);
5711 return INT2NUM(mol->nangles);
5716 * ndihedrals -> Integer
5718 * Returns the number of dihedrals.
5721 s_Molecule_Ndihedrals(VALUE self)
5724 Data_Get_Struct(self, Molecule, mol);
5725 return INT2NUM(mol->ndihedrals);
5730 * nimpropers -> Integer
5732 * Returns the number of impropers.
5735 s_Molecule_Nimpropers(VALUE self)
5738 Data_Get_Struct(self, Molecule, mol);
5739 return INT2NUM(mol->nimpropers);
5744 * nresidues -> Integer
5746 * Returns the number of residues.
5749 s_Molecule_Nresidues(VALUE self)
5752 Data_Get_Struct(self, Molecule, mol);
5753 return INT2NUM(mol->nresidues);
5758 * nresidues = Integer
5760 * Change the number of residues.
5763 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5766 int ival = NUM2INT(val);
5767 Data_Get_Struct(self, Molecule, mol);
5768 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5769 if (ival != mol->nresidues)
5770 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5776 * max_residue_number(atom_group = nil) -> Integer
5778 * Returns the maximum residue number actually used. If an atom group is given, only
5779 * these atoms are examined. If no atom is present, nil is returned.
5782 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5788 Data_Get_Struct(self, Molecule, mol);
5789 rb_scan_args(argc, argv, "01", &gval);
5790 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5791 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5792 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5797 * min_residue_number(atom_group = nil) -> Integer
5799 * Returns the minimum residue number actually used. If an atom group is given, only
5800 * these atoms are examined. If no atom is present, nil is returned.
5803 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5809 Data_Get_Struct(self, Molecule, mol);
5810 rb_scan_args(argc, argv, "01", &gval);
5811 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5812 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5813 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5818 * each_atom(atom_group = nil) {|aref| ...}
5820 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5821 * group is given, only these atoms are processed.
5822 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5823 * is self (a Molecule object).
5826 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5834 Data_Get_Struct(self, Molecule, mol);
5835 rb_scan_args(argc, argv, "01", &gval);
5836 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5837 arval = ValueFromMoleculeAndIndex(mol, 0);
5838 Data_Get_Struct(arval, AtomRef, aref);
5839 for (i = 0; i < mol->natoms; i++) {
5841 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5845 IntGroupRelease(ig);
5849 #pragma mark ------ Atom Group ------
5852 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5854 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5855 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5856 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5857 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5864 * atom_group {|aref| ...}
5865 * atom_group(arg1, arg2, ...)
5866 * atom_group(arg1, arg2, ...) {|aref| ...}
5868 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5869 * If arguments are given, then the atoms reprensented by the arguments are added to the
5870 * group. For a conversion of a string to an atom index, see the description
5871 * of Molecule#atom_index.
5872 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5873 * representing each atom, and the atoms are removed from the result if the block returns false.
5877 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5879 IntGroup *ig1, *ig2;
5881 Int i, startPt, interval;
5882 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5883 Data_Get_Struct(retval, IntGroup, ig1);
5884 Data_Get_Struct(self, Molecule, mol);
5886 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5889 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5890 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5891 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5892 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5893 ig2 = IntGroupFromValue(*argv);
5894 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5895 interval = IntGroupGetInterval(ig2, i);
5896 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5898 IntGroupRelease(ig2);
5899 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5901 values[0] = (VALUE)mol;
5902 values[1] = (VALUE)ig1;
5903 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5905 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5910 if (rb_block_given_p()) {
5911 /* Evaluate the given block with an AtomRef as the argument, and delete
5912 the index if the block returns false */
5913 AtomRef *aref = AtomRefNew(mol, 0);
5914 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5915 ig2 = IntGroupNew();
5916 IntGroupCopy(ig2, ig1);
5917 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5919 if (startPt >= mol->natoms)
5921 aref->idx = startPt;
5922 resval = rb_yield(arval);
5924 IntGroupRemove(ig1, startPt, 1);
5926 IntGroupRelease(ig2);
5929 /* Remove points that are out of bounds */
5930 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5937 * selection -> IntGroup
5939 * Returns the current selection.
5942 s_Molecule_Selection(VALUE self)
5947 Data_Get_Struct(self, Molecule, mol);
5948 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5949 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
5950 val = ValueFromIntGroup(ig);
5951 IntGroupRelease(ig);
5953 val = IntGroup_Alloc(rb_cIntGroup);
5959 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5963 Data_Get_Struct(self, Molecule, mol);
5967 ig = s_Molecule_AtomGroupFromValue(self, val);
5969 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
5971 MoleculeSetSelection(mol, ig);
5973 IntGroupRelease(ig);
5979 * selection = IntGroup
5981 * Set the current selection. The right-hand operand may be nil.
5982 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
5985 s_Molecule_SetSelection(VALUE self, VALUE val)
5987 return s_Molecule_SetSelectionSub(self, val, 0);
5992 * set_undoable_selection(IntGroup)
5994 * Set the current selection with undo registration. The right-hand operand may be nil.
5995 * This operation is undoable.
5998 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6000 return s_Molecule_SetSelectionSub(self, val, 1);
6003 #pragma mark ------ Editing ------
6007 * extract(group, dummy_flag = nil) -> Molecule
6009 * Extract the atoms given by group and return as a new molecule object.
6010 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6011 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6012 * names beginning with an underscore) and included in the new molecule object.
6015 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6017 Molecule *mol1, *mol2;
6019 VALUE group, dummy_flag, retval;
6020 Data_Get_Struct(self, Molecule, mol1);
6021 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6022 ig = s_Molecule_AtomGroupFromValue(self, group);
6023 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6026 retval = ValueFromMolecule(mol2);
6028 IntGroupRelease(ig);
6034 * add(molecule2) -> self
6036 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6038 This operation is undoable.
6041 s_Molecule_Add(VALUE self, VALUE val)
6043 Molecule *mol1, *mol2;
6044 Data_Get_Struct(self, Molecule, mol1);
6045 mol2 = MoleculeFromValue(val);
6046 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6052 * remove(group) -> Molecule
6054 * The atoms designated by the given group are removed from the molecule.
6055 * This operation is undoable.
6058 s_Molecule_Remove(VALUE self, VALUE group)
6063 IntGroupIterator iter;
6065 Data_Get_Struct(self, Molecule, mol1);
6066 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6067 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6068 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6069 Data_Get_Struct(group, IntGroup, ig);
6071 /* Remove the bonds between the two fragments */
6072 /* (This is necessary for undo to work correctly) */
6073 IntGroupIteratorInit(ig, &iter);
6075 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6076 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6078 cp = AtomConnectData(&ap->connect);
6079 for (j = 0; j < ap->connect.count; j++) {
6081 if (!IntGroupLookup(ig, n, NULL)) {
6082 /* bond i-n, i is in ig and n is not */
6083 int k = MoleculeLookupBond(mol1, i, n);
6087 IntGroupAdd(bg, k, 1);
6092 IntGroupIteratorRelease(&iter);
6095 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6096 IntGroupRelease(bg);
6099 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6106 * create_atom(name, pos = -1) -> AtomRef
6108 * Create a new atom with the specified name (may contain residue
6109 * information) and position (if position is out of range, the atom is appended at
6110 * the end). Returns the reference to the new atom.
6111 * This operation is undoable.
6114 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6121 char *p, resName[6], atomName[6];
6123 Data_Get_Struct(self, Molecule, mol);
6124 rb_scan_args(argc, argv, "02", &name, &ival);
6126 pos = NUM2INT(rb_Integer(ival));
6129 p = StringValuePtr(name);
6131 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6132 if (atomName[0] == 0)
6133 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6136 if (p == NULL || p[0] == 0) {
6137 memset(atomName, 0, 4);
6140 memset(&arec, 0, sizeof(arec));
6141 strncpy(arec.aname, atomName, 4);
6143 strncpy(arec.resName, resName, 4);
6144 arec.resSeq = resSeq;
6146 arec.occupancy = 1.0;
6147 // i = MoleculeCreateAnAtom(mol, &arec);
6148 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6150 aref = AtomRefNew(mol, pos);
6151 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6156 * duplicate_atom(atomref, pos = -1) -> AtomRef
6158 * Create a new atom with the same attributes (but no bonding information)
6159 * with the specified atom. Returns the reference to the new atom.
6160 * This operation is undoable.
6163 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6169 VALUE retval, aval, ival;
6171 Data_Get_Struct(self, Molecule, mol);
6172 rb_scan_args(argc, argv, "11", &aval, &ival);
6173 if (FIXNUM_P(aval)) {
6174 int idx = NUM2INT(aval);
6175 if (idx < 0 || idx >= mol->natoms)
6176 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6177 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6179 apsrc = s_AtomFromValue(aval);
6182 rb_raise(rb_eMolbyError, "bad atom specification");
6184 pos = NUM2INT(rb_Integer(ival));
6186 AtomDuplicate(&arec, apsrc);
6187 arec.connect.count = 0;
6188 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6191 aref = AtomRefNew(mol, pos);
6192 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6200 * create_bond(n1, n2, ...) -> Integer
6202 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6203 * do nothing for that pair. Returns the number of bonds actually created.
6204 * This operation is undoable.
6207 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6210 Int i, j, k, *ip, old_nbonds;
6212 rb_raise(rb_eMolbyError, "missing arguments");
6214 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6215 Data_Get_Struct(self, Molecule, mol);
6216 ip = ALLOC_N(Int, argc + 1);
6217 for (i = j = 0; i < argc; i++, j++) {
6218 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6220 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6221 j -= 2; /* This bond is already present: skip it */
6223 for (k = 0; k < j - 1; k += 2) {
6224 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6225 j -= 2; /* The same entry is already in the argument */
6232 old_nbonds = mol->nbonds;
6234 ip[j] = kInvalidIndex;
6235 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6239 rb_raise(rb_eMolbyError, "atom index out of range");
6241 rb_raise(rb_eMolbyError, "too many bonds");
6243 rb_raise(rb_eMolbyError, "duplicate bonds");
6245 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6247 rb_raise(rb_eMolbyError, "error in creating bonds");
6248 return INT2NUM(mol->nbonds - old_nbonds);
6253 * molecule.remove_bonds(n1, n2, ...) -> Integer
6255 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6256 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6257 * This operation is undoable.
6260 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6266 rb_raise(rb_eMolbyError, "missing arguments");
6268 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6269 Data_Get_Struct(self, Molecule, mol);
6271 for (i = j = 0; i < argc; i++, j = 1 - j) {
6272 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6274 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6278 IntGroupAdd(bg, k, 1);
6283 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6284 i = IntGroupGetCount(bg);
6285 IntGroupRelease(bg);
6292 * assign_bond_order(idx, d1)
6293 * assign_bond_orders(group, [d1, d2, ...])
6295 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6296 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6297 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6298 * (This may change in the future)
6299 * This operation is undoable.
6302 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6306 Data_Get_Struct(self, Molecule, mol);
6307 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6308 /* The first form */
6309 Int idx = NUM2INT(rb_Integer(idxval));
6310 Double d1 = NUM2DBL(rb_Float(dval));
6311 if (idx < 0 || idx >= mol->nbonds)
6312 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6313 ig = IntGroupNewWithPoints(idx, 1, -1);
6314 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6315 IntGroupRelease(ig);
6319 ig = IntGroupFromValue(idxval);
6320 n = IntGroupGetCount(ig);
6322 rb_raise(rb_eMolbyError, "the bond index is empty");
6323 dval = rb_ary_to_ary(dval);
6324 dp = (Double *)calloc(sizeof(Double), n);
6325 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6326 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6328 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6330 IntGroupRelease(ig);
6337 * get_bond_order(idx) -> Float
6338 * get_bond_orders(group) -> Array
6340 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6341 * In the second form, the bond orders at the indices in the group are returned as an array.
6342 * If no bond order information have been assigned, returns nil (the first form)
6343 * or an empty array (the second form).
6346 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6352 Int i, n, numericArg;
6353 Data_Get_Struct(self, Molecule, mol);
6354 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6355 /* The first form */
6356 Int idx = NUM2INT(rb_Integer(idxval));
6357 if (idx < 0 || idx >= mol->nbonds)
6358 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6359 if (mol->bondOrders == NULL)
6361 ig = IntGroupNewWithPoints(idx, 1, -1);
6365 if (mol->bondOrders == NULL)
6366 return rb_ary_new();
6367 ig = IntGroupFromValue(idxval);
6368 n = IntGroupGetCount(ig);
6370 rb_raise(rb_eMolbyError, "the bond index is empty");
6373 dp = (Double *)calloc(sizeof(Double), n);
6374 MoleculeGetBondOrders(mol, dp, ig);
6376 retval = rb_float_new(dp[0]);
6378 retval = rb_ary_new();
6379 for (i = 0; i < n; i++)
6380 rb_ary_push(retval, rb_float_new(dp[i]));
6383 IntGroupRelease(ig);
6389 * bond_exist?(idx1, idx2) -> bool
6391 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6392 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6395 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6401 Data_Get_Struct(self, Molecule, mol);
6402 idx1 = NUM2INT(rb_Integer(ival1));
6403 idx2 = NUM2INT(rb_Integer(ival2));
6404 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6405 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6406 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6407 cp = AtomConnectData(&ap->connect);
6408 for (i = 0; i < ap->connect.count; i++) {
6417 * add_angle(n1, n2, n3) -> Molecule
6419 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6420 * when a bond is created, so it is rarely necessary to use this method explicitly.
6421 * This operation is undoable.
6424 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6428 Data_Get_Struct(self, Molecule, mol);
6429 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6430 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6431 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6432 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6433 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6434 n[3] = kInvalidIndex;
6435 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6441 * remove_angle(n1, n2, n3) -> Molecule
6443 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6444 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6445 * This operation is undoable.
6448 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6453 Data_Get_Struct(self, Molecule, mol);
6454 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6455 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6456 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6457 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6458 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6459 ig = IntGroupNewWithPoints(n[3], 1, -1);
6460 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6461 IntGroupRelease(ig);
6467 * add_dihedral(n1, n2, n3, n4) -> Molecule
6469 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6470 * when a bond is created, so it is rarely necessary to use this method explicitly.
6471 * This operation is undoable.
6474 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6478 Data_Get_Struct(self, Molecule, mol);
6479 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6480 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6481 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6482 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6483 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6484 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6485 n[4] = kInvalidIndex;
6486 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6492 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6494 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6495 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6496 * This operation is undoable.
6499 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6504 Data_Get_Struct(self, Molecule, mol);
6505 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6506 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6507 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6508 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6509 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6510 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6511 ig = IntGroupNewWithPoints(n[4], 1, -1);
6512 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6513 IntGroupRelease(ig);
6519 * add_improper(n1, n2, n3, n4) -> Molecule
6521 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6522 * not automatically added when a new bond is created, so this method is more useful than
6523 * the angle/dihedral counterpart.
6524 * This operation is undoable.
6527 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6531 Data_Get_Struct(self, Molecule, mol);
6532 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6533 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6534 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6535 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6536 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6537 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6538 n[4] = kInvalidIndex;
6539 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6545 * remove_improper(n1, n2, n3, n4) -> Molecule
6546 * remove_improper(intgroup) -> Molecule
6548 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6549 * Returns self. Unlike angles and dihedrals, impropers are
6550 * not automatically added when a new bond is created, so this method is more useful than
6551 * the angle/dihedral counterpart.
6552 * This operation is undoable.
6555 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6558 VALUE v1, v2, v3, v4;
6561 Data_Get_Struct(self, Molecule, mol);
6563 ig = IntGroupFromValue(argv[0]);
6565 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6566 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6567 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6568 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6569 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6570 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6571 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6572 ig = IntGroupNewWithPoints(n[4], 1, -1);
6574 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6575 IntGroupRelease(ig);
6581 * assign_residue(group, res) -> Molecule
6583 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6584 * or "resname.resno". When the residue number is not specified, the residue number of
6585 * the first atom in the group is used.
6586 * This operation is undoable.
6589 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6593 char *p, *pp, buf[16];
6596 Data_Get_Struct(self, Molecule, mol);
6598 /* Parse the argument res */
6599 if (FIXNUM_P(res)) {
6600 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6601 resid = NUM2INT(res);
6604 p = StringValuePtr(res);
6605 pp = strchr(p, '.');
6607 resid = atoi(pp + 1);
6613 if (n > sizeof buf - 1)
6618 ig = s_Molecule_AtomGroupFromValue(self, range);
6619 if (ig == NULL || IntGroupGetCount(ig) == 0)
6623 /* Use the residue number of the first specified atom */
6624 n = IntGroupGetNthPoint(ig, 0);
6625 if (n >= mol->natoms)
6626 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6627 ap = ATOM_AT_INDEX(mol->atoms, n);
6630 /* Change the residue number */
6631 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6632 /* Change the residue name if necessary */
6636 seqs[1] = kInvalidIndex; */
6637 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6639 IntGroupRelease(ig);
6645 * offset_residue(group, offset) -> Molecule
6647 * Offset the residue number of the specified atoms. If any of the residue number gets
6648 * negative, then exception is thrown.
6649 * This operation is undoable.
6652 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6657 Data_Get_Struct(self, Molecule, mol);
6658 ig = s_Molecule_AtomGroupFromValue(self, range);
6659 ofs = NUM2INT(offset);
6660 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6662 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6663 IntGroupRelease(ig);
6669 * renumber_atoms(array) -> IntGroup
6671 * Change the order of atoms so that the atoms specified in the array argument appear
6672 * in this order from the top of the molecule. The atoms that are not included in array
6673 * are placed after these atoms, and these atoms are returned as an intGroup.
6674 * This operation is undoable.
6677 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6683 VALUE *valp, retval;
6684 Data_Get_Struct(self, Molecule, mol);
6685 if (TYPE(array) != T_ARRAY)
6686 array = rb_funcall(array, rb_intern("to_a"), 0);
6687 n = RARRAY_LEN(array);
6688 valp = RARRAY_PTR(array);
6689 new2old = ALLOC_N(Int, n + 1);
6690 for (i = 0; i < n; i++)
6691 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6692 new2old[i] = kInvalidIndex;
6693 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6695 rb_raise(rb_eMolbyError, "Atom index out of range");
6697 rb_raise(rb_eMolbyError, "Duplicate entry");
6699 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6700 retval = IntGroup_Alloc(rb_cIntGroup);
6701 Data_Get_Struct(retval, IntGroup, ig);
6702 if (mol->natoms > n)
6703 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6710 * set_atom_attr(index, key, value)
6712 * Set the atom attribute for the specified atom.
6713 * This operation is undoable.
6716 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6720 Data_Get_Struct(self, Molecule, mol);
6721 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6722 oldval = s_AtomRef_GetAttr(aref, key);
6725 s_AtomRef_SetAttr(aref, key, val);
6731 * get_atom_attr(index, key)
6733 * Get the atom attribute for the specified atom.
6736 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6738 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6741 #pragma mark ------ Undo Support ------
6745 * register_undo(script, *args)
6747 * Register an undo operation with the current molecule.
6750 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6755 Data_Get_Struct(self, Molecule, mol);
6756 rb_scan_args(argc, argv, "1*", &script, &args);
6757 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6758 MolActionCallback_registerUndo(mol, act);
6764 * undo_enabled? -> bool
6766 * Returns true if undo is enabled for this molecule; otherwise no.
6769 s_Molecule_UndoEnabled(VALUE self)
6772 Data_Get_Struct(self, Molecule, mol);
6773 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6780 * undo_enabled = bool
6782 * Enable or disable undo.
6785 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6788 Data_Get_Struct(self, Molecule, mol);
6789 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6793 #pragma mark ------ Measure ------
6796 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6798 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6799 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6800 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6802 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6808 * center_of_mass(group = nil) -> Vector3D
6810 * Calculate the center of mass for the given set of atoms. The argument
6811 * group is null, then all atoms are considered.
6814 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6820 Data_Get_Struct(self, Molecule, mol);
6821 rb_scan_args(argc, argv, "01", &group);
6822 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6823 s_Molecule_DoCenterOfMass(mol, &v, ig);
6825 IntGroupRelease(ig);
6826 return ValueFromVector(&v);
6831 * centralize(group = nil) -> self
6833 * Translate the molecule so that the center of mass of the given group is located
6834 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6837 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6843 Data_Get_Struct(self, Molecule, mol);
6844 rb_scan_args(argc, argv, "01", &group);
6845 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6846 s_Molecule_DoCenterOfMass(mol, &v, ig);
6848 IntGroupRelease(ig);
6852 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6858 * bounds(group = nil) -> [min, max]
6860 * Calculate the boundary. The return value is an array of two Vector3D objects.
6863 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6871 Data_Get_Struct(self, Molecule, mol);
6872 rb_scan_args(argc, argv, "01", &group);
6873 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6874 if (ig != NULL && IntGroupGetCount(ig) == 0)
6875 rb_raise(rb_eMolbyError, "atom group is empty");
6876 vmin.x = vmin.y = vmin.z = 1e30;
6877 vmax.x = vmax.y = vmax.z = -1e30;
6878 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6880 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6896 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6899 /* Get atom position or a vector */
6901 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6903 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6904 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6905 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6907 VectorFromValue(val, vp);
6913 * measure_bond(n1, n2) -> Float
6915 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
6916 * or Vector3D values.
6917 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6920 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6924 Data_Get_Struct(self, Molecule, mol);
6925 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6926 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6927 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6932 * measure_angle(n1, n2, n3) -> Float
6934 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
6935 * or Vector3D values. The return value is in degree.
6936 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6939 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6944 Data_Get_Struct(self, Molecule, mol);
6945 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6946 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6947 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6948 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6950 return Qnil; /* Cannot define */
6951 else return rb_float_new(d);
6956 * measure_dihedral(n1, n2, n3, n4) -> Float
6958 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
6959 * or Vector3D values. The return value is in degree.
6960 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6963 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6966 Vector v1, v2, v3, v4;
6968 Data_Get_Struct(self, Molecule, mol);
6969 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6970 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6971 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6972 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
6973 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
6975 return Qnil; /* Cannot define */
6976 else return rb_float_new(d);
6981 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
6983 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
6984 * first and second atom in the pair should belong to group1 and group2, respectively.
6985 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
6988 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
6991 VALUE limval, gval1, gval2, rval, igval;
6992 IntGroup *ig1, *ig2;
6993 IntGroupIterator iter1, iter2;
6999 MDExclusion *exinfo;
7002 Data_Get_Struct(self, Molecule, mol);
7003 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7004 lim = NUM2DBL(rb_Float(limval));
7006 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7008 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7010 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7012 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7014 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7016 if (!RTEST(igval)) {
7017 /* Use the exclusion table in MDArena */
7018 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7019 VALUE mval = ValueFromMolecule(mol);
7020 s_RebuildMDParameterIfNecessary(mval, Qnil);
7022 exinfo = mol->arena->exinfo; /* May be NULL */
7023 exlist = mol->arena->exlist;
7028 IntGroupIteratorInit(ig1, &iter1);
7029 IntGroupIteratorInit(ig2, &iter2);
7032 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7034 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7036 if (exinfo != NULL) {
7037 exn1 = exinfo[n[0]].index1;
7038 exn2 = exinfo[n[0] + 1].index1;
7039 } else exn1 = exn2 = -1;
7040 IntGroupIteratorReset(&iter2);
7041 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7042 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7044 continue; /* Same atom */
7045 if (exinfo != NULL) {
7046 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7047 for (i = exn1; i < exn2; i++) {
7048 if (exlist[i] == n[1])
7052 continue; /* Should be excluded */
7054 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7055 /* Is this pair already registered? */
7057 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7058 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7062 /* Not registered yet */
7063 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7068 IntGroupIteratorRelease(&iter2);
7069 IntGroupIteratorRelease(&iter1);
7070 IntGroupRelease(ig2);
7071 IntGroupRelease(ig1);
7072 rval = rb_ary_new2(npairs);
7073 if (pairs != NULL) {
7074 for (i = 0; i < npairs; i++) {
7075 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7084 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7086 * Find atoms that are within the threshold distance from the given atom.
7087 * (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.)
7088 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7089 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7090 * If limit is not given, a default value of 1.2 is used.
7091 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7094 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7097 VALUE aval, limval, radval;
7098 double limit, radius;
7099 Int n1, nbonds, *bonds, an;
7101 Data_Get_Struct(self, Molecule, mol);
7102 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7103 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)) {
7104 VectorFromValue(aval, &v);
7106 radius = gElementParameters[6].radius;
7108 radius = NUM2DBL(rb_Float(radval));
7111 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7112 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7113 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7114 if (an >= 0 && an < gCountElementParameters)
7115 radius = gElementParameters[an].radius;
7116 else radius = gElementParameters[6].radius;
7121 limit = NUM2DBL(rb_Float(limval));
7122 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7124 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7125 aval = rb_ary_new();
7127 for (n1 = 0; n1 < nbonds; n1++)
7128 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7136 * guess_bonds(limit = 1.2) -> Integer
7138 * Create bonds between atoms that are within the threshold distance.
7139 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7140 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7141 * If limit is not given, a default value of 1.2 is used.
7142 * The number of the newly created bonds is returned.
7143 * This operation is undoable.
7146 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7152 Data_Get_Struct(self, Molecule, mol);
7153 rb_scan_args(argc, argv, "01", &limval);
7157 limit = NUM2DBL(rb_Float(limval));
7158 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7160 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7163 return INT2NUM(nbonds);
7166 #pragma mark ------ Cell and Symmetry ------
7170 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7172 * Returns the unit cell parameters. If cell is not set, returns nil.
7175 s_Molecule_Cell(VALUE self)
7180 Data_Get_Struct(self, Molecule, mol);
7181 if (mol->cell == NULL)
7183 val = rb_ary_new2(6);
7184 for (i = 0; i < 6; i++)
7185 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7186 if (mol->cell->has_sigma) {
7187 for (i = 0; i < 6; i++) {
7188 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7196 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7197 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7199 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7200 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7201 This operation is undoable.
7202 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7205 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7209 int i, convert_coord, n;
7211 Data_Get_Struct(self, Molecule, mol);
7212 rb_scan_args(argc, argv, "11", &val, &cval);
7217 val = rb_ary_to_ary(val);
7218 len = RARRAY_LEN(val);
7221 } else if (len >= 6) {
7223 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7224 for (i = 0; i < n; i++)
7225 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7227 convert_coord = (RTEST(cval) ? 1 : 0);
7228 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7234 * box -> [avec, bvec, cvec, origin, flags]
7236 * Get the unit cell information in the form of a periodic bounding box.
7237 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7238 * Integers which define whether the system is periodic along the axis.
7239 * If no unit cell is defined, nil is returned.
7242 s_Molecule_Box(VALUE self)
7246 Data_Get_Struct(self, Molecule, mol);
7247 if (mol == NULL || mol->cell == NULL)
7249 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7250 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7251 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7252 v[3] = ValueFromVector(&(mol->cell->origin));
7253 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7254 val = rb_ary_new4(5, v);
7260 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7261 * set_box(d, origin = [0, 0, 0])
7264 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7265 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7267 Flags, if present, is a 3-member array of Integers defining whether the system is
7268 periodic along the axis.
7269 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7270 In the second form, an isotropic box with cell-length d is set.
7271 In the third form, the existing box is cleared.
7272 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7275 s_Molecule_SetBox(VALUE self, VALUE aval)
7279 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7281 Vector origin = {0, 0, 0};
7284 int i, convertCoordinates = 0;
7285 Data_Get_Struct(self, Molecule, mol);
7287 MolActionCreateAndPerform(mol, gMolActionClearBox);
7290 aval = rb_ary_to_ary(aval);
7291 for (i = 0; i < 6; i++) {
7292 if (i < RARRAY_LEN(aval))
7293 v[i] = (RARRAY_PTR(aval))[i];
7297 MolActionCreateAndPerform(mol, gMolActionClearBox);
7300 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7301 d = NUM2DBL(rb_Float(v[0]));
7302 for (i = 0; i < 3; i++)
7303 VecScale(vv[i], ax[i], d);
7305 VectorFromValue(v[1], &origin);
7306 flags[0] = flags[1] = flags[2] = 1;
7308 for (i = 0; i < 3; i++) {
7311 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7312 d = NUM2DBL(rb_Float(v[i]));
7313 VecScale(vv[i], ax[i], d);
7315 VectorFromValue(v[i], &vv[i]);
7317 flags[i] = (VecLength2(vv[i]) > 0.0);
7320 VectorFromValue(v[3], &origin);
7322 for (i = 0; i < 3; i++) {
7323 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7324 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7328 convertCoordinates = 1;
7330 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7336 * cell_periodicity -> [n1, n2, n3]
7338 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7342 s_Molecule_CellPeriodicity(VALUE self)
7345 Data_Get_Struct(self, Molecule, mol);
7346 if (mol->cell == NULL)
7348 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7353 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7354 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7356 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7357 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7358 * If cell is not defined, exception is raised.
7359 * This operation is undoable.
7362 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7366 Data_Get_Struct(self, Molecule, mol);
7367 if (mol->cell == NULL)
7368 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7371 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7372 flag = NUM2INT(rb_Integer(arg));
7376 arg = rb_ary_to_ary(arg);
7378 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7379 arg0 = RARRAY_PTR(arg)[i];
7380 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7381 flag |= (1 << (2 - i));
7384 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7390 * cell_flexibility -> bool
7392 * Returns the unit cell is flexible or not
7395 s_Molecule_CellFlexibility(VALUE self)
7397 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7400 Data_Get_Struct(self, Molecule, mol);
7401 if (mol->cell == NULL)
7403 if (mol->useFlexibleCell)
7405 else return Qfalse; */
7410 * self.cell_flexibility = bool
7411 * set_cell_flexibility(bool)
7413 * Change the unit cell is flexible or not
7416 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7418 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7421 Data_Get_Struct(self, Molecule, mol);
7422 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7428 * cell_transform -> Transform
7430 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7431 * If cell is not defined, nil is returned.
7434 s_Molecule_CellTransform(VALUE self)
7437 Data_Get_Struct(self, Molecule, mol);
7438 if (mol == NULL || mol->cell == NULL)
7440 return ValueFromTransform(&(mol->cell->tr));
7445 * symmetry -> Array of Transforms
7446 * symmetries -> Array of Transforms
7448 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7449 * returns an empty array.
7452 s_Molecule_Symmetry(VALUE self)
7457 Data_Get_Struct(self, Molecule, mol);
7458 if (mol->nsyms <= 0)
7459 return rb_ary_new();
7460 val = rb_ary_new2(mol->nsyms);
7461 for (i = 0; i < mol->nsyms; i++) {
7462 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7469 * nsymmetries -> Integer
7471 * Get the number of currently defined symmetry operations.
7474 s_Molecule_Nsymmetries(VALUE self)
7477 Data_Get_Struct(self, Molecule, mol);
7478 return INT2NUM(mol->nsyms);
7483 * add_symmetry(Transform) -> Integer
7485 * Add a new symmetry operation. If no symmetry operation is defined and the
7486 * given argument is not an identity transform, then also add an identity
7487 * transform at the index 0.
7488 * Returns the total number of symmetries after operation.
7491 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7495 Data_Get_Struct(self, Molecule, mol);
7496 TransformFromValue(trans, &tr);
7497 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7498 return INT2NUM(mol->nsyms);
7503 * remove_symmetry(count = nil) -> Integer
7504 * remove_symmetries(count = nil) -> Integer
7506 * Remove the specified number of symmetry operations. The last added ones are removed
7507 * first. If count is nil, then all symmetry operations are removed. Returns the
7508 * number of leftover symmetries.
7511 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7516 Data_Get_Struct(self, Molecule, mol);
7517 rb_scan_args(argc, argv, "01", &cval);
7521 n = NUM2INT(rb_Integer(cval));
7522 if (n < 0 || n > mol->nsyms)
7523 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7524 if (n == mol->nsyms)
7527 for (i = 0; i < n; i++)
7528 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7529 return INT2NUM(mol->nsyms);
7534 * wrap_unit_cell(group) -> Vector3D
7536 * Move the specified group so that the center of mass of the group is within the
7537 * unit cell. The offset vector is returned. If no periodic box is defined,
7538 * exception is raised.
7541 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7546 Data_Get_Struct(self, Molecule, mol);
7547 if (mol->cell == NULL)
7548 rb_raise(rb_eMolbyError, "no unit cell is defined");
7549 ig = s_Molecule_AtomGroupFromValue(self, gval);
7550 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7551 TransformVec(&v, mol->cell->rtr, &cv);
7552 if (mol->cell->flags[0])
7554 if (mol->cell->flags[1])
7556 if (mol->cell->flags[2])
7558 TransformVec(&dv, mol->cell->tr, &v);
7560 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7561 IntGroupRelease(ig);
7562 return ValueFromVector(&dv);
7567 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7569 * Expand the specified part of the molecule by the given symmetry operation.
7570 * Returns the array of atom indices corresponding to the expanded atoms.
7571 * If allow_overlap is true, then new atoms are created even when the
7572 * coordinates coincide with the some other atom (special position) of the
7573 * same element; otherwise, such atom will not be created and the index of the
7574 * existing atom is given in the returned array.
7577 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7580 VALUE gval, sval, xval, yval, zval, rval, oval;
7582 Int n[4], allow_overlap;
7586 Data_Get_Struct(self, Molecule, mol);
7587 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7588 n[0] = NUM2INT(rb_Integer(sval));
7589 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7590 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7591 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7592 allow_overlap = (RTEST(oval) ? 1 : 0);
7593 ig = s_Molecule_AtomGroupFromValue(self, gval);
7594 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7595 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7596 natoms = mol->natoms;
7598 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7600 rval = rb_ary_new2(nidx);
7601 while (--nidx >= 0) {
7602 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7604 /* if (natoms == mol->natoms)
7607 rval = IntGroup_Alloc(rb_cIntGroup);
7608 Data_Get_Struct(rval, IntGroup, ig);
7609 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7616 * amend_by_symmetry(group = nil) -> IntGroup
7618 * Expand the specified part of the molecule by the given symmetry operation.
7619 * Returns an IntGroup containing the added atoms.
7622 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7627 Data_Get_Struct(self, Molecule, mol);
7628 rb_scan_args(argc, argv, "01", &gval);
7630 ig = s_Molecule_AtomGroupFromValue(self, gval);
7632 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7633 rval = ValueFromIntGroup(ig2);
7634 IntGroupRelease(ig2);
7638 #pragma mark ------ Transforms ------
7642 * translate(vec, group = nil) -> Molecule
7644 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7645 * This operation is undoable.
7648 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7654 Data_Get_Struct(self, Molecule, mol);
7655 rb_scan_args(argc, argv, "11", &vec, &group);
7656 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7657 VectorFromValue(vec, &v);
7658 // MoleculeTranslate(mol, &v, ig);
7659 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7661 IntGroupRelease(ig);
7667 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7669 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7670 * If group is given, only atoms in the group are moved.
7671 * This operation is undoable.
7674 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7677 volatile VALUE aval, anval, cval, gval;
7682 Data_Get_Struct(self, Molecule, mol);
7683 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7684 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7685 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7686 VectorFromValue(aval, &av);
7688 cv.x = cv.y = cv.z = 0.0;
7690 VectorFromValue(cval, &cv);
7691 if (TransformForRotation(tr, &av, angle, &cv))
7692 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7693 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7695 IntGroupRelease(ig);
7701 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7703 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7704 * axis must not be a zero vector.
7705 * If group is given, only atoms in the group are moved.
7706 * This operation is undoable.
7709 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7712 volatile VALUE aval, cval, gval;
7716 Data_Get_Struct(self, Molecule, mol);
7717 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7718 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7719 VectorFromValue(aval, &av);
7721 cv.x = cv.y = cv.z = 0.0;
7723 VectorFromValue(cval, &cv);
7724 if (TransformForReflection(tr, &av, &cv))
7725 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7726 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7728 IntGroupRelease(ig);
7734 * invert(center = [0,0,0], group = nil) -> Molecule
7736 * Invert the molecule with the given center.
7737 * If group is given, only atoms in the group are moved.
7738 * This operation is undoable.
7741 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7744 volatile VALUE cval, gval;
7748 Data_Get_Struct(self, Molecule, mol);
7749 rb_scan_args(argc, argv, "02", &cval, &gval);
7750 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7752 cv.x = cv.y = cv.z = 0.0;
7754 VectorFromValue(cval, &cv);
7755 TransformForInversion(tr, &cv);
7756 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7758 IntGroupRelease(ig);
7764 * transform(transform, group = nil) -> Molecule
7766 * Transform the molecule by the given Transform object.
7767 * If group is given, only atoms in the group are moved.
7768 * This operation is undoable.
7771 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7777 Data_Get_Struct(self, Molecule, mol);
7778 rb_scan_args(argc, argv, "11", &trans, &group);
7779 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7780 TransformFromValue(trans, &tr);
7781 /* MoleculeTransform(mol, tr, ig); */
7782 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7784 IntGroupRelease(ig);
7790 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7792 * Get the transform corresponding to the symmetry operation. The symop can either be
7793 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7794 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7795 * Otherwise, the returned transform is for fractional coordinates.
7796 * Raises exception when no cell or no transform are defined.
7799 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7805 Data_Get_Struct(self, Molecule, mol);
7806 if (mol->cell == NULL)
7807 rb_raise(rb_eMolbyError, "no unit cell is defined");
7808 if (mol->nsyms == 0)
7809 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7810 rb_scan_args(argc, argv, "11", &sval, &fval);
7811 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7812 symop.sym = NUM2INT(rb_Integer(sval));
7813 symop.dx = symop.dy = symop.dz = 0;
7815 sval = rb_ary_to_ary(sval);
7816 if (RARRAY_LEN(sval) < 4)
7817 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7818 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7819 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7820 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7821 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7823 if (symop.sym >= mol->nsyms)
7824 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7825 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7826 return ValueFromTransform(&tr);
7831 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7833 * Get the symmetry operation corresponding to the given transform.
7834 * If is_cartesian is true, the given transform is for cartesian coordinates.
7835 * Otherwise, the given transform is for fractional coordinates.
7836 * Raises exception when no cell or no transform are defined.
7839 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7846 Data_Get_Struct(self, Molecule, mol);
7847 if (mol->cell == NULL)
7848 rb_raise(rb_eMolbyError, "no unit cell is defined");
7849 if (mol->nsyms == 0)
7850 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7851 rb_scan_args(argc, argv, "11", &tval, &fval);
7852 TransformFromValue(tval, &tr);
7853 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7855 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7857 return Qnil; /* Not found */
7861 #pragma mark ------ Frames ------
7865 * select_frame(index)
7868 * Select the specified frame. If successful, returns true, otherwise returns false.
7871 s_Molecule_SelectFrame(VALUE self, VALUE val)
7874 int ival = NUM2INT(val);
7875 Data_Get_Struct(self, Molecule, mol);
7876 ival = MoleculeSelectFrame(mol, ival, 1);
7886 * Get the current frame.
7889 s_Molecule_Frame(VALUE self)
7892 Data_Get_Struct(self, Molecule, mol);
7893 return INT2NUM(mol->cframe);
7898 * nframes -> Integer
7900 * Get the number of frames.
7903 s_Molecule_Nframes(VALUE self)
7906 Data_Get_Struct(self, Molecule, mol);
7907 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7912 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7913 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7915 * Insert new frames at the indices specified by the intGroup. If the first argument is
7916 * an integer, a single new frame is inserted at that index. If the first argument is
7917 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7918 * should be an array of arrays of Vector3Ds, then those coordinates are set
7919 * to the new frame. Otherwise, the coordinates of current molecule are copied
7921 * Returns an intGroup representing the inserted frames if successful, nil if not.
7924 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7926 VALUE val, coords, cells;
7929 int count, ival, i, j, len, len_c, len2, nframes;
7932 Data_Get_Struct(self, Molecule, mol);
7933 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7934 if (coords != Qnil) {
7935 if (TYPE(coords) != T_ARRAY)
7936 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7937 len = RARRAY_LEN(coords);
7939 if (cells != Qnil) {
7940 if (mol->cell == NULL)
7941 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7942 if (TYPE(cells) != T_ARRAY)
7943 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7944 len_c = RARRAY_LEN(cells);
7946 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7947 nframes = MoleculeGetNumberOfFrames(mol);
7949 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7950 val = ValueFromIntGroup(ig);
7952 ig = IntGroupFromValue(val);
7954 count = IntGroupGetCount(ig); /* Count is updated here */
7955 vp = ALLOC_N(Vector, mol->natoms * count);
7957 vp2 = ALLOC_N(Vector, 4 * count);
7961 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7962 ptr = RARRAY_PTR(coords);
7963 for (i = 0; i < count; i++) {
7964 if (TYPE(ptr[i]) != T_ARRAY)
7965 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7966 len2 = RARRAY_LEN(ptr[i]);
7967 if (len2 < mol->natoms)
7968 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7969 ptr2 = RARRAY_PTR(ptr[i]);
7970 for (j = 0; j < mol->natoms; j++)
7971 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7975 for (i = 0; i < count; i++) {
7976 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7977 vp[i * mol->natoms + j] = ap->r;
7983 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7984 ptr = RARRAY_PTR(cells);
7985 for (i = 0; i < count; i++) {
7986 if (TYPE(ptr[i]) != T_ARRAY)
7987 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7988 len2 = RARRAY_LEN(ptr[i]);
7990 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7991 ptr2 = RARRAY_PTR(ptr[i]);
7992 for (j = 0; j < 4; j++)
7993 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7996 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7997 IntGroupRelease(ig);
8001 return (ival >= 0 ? val : Qnil);
8006 * create_frame(coordinates = nil) -> Integer
8007 * create_frames(coordinates = nil) -> Integer
8009 * Same as molecule.insert_frames(nil, coordinates).
8012 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8015 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8017 return s_Molecule_InsertFrames(3, vals, self);
8022 * remove_frames(IntGroup, wantCoordinates = false)
8024 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8025 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8026 * removed frames is returned if operation is successful.
8029 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8036 Data_Get_Struct(self, Molecule, mol);
8037 rb_scan_args(argc, argv, "11", &val, &flag);
8038 ig = IntGroupFromValue(val);
8039 count = IntGroupGetCount(ig);
8041 /* Create return value before removing frames */
8046 retval = rb_ary_new2(count);
8047 for (i = 0; i < count; i++) {
8048 n = IntGroupGetNthPoint(ig, i);
8049 coords = rb_ary_new2(mol->natoms);
8050 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8051 if (n < ap->nframes && n != mol->cframe)
8054 rb_ary_push(coords, ValueFromVector(&v));
8056 rb_ary_push(retval, coords);
8058 } else retval = Qtrue;
8059 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8066 * each_frame {|n| ...}
8068 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8069 * the frame number. After completion, the original frame number is restored.
8072 s_Molecule_EachFrame(VALUE self)
8074 int i, cframe, nframes;
8076 Data_Get_Struct(self, Molecule, mol);
8077 cframe = mol->cframe;
8078 nframes = MoleculeGetNumberOfFrames(mol);
8080 for (i = 0; i < nframes; i++) {
8081 MoleculeSelectFrame(mol, i, 1);
8082 rb_yield(INT2NUM(i));
8084 MoleculeSelectFrame(mol, cframe, 1);
8091 * get_coord_from_frame(index, group = nil)
8093 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8094 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8095 * copied; now they are always copied)
8098 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8101 VALUE ival, gval, cval;
8102 Int index, i, j, n, nn;
8104 IntGroupIterator iter;
8107 Data_Get_Struct(self, Molecule, mol);
8108 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8110 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8111 index = NUM2INT(rb_Integer(ival));
8112 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8114 rb_raise(rb_eMolbyError, "No frame is present");
8116 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8119 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8121 ig = s_Molecule_AtomGroupFromValue(self, gval);
8123 n = IntGroupGetCount(ig);
8125 vp = (Vector *)calloc(sizeof(Vector), n);
8126 IntGroupIteratorInit(ig, &iter);
8129 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8130 ap = ATOM_AT_INDEX(mol->atoms, i);
8131 if (index < ap->nframes) {
8132 vp[j] = ap->frames[index];
8140 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8142 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8143 vp = mol->frame_cells + index * 4;
8144 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8146 IntGroupIteratorRelease(&iter);
8148 /* Copy the extra properties */
8149 IntGroupRelease(ig);
8150 for (i = 0; i < mol->nmolprops; i++) {
8151 Double *dp = (Double *)malloc(sizeof(Double));
8153 IntGroupAdd(ig, mol->cframe, 1);
8154 *dp = mol->molprops[i].propvals[index];
8155 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8157 IntGroupRelease(ig);
8165 * reorder_frames(old_indices)
8167 * Reorder the frames. The argument is an array of integers that specify the 'old'
8168 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8169 * same as the old frames 2/0/1, respectively.
8170 * The argument must have the same number of integers as the number of frames.
8173 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8176 Int *ip, *ip2, i, n, nframes;
8177 Data_Get_Struct(self, Molecule, mol);
8178 aval = rb_ary_to_ary(aval);
8179 nframes = MoleculeGetNumberOfFrames(mol);
8180 if (RARRAY_LEN(aval) != nframes)
8181 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8182 ip2 = (Int *)calloc(sizeof(Int), nframes);
8183 ip = (Int *)calloc(sizeof(Int), nframes);
8184 for (i = 0; i < nframes; i++) {
8185 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8186 if (n < 0 || n >= nframes || ip2[n] != 0) {
8189 if (n < 0 || n >= nframes)
8190 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8192 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8198 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8203 #pragma mark ------ Fragments ------
8207 * fragment(n1, *exatoms) -> IntGroup
8208 * fragment(group, *exatoms) -> IntGroup
8210 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8211 * those atoms will not be counted during the search.
8214 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8217 IntGroup *baseg, *ig, *exatoms;
8219 volatile VALUE nval, exval;
8220 Data_Get_Struct(self, Molecule, mol);
8221 rb_scan_args(argc, argv, "1*", &nval, &exval);
8222 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8224 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8226 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8228 if (RARRAY_LEN(exval) == 0) {
8231 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8232 Data_Get_Struct(exval, IntGroup, exatoms);
8234 if (baseg == NULL) {
8235 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8237 IntGroupIterator iter;
8238 IntGroupIteratorInit(baseg, &iter);
8239 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8242 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8244 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8246 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8248 IntGroupAddIntGroup(ig, subg);
8249 IntGroupRelease(subg);
8254 IntGroupIteratorRelease(&iter);
8255 IntGroupRelease(baseg);
8258 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8259 nval = ValueFromIntGroup(ig);
8260 IntGroupRelease(ig);
8266 * fragments(exclude = nil)
8268 * Returns the fragments as an array of IntGroups.
8269 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8270 * in defining the fragment.
8273 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8276 IntGroup *ag, *fg, *eg;
8277 VALUE gval, exval, retval;
8278 Data_Get_Struct(self, Molecule, mol);
8281 if (mol->natoms == 0)
8282 return rb_ary_new();
8283 rb_scan_args(argc, argv, "01", &exval);
8287 eg = IntGroupFromValue(exval);
8288 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8290 IntGroupRemoveIntGroup(ag, eg);
8291 retval = rb_ary_new();
8292 while (IntGroupGetCount(ag) > 0) {
8293 int n = IntGroupGetNthPoint(ag, 0);
8294 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8296 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8297 gval = ValueFromIntGroup(fg);
8298 rb_ary_push(retval, gval);
8299 IntGroupRemoveIntGroup(ag, fg);
8300 IntGroupRelease(fg);
8302 IntGroupRelease(ag);
8304 IntGroupRelease(eg);
8310 * each_fragment(exclude = nil) {|group| ...}
8312 * Execute the block, with the IntGroup object for each fragment as the argument.
8313 * Atoms or bonds should not be added or removed during the execution of the block.
8314 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8315 * in defining the fragment.
8318 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8321 IntGroup *ag, *fg, *eg;
8323 Data_Get_Struct(self, Molecule, mol);
8324 if (mol == NULL || mol->natoms == 0)
8326 rb_scan_args(argc, argv, "01", &exval);
8330 eg = IntGroupFromValue(exval);
8331 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8333 IntGroupRemoveIntGroup(ag, eg);
8334 while (IntGroupGetCount(ag) > 0) {
8335 int n = IntGroupGetNthPoint(ag, 0);
8336 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8338 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8339 gval = ValueFromIntGroup(fg);
8341 IntGroupRemoveIntGroup(ag, fg);
8342 IntGroupRelease(fg);
8344 IntGroupRelease(ag);
8346 IntGroupRelease(eg);
8352 * detachable?(group) -> [n1, n2]
8354 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8355 * of the molecule via only one bond. If it is, then the indices of the atoms
8356 * belonging to the bond is returned, the first element being the atom included
8357 * in the fragment. Otherwise, Qnil is returned.
8360 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8366 Data_Get_Struct(self, Molecule, mol);
8367 ig = s_Molecule_AtomGroupFromValue(self, gval);
8368 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8369 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8370 } else retval = Qnil;
8371 IntGroupRelease(ig);
8377 * bonds_on_border(group = selection) -> Array of Array of two Integers
8379 * Returns an array of bonds that connect an atom in the group and an atom out
8380 * of the group. The first atom in the bond always belongs to the group. If no
8381 * such bonds are present, an empty array is returned.
8384 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8389 Data_Get_Struct(self, Molecule, mol);
8390 rb_scan_args(argc, argv, "01", &gval);
8392 ig = MoleculeGetSelection(mol);
8396 ig = s_Molecule_AtomGroupFromValue(self, gval);
8398 retval = rb_ary_new();
8401 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8403 IntGroupIterator iter;
8405 IntGroupIteratorInit(bg, &iter);
8406 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8407 /* The atoms at the border */
8409 n1 = mol->bonds[i * 2];
8410 n2 = mol->bonds[i * 2 + 1];
8411 if (IntGroupLookupPoint(ig, n1) < 0) {
8415 if (IntGroupLookupPoint(ig, n1) < 0)
8416 continue; /* Actually this is an internal error */
8418 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8420 IntGroupIteratorRelease(&iter);
8422 IntGroupRelease(bg);
8423 IntGroupRelease(ig);
8427 /* Calculate the transform that moves the current coordinates to the reference
8428 coordinates with least displacements. */
8430 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8438 Double eigen_val[3];
8439 Vector eigen_vec[3];
8441 IntGroupIterator iter;
8443 natoms = mol->natoms;
8445 IntGroupIteratorInit(ig, &iter);
8447 /* Calculate the weighted center */
8451 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8452 ap1 = ATOM_AT_INDEX(ap, in);
8453 w1 = (weights != NULL ? weights[i] : ap1->weight);
8454 VecScaleInc(org1, ap1->r, w1);
8455 VecScaleInc(org2, ref[i], w1);
8459 VecScaleSelf(org1, w);
8460 VecScaleSelf(org2, w);
8462 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8463 /* Matrix to diagonalize = R * tR */
8464 memset(r, 0, sizeof(Mat33));
8465 memset(q, 0, sizeof(Mat33));
8466 memset(u, 0, sizeof(Mat33));
8468 IntGroupIteratorReset(&iter);
8469 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8471 ap1 = ATOM_AT_INDEX(ap, in);
8472 w1 = (weights != NULL ? weights[i] : ap1->weight);
8474 VecSub(v1, ap1->r, org1);
8475 VecSub(v2, ref[i], org2);
8476 r[0] += w1 * v1.x * v2.x;
8477 r[1] += w1 * v1.y * v2.x;
8478 r[2] += w1 * v1.z * v2.x;
8479 r[3] += w1 * v1.x * v2.y;
8480 r[4] += w1 * v1.y * v2.y;
8481 r[5] += w1 * v1.z * v2.y;
8482 r[6] += w1 * v1.x * v2.z;
8483 r[7] += w1 * v1.y * v2.z;
8484 r[8] += w1 * v1.z * v2.z;
8487 for (i = 0; i < 9; i++)
8489 for (i = 0; i < 3; i++) {
8490 for (j = 0; j < 3; j++) {
8491 for (k = 0; k < 3; k++) {
8492 q[i+j*3] += r[i+k*3] * r[j+k*3];
8497 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8498 IntGroupIteratorRelease(&iter);
8499 return -1.0; /* Cannot determine the eigenvector */
8502 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8503 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8504 MatrixTranspose(r, r);
8505 for (i = 0; i < 3; i++) {
8506 MatrixVec(&s[i], r, &eigen_vec[i]);
8507 w1 = 1.0 / sqrt(eigen_val[i]);
8508 VecScaleSelf(s[i], w1);
8510 for (k = 0; k < 3; k++) {
8511 u[0] += s[k].x * eigen_vec[k].x;
8512 u[1] += s[k].y * eigen_vec[k].x;
8513 u[2] += s[k].z * eigen_vec[k].x;
8514 u[3] += s[k].x * eigen_vec[k].y;
8515 u[4] += s[k].y * eigen_vec[k].y;
8516 u[5] += s[k].z * eigen_vec[k].y;
8517 u[6] += s[k].x * eigen_vec[k].z;
8518 u[7] += s[k].y * eigen_vec[k].z;
8519 u[8] += s[k].z * eigen_vec[k].z;
8522 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8523 MatrixVec(&org1, u, &org1);
8525 for (i = 0; i < 9; i++)
8531 /* Calculate rmsd */
8532 IntGroupIteratorReset(&iter);
8534 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8536 ap1 = ATOM_AT_INDEX(ap, in);
8537 TransformVec(&tv, trans, &ap1->r);
8539 w += VecLength2(tv);
8542 IntGroupIteratorRelease(&iter);
8548 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8550 * Calculate the transform to fit the given group to the set of reference coordinates.
8551 * The reference coordinates ref is given as either a frame number, an array of
8552 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8553 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8554 * Return values are the transform (that converts the present coordinates to the
8555 * target coordinates) and root mean square deviation (without weight).
8558 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8562 VALUE gval, rval, wval;
8564 IntGroupIterator iter;
8565 int nn, errno, i, j, in, status;
8567 Double *weights, dval[3];
8570 Data_Get_Struct(self, Molecule, mol);
8571 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8573 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8575 ig = IntGroupFromValue(gval);
8576 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8577 IntGroupRelease(ig);
8578 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8580 ref = (Vector *)calloc(sizeof(Vector), nn);
8581 weights = (Double *)calloc(sizeof(Double), nn);
8582 IntGroupIteratorInit(ig, &iter);
8583 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8584 int fn = NUM2INT(rb_Integer(rval));
8585 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8590 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8591 ap = ATOM_AT_INDEX(mol->atoms, in);
8592 if (fn < ap->nframes)
8593 ref[i] = ap->frames[fn];
8594 else ref[i] = ap->r;
8596 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8597 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8598 if (m->row * m->column < nn * 3) {
8602 for (i = 0; i < nn; i++) {
8603 ref[i].x = m->data[i * 3];
8604 ref[i].y = m->data[i * 3 + 1];
8605 ref[i].z = m->data[i * 3 + 2];
8609 rval = rb_protect(rb_ary_to_ary, rval, &status);
8614 if (RARRAY_LEN(rval) < nn) {
8618 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8619 /* Array of 3*nn numbers */
8620 if (RARRAY_LEN(rval) < nn * 3) {
8624 for (i = 0; i < nn; i++) {
8625 for (j = 0; j < 3; j++) {
8626 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8631 dval[j] = NUM2DBL(aval);
8638 /* Array of nn Vector3Ds or Arrays */
8639 for (i = 0; i < nn; i++) {
8640 aval = (RARRAY_PTR(rval))[i];
8641 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8642 VectorFromValue(aval, &ref[i]);
8644 aval = rb_protect(rb_ary_to_ary, aval, &status);
8649 if (RARRAY_LEN(aval) < 3) {
8654 for (j = 0; j < 3; j++) {
8655 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8660 dval[j] = NUM2DBL(aaval);
8670 /* Use atomic weights */
8671 IntGroupIteratorReset(&iter);
8672 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8673 ap = ATOM_AT_INDEX(mol->atoms, in);
8674 weights[i] = ap->weight;
8677 wval = rb_protect(rb_ary_to_ary, wval, &status);
8682 if (RARRAY_LEN(wval) < nn) {
8686 for (i = 0; i < nn; i++) {
8687 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8692 weights[i] = NUM2DBL(wwval);
8695 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8702 IntGroupIteratorRelease(&iter);
8706 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8707 } else if (errno == 1) {
8708 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8709 } else if (errno == 2) {
8710 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8711 } else if (errno == 3) {
8712 rb_jump_tag(status);
8713 } else if (errno == 4) {
8714 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8715 } else if (errno == 5) {
8716 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8717 } else if (errno == 6) {
8718 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8720 return Qnil; /* Not reached */
8723 #pragma mark ------ Screen Display ------
8729 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8732 s_Molecule_Display(VALUE self)
8735 Data_Get_Struct(self, Molecule, mol);
8736 if (mol->mview != NULL)
8737 MainViewCallback_display(mol->mview);
8745 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8748 s_Molecule_MakeFront(VALUE self)
8751 Data_Get_Struct(self, Molecule, mol);
8752 if (mol->mview != NULL)
8753 MainViewCallback_makeFront(mol->mview);
8759 * update_enabled? -> bool
8761 * Returns true if screen update is enabled; otherwise no.
8764 s_Molecule_UpdateEnabled(VALUE self)
8767 Data_Get_Struct(self, Molecule, mol);
8768 if (mol->mview != NULL && !mol->mview->freezeScreen)
8775 * update_enabled = bool
8777 * Enable or disable screen update. This is effective for automatic update on modification.
8778 * Explicit call to molecule.display() always updates the screen.
8781 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8784 Data_Get_Struct(self, Molecule, mol);
8785 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8786 if (mol->mview != NULL)
8787 mol->mview->freezeScreen = (val == Qfalse);
8795 * show_unitcell(bool)
8796 * show_unitcell = bool
8798 * Set the flag whether to show the unit cell. If no argument is given, the
8799 * current flag is returned.
8802 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8805 Data_Get_Struct(self, Molecule, mol);
8806 if (mol->mview == NULL)
8809 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8810 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8812 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8818 * show_hydrogens(bool)
8819 * show_hydrogens = bool
8821 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8822 * current flag is returned.
8825 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8828 Data_Get_Struct(self, Molecule, mol);
8829 if (mol->mview == NULL)
8832 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8833 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8835 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8841 * show_dummy_atoms(bool)
8842 * show_dummy_atoms = bool
8844 * Set the flag whether to show the dummy atoms. If no argument is given, the
8845 * current flag is returned.
8848 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8851 Data_Get_Struct(self, Molecule, mol);
8852 if (mol->mview == NULL)
8855 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8856 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8858 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8864 * show_expanded(bool)
8865 * show_expanded = bool
8867 * Set the flag whether to show the expanded atoms. If no argument is given, the
8868 * current flag is returned.
8871 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8874 Data_Get_Struct(self, Molecule, mol);
8875 if (mol->mview == NULL)
8878 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8879 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8881 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8887 * show_ellipsoids(bool)
8888 * show_ellipsoids = bool
8890 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8891 * current flag is returned.
8894 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8897 Data_Get_Struct(self, Molecule, mol);
8898 if (mol->mview == NULL)
8901 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8902 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8904 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8909 * is_atom_visible(index) -> Boolean
8911 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8912 * as well as the molecule attributes (showHydrogens, etc.)
8915 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8920 Data_Get_Struct(self, Molecule, mol);
8921 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8922 if (idx < 0 || idx >= mol->natoms)
8924 ap = ATOM_AT_INDEX(mol->atoms, idx);
8925 if (mol->mview != NULL) {
8926 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8928 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8930 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8933 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8938 * hidden_atoms -> IntGroup
8940 * Returns the currently hidden atoms.
8943 s_Molecule_HiddenAtoms(VALUE self)
8945 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8946 return Qnil; /* Not reached */
8951 * set_hidden_atoms(IntGroup)
8952 * self.hidden_atoms = IntGroup
8954 * Hide the specified atoms. This operation is _not_ undoable.
8957 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8959 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8960 return Qnil; /* Not reached */
8965 * show_graphite -> Integer
8966 * show_graphite = Integer
8967 * show_graphite = boolean
8969 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
8970 * number of rings to display for each direction.
8971 * If the argument is boolean, only the show/hide flag is set.
8974 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8977 Data_Get_Struct(self, Molecule, mol);
8978 if (mol->mview == NULL)
8981 if (argv[0] == Qnil || argv[0] == Qfalse)
8982 mol->mview->showGraphiteFlag = 0;
8983 else if (argv[0] == Qtrue)
8984 mol->mview->showGraphiteFlag = 1;
8986 int n = NUM2INT(rb_Integer(argv[0]));
8988 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
8989 mol->mview->showGraphite = n;
8991 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8993 return INT2NUM(mol->mview->showGraphite);
8998 * show_graphite? -> boolean
9000 * Return whether the graphite is set visible or not.
9003 s_Molecule_ShowGraphiteFlag(VALUE self)
9006 Data_Get_Struct(self, Molecule, mol);
9007 if (mol->mview == NULL)
9009 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9014 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9015 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9016 * show_periodic_image = boolean
9018 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9019 * set but no visual effects are observed.
9020 * If the argument is boolean, only the show/hide flag is modified.
9023 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9029 Data_Get_Struct(self, Molecule, mol);
9030 if (mol->mview == NULL)
9032 rb_scan_args(argc, argv, "01", &val);
9034 /* Change current settings */
9035 if (val == Qnil || val == Qfalse)
9036 mol->mview->showPeriodicImageFlag = 0;
9037 else if (val == Qtrue)
9038 mol->mview->showPeriodicImageFlag = 1;
9040 val = rb_ary_to_ary(val);
9041 for (i = 0; i < 6; i++) {
9042 if (i < RARRAY_LEN(val))
9043 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9045 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9046 rb_raise(rb_eMolbyError, "bad arguments");
9047 for (i = 0; i < 6; i++)
9048 mol->mview->showPeriodicImage[i] = ival[i];
9050 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9053 for (i = 0; i < 6; i++)
9054 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9060 * show_periodic_image? -> boolean
9062 * Return whether the periodic images are set to visible or not. This flag is
9063 * independent from the show_periodic_image settings.
9066 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9069 Data_Get_Struct(self, Molecule, mol);
9070 if (mol->mview == NULL)
9072 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9077 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9078 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9079 * show_rotation_center = boolean
9081 * Set to show the rotation center of the screen.
9082 * If the argument is boolean, only the show/hide flag is modified.
9085 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9088 Data_Get_Struct(self, Molecule, mol);
9089 if (mol->mview == NULL)
9092 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9093 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9095 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9104 * Set the flag whether to draw the model in line mode. If no argument is given, the
9105 * current flag is returned.
9108 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9111 Data_Get_Struct(self, Molecule, mol);
9112 if (mol->mview == NULL)
9115 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9116 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9118 return (mol->mview->lineMode ? Qtrue : Qfalse);
9123 * atom_radius = float
9126 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9128 * If no argument is given, the current value is returned.
9131 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9134 Data_Get_Struct(self, Molecule, mol);
9135 if (mol->mview == NULL)
9138 double rad = NUM2DBL(rb_Float(argv[0]));
9140 mol->mview->atomRadius = rad;
9141 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9145 return rb_float_new(mol->mview->atomRadius);
9150 * bond_radius = float
9153 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9154 * If no argument is given, the current value is returned.
9157 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9160 Data_Get_Struct(self, Molecule, mol);
9161 if (mol->mview == NULL)
9164 double rad = NUM2DBL(rb_Float(argv[0]));
9166 mol->mview->bondRadius = rad;
9167 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9171 return rb_float_new(mol->mview->bondRadius);
9176 * atom_resolution = integer
9179 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9180 * (Default = 12; minimum = 6)
9181 * If no argument is given, the current value is returned.
9184 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9187 Data_Get_Struct(self, Molecule, mol);
9188 if (mol->mview == NULL)
9191 int res = NUM2INT(rb_Integer(argv[0]));
9193 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9194 mol->mview->atomResolution = res;
9195 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9196 return INT2NUM(res);
9198 return INT2NUM(mol->mview->atomResolution);
9203 * bond_resolution = integer
9206 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9207 * (Default = 8; minimum = 4)
9208 * If no argument is given, the current value is returned.
9211 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9214 Data_Get_Struct(self, Molecule, mol);
9215 if (mol->mview == NULL)
9218 int res = NUM2INT(rb_Integer(argv[0]));
9220 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9221 mol->mview->bondResolution = res;
9222 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9223 return INT2NUM(res);
9225 return INT2NUM(mol->mview->bondResolution);
9232 * Resize the model drawing to fit in the window.
9235 s_Molecule_ResizeToFit(VALUE self)
9238 Data_Get_Struct(self, Molecule, mol);
9239 if (mol->mview != NULL)
9240 MainView_resizeToFit(mol->mview);
9246 * get_view_rotation -> [[ax, ay, az], angle]
9248 * Get the current rotation for the view. Angle is in degree, not radian.
9251 s_Molecule_GetViewRotation(VALUE self)
9256 Data_Get_Struct(self, Molecule, mol);
9257 if (mol->mview == NULL)
9259 TrackballGetRotate(mol->mview->track, f);
9260 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9264 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9269 * get_view_scale -> float
9271 * Get the current scale for the view.
9274 s_Molecule_GetViewScale(VALUE self)
9277 Data_Get_Struct(self, Molecule, mol);
9278 if (mol->mview == NULL)
9280 return rb_float_new(TrackballGetScale(mol->mview->track));
9285 * get_view_center -> Vector
9287 * Get the current center point of the view.
9290 s_Molecule_GetViewCenter(VALUE self)
9295 Data_Get_Struct(self, Molecule, mol);
9296 if (mol->mview == NULL)
9298 TrackballGetTranslate(mol->mview->track, f);
9299 v.x = -f[0] * mol->mview->dimension;
9300 v.y = -f[1] * mol->mview->dimension;
9301 v.z = -f[2] * mol->mview->dimension;
9302 return ValueFromVector(&v);
9307 * set_view_rotation([ax, ay, az], angle) -> self
9309 * Set the current rotation for the view. Angle is in degree, not radian.
9312 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9317 Data_Get_Struct(self, Molecule, mol);
9318 if (mol->mview == NULL)
9320 VectorFromValue(aval, &v);
9321 if (NormalizeVec(&v, &v))
9322 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9326 f[0] = -NUM2DBL(rb_Float(angval));
9327 TrackballSetRotate(mol->mview->track, f);
9328 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9334 * set_view_scale(scale) -> self
9336 * Set the current scale for the view.
9339 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9342 Data_Get_Struct(self, Molecule, mol);
9343 if (mol->mview == NULL)
9345 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9346 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9352 * set_view_center(vec) -> self
9354 * Set the current center point of the view.
9357 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9362 Data_Get_Struct(self, Molecule, mol);
9363 if (mol->mview == NULL)
9365 VectorFromValue(aval, &v);
9366 f[0] = -v.x / mol->mview->dimension;
9367 f[1] = -v.y / mol->mview->dimension;
9368 f[2] = -v.z / mol->mview->dimension;
9369 TrackballSetTranslate(mol->mview->track, f);
9370 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9376 * set_background_color(red, green, blue)
9378 * Set the background color of the model window.
9381 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9384 Data_Get_Struct(self, Molecule, mol);
9385 if (mol->mview != NULL) {
9386 VALUE rval, gval, bval;
9387 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9388 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9395 * export_graphic(fname, scale = 1.0, bg_color = -1)
9397 * Export the current graphic to a PNG or TIF file (determined by the extension).
9398 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9402 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9405 VALUE fval, sval, bval;
9409 Data_Get_Struct(self, Molecule, mol);
9410 if (mol->mview == NULL)
9411 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9412 rb_scan_args(argc, argv, "12", &fval, &sval, &bval);
9413 fname = FileStringValuePtr(fval);
9416 else scale = NUM2DBL(rb_Float(sval));
9419 else bg_color = NUM2INT(rb_Integer(bval));
9420 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color) == 0)
9425 #pragma mark ------ Graphics ------
9429 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9431 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9432 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9433 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9434 * points: an array of Vectors
9438 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9444 VALUE kval, cval, pval, fval, ival;
9445 Data_Get_Struct(self, Molecule, mol);
9446 if (mol->mview == NULL)
9447 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9448 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9449 idx = NUM2INT(rb_Integer(ival));
9451 idx = mol->mview->ngraphics;
9452 else if (idx < 0 || idx > mol->mview->ngraphics)
9453 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9454 memset(&g, 0, sizeof(g));
9456 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9457 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9459 kval = rb_obj_as_string(kval);
9460 p = StringValuePtr(kval);
9461 if (strcmp(p, "line") == 0)
9462 g.kind = kMainViewGraphicLine;
9463 else if (strcmp(p, "poly") == 0)
9464 g.kind = kMainViewGraphicPoly;
9465 else if (strcmp(p, "cylinder") == 0)
9466 g.kind = kMainViewGraphicCylinder;
9467 else if (strcmp(p, "cone") == 0)
9468 g.kind = kMainViewGraphicCone;
9469 else if (strcmp(p, "ellipsoid") == 0)
9470 g.kind = kMainViewGraphicEllipsoid;
9471 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9473 g.closed = (RTEST(fval) ? 1 : 0);
9474 cval = rb_ary_to_ary(cval);
9475 n = RARRAY_LEN(cval);
9476 if (n < 3 || n >= 5)
9477 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9480 for (i = 0; i < n; i++)
9481 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9482 pval = rb_ary_to_ary(pval);
9483 n = RARRAY_LEN(pval);
9484 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9486 rb_raise(rb_eArgError, "no control points are given");
9488 case kMainViewGraphicLine:
9490 rb_raise(rb_eArgError, "the line object must have at least two control points");
9492 case kMainViewGraphicPoly:
9494 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9496 case kMainViewGraphicCylinder:
9497 case kMainViewGraphicCone:
9499 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9502 case kMainViewGraphicEllipsoid:
9506 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9509 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9510 for (i = 0; i < n; i++) {
9512 VALUE rval = RARRAY_PTR(pval)[i];
9514 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9515 /* The float argument can also be given as a vector (for simplify undo registration) */
9516 VectorFromValue(rval, &v);
9518 v.x = NUM2DBL(rb_Float(rval));
9522 VectorFromValue(rval, &v);
9524 g.points[i * 3] = v.x;
9525 g.points[i * 3 + 1] = v.y;
9526 g.points[i * 3 + 2] = v.z;
9528 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9530 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9531 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9532 g.points[7] = g.points[11] = g.points[3];
9534 MainView_insertGraphic(mol->mview, idx, &g);
9539 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9540 MolActionCallback_registerUndo(mol, act);
9541 MolActionRelease(act);
9544 return INT2NUM(idx);
9549 * create_graphic(kind, color, points, fill = nil) -> integer
9551 * Create a new graphic object. The arguments are similar as insert_graphic.
9554 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9557 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9558 args[0] = INT2NUM(-1);
9559 return s_Molecule_InsertGraphic(argc + 1, args, self);
9564 * remove_graphic(index) -> integer
9566 * Remove a graphic object.
9569 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9573 Data_Get_Struct(self, Molecule, mol);
9574 if (mol->mview == NULL)
9575 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9576 i = NUM2INT(rb_Integer(ival));
9577 if (i < 0 || i >= mol->mview->ngraphics)
9578 rb_raise(rb_eArgError, "graphic index is out of range");
9580 /* Prepare data for undo */
9581 MainViewGraphic *gp;
9586 gp = mol->mview->graphics + i;
9587 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9588 for (n = 0; n < gp->npoints; n++) {
9589 vp[n].x = gp->points[n * 3];
9590 vp[n].y = gp->points[n * 3 + 1];
9591 vp[n].z = gp->points[n * 3 + 2];
9593 col[0] = gp->rgba[0];
9594 col[1] = gp->rgba[1];
9595 col[2] = gp->rgba[2];
9596 col[3] = gp->rgba[3];
9597 if (gp->visible == 0) {
9598 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9599 MolActionCallback_registerUndo(mol, act);
9600 MolActionRelease(act);
9602 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9603 MolActionCallback_registerUndo(mol, act);
9605 MolActionRelease(act);
9607 MainView_removeGraphic(mol->mview, i);
9613 * ngraphics -> integer
9615 * Get the number of graphic objects.
9618 s_Molecule_NGraphics(VALUE self)
9621 Data_Get_Struct(self, Molecule, mol);
9622 if (mol->mview == NULL)
9623 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9624 return INT2NUM(mol->mview->ngraphics);
9629 * get_graphic_point(graphic_index, point_index) -> value
9630 * get_graphic_points(graphic_index) -> values
9632 * Get the point_index-th control point of graphic_index-th graphic object.
9633 * Get an array of all control points with the given values.
9637 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9639 MainViewGraphic *gp;
9644 Data_Get_Struct(self, Molecule, mol);
9645 if (mol->mview == NULL)
9646 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9647 rb_scan_args(argc, argv, "11", &gval, &pval);
9648 index = NUM2INT(rb_Integer(gval));
9649 if (index < 0 || index >= mol->mview->ngraphics)
9650 rb_raise(rb_eArgError, "the graphic index is out of range");
9651 gp = mol->mview->graphics + index;
9653 pindex = NUM2INT(rb_Integer(pval));
9654 if (pindex < 0 || pindex >= gp->npoints)
9655 rb_raise(rb_eArgError, "the point index is out of range");
9656 v.x = gp->points[pindex * 3];
9657 v.y = gp->points[pindex * 3 + 1];
9658 v.z = gp->points[pindex * 3 + 2];
9659 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9660 return rb_float_new(v.x);
9662 return ValueFromVector(&v);
9665 pval = rb_ary_new();
9666 for (pindex = 0; pindex < gp->npoints; pindex++) {
9667 v.x = gp->points[pindex * 3];
9668 v.y = gp->points[pindex * 3 + 1];
9669 v.z = gp->points[pindex * 3 + 2];
9670 rb_ary_push(pval, ValueFromVector(&v));
9678 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9679 * set_graphic_points(graphic_index, new_values) -> new_values
9681 * Change the point_index-th control point of graphic_index-th graphic object.
9682 * Replace the control points with the given values.
9686 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9688 MainViewGraphic *gp;
9692 VALUE gval, pval, nval;
9694 Data_Get_Struct(self, Molecule, mol);
9695 if (mol->mview == NULL)
9696 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9697 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9698 index = NUM2INT(rb_Integer(gval));
9699 if (index < 0 || index >= mol->mview->ngraphics)
9700 rb_raise(rb_eArgError, "the graphic index is out of range");
9701 gp = mol->mview->graphics + index;
9703 pindex = NUM2INT(rb_Integer(pval));
9704 if (pindex < 0 || pindex >= gp->npoints)
9705 rb_raise(rb_eArgError, "the point index is out of range");
9706 v0.x = gp->points[pindex * 3];
9707 v0.y = gp->points[pindex * 3 + 1];
9708 v0.z = gp->points[pindex * 3 + 2];
9709 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9710 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9711 v.x = NUM2DBL(rb_Float(nval));
9713 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9714 v.x = NUM2DBL(rb_Float(nval));
9716 gp->points[7] = gp->points[11] = v.x;
9717 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9718 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9721 v.x = kInvalidFloat;
9723 } else VectorFromValue(nval, &v);
9725 gp->points[pindex * 3] = v.x;
9726 gp->points[pindex * 3 + 1] = v.y;
9727 gp->points[pindex * 3 + 2] = v.z;
9728 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9732 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9733 for (pindex = 0; pindex < gp->npoints; pindex++) {
9734 vp[pindex].x = gp->points[pindex * 3];
9735 vp[pindex].y = gp->points[pindex * 3 + 1];
9736 vp[pindex].z = gp->points[pindex * 3 + 2];
9738 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9740 pval = rb_ary_to_ary(pval);
9741 len = RARRAY_LEN(pval);
9742 if (gp->npoints < len) {
9743 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9745 } else if (gp->npoints > len) {
9748 case kMainViewGraphicLine: len2 = 2; break;
9749 case kMainViewGraphicPoly: len2 = 3; break;
9750 case kMainViewGraphicCylinder: len2 = 3; break;
9751 case kMainViewGraphicCone: len2 = 3; break;
9752 case kMainViewGraphicEllipsoid: len2 = 4; break;
9758 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9759 aval = RARRAY_PTR(pval)[pindex];
9760 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9761 v.x = NUM2DBL(rb_Float(aval));
9763 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9764 v.x = NUM2DBL(rb_Float(aval));
9766 gp->points[7] = gp->points[11] = v.x;
9767 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9769 } else VectorFromValue(aval, &v);
9770 gp->points[pindex * 3] = v.x;
9771 gp->points[pindex * 3 + 1] = v.y;
9772 gp->points[pindex * 3 + 2] = v.z;
9775 MolActionCallback_registerUndo(mol, act);
9776 MolActionRelease(act);
9777 MoleculeCallback_notifyModification(mol, 0);
9783 * get_graphic_color(graphic_index) -> value
9785 * Get the color of graphic_index-th graphic object
9788 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9790 MainViewGraphic *gp;
9793 Data_Get_Struct(self, Molecule, mol);
9794 if (mol->mview == NULL)
9795 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9796 index = NUM2INT(rb_Integer(gval));
9797 if (index < 0 || index >= mol->mview->ngraphics)
9798 rb_raise(rb_eArgError, "the graphic index is out of range");
9799 gp = mol->mview->graphics + index;
9800 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]));
9805 * set_graphic_color(graphic_index, new_value) -> new_value
9807 * Change the color of graphic_index-th graphic object
9811 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9813 MainViewGraphic *gp;
9818 Data_Get_Struct(self, Molecule, mol);
9819 if (mol->mview == NULL)
9820 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9821 index = NUM2INT(rb_Integer(gval));
9822 if (index < 0 || index >= mol->mview->ngraphics)
9823 rb_raise(rb_eArgError, "the graphic index is out of range");
9824 gp = mol->mview->graphics + index;
9825 for (i = 0; i < 4; i++)
9827 cval = rb_ary_to_ary(cval);
9828 n = RARRAY_LEN(cval);
9829 if (n != 3 && n != 4)
9830 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9832 for (i = 0; i < n; i++) {
9833 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9837 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9838 MolActionCallback_registerUndo(mol, act);
9839 MolActionRelease(act);
9840 MoleculeCallback_notifyModification(mol, 0);
9846 * show_graphic(graphic_index) -> self
9848 * Enable the visible flag of the graphic_index-th graphic object
9852 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9854 MainViewGraphic *gp;
9857 Data_Get_Struct(self, Molecule, mol);
9858 if (mol->mview == NULL)
9859 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9860 index = NUM2INT(rb_Integer(gval));
9861 if (index < 0 || index >= mol->mview->ngraphics)
9862 rb_raise(rb_eArgError, "the graphic index is out of range");
9863 gp = mol->mview->graphics + index;
9865 MoleculeCallback_notifyModification(mol, 0);
9871 * hide_graphic(graphic_index) -> self
9873 * Disable the visible flag of the graphic_index-th graphic object
9877 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9879 MainViewGraphic *gp;
9882 Data_Get_Struct(self, Molecule, mol);
9883 if (mol->mview == NULL)
9884 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9885 index = NUM2INT(rb_Integer(gval));
9886 if (index < 0 || index >= mol->mview->ngraphics)
9887 rb_raise(rb_eArgError, "the graphic index is out of range");
9888 gp = mol->mview->graphics + index;
9890 MoleculeCallback_notifyModification(mol, 0);
9898 * Show the string in the info text box.
9901 s_Molecule_ShowText(VALUE self, VALUE arg)
9904 Data_Get_Struct(self, Molecule, mol);
9905 if (mol->mview != NULL)
9906 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9910 #pragma mark ------ MD Support ------
9914 * md_arena -> MDArena
9916 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
9917 * this molecule, a new arena is created.
9920 s_Molecule_MDArena(VALUE self)
9924 Data_Get_Struct(self, Molecule, mol);
9925 if (mol->arena == NULL)
9927 retval = ValueFromMDArena(mol->arena);
9933 * set_parameter_attr(type, index, key, value, src) -> value
9935 * This method is used only internally.
9938 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9940 /* This method is called from MolAction to change a MM parameter attribute. */
9945 Data_Get_Struct(self, Molecule, mol);
9946 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9947 vval = s_ParameterRef_SetAttr(pval, kval, vval);
9949 /* This is the special part of this method; it allows modification of the src field. */
9950 /* (ParameterRef#set_attr sets 0 to the src field) */
9951 Data_Get_Struct(pval, ParameterRef, pref);
9952 up = ParameterRefGetPar(pref);
9953 up->bond.src = FIX2INT(sval);
9960 * parameter -> Parameter
9962 * Get the local parameter of this molecule. If not defined, returns nil.
9965 s_Molecule_Parameter(VALUE self)
9968 Data_Get_Struct(self, Molecule, mol);
9969 /* if (mol->par == NULL)
9971 return s_NewParameterValueFromValue(self);
9976 * start_step -> Integer
9978 * Returns the start step (defined by dcd format).
9981 s_Molecule_StartStep(VALUE self)
9984 Data_Get_Struct(self, Molecule, mol);
9985 return INT2NUM(mol->startStep);
9990 * start_step = Integer
9992 * Set the start step (defined by dcd format).
9995 s_Molecule_SetStartStep(VALUE self, VALUE val)
9998 Data_Get_Struct(self, Molecule, mol);
9999 mol->startStep = NUM2INT(rb_Integer(val));
10005 * steps_per_frame -> Integer
10007 * Returns the number of steps between frames (defined by dcd format).
10010 s_Molecule_StepsPerFrame(VALUE self)
10013 Data_Get_Struct(self, Molecule, mol);
10014 return INT2NUM(mol->stepsPerFrame);
10019 * steps_per_frame = Integer
10021 * Set the number of steps between frames (defined by dcd format).
10024 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10027 Data_Get_Struct(self, Molecule, mol);
10028 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10034 * ps_per_step -> Float
10036 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10039 s_Molecule_PsPerStep(VALUE self)
10042 Data_Get_Struct(self, Molecule, mol);
10043 return rb_float_new(mol->psPerStep);
10048 * ps_per_step = Float
10050 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10053 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10056 Data_Get_Struct(self, Molecule, mol);
10057 mol->psPerStep = NUM2DBL(rb_Float(val));
10062 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10064 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.");
10067 #pragma mark ------ MO Handling ------
10071 * selectedMO -> IntGroup
10073 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10074 * is not selected, returns nil. If the MO info table is selected but no MOs
10075 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10078 s_Molecule_SelectedMO(VALUE self)
10083 Data_Get_Struct(self, Molecule, mol);
10084 if (mol->mview == NULL)
10086 ig = MainView_selectedMO(mol->mview);
10089 IntGroupOffset(ig, 1);
10090 val = ValueFromIntGroup(ig);
10091 IntGroupRelease(ig);
10097 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10099 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10100 * If the molecule does not contain a basis set information, then returns nil.
10103 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10106 Vector o, dx, dy, dz;
10109 Int npoints = 80 * 80 * 80;
10110 Data_Get_Struct(self, Molecule, mol);
10111 if (mol->bset == NULL)
10113 rb_scan_args(argc, argv, "01", &nval);
10115 npoints = NUM2INT(rb_Integer(nval));
10116 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10118 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));
10122 s_Cubegen_callback(double progress, void *ref)
10124 MyAppCallback_setProgressValue(progress);
10125 if (MyAppCallback_checkInterrupt())
10132 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10133 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10135 * Calculate the molecular orbital with number mo and create a 'cube' file.
10136 * In the first form, the cube size is estimated from the atomic coordinates. In the
10137 * second form, the cube dimension is explicitly given.
10138 * Returns fname when successful, nil otherwise.
10139 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10140 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10141 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10144 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10146 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10148 Int mono, nx, ny, nz, npoints;
10149 Vector o, dx, dy, dz;
10152 Data_Get_Struct(self, Molecule, mol);
10153 if (mol->bset == NULL)
10154 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10155 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10157 /* Set up parameters */
10158 mono = NUM2INT(rb_Integer(mval));
10159 if (mono <= 0 || mono > mol->bset->ncomps)
10160 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
10162 if (mol->bset->rflag != 0)
10163 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10164 mono += mol->bset->ncomps;
10167 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10168 /* Automatic grid formation */
10170 npoints = NUM2INT(rb_Integer(oval));
10174 else if (npoints < 8)
10175 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10176 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10177 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10181 VectorFromValue(oval, &o);
10182 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10183 VectorFromValue(dxval, &dx);
10185 dx.x = NUM2DBL(rb_Float(dxval));
10188 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10189 VectorFromValue(dyval, &dy);
10191 dy.y = NUM2DBL(rb_Float(dyval));
10194 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10195 VectorFromValue(dzval, &dz);
10197 dz.z = NUM2DBL(rb_Float(dzval));
10200 nx = NUM2INT(rb_Integer(nxval));
10201 ny = NUM2INT(rb_Integer(nyval));
10202 nz = NUM2INT(rb_Integer(nzval));
10203 if (nx <= 0 || ny <= 0 || nz <= 0)
10204 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10205 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10206 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);
10210 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10213 else if (index < 0)
10214 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10216 /* Output to file */
10217 MoleculeCallback_displayName(mol, buf, sizeof buf);
10218 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10220 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10222 /* Discard the cube */
10223 MoleculeClearCubeAtIndex(mol, index);
10229 * create_surface(mo, attr = nil)
10231 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10232 * then it denotes the beta orbital.
10233 * If mo is nil, then the attributes of the current surface are modified.
10235 * :npoints : the approximate number of grid points
10236 * :expand : the scale factor to expand/shrink the display box size for each atom,
10237 * :thres : the threshold for the isovalue surface
10238 * If the molecule does not contain MO information, raises exception.
10241 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10244 Vector o, dx, dy, dz;
10245 Int nmo, nx, ny, nz, i;
10246 Int need_recalc = 0;
10247 VALUE nval, hval, aval;
10252 Data_Get_Struct(self, Molecule, mol);
10253 rb_scan_args(argc, argv, "11", &nval, &hval);
10254 if (mol->bset == NULL)
10255 rb_raise(rb_eMolbyError, "No MO information is given");
10256 if (nval == Qnil) {
10258 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10259 nmo = mol->bset->nmos + 1;
10261 nmo = NUM2INT(rb_Integer(nval));
10262 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10263 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);
10265 nmo = -nmo + mol->bset->ncomps;
10267 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10268 npoints = NUM2INT(rb_Integer(aval));
10270 } else if (mol->mcube != NULL) {
10271 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10272 } else npoints = 80 * 80 * 80;
10273 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10274 expand = NUM2DBL(rb_Float(aval));
10275 } else if (mol->mcube != NULL) {
10276 expand = mol->mcube->expand;
10277 } else expand = 1.0;
10278 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10279 thres = NUM2DBL(rb_Float(aval));
10280 } else if (mol->mcube != NULL) {
10281 thres = mol->mcube->thres;
10282 } else thres = 0.05;
10283 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10284 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10285 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10286 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10287 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10289 for (nx = 0; nx < 2; nx++) {
10290 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10291 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10292 aval = rb_ary_to_ary(aval);
10293 if (RARRAY_LEN(aval) < 3) {
10295 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10297 for (i = 0; i < 4; i++)
10298 d[i] = mol->mcube->c[nx].rgba[i];
10299 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10300 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10301 if (d[i] < 0.0 && d[i] > 1.0)
10304 for (i = 0; i < 4; i++)
10305 mol->mcube->c[nx].rgba[i] = d[i];
10308 if (mol->mcube->expand != expand)
10310 mol->mcube->thres = thres;
10311 mol->mcube->expand = expand;
10313 if (mol->mcube->idn < 0)
10314 return self; /* Only set attributes for now */
10316 nmo = mol->mcube->idn; /* Force recalculation */
10318 if (MoleculeUpdateMCube(mol, nmo) != 0)
10319 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10325 * set_surface_attr(attr = nil)
10327 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10330 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10335 return s_Molecule_CreateSurface(2, args, self);
10342 * Get the number of electrostatic potential info.
10345 s_Molecule_NElpots(VALUE self)
10348 Data_Get_Struct(self, Molecule, mol);
10349 return INT2NUM(mol->nelpots);
10356 * Get the electrostatic potential info at the given index. If present, then the
10357 * return value is [Vector, Float] (position and potential). If not present, then
10361 s_Molecule_Elpot(VALUE self, VALUE ival)
10365 Data_Get_Struct(self, Molecule, mol);
10366 idx = NUM2INT(rb_Integer(ival));
10367 if (idx < 0 || idx >= mol->nelpots)
10369 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10376 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10377 * cube and marching cube information are discarded. This operation is _not_ undoable!
10380 s_Molecule_ClearBasisSet(VALUE self)
10383 Data_Get_Struct(self, Molecule, mol);
10385 if (mol->bset != NULL) {
10386 BasisSetRelease(mol->bset);
10389 if (mol->mcube != NULL) {
10390 MoleculeDeallocateMCube(mol->mcube);
10399 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10401 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10402 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10406 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10409 int sym, nprims, a_idx, n;
10410 Data_Get_Struct(self, Molecule, mol);
10411 a_idx = NUM2INT(rb_Integer(aval));
10412 sym = NUM2INT(rb_Integer(symval));
10413 nprims = NUM2INT(rb_Integer(npval));
10414 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10416 rb_raise(rb_eMolbyError, "Molecule is emptry");
10418 rb_raise(rb_eMolbyError, "Low memory");
10420 rb_raise(rb_eMolbyError, "Unknown orbital type");
10422 rb_raise(rb_eMolbyError, "Unknown error");
10428 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10430 * To be used internally. Add a gaussian primitive coefficients.
10433 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10437 Double exponent, contraction, contraction_sp;
10438 Data_Get_Struct(self, Molecule, mol);
10439 exponent = NUM2DBL(rb_Float(expval));
10440 contraction = NUM2DBL(rb_Float(cval));
10441 contraction_sp = NUM2DBL(rb_Float(cspval));
10442 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10444 rb_raise(rb_eMolbyError, "Molecule is emptry");
10446 rb_raise(rb_eMolbyError, "Low memory");
10448 rb_raise(rb_eMolbyError, "Unknown error");
10454 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10456 * Get the Gaussian shell information for the given MO coefficient index.
10457 * The symmetry code is the same as in add_gaussian_orbital_shell.
10458 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10459 * is the number of MO component belonging to this shell.
10462 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10467 Data_Get_Struct(self, Molecule, mol);
10468 if (mol->bset == NULL)
10469 rb_raise(rb_eMolbyError, "No basis set information is defined");
10470 s_idx = NUM2INT(rb_Integer(sval));
10471 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10473 sp = mol->bset->shells + s_idx;
10476 case kGTOType_S: sym = 0; break;
10477 case kGTOType_SP: sym = -1; break;
10478 case kGTOType_P: sym = 1; break;
10479 case kGTOType_D: sym = 2; break;
10480 case kGTOType_D5: sym = -2; break;
10481 case kGTOType_F: sym = 3; break;
10482 case kGTOType_F7: sym = -3; break;
10483 case kGTOType_G: sym = 4; break;
10484 case kGTOType_G9: sym = -4; break;
10486 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10488 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10493 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10495 * Get the Gaussian primitive coefficients for the given MO component.
10498 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10504 VALUE retval, aval;
10505 Data_Get_Struct(self, Molecule, mol);
10506 if (mol->bset == NULL)
10507 rb_raise(rb_eMolbyError, "No basis set information is defined");
10508 s_idx = NUM2INT(rb_Integer(sval));
10509 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10511 sp = mol->bset->shells + s_idx;
10512 pp = mol->bset->priminfos + sp->p_idx;
10513 retval = rb_ary_new2(sp->nprim);
10514 for (i = 0; i < sp->nprim; i++) {
10515 if (sp->sym == kGTOType_SP) {
10516 /* With P contraction coefficient */
10517 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10519 /* Without P contraction coefficient */
10520 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10522 rb_ary_store(retval, i, aval);
10529 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10531 * Get the Gaussian shell information for the given MO coefficient index.
10534 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10537 Int n, c, atom_idx, shell_idx;
10539 Data_Get_Struct(self, Molecule, mol);
10540 if (mol->bset == NULL)
10541 rb_raise(rb_eMolbyError, "No basis set information is defined");
10542 c = NUM2INT(rb_Integer(cval));
10543 if (c < 0 || c >= mol->bset->ncomps)
10545 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10547 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10548 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10553 * clear_mo_coefficients
10555 * Clear the existing MO coefficients.
10558 s_Molecule_ClearMOCoefficients(VALUE self)
10561 Data_Get_Struct(self, Molecule, mol);
10562 if (mol->bset != NULL) {
10563 if (mol->bset->moenergies != NULL) {
10564 free(mol->bset->moenergies);
10565 mol->bset->moenergies = NULL;
10567 if (mol->bset->mo != NULL) {
10568 free(mol->bset->mo);
10569 mol->bset->mo = NULL;
10571 mol->bset->nmos = 0;
10578 * set_mo_coefficients(idx, energy, coefficients)
10580 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10581 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10582 * Energy is the MO energy, and coefficients is an array
10583 * of MO coefficients.
10586 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10589 Int idx, ncomps, i;
10592 Data_Get_Struct(self, Molecule, mol);
10593 idx = NUM2INT(rb_Integer(ival));
10594 energy = NUM2DBL(rb_Float(eval));
10595 aval = rb_ary_to_ary(aval);
10596 ncomps = RARRAY_LEN(aval);
10597 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10598 if (coeffs == NULL) {
10602 for (i = 0; i < ncomps; i++)
10603 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10604 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10607 rb_raise(rb_eMolbyError, "Molecule is emptry");
10609 rb_raise(rb_eMolbyError, "Low memory");
10611 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10613 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10615 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10617 rb_raise(rb_eMolbyError, "Unknown error");
10623 * get_mo_coefficients(idx)
10625 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10628 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10631 Int idx, ncomps, n;
10635 Data_Get_Struct(self, Molecule, mol);
10636 idx = NUM2INT(rb_Integer(ival));
10639 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10641 rb_raise(rb_eMolbyError, "Molecule is emptry");
10643 rb_raise(rb_eMolbyError, "No basis set information is present");
10645 return Qnil; /* Silently returns nil */
10646 retval = rb_ary_new2(ncomps);
10647 for (n = 0; n < ncomps; n++)
10648 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10655 * get_mo_energy(idx)
10657 * To be used internally. Get the MO energy for the given MO index (1-based).
10660 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10665 Data_Get_Struct(self, Molecule, mol);
10666 idx = NUM2INT(rb_Integer(ival));
10667 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10669 rb_raise(rb_eMolbyError, "Molecule is emptry");
10671 rb_raise(rb_eMolbyError, "No basis set information is present");
10674 return rb_float_new(energy);
10677 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10680 s_InitMOInfoKeys(void)
10682 if (sTypeSym == 0) {
10683 sTypeSym = ID2SYM(rb_intern("type"));
10684 sAlphaSym = ID2SYM(rb_intern("alpha"));
10685 sBetaSym = ID2SYM(rb_intern("beta"));
10686 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10687 sNshellsSym = ID2SYM(rb_intern("nshells"));
10693 * set_mo_info(hash)
10695 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10696 * :alpha=>integer, :beta=>integer
10699 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10703 Int rflag, na, nb, n;
10705 Data_Get_Struct(self, Molecule, mol);
10706 if (mol->bset != NULL) {
10707 rflag = mol->bset->rflag;
10708 na = mol->bset->ne_alpha;
10709 nb = mol->bset->ne_beta;
10715 if (hval != Qnil) {
10716 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10717 s = StringValuePtr(aval);
10718 if (strcasecmp(s, "RHF") == 0)
10720 else if (strcasecmp(s, "UHF") == 0)
10722 else if (strcasecmp(s, "ROHF") == 0)
10725 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10726 n = NUM2INT(rb_Integer(aval));
10730 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10731 n = NUM2INT(rb_Integer(aval));
10735 MoleculeSetMOInfo(mol, rflag, na, nb);
10744 * Get the MO info. The key is as described in set_mo_info.
10745 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10748 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10751 Data_Get_Struct(self, Molecule, mol);
10752 if (mol->bset == NULL)
10754 if (kval == sTypeSym) {
10755 switch (mol->bset->rflag) {
10756 case 0: return rb_str_new2("UHF");
10757 case 1: return rb_str_new2("RHF");
10758 case 2: return rb_str_new2("ROHF");
10759 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10761 } else if (kval == sAlphaSym) {
10762 return INT2NUM(mol->bset->ne_alpha);
10763 } else if (kval == sBetaSym) {
10764 return INT2NUM(mol->bset->ne_beta);
10765 } else if (kval == sNcompsSym) {
10766 return INT2NUM(mol->bset->ncomps);
10767 } else if (kval == sNshellsSym) {
10768 return INT2NUM(mol->bset->nshells);
10770 kval = rb_inspect(kval);
10771 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10772 return Qnil; /* Does not reach here */
10780 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10783 s_Molecule_MOType(VALUE self)
10785 return s_Molecule_GetMOInfo(self, sTypeSym);
10788 #pragma mark ------ Molecular Topology ------
10792 * search_equivalent_atoms(ig = nil)
10794 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10797 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10803 Data_Get_Struct(self, Molecule, mol);
10804 if (mol->natoms == 0)
10806 rb_scan_args(argc, argv, "01", &val);
10808 ig = IntGroupFromValue(val);
10810 result = MoleculeSearchEquivalentAtoms(mol, ig);
10811 if (result == NULL)
10812 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10814 IntGroupRelease(ig);
10815 val = rb_ary_new2(mol->natoms);
10816 for (i = 0; i < mol->natoms; i++)
10817 rb_ary_push(val, INT2NUM(result[i]));
10824 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10826 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10827 * Name is the name of the new pi anchor, and group is the atoms that define
10828 * the pi system. Type (a String) is an atom type for MM implementation.
10829 * Weights represent the relative significance of the component atoms; if omitted, then
10830 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10831 * The weight values will be normalized so that the sum of the weights is 1.0.
10832 * The weight values must be positive.
10833 * Index is the atom index where the created pi-anchor is inserted in the
10834 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10835 * having the largest index.
10836 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10837 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10840 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10845 Int i, n, idx, last_component;
10849 if (argc < 2 || argc >= 6)
10850 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10854 Data_Get_Struct(self, Molecule, mol);
10855 ig = IntGroupFromValue(gval);
10856 memset(&a, 0, sizeof(a));
10857 memset(&an, 0, sizeof(an));
10858 strncpy(a.aname, StringValuePtr(nval), 4);
10859 if (a.aname[0] == '_')
10860 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10861 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
10862 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10863 if (n >= mol->natoms) {
10864 AtomConnectResize(&an.connect, 0);
10865 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10867 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10868 last_component = n;
10870 if (an.connect.count == 0)
10871 rb_raise(rb_eMolbyError, "no atoms are specified");
10872 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10873 for (i = 0; i < an.connect.count; i++) {
10874 an.coeffs[i] = 1.0 / an.connect.count;
10876 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10878 if (argv[0] != Qnil)
10879 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10883 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10884 if (argv[0] != Qnil) {
10885 VALUE aval = rb_ary_to_ary(argv[0]);
10887 if (RARRAY_LEN(aval) != an.connect.count)
10888 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10889 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10890 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10892 rb_raise(rb_eMolbyError, "the weight value must be positive");
10896 for (i = 0; i < an.connect.count; i++)
10897 an.coeffs[i] /= sum;
10902 if (argc > 0 && argv[0] != Qnil) {
10904 idx = NUM2INT(rb_Integer(argv[0]));
10906 if (idx < 0 || idx > mol->natoms) {
10907 /* Immediately after the last specified atom */
10908 idx = last_component + 1;
10910 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10911 memmove(a.anchor, &an, sizeof(PiAnchor));
10912 /* Use residue information of the last specified atom */
10913 ap = ATOM_AT_INDEX(mol->atoms, last_component);
10914 a.resSeq = ap->resSeq;
10915 strncpy(a.resName, ap->resName, 4);
10916 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10918 MoleculeCalculatePiAnchorPosition(mol, idx);
10919 aref = AtomRefNew(mol, idx);
10920 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10923 #pragma mark ------ Molecular Properties ------
10927 * set_property(name, value[, index]) -> value
10928 * set_property(name, values, group) -> values
10930 * Set molecular property. A property is a floating-point number with a specified name,
10931 * and can be set for each frame separately. The name of the property is given as a String.
10932 * The value can be a single floating point number, which is set to the current frame.
10936 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
10939 VALUE nval, vval, ival;
10942 Int i, n, idx, fidx;
10944 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
10945 Data_Get_Struct(self, Molecule, mol);
10946 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
10947 idx = NUM2INT(rb_Integer(nval));
10948 if (idx < 0 || idx >= mol->nmolprops)
10949 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
10951 name = StringValuePtr(nval);
10952 idx = MoleculeLookUpProperty(mol, name);
10954 idx = MoleculeCreateProperty(mol, name);
10956 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
10959 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
10961 fidx = mol->cframe;
10963 fidx = NUM2INT(rb_Integer(ival));
10964 n = MoleculeGetNumberOfFrames(mol);
10965 if (fidx < 0 || fidx >= n)
10966 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
10968 ig = IntGroupNewWithPoints(fidx, 1, -1);
10969 dp = (Double *)malloc(sizeof(Double));
10970 *dp = NUM2DBL(rb_Float(vval));
10973 vval = rb_ary_to_ary(vval);
10974 ig = IntGroupFromValue(ival);
10975 n = IntGroupGetCount(ig);
10977 rb_raise(rb_eMolbyError, "No frames are specified");
10978 if (RARRAY_LEN(vval) < n)
10979 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
10980 dp = (Double *)calloc(sizeof(Double), n);
10981 for (i = 0; i < n; i++)
10982 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
10985 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
10987 IntGroupRelease(ig);
10993 * get_property(name[, index]) -> value
10994 * get_property(name, group) -> values
10996 * Get molecular property. In the first form, a property value for a single frame is returned.
10997 * (If index is omitted, then the value for the current frame is given)
10998 * In the second form, an array of property values for the given frames is returned.
10999 * If name is not one of known properties or a valid index integer, exception is raised.
11002 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11008 Int i, n, idx, fidx;
11010 rb_scan_args(argc, argv, "11", &nval, &ival);
11011 Data_Get_Struct(self, Molecule, mol);
11012 if (mol->nmolprops == 0)
11013 rb_raise(rb_eMolbyError, "The molecule has no properties");
11014 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11015 idx = NUM2INT(rb_Integer(nval));
11016 if (idx < 0 || idx >= mol->nmolprops)
11017 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11019 name = StringValuePtr(nval);
11020 idx = MoleculeLookUpProperty(mol, name);
11022 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11024 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11026 fidx = mol->cframe;
11028 fidx = NUM2INT(rb_Integer(ival));
11029 n = MoleculeGetNumberOfFrames(mol);
11030 if (fidx < 0 || fidx >= n)
11031 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11033 ig = IntGroupNewWithPoints(fidx, 1, -1);
11034 ival = INT2FIX(fidx);
11037 ig = IntGroupFromValue(ival);
11038 n = IntGroupGetCount(ig);
11040 return rb_ary_new();
11042 dp = (Double *)calloc(sizeof(Double), n);
11043 MoleculeGetProperty(mol, idx, ig, dp);
11044 if (FIXNUM_P(ival))
11045 ival = rb_float_new(dp[0]);
11047 ival = rb_ary_new();
11048 for (i = n - 1; i >= 0; i--) {
11049 nval = rb_float_new(dp[i]);
11050 rb_ary_store(ival, i, nval);
11054 IntGroupRelease(ig);
11060 * property_names -> Array
11062 * Get an array of property names.
11065 s_Molecule_PropertyNames(VALUE self)
11070 Data_Get_Struct(self, Molecule, mol);
11071 rval = rb_ary_new();
11072 for (i = mol->nmolprops - 1; i >= 0; i--) {
11073 nval = rb_str_new2(mol->molprops[i].propname);
11074 rb_ary_store(rval, i, nval);
11079 #pragma mark ------ Class methods ------
11083 * current -> Molecule
11085 * Get the currently "active" molecule.
11088 s_Molecule_Current(VALUE klass)
11090 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11095 * Molecule[] -> Molecule
11096 * Molecule[n] -> Molecule
11097 * Molecule[name] -> Molecule
11098 * Molecule[name, k] -> Molecule
11099 * Molecule[regex] -> Molecule
11100 * Molecule[regex, k] -> Molecule
11102 * Molecule[] is equivalent to Molecule.current.
11103 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11104 * Molecule[name] gives the first document (in the order of creation time) that has
11105 * the given name. If a second argument (k) is given, the k-th document that has the
11106 * given name is returned.
11107 * Molecule[regex] gives the first document (in the order of creation time) that
11108 * has a name matching the regular expression. If a second argument (k) is given,
11109 * the k-th document that has a name matching the re is returned.
11112 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11118 rb_scan_args(argc, argv, "02", &val, &kval);
11120 return s_Molecule_Current(klass);
11121 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11122 idx = NUM2INT(val);
11123 mol = MoleculeCallback_moleculeAtIndex(idx);
11124 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11125 char *p = StringValuePtr(val);
11126 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11127 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11128 MoleculeCallback_displayName(mol, buf, sizeof buf);
11129 if (strcmp(buf, p) == 0 && --k == 0)
11132 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11133 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11134 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11136 MoleculeCallback_displayName(mol, buf, sizeof buf);
11137 name = rb_str_new2(buf);
11138 if (rb_reg_match(val, name) != Qnil && --k == 0)
11141 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11145 else return ValueFromMolecule(mol);
11150 * list -> array of Molecules
11152 * Get the list of molecules associated to the documents, in the order of creation
11153 * time of the document. If no document is open, returns an empry array.
11156 s_Molecule_List(VALUE klass)
11162 ary = rb_ary_new();
11163 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11164 rb_ary_push(ary, ValueFromMolecule(mol));
11172 * ordered_list -> array of Molecules
11174 * Get the list of molecules associated to the documents, in the order of front-to-back
11175 * ordering of the associated window. If no document is open, returns an empry array.
11178 s_Molecule_OrderedList(VALUE klass)
11184 ary = rb_ary_new();
11185 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11186 rb_ary_push(ary, ValueFromMolecule(mol));
11192 #pragma mark ------ Call Subprocess ------
11194 /* The callback functions for call_subprocess_async */
11196 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11199 VALUE procval, retval, args[2];
11200 args[0] = ValueFromMolecule(mol);
11201 args[1] = INT2NUM(status);
11202 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11203 if (procval != Qnil) {
11204 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11205 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11212 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11215 VALUE procval, retval, args[2];
11216 args[0] = ValueFromMolecule(mol);
11217 args[1] = INT2NUM(tcount);
11218 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11219 if (procval != Qnil) {
11220 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11221 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11229 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11231 * Call subprocess asynchronically.
11232 * If end_callback is given, it will be called (with two arguments self and termination status)
11233 * when the subprocess terminated.
11234 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11235 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11236 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11237 * filename begins with ">>", then the message will be appended to the file.
11238 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11239 * If the argument is nil, then the message will be sent to the Ruby console.
11240 * Returns the process ID as an integer.
11243 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11245 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11249 FILE *fpout, *fperr;
11250 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11251 Data_Get_Struct(self, Molecule, mol);
11253 if (stdout_val == Qnil) {
11256 sout = StringValuePtr(stdout_val);
11257 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11260 if (strncmp(sout, ">>", 2) == 0) {
11262 fpout = fopen(sout, "a");
11266 fpout = fopen(sout, "w");
11269 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11272 if (stderr_val == Qnil) {
11275 serr = StringValuePtr(stderr_val);
11276 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11279 if (strncmp(serr, ">>", 2) == 0) {
11281 fpout = fopen(serr, "a");
11285 fperr = fopen(serr, "w");
11288 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11292 /* Register procs as instance variables */
11293 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11294 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11295 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11296 if (fpout != NULL && fpout != (FILE *)1)
11298 if (fperr != NULL && fperr != (FILE *)1)
11303 #pragma mark ====== Define Molby Classes ======
11310 /* Define module Molby */
11311 rb_mMolby = rb_define_module("Molby");
11313 /* Define Vector3D, Transform, IntGroup */
11316 /* Define MDArena */
11317 Init_MolbyMDTypes();
11319 /* class Molecule */
11320 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11322 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11323 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11324 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11325 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11326 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11328 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11329 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11330 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11331 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11332 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11333 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11334 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11335 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11336 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11337 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11338 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11339 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11340 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11341 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11342 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11343 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11344 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11345 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11346 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11347 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11348 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11350 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11351 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11352 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11353 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11354 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11356 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11357 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11358 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11359 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11360 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11361 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11362 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11363 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11364 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11365 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11366 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11367 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11368 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11369 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11370 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11372 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11373 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11374 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11375 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11376 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11378 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11379 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11380 rb_define_alias(rb_cMolecule, "+", "add");
11381 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11382 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11383 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11384 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11385 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11386 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11387 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11388 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11389 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11390 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11391 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11392 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11393 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11394 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11395 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11396 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11397 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11398 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11399 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11400 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11401 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11403 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11404 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11405 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11406 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11407 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11409 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11410 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11411 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11412 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11413 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11414 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11415 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11416 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11417 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11419 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11420 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11421 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11422 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11423 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11424 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11425 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11426 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11427 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11428 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11429 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11430 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11431 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11432 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11433 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11434 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11435 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11436 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11437 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11438 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11439 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11440 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11442 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11443 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11444 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11445 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11446 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11447 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11448 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11450 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11451 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11452 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11453 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11454 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11455 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11456 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11457 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11458 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11459 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11460 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11461 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11462 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11464 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11465 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11466 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11467 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11468 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11469 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11471 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11472 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11473 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11474 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11475 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11476 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11477 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11478 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11479 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11480 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11481 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11482 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11483 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11484 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11485 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11486 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11487 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11488 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11489 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11490 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11491 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11492 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11493 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11494 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11495 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11496 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11497 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11498 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11499 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11500 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11501 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11502 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11503 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11504 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11505 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11506 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11507 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11508 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11509 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11510 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11511 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11512 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11513 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11514 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11515 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11516 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11517 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11518 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11519 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11520 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11521 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11522 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11523 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11524 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11526 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11527 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11528 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11529 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11530 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11531 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11532 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11533 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11534 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11535 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11536 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11537 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11538 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11540 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11541 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11542 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11543 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11544 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11545 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11546 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11547 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11548 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11549 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11550 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11551 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11552 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11553 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11555 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11556 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11557 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11558 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11559 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11560 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11561 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11562 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11563 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11564 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11565 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11566 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11567 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11568 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11569 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11570 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11571 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11572 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11573 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11574 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11576 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11577 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11579 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11580 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11581 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11583 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11584 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11585 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11586 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11588 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11590 /* class MolEnumerable */
11591 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11592 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11593 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11594 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11595 rb_define_alias(rb_cMolEnumerable, "size", "length");
11596 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11597 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11599 /* class AtomRef */
11600 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11601 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11603 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11604 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11605 s_AtomAttrDefTable[i].id = rb_intern(buf);
11606 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11608 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11610 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11611 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11612 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11613 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11614 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11615 rb_global_variable(&s_SetAtomAttrString);
11616 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11617 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11619 /* class Parameter */
11620 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11621 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11622 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11623 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11624 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11625 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11626 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11627 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11628 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11629 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11630 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11631 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11632 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11633 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11634 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11635 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11636 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11637 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11638 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11639 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11640 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11641 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11642 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11643 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11644 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11645 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11646 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11647 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11648 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11649 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11650 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11651 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11652 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11653 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11654 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11655 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11656 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11657 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11658 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11659 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11660 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11661 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11662 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11663 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11664 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11665 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11666 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11667 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11668 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11669 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11670 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11671 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11672 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11673 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11675 /* class ParEnumerable */
11676 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11677 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11678 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11679 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11680 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11681 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11682 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11683 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11684 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11685 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11686 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11688 /* class ParameterRef */
11689 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11690 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11692 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11693 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11694 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11695 if (s_ParameterAttrDefTable[i].symref != NULL)
11696 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11697 if (s_ParameterAttrDefTable[i].setter != NULL) {
11699 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11702 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11703 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11704 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11705 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11706 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11707 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11708 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11709 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11711 /* class MolbyError */
11712 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11714 /* module Kernel */
11715 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11716 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11717 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11718 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11719 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11720 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11721 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11722 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11723 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11724 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11725 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11726 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11727 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11728 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11729 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11730 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11731 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11732 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11733 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11734 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11735 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11736 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11737 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11738 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11741 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11743 s_ID_equal = rb_intern("==");
11744 g_RubyID_call = rb_intern("call");
11746 s_InitMOInfoKeys();
11748 /* Symbols for graphics */
11749 s_LineSym = ID2SYM(rb_intern("line"));
11750 s_PolySym = ID2SYM(rb_intern("poly"));
11751 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11752 s_ConeSym = ID2SYM(rb_intern("cone"));
11753 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11756 #pragma mark ====== Interface with RubyDialog class ======
11759 RubyDialogCallback_parentModule(void)
11761 return (RubyValue)rb_mMolby;
11764 #pragma mark ====== External functions ======
11766 static VALUE s_ruby_top_self = Qfalse;
11767 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11768 static VALUE s_ruby_export_local_variables = Qfalse;
11771 s_evalRubyScriptOnMoleculeSub(VALUE val)
11773 void **ptr = (void **)val;
11774 Molecule *mol = (Molecule *)ptr[1];
11775 VALUE sval, fnval, lnval, retval;
11778 /* Clear the error information (store in the history array if necessary) */
11779 sval = rb_errinfo();
11780 if (sval != Qnil) {
11781 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11782 rb_set_errinfo(Qnil);
11785 if (s_ruby_top_self == Qfalse) {
11786 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11788 if (s_ruby_get_binding_for_molecule == Qfalse) {
11790 "lambda { |_mol_, _bind_| \n"
11791 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11792 " _proc_.call(_mol_) } ";
11793 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11794 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11796 if (s_ruby_export_local_variables == Qfalse) {
11798 "lambda { |_bind_| \n"
11799 " # find local variables newly defined in _bind_ \n"
11800 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11801 " _a_.each { |_vsym_| \n"
11802 " _vname_ = _vsym_.to_s \n"
11803 " _vval_ = _bind_.eval(_vname_) \n"
11804 " # Define local variable \n"
11805 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11806 " # Then set value \n"
11807 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11810 s_ruby_export_local_variables = rb_eval_string(s2);
11811 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11813 if (ptr[2] == NULL) {
11815 /* String literal: we need to specify string encoding */
11816 #if defined(__WXMSW__)
11817 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11819 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11821 sval = rb_str_new2(scr);
11823 fnval = rb_str_new2("(eval)");
11824 lnval = INT2FIX(0);
11826 sval = rb_str_new2((char *)ptr[0]);
11827 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11828 lnval = INT2FIX(1);
11830 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11832 VALUE mval = ValueFromMolecule(mol);
11833 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11835 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11837 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11843 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11847 VALUE save_interrupt_flag;
11848 /* char *save_ruby_sourcefile;
11849 int save_ruby_sourceline; */
11850 if (gMolbyIsCheckingInterrupt) {
11851 MolActionAlertRubyIsRunning();
11853 return (RubyValue)Qnil;
11856 args[0] = (void *)script;
11857 args[1] = (void *)mol;
11858 args[2] = (void *)fname;
11859 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
11860 /* save_ruby_sourcefile = ruby_sourcefile;
11861 save_ruby_sourceline = ruby_sourceline; */
11862 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
11863 if (*status != 0) {
11864 /* Is this 'exit' exception? */
11865 VALUE last_exception = rb_gv_get("$!");
11866 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
11867 /* Capture exit and return the status value */
11868 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
11870 rb_set_errinfo(Qnil);
11873 s_SetInterruptFlag(Qnil, save_interrupt_flag);
11874 /* ruby_sourcefile = save_ruby_sourcefile;
11875 ruby_sourceline = save_ruby_sourceline; */
11881 Ruby_showValue(RubyValue value, char **outValueString)
11883 VALUE val = (VALUE)value;
11884 if (gMolbyIsCheckingInterrupt) {
11885 MolActionAlertRubyIsRunning();
11892 val = rb_protect(rb_inspect, val, &status);
11896 str = StringValuePtr(val);
11897 if (outValueString != NULL)
11898 *outValueString = strdup(str);
11899 MyAppCallback_showScriptMessage("%s", str);
11901 if (outValueString != NULL)
11902 *outValueString = NULL;
11908 Ruby_showError(int status)
11910 static const int tag_raise = 6;
11911 char *msg = NULL, *msg2;
11912 VALUE val, backtrace;
11913 int interrupted = 0;
11914 if (status == tag_raise) {
11915 VALUE errinfo = rb_errinfo();
11916 VALUE eclass = CLASS_OF(errinfo);
11917 if (eclass == rb_eInterrupt) {
11923 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
11925 val = rb_eval_string_protect("$!.to_s", &status);
11927 msg = RSTRING_PTR(val);
11928 else msg = "(message not available)";
11930 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
11931 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
11937 Molby_getDescription(void)
11939 extern const char *gVersionString, *gCopyrightString;
11940 extern int gRevisionNumber;
11941 extern char *gLastBuildString;
11943 char *revisionString;
11944 if (gRevisionNumber > 0) {
11945 asprintf(&revisionString, ", revision %d", gRevisionNumber);
11946 } else revisionString = "";
11948 "Molby %s%s\n%s\nLast compile: %s\n"
11949 #if !defined(__CMDMAC__)
11955 "ruby %s, http://www.ruby-lang.org/\n"
11957 "FFTW 3.3.2, http://www.fftw.org/\n"
11958 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
11959 " and Massachusetts Institute of Technology",
11960 gVersionString, revisionString, gCopyrightString, gLastBuildString,
11961 #if !defined(__CMDMAC__)
11962 MyAppCallback_getGUIDescriptionString(),
11964 gRubyVersion, gRubyCopyright);
11965 if (revisionString[0] != 0)
11966 free(revisionString);
11971 Molby_startup(const char *script, const char *dir)
11976 char *respath, *p, *wbuf;
11978 /* Get version/copyright string from Ruby interpreter */
11980 gRubyVersion = strdup(ruby_version);
11981 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
11982 #if defined(__CMDMAC__)
11985 " ", /* Indent for displaying in About dialog */
11987 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
11990 /* Read build and revision information for Molby */
11993 extern int gRevisionNumber;
11994 extern char *gLastBuildString;
11995 FILE *fp = fopen("../buildInfo.txt", "r");
11996 gLastBuildString = "";
11998 if (fgets(buf, sizeof(buf), fp) != NULL) {
11999 char *p1 = strchr(buf, '\"');
12000 char *p2 = strrchr(buf, '\"');
12001 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12002 memmove(buf, p1 + 1, p2 - p1 - 1);
12003 buf[p2 - p1 - 1] = 0;
12004 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12009 fp = fopen("../revisionInfo.txt", "r");
12010 gRevisionNumber = 0;
12012 if (fgets(buf, sizeof(buf), fp) != NULL) {
12013 gRevisionNumber = strtol(buf, NULL, 0);
12019 #if defined(__CMDMAC__)
12020 wbuf = Molby_getDescription();
12021 printf("%s\n", wbuf);
12025 /* Read atom display parameters */
12026 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12027 #if defined(__CMDMAC__)
12028 fprintf(stderr, "%s\n", wbuf);
12030 MyAppCallback_setConsoleColor(1);
12031 MyAppCallback_showScriptMessage("%s", wbuf);
12032 MyAppCallback_setConsoleColor(0);
12037 /* Read default parameters */
12038 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12039 if (wbuf != NULL) {
12040 #if defined(__CMDMAC__)
12041 fprintf(stderr, "%s\n", wbuf);
12043 MyAppCallback_setConsoleColor(1);
12044 MyAppCallback_showScriptMessage("%s", wbuf);
12045 MyAppCallback_setConsoleColor(0);
12050 /* Initialize Ruby interpreter */
12053 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12054 it causes rb_bug() (= fatal error) during ruby_init().
12055 As a workaround, these standard streams are reopend as
12057 freopen("NUL", "r", stdin);
12058 freopen("NUL", "w", stdout);
12059 freopen("NUL", "w", stderr);
12065 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12067 Init_trans_japanese_sjis();
12070 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12072 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12073 ruby_incpush(libpath);
12077 ruby_script("Molby");
12079 /* Find the resource path (the parent directory of the given directory) */
12080 respath = strdup(dir);
12081 p = strrchr(respath, '/');
12082 if (p == NULL && PATH_SEPARATOR != '/')
12083 p = strrchr(respath, PATH_SEPARATOR);
12086 val = Ruby_NewFileStringValue(respath);
12087 rb_define_global_const("MolbyResourcePath", val);
12090 /* Define Molby classes */
12092 RubyDialogInitClass();
12094 rb_define_const(rb_mMolby, "ResourcePath", val);
12095 val = Ruby_NewFileStringValue(dir);
12096 rb_define_const(rb_mMolby, "ScriptPath", val);
12097 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12098 val = Ruby_NewFileStringValue(p);
12099 rb_define_const(rb_mMolby, "MbsfPath", val);
12102 p = MyAppCallback_getHomeDir();
12103 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12104 rb_define_const(rb_mMolby, "HomeDirectory", val);
12106 p = MyAppCallback_getDocumentHomeDir();
12107 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12108 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12111 #if defined(__CMDMAC__)
12112 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12114 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12119 /* Create objects for stdout and stderr */
12120 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12121 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12122 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12123 rb_gv_set("$stdout", val);
12124 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12125 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12126 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12127 rb_gv_set("$stderr", val);
12129 /* Create objects for stdin */
12130 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12131 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12132 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12133 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12134 rb_gv_set("$stdin", val);
12138 /* Global variable to hold error information */
12139 rb_define_variable("$backtrace", &gMolbyBacktrace);
12140 rb_define_variable("$error_history", &gMolbyErrorHistory);
12141 gMolbyErrorHistory = rb_ary_new();
12143 /* Global variables for script menus */
12144 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12145 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12146 gScriptMenuCommands = rb_ary_new();
12147 gScriptMenuEnablers = rb_ary_new();
12150 /* Register interrupt check code */
12151 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12155 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12156 s_SetIntervalTimer(0, 50);
12159 /* Read the startup script */
12160 if (script != NULL && script[0] != 0) {
12161 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12163 rb_load_protect(rb_str_new2(script), 0, &status);
12166 Ruby_showError(status);
12168 MyAppCallback_showScriptMessage("Done.\n");
12173 Molby_buildARGV(int argc, const char **argv)
12176 rb_ary_clear(rb_argv);
12177 for (i = 0; i < argc; i++) {
12178 VALUE arg = rb_tainted_str_new2(argv[i]);
12180 rb_ary_push(rb_argv, arg);