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__)
347 s = StringValuePtr(sval);
349 /* Convert the end-of-line characters */
350 { const char *p; int nc; char *np;
352 for (p = s; *p != 0; p++) {
356 ns = (char *)malloc(strlen(s) + nc + 1);
357 for (np = ns, p = s; *p != 0; p++, np++) {
365 ns = (char *)malloc(strlen(s) + 1);
369 /* wxMac still has Carbon code. Oops. */
370 for (np = ns; *np != 0; np++) {
377 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
378 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
387 * Put the message in the main text view in black color.
390 s_StandardOutput(VALUE self, VALUE str)
393 MyAppCallback_setConsoleColor(0);
394 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
402 * Put the message in the main text view in red color.
405 s_StandardErrorOutput(VALUE self, VALUE str)
408 MyAppCallback_setConsoleColor(1);
409 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
410 MyAppCallback_setConsoleColor(0);
419 * Flush the standard (error) output. Actually do nothing.
422 s_FlushConsoleOutput(VALUE self)
429 * stdin.gets(rs = $/)
431 * Read one line message via dialog box.
434 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
437 pval = rb_str_new2("Enter a line:");
438 rval = s_Kernel_Ask(1, &pval, self);
441 rb_str_cat2(rval, "\n");
447 * stdin.method_missing(name, args, ...)
449 * Throw an exception, noting only gets and readline are defined.
452 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
455 rb_scan_args(argc, argv, "10", &nval);
456 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
457 return Qnil; /* Not reached */
460 #pragma mark ====== Track key events ======
462 /* User interrupt handling
463 * User interrupt (command-period on Mac OS) is handled by periodic polling of
464 * key events. This polling should only be enabled during "normal" execution
465 * of scripts and must be disabled when the rest of the application (or Ruby
466 * script itself) is handling GUI. This is ensured by appropriate calls to
467 * enable_interrupt and disable_interrupt. */
469 static VALUE s_interrupt_flag = Qfalse;
472 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
474 volatile VALUE message;
476 if (Ruby_GetInterruptFlag() == Qtrue) {
477 rb_scan_args(argc, argv, "01", &message);
479 p = StringValuePtr(message);
482 MyAppCallback_showProgressPanel(p);
488 s_HideProgressPanel(VALUE self)
490 MyAppCallback_hideProgressPanel();
495 s_SetProgressValue(VALUE self, VALUE val)
497 double dval = NUM2DBL(rb_Float(val));
498 MyAppCallback_setProgressValue(dval);
503 s_SetProgressMessage(VALUE self, VALUE msg)
508 else p = StringValuePtr(msg);
509 MyAppCallback_setProgressMessage(p);
514 s_SetInterruptFlag(VALUE self, VALUE val)
518 if (val == Qfalse || val == Qnil)
522 oldval = s_interrupt_flag;
524 s_interrupt_flag = val;
526 s_HideProgressPanel(self);
533 s_GetInterruptFlag(VALUE self)
535 return s_SetInterruptFlag(self, Qundef);
539 Ruby_SetInterruptFlag(VALUE val)
541 return s_SetInterruptFlag(Qnil, val);
545 Ruby_GetInterruptFlag(void)
547 return s_SetInterruptFlag(Qnil, Qundef);
552 * check_interrupt -> integer
554 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
557 s_Kernel_CheckInterrupt(VALUE self)
559 if (Ruby_GetInterruptFlag() == Qfalse)
561 else if (MyAppCallback_checkInterrupt())
563 else return INT2NUM(0);
566 static volatile unsigned long sITimerCount = 0;
569 static HANDLE sITimerEvent;
570 static HANDLE sITimerThread;
571 static int sITimerInterval;
573 static __stdcall unsigned
574 s_ITimerThreadFunc(void *p)
576 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
582 #elif USE_PTHREAD_FOR_TIMER
585 static pthread_t sTimerThread;
587 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
588 static volatile signed char sTimerFlag = -1;
589 static volatile int sTimerIntervalMicrosec = 0;
592 s_TimerThreadEntry(void *param)
595 usleep(sTimerIntervalMicrosec);
598 else if (sTimerFlag == -2)
607 s_SignalAction(int n)
613 s_SetIntervalTimer(int n, int msec)
617 /* Start interval timer */
618 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
619 sITimerInterval = msec;
621 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
624 /* Stop interval timer */
626 SetEvent(sITimerEvent); /* Tell thread to terminate */
628 WaitForSingleObject(sITimerThread, 1000);
629 CloseHandle(sITimerThread);
632 CloseHandle(sITimerEvent);
634 sITimerThread = NULL;
636 #elif USE_PTHREAD_FOR_TIMER
638 if (sTimerFlag == -1) {
639 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
641 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
644 sTimerFlag = 0; /* Active */
645 sTimerIntervalMicrosec = msec * 1000;
646 } else if (sTimerFlag != -1)
647 sTimerFlag = 1; /* Inactive */
649 static struct itimerval sOldValue;
650 static struct sigaction sOldAction;
651 struct itimerval val;
652 struct sigaction act;
655 act.sa_handler = s_SignalAction;
658 sigaction(SIGALRM, &act, &sOldAction);
659 val.it_value.tv_sec = 0;
660 val.it_value.tv_usec = msec * 1000;
661 val.it_interval.tv_sec = 0;
662 val.it_interval.tv_usec = msec * 1000;
663 setitimer(ITIMER_REAL, &val, &sOldValue);
665 setitimer(ITIMER_REAL, &sOldValue, &val);
666 sigaction(SIGALRM, &sOldAction, &act);
672 s_GetTimerCount(void)
678 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
679 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
681 if (s_interrupt_flag != Qfalse) {
682 static unsigned long sLastTime = 0;
683 unsigned long currentTime;
685 currentTime = s_GetTimerCount();
686 if (currentTime != sLastTime) {
687 sLastTime = currentTime;
688 gMolbyIsCheckingInterrupt = 1;
689 flag = MyAppCallback_checkInterrupt();
690 gMolbyIsCheckingInterrupt = 0;
692 s_SetInterruptFlag(Qnil, Qfalse);
699 #pragma mark ====== Menu handling ======
703 * register_menu(title, method, enable_proc = nil)
705 * Register the method (specified as a symbol) in the script menu.
706 * The method must be either an instance method of Molecule with no argument,
707 * or a class method of Molecule with one argument (the current molecule),
708 * or a proc object with one argument (the current molecule).
709 * The menu associated with the class method can be invoked even when no document
710 * is open (the argument is set to Qnil in this case). On the other hand, the
711 * menu associated with the instance method can only be invoked when at least one
712 * document is active.
713 * If enable_proc is non-nil, then it is called whenever the availability of
714 * the menu command is tested. It is usually a proc object with one argument
715 * (the current molecule or nil). As a special case, the following symbols can
716 * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
717 * the top-level molecule has at least one atom), :selection (enabled when
718 * the top-level molecule has one or more selected atoms).
721 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
724 VALUE tval, mval, pval;
725 static VALUE sMolSym, sNonEmptySym, sSelectionSym;
726 static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
727 rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
728 tval = rb_str_to_str(tval);
729 n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
732 if (TYPE(mval) == T_SYMBOL) {
733 /* Create an appropriate proc object */
734 const char *name = rb_id2name(SYM2ID(mval));
736 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
737 /* Defined as a Molecule method */
738 asprintf(&s, "lambda { |m| m.%s }", name);
740 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
741 /* Defined as a Molecule class method */
742 asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
744 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
745 mval = rb_eval_string(s);
748 if (sMolSym == Qfalse) {
749 sMolSym = ID2SYM(rb_intern("mol"));
750 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
751 sSelectionSym = ID2SYM(rb_intern("selection"));
752 sMolProc = rb_eval_string("lambda { |m| m != nil }");
753 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
754 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
755 sTrueProc = rb_eval_string("lambda { |m| true }");
756 rb_global_variable(&sMolProc);
757 rb_global_variable(&sNonEmptyProc);
758 rb_global_variable(&sSelectionProc);
759 rb_global_variable(&sTrueProc);
767 } else if (pval == sMolSym)
769 else if (pval == sNonEmptySym)
770 pval = sNonEmptyProc;
771 else if (pval == sSelectionSym)
772 pval = sSelectionProc;
773 rb_ary_store(gScriptMenuCommands, n, mval);
774 rb_ary_store(gScriptMenuEnablers, n, pval);
779 s_Kernel_LookupMenu(VALUE self, VALUE title)
781 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
786 s_Ruby_UpdateUI_handler(VALUE data)
788 void **p = (void **)data;
789 int index = (int)p[0];
790 Molecule *mol = (Molecule *)p[1];
791 int *outChecked = (int *)p[2];
792 char **outTitle = (char **)p[3];
793 VALUE mval = ValueFromMolecule(mol);
794 VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
795 static ID call_id = 0;
797 call_id = rb_intern("call");
800 pval = rb_funcall(pval, call_id, 1, mval);
801 if (rb_obj_is_kind_of(pval, rb_cArray)) {
803 if (outChecked != NULL) {
804 val = rb_ary_entry(pval, 1); /* Checked or not */
805 *outChecked = (RTEST(val) ? 1 : 0);
807 if (outTitle != NULL) {
808 val = rb_ary_entry(pval, 2); /* Text */
809 if (TYPE(val) == T_STRING) {
810 *outTitle = strdup(StringValuePtr(val));
813 pval = rb_ary_entry(pval, 0);
819 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
824 p[0] = (void *)(intptr_t)index;
828 retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
829 return (RTEST(retval) ? 1 : 0);
834 s_Ruby_methodType_sub(VALUE data)
836 const char **p = (const char **)data;
837 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
838 ID mid = rb_intern(p[1]);
840 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
842 else if (rb_respond_to(klass, mid))
845 return INT2FIX(ival);
848 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
849 has the singleton method (class method) with the given name, 0 otherwise. */
851 Ruby_methodType(const char *className, const char *methodName)
858 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
860 return FIX2INT(retval);
867 * execute_script_file(fname)
869 * Execute the script in the given file. If a molecule is active, then
870 * the script is evaluated as Molecule.current.instance_eval(script).
871 * Before entering the script, the current directory is set to the parent
872 * directory of the script.
875 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
878 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
879 if (retval == (VALUE)6 && status == -1)
880 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
890 * Get the directory suitable for storing user documents. On Windows
891 * it is the home directory + "My Documents". On other platforms
892 * it is the home directory.
895 s_Kernel_DocumentHome(VALUE self)
897 char *s = MyAppCallback_getDocumentHomeDir();
898 VALUE retval = Ruby_NewFileStringValue(s);
903 /* The callback function for call_subprocess */
905 s_Kernel_CallSubProcess_Callback(void *data)
908 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
909 if (status != 0 || retval == Qnil || retval == Qfalse)
916 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
918 * Call subprocess. A progress dialog window is displayed, with a message
919 * "Running #{process_name}...".
920 * A callback proc can be given, which is called periodically during execution. If the proc returns
921 * nil or false, then the execution will be interrupted.
922 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
923 * filename begins with ">>", then the message will be appended to the file.
924 * If the filename is "/dev/null" or "NUL", then the message will be lost.
925 * If the argument is nil, then the message will be sent to the Ruby console.
928 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
930 VALUE cmd, procname, cproc, stdout_val, stderr_val;
931 int n, exitstatus, pid;
935 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
937 if (stdout_val == Qnil) {
940 sout = StringValuePtr(stdout_val);
941 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
944 if (strncmp(sout, ">>", 2) == 0) {
946 fpout = fopen(sout, "a");
950 fpout = fopen(sout, "w");
953 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
956 if (stderr_val == Qnil) {
959 serr = StringValuePtr(stderr_val);
960 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
963 if (strncmp(serr, ">>", 2) == 0) {
965 fpout = fopen(serr, "a");
969 fperr = fopen(serr, "w");
972 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
976 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
978 if (fpout != NULL && fpout != (FILE *)1)
980 if (fperr != NULL && fperr != (FILE *)1)
992 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
995 s_Kernel_Backquote(VALUE self, VALUE cmd)
998 int n, exitstatus, pid;
1000 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1001 /* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1002 if (n >= 0 && buf != NULL) {
1003 val = Ruby_NewEncodedStringValue(buf, 0);
1006 val = Ruby_NewEncodedStringValue("", 0);
1008 rb_last_status_set(exitstatus, pid);
1012 #pragma mark ====== User defaults ======
1016 * get_global_settings(key)
1018 * Get a setting data for key from the application preferences.
1021 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1023 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1025 VALUE retval = rb_eval_string(p);
1033 * set_global_settings(key, value)
1035 * Set a setting data for key to the application preferences.
1038 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1040 VALUE sval = rb_inspect(value);
1041 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1045 #pragma mark ====== IO extension ======
1048 s_Ruby_str_encode_protected(VALUE val)
1050 return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1051 ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1058 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1061 s_IO_gets_any_eol(VALUE self)
1063 VALUE val, val2, cval;
1066 static ID id_getbyte = 0, id_ungetbyte;
1067 if (id_getbyte == 0) {
1068 id_getbyte = rb_intern("getbyte");
1069 id_ungetbyte = rb_intern("ungetbyte");
1073 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1074 c = NUM2INT(rb_Integer(cval));
1076 cval = rb_funcall(self, id_getbyte, 0);
1078 c = NUM2INT(rb_Integer(cval));
1080 rb_funcall(self, id_ungetbyte, 1, cval);
1083 } else if (c != 0x0a) {
1088 val = rb_str_new(buf, i);
1090 rb_str_append(val, rb_str_new(buf, i));
1095 if (cval == Qnil && i == 0 && val == Qnil)
1096 return Qnil; /* End of file */
1099 val = rb_str_new(buf, i);
1101 rb_str_append(val, rb_str_new(buf, i));
1102 val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
1106 /* Needs a end-of-line mark */
1107 cval = rb_gv_get("$/");
1108 rb_str_append(val, cval);
1110 rb_gv_set("$_", val);
1114 #pragma mark ====== Utility functions (protected funcall) ======
1116 struct Ruby_funcall2_record {
1124 s_Ruby_funcall2_sub(VALUE data)
1126 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1127 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1131 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1133 struct Ruby_funcall2_record rec;
1138 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1142 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1144 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1147 #pragma mark ====== ParameterRef Class ======
1150 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1154 Data_Get_Struct(self, ParameterRef, pref);
1156 *typep = pref->parType;
1157 if (pref->parType == kElementParType) {
1158 up = (UnionPar *)&gElementParameters[pref->idx];
1160 up = ParameterRefGetPar(pref);
1161 if (checkEditable) {
1163 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1164 if (up->bond.src != 0 && up->bond.src != -1)
1165 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1172 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1176 Data_Get_Struct(self, ParameterRef, pref);
1177 if (pref->mol == NULL)
1179 up = ParameterRefGetPar(pref);
1180 if (key != s_SourceSym)
1181 up->bond.src = 0; /* Becomes automatically molecule-local */
1182 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1185 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1186 MolActionCallback_registerUndo(pref->mol, act);
1187 MoleculeCallback_notifyModification(pref->mol, 0);
1188 pref->mol->needsMDRebuild = 1;
1193 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1195 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1197 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1199 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1203 s_AtomTypeIndexFromValue(VALUE val)
1205 if (rb_obj_is_kind_of(val, rb_cNumeric))
1206 return NUM2INT(val);
1208 return AtomTypeEncodeToUInt(StringValuePtr(val));
1211 static const char *s_ParameterTypeNames[] = {
1212 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1214 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1217 s_ParTypeFromValue(VALUE val)
1221 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1222 if (s_ParameterTypeIDs[0] == 0) {
1223 for (i = 0; i < n; i++)
1224 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1226 valid = rb_to_id(val);
1227 for (i = 0; i < n; i++) {
1228 if (valid == s_ParameterTypeIDs[i]) {
1230 return kElementParType;
1231 else return kFirstParType + i;
1234 return kInvalidParType;
1241 * Get the index in the parameter list.
1243 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1245 Data_Get_Struct(self, ParameterRef, pref);
1246 return INT2NUM(pref->idx);
1251 * par_type -> String
1253 * Get the parameter type, like "bond", "angle", etc.
1255 static VALUE s_ParameterRef_GetParType(VALUE self) {
1257 s_UnionParFromValue(self, &tp, 0);
1258 if (tp == kElementParType)
1259 return rb_str_new2("element");
1260 tp -= kFirstParType;
1261 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1262 return rb_str_new2(s_ParameterTypeNames[tp]);
1263 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1268 * atom_type -> String or Array of String
1269 * atom_types -> String or Array of String
1271 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1272 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1273 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1274 * The atom type may be "X", which is a wildcard that matches any atom type.
1276 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1281 up = s_UnionParFromValue(self, &tp, 0);
1282 n = ParameterGetAtomTypes(tp, up, types);
1284 rb_raise(rb_eMolbyError, "invalid member atom_types");
1285 for (i = 0; i < n; i++) {
1286 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1287 vals[i] = INT2NUM(types[i]);
1289 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1294 return rb_ary_new4(n, vals);
1301 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1303 static VALUE s_ParameterRef_GetK(VALUE self) {
1307 up = s_UnionParFromValue(self, &tp, 0);
1310 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1312 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1313 case kDihedralParType:
1314 case kImproperParType:
1315 if (up->torsion.mult == 1)
1316 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1317 n = up->torsion.mult;
1320 for (i = 0; i < n; i++)
1321 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1322 return rb_ary_new4(n, vals);
1324 rb_raise(rb_eMolbyError, "invalid member k");
1332 * Get the equilibrium bond length. Only available for bond parameters.
1334 static VALUE s_ParameterRef_GetR0(VALUE self) {
1337 up = s_UnionParFromValue(self, &tp, 0);
1338 if (tp == kBondParType)
1339 return rb_float_new(up->bond.r0);
1340 else rb_raise(rb_eMolbyError, "invalid member r0");
1347 * Get the equilibrium angle (in degree). Only available for angle parameters.
1349 static VALUE s_ParameterRef_GetA0(VALUE self) {
1352 up = s_UnionParFromValue(self, &tp, 0);
1353 if (tp == kAngleParType)
1354 return rb_float_new(up->angle.a0 * kRad2Deg);
1355 else rb_raise(rb_eMolbyError, "invalid member a0");
1362 * Get the multiplicity. Only available for dihedral and improper parameters.
1363 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1365 static VALUE s_ParameterRef_GetMult(VALUE self) {
1368 up = s_UnionParFromValue(self, &tp, 0);
1369 if (tp == kDihedralParType || tp == kImproperParType)
1370 return rb_float_new(up->torsion.mult);
1371 else rb_raise(rb_eMolbyError, "invalid member mult");
1376 * period -> Integer or Array of Integers
1378 * Get the periodicity. Only available for dihedral and improper parameters.
1379 * If the multiplicity is larger than 1, then an array of integers is returned.
1380 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1382 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1386 up = s_UnionParFromValue(self, &tp, 0);
1387 if (tp == kDihedralParType || tp == kImproperParType) {
1388 if (up->torsion.mult == 1)
1389 return INT2NUM(up->torsion.period[0]);
1390 n = up->torsion.mult;
1393 for (i = 0; i < n; i++)
1394 vals[i] = INT2NUM(up->torsion.period[i]);
1395 return rb_ary_new4(n, vals);
1396 } else rb_raise(rb_eMolbyError, "invalid member period");
1401 * phi0 -> Float or Array of Floats
1403 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1404 * If the multiplicity is larger than 1, then an array of floats is returned.
1405 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1407 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1411 up = s_UnionParFromValue(self, &tp, 0);
1412 if (tp == kDihedralParType || tp == kImproperParType) {
1413 if (up->torsion.mult == 1)
1414 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1415 n = up->torsion.mult;
1418 for (i = 0; i < n; i++)
1419 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1420 return rb_ary_new4(n, vals);
1421 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1428 * Get the "A" value for the van der Waals parameter.
1431 static VALUE s_ParameterRef_GetA(VALUE self) {
1434 up = s_UnionParFromValue(self, &tp, 0);
1435 if (tp == kVdwParType)
1436 return rb_float_new(up->vdw.A);
1437 else if (tp == kVdwPairParType)
1438 return rb_float_new(up->vdwp.A);
1439 else rb_raise(rb_eMolbyError, "invalid member A");
1447 * Get the "B" value for the van der Waals parameter.
1450 static VALUE s_ParameterRef_GetB(VALUE self) {
1453 up = s_UnionParFromValue(self, &tp, 0);
1454 if (tp == kVdwParType)
1455 return rb_float_new(up->vdw.B);
1456 else if (tp == kVdwPairParType)
1457 return rb_float_new(up->vdwp.B);
1458 else rb_raise(rb_eMolbyError, "invalid member B");
1466 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1468 static VALUE s_ParameterRef_GetReq(VALUE self) {
1471 /* Double a, b, r; */
1473 up = s_UnionParFromValue(self, &tp, 0);
1474 if (tp == kVdwParType) {
1478 } else if (tp == kVdwPairParType) {
1482 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1483 /* if (a == 0.0 || b == 0.0) */
1484 return rb_float_new(r);
1485 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1492 * Get the minimum energy for the van der Waals parameter.
1494 static VALUE s_ParameterRef_GetEps(VALUE self) {
1499 up = s_UnionParFromValue(self, &tp, 0);
1500 if (tp == kVdwParType) {
1504 } else if (tp == kVdwPairParType) {
1508 } else rb_raise(rb_eMolbyError, "invalid member eps");
1509 /* if (a == 0.0 || b == 0.0) */
1510 return rb_float_new(eps * INTERNAL2KCAL);
1511 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1518 * Get the "A" value for the 1-4 van der Waals parameter.
1521 static VALUE s_ParameterRef_GetA14(VALUE self) {
1524 up = s_UnionParFromValue(self, &tp, 0);
1525 if (tp == kVdwParType)
1526 return rb_float_new(up->vdw.A14);
1527 else if (tp == kVdwPairParType)
1528 return rb_float_new(up->vdwp.A14);
1529 else rb_raise(rb_eMolbyError, "invalid member A14");
1537 * Get the "B" value for the 1-4 van der Waals parameter.
1540 static VALUE s_ParameterRef_GetB14(VALUE self) {
1543 up = s_UnionParFromValue(self, &tp, 0);
1544 if (tp == kVdwParType)
1545 return rb_float_new(up->vdw.B14);
1546 else if (tp == kVdwPairParType)
1547 return rb_float_new(up->vdwp.B14);
1548 else rb_raise(rb_eMolbyError, "invalid member B14");
1556 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1558 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1561 /* Double a, b, r; */
1563 up = s_UnionParFromValue(self, &tp, 0);
1564 if (tp == kVdwParType) {
1568 } else if (tp == kVdwPairParType) {
1569 /* a = up->vdwp.A14;
1570 b = up->vdwp.B14; */
1571 r = up->vdwp.r_eq14;
1572 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1573 /* if (a == 0.0 || b == 0.0) */
1574 return rb_float_new(r);
1575 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1582 * Get the minimum energy for the 1-4 van der Waals parameter.
1584 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1589 up = s_UnionParFromValue(self, &tp, 0);
1590 if (tp == kVdwParType) {
1593 eps = up->vdw.eps14;
1594 } else if (tp == kVdwPairParType) {
1595 /* a = up->vdwp.A14;
1596 b = up->vdwp.B14; */
1597 eps = up->vdwp.eps14;
1598 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1599 /* if (a == 0.0 || b == 0.0) */
1600 return rb_float_new(eps * INTERNAL2KCAL);
1601 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1608 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1610 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1613 up = s_UnionParFromValue(self, &tp, 0);
1614 if (tp == kVdwCutoffParType)
1615 return rb_float_new(up->vdwcutoff.cutoff);
1616 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1623 * Get the atomic (covalent) radius for the element parameter.
1625 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1628 up = s_UnionParFromValue(self, &tp, 0);
1629 if (tp == kElementParType)
1630 return rb_float_new(up->atom.radius);
1631 else rb_raise(rb_eMolbyError, "invalid member radius");
1636 * vdw_radius -> Float
1638 * Get the van der Waals radius for the element parameter. (0 if not given)
1640 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1643 up = s_UnionParFromValue(self, &tp, 0);
1644 if (tp == kElementParType)
1645 return rb_float_new(up->atom.vdw_radius);
1646 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1651 * color -> [Float, Float, Float]
1653 * Get the rgb color for the element parameter.
1655 static VALUE s_ParameterRef_GetColor(VALUE self) {
1658 up = s_UnionParFromValue(self, &tp, 0);
1659 if (tp == kElementParType)
1660 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));
1661 else rb_raise(rb_eMolbyError, "invalid member color");
1666 * atomic_number -> Integer
1668 * Get the atomic number for the vdw or element parameter.
1670 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1673 up = s_UnionParFromValue(self, &tp, 0);
1674 if (tp == kElementParType)
1675 return INT2NUM(up->atom.number);
1676 else if (tp == kVdwParType)
1677 return INT2NUM(up->vdw.atomicNumber);
1678 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1685 * Get the name for the element parameter.
1687 static VALUE s_ParameterRef_GetName(VALUE self) {
1690 up = s_UnionParFromValue(self, &tp, 0);
1691 if (tp == kElementParType) {
1693 strncpy(name, up->atom.name, 4);
1695 return rb_str_new2(name);
1696 } else rb_raise(rb_eMolbyError, "invalid member name");
1703 * Get the atomic weight for the element parameter.
1705 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1708 up = s_UnionParFromValue(self, &tp, 0);
1709 if (tp == kElementParType)
1710 return rb_float_new(up->atom.weight);
1711 else if (tp == kVdwParType)
1712 return rb_float_new(up->vdw.weight);
1713 else rb_raise(rb_eMolbyError, "invalid member weight");
1718 * fullname -> String
1720 * Get the full name for the element parameter.
1722 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1725 up = s_UnionParFromValue(self, &tp, 0);
1726 if (tp == kElementParType) {
1728 strncpy(fullname, up->atom.fullname, 15);
1730 return rb_str_new2(fullname);
1731 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1738 * Get the comment for the parameter.
1740 static VALUE s_ParameterRef_GetComment(VALUE self) {
1743 up = s_UnionParFromValue(self, &tp, 0);
1747 else return rb_str_new2(ParameterGetComment(com));
1754 * Get the source string for the parameter. Returns false for undefined parameter,
1755 * and nil for "local" parameter that is specific for the molecule.
1757 static VALUE s_ParameterRef_GetSource(VALUE self) {
1760 up = s_UnionParFromValue(self, &tp, 0);
1763 return Qfalse; /* undefined */
1765 return Qnil; /* local */
1766 else return rb_str_new2(ParameterGetComment(src));
1770 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1777 if (rb_obj_is_kind_of(val, rb_cString)) {
1778 char *s = StringValuePtr(val);
1780 for (i = 0; i < n; i++) {
1783 /* Skip leading separaters */
1784 while (*s == '-' || *s == ' ' || *s == '\t')
1786 for (p = s; *p != 0; p++) {
1787 if (*p == '-' || *p == ' ' || *p == '\t')
1791 if (len >= sizeof(buf))
1792 len = sizeof(buf) - 1;
1793 strncpy(buf, s, len);
1795 /* Skip trailing blanks */
1796 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1799 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1800 if (buf[0] >= '0' && buf[0] <= '9')
1801 types[i] = atoi(buf);
1803 types[i] = AtomTypeEncodeToUInt(buf);
1804 if (p == NULL || *p == 0) {
1810 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1813 val = rb_ary_to_ary(val);
1814 if (RARRAY_LEN(val) != n)
1815 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1816 valp = RARRAY_PTR(val);
1818 for (i = 0; i < n; i++) {
1819 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1820 types[i] = NUM2INT(rb_Integer(valp[i]));
1822 VALUE sval = valp[i];
1823 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1828 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1833 up = s_UnionParFromValue(self, &tp, 1);
1834 oldval = s_ParameterRef_GetAtomTypes(self);
1835 oldsrc = up->bond.src;
1838 s_ScanAtomTypes(val, 2, types);
1839 up->bond.type1 = types[0];
1840 up->bond.type2 = types[1];
1843 s_ScanAtomTypes(val, 3, types);
1844 up->angle.type1 = types[0];
1845 up->angle.type2 = types[1];
1846 up->angle.type3 = types[2];
1848 case kDihedralParType:
1849 case kImproperParType:
1850 s_ScanAtomTypes(val, 4, types);
1851 up->torsion.type1 = types[0];
1852 up->torsion.type2 = types[1];
1853 up->torsion.type3 = types[2];
1854 up->torsion.type4 = types[3];
1857 s_ScanAtomTypes(val, 1, types);
1858 up->vdw.type1 = types[0];
1860 case kVdwPairParType:
1861 s_ScanAtomTypes(val, 2, types);
1862 up->vdwp.type1 = types[0];
1863 up->vdwp.type2 = types[1];
1865 case kVdwCutoffParType:
1866 s_ScanAtomTypes(val, 2, types);
1867 up->vdwcutoff.type1 = types[0];
1868 up->vdwcutoff.type2 = types[1];
1873 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1877 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1879 Int tp, i, n, oldsrc;
1880 VALUE *valp, oldval;
1881 up = s_UnionParFromValue(self, &tp, 1);
1882 oldval = s_ParameterRef_GetK(self);
1883 oldsrc = up->bond.src;
1886 val = rb_Float(val);
1887 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1890 val = rb_Float(val);
1891 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1893 case kDihedralParType:
1894 case kImproperParType:
1895 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1896 up->torsion.mult = 1;
1897 val = rb_Float(val);
1898 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1901 n = up->torsion.mult;
1904 val = rb_ary_to_ary(val);
1905 if (RARRAY_LEN(val) != n)
1906 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1907 valp = RARRAY_PTR(val);
1908 for (i = 0; i < n; i++) {
1909 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1913 rb_raise(rb_eMolbyError, "invalid member k");
1915 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1919 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1923 up = s_UnionParFromValue(self, &tp, 1);
1924 oldval = s_ParameterRef_GetR0(self);
1925 oldsrc = up->bond.src;
1926 if (tp == kBondParType) {
1927 val = rb_Float(val);
1928 up->bond.r0 = NUM2DBL(val);
1929 } else rb_raise(rb_eMolbyError, "invalid member r0");
1930 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1934 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1938 up = s_UnionParFromValue(self, &tp, 1);
1939 oldval = s_ParameterRef_GetA0(self);
1940 oldsrc = up->bond.src;
1941 if (tp == kAngleParType) {
1942 val = rb_Float(val);
1943 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1944 } else rb_raise(rb_eMolbyError, "invalid member a0");
1945 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1949 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1953 up = s_UnionParFromValue(self, &tp, 1);
1954 oldval = s_ParameterRef_GetMult(self);
1955 oldsrc = up->bond.src;
1956 if (tp == kDihedralParType || tp == kImproperParType) {
1958 val = rb_Integer(val);
1961 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1962 up->torsion.mult = i;
1963 } else rb_raise(rb_eMolbyError, "invalid member mult");
1964 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1968 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1970 Int tp, i, n, oldsrc;
1971 VALUE *valp, oldval;
1972 up = s_UnionParFromValue(self, &tp, 1);
1973 oldval = s_ParameterRef_GetPeriod(self);
1974 oldsrc = up->bond.src;
1975 if (tp == kDihedralParType || tp == kImproperParType) {
1976 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1977 up->torsion.mult = 1;
1978 val = rb_Integer(val);
1979 up->torsion.period[0] = NUM2INT(val);
1981 n = up->torsion.mult;
1984 val = rb_ary_to_ary(val);
1985 if (RARRAY_LEN(val) != n)
1986 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1987 valp = RARRAY_PTR(val);
1988 for (i = 0; i < n; i++) {
1989 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1992 } else rb_raise(rb_eMolbyError, "invalid member period");
1993 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1997 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1999 Int tp, i, n, oldsrc;
2000 VALUE *valp, oldval;
2001 up = s_UnionParFromValue(self, &tp, 1);
2002 oldval = s_ParameterRef_GetPhi0(self);
2003 oldsrc = up->bond.src;
2004 if (tp == kDihedralParType || tp == kImproperParType) {
2005 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2006 up->torsion.mult = 1;
2007 val = rb_Float(val);
2008 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2010 n = up->torsion.mult;
2013 val = rb_ary_to_ary(val);
2014 if (RARRAY_LEN(val) != n)
2015 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2016 valp = RARRAY_PTR(val);
2017 for (i = 0; i < n; i++)
2018 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2020 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2021 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2026 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2031 up = s_UnionParFromValue(self, &tp, 1);
2032 oldval = s_ParameterRef_GetA(self);
2033 oldsrc = up->bond.src;
2034 val = rb_Float(val);
2036 if (tp == kVdwParType)
2038 else if (tp == kVdwPairParType)
2040 else rb_raise(rb_eMolbyError, "invalid member A");
2041 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2045 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2050 up = s_UnionParFromValue(self, &tp, 1);
2051 oldval = s_ParameterRef_GetB(self);
2052 oldsrc = up->bond.src;
2053 val = rb_Float(val);
2055 if (tp == kVdwParType)
2057 else if (tp == kVdwPairParType)
2059 else rb_raise(rb_eMolbyError, "invalid member B");
2060 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2065 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2070 up = s_UnionParFromValue(self, &tp, 1);
2071 oldval = s_ParameterRef_GetReq(self);
2072 oldsrc = up->bond.src;
2073 val = rb_Float(val);
2075 if (tp == kVdwParType) {
2077 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2078 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2079 } else if (tp == kVdwPairParType) {
2081 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2082 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2083 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2084 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2088 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2093 up = s_UnionParFromValue(self, &tp, 1);
2094 oldval = s_ParameterRef_GetEps(self);
2095 oldsrc = up->bond.src;
2096 val = rb_Float(val);
2097 e = NUM2DBL(val) * KCAL2INTERNAL;
2098 if (tp == kVdwParType) {
2100 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2101 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2102 } else if (tp == kVdwPairParType) {
2104 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2105 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2106 } else rb_raise(rb_eMolbyError, "invalid member eps");
2107 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2112 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2117 up = s_UnionParFromValue(self, &tp, 1);
2118 oldval = s_ParameterRef_GetA14(self);
2119 oldsrc = up->bond.src;
2120 val = rb_Float(val);
2122 if (tp == kVdwParType)
2124 else if (tp == kVdwPairParType)
2126 else rb_raise(rb_eMolbyError, "invalid member A14");
2127 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2131 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2136 up = s_UnionParFromValue(self, &tp, 1);
2137 oldval = s_ParameterRef_GetB14(self);
2138 oldsrc = up->bond.src;
2139 val = rb_Float(val);
2141 if (tp == kVdwParType)
2143 else if (tp == kVdwPairParType)
2145 else rb_raise(rb_eMolbyError, "invalid member B14");
2146 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2151 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2156 up = s_UnionParFromValue(self, &tp, 1);
2157 oldval = s_ParameterRef_GetReq14(self);
2158 oldsrc = up->bond.src;
2159 val = rb_Float(val);
2161 if (tp == kVdwParType) {
2163 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2164 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2165 } else if (tp == kVdwPairParType) {
2166 up->vdwp.r_eq14 = r;
2167 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2168 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2169 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2170 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2174 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2179 up = s_UnionParFromValue(self, &tp, 1);
2180 oldval = s_ParameterRef_GetEps14(self);
2181 oldsrc = up->bond.src;
2182 val = rb_Float(val);
2183 e = NUM2DBL(val) * KCAL2INTERNAL;
2184 if (tp == kVdwParType) {
2186 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2187 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2188 } else if (tp == kVdwPairParType) {
2190 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2191 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2192 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2193 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2197 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2201 oldval = s_ParameterRef_GetCutoff(self);
2202 oldsrc = up->bond.src;
2203 up = s_UnionParFromValue(self, &tp, 1);
2204 val = rb_Float(val);
2205 if (tp == kVdwCutoffParType) {
2206 up->vdwcutoff.cutoff = NUM2DBL(val);
2207 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2208 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2212 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2216 up = s_UnionParFromValue(self, &tp, 1);
2217 oldval = s_ParameterRef_GetRadius(self);
2218 oldsrc = up->bond.src;
2219 val = rb_Float(val);
2220 if (tp == kElementParType) {
2221 up->atom.radius = NUM2DBL(val);
2222 } else rb_raise(rb_eMolbyError, "invalid member radius");
2223 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2227 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2231 up = s_UnionParFromValue(self, &tp, 1);
2232 oldval = s_ParameterRef_GetVdwRadius(self);
2233 oldsrc = up->bond.src;
2234 val = rb_Float(val);
2235 if (tp == kElementParType) {
2236 up->atom.vdw_radius = NUM2DBL(val);
2237 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2238 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2242 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2245 VALUE *valp, oldval;
2246 up = s_UnionParFromValue(self, &tp, 1);
2247 oldval = s_ParameterRef_GetColor(self);
2248 oldsrc = up->bond.src;
2249 val = rb_ary_to_ary(val);
2250 if (RARRAY_LEN(val) != 3)
2251 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2252 valp = RARRAY_PTR(val);
2253 if (tp == kElementParType) {
2254 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2255 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2256 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2257 } else rb_raise(rb_eMolbyError, "invalid member color");
2258 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2262 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2266 up = s_UnionParFromValue(self, &tp, 1);
2267 oldval = s_ParameterRef_GetAtomicNumber(self);
2268 oldsrc = up->bond.src;
2269 val = rb_Integer(val);
2270 if (tp == kElementParType)
2271 up->atom.number = NUM2INT(val);
2272 else if (tp == kVdwParType) {
2273 up->vdw.atomicNumber = NUM2INT(val);
2274 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2275 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2276 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2280 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2284 up = s_UnionParFromValue(self, &tp, 1);
2285 oldval = s_ParameterRef_GetName(self);
2286 oldsrc = up->bond.src;
2287 if (tp == kElementParType) {
2288 strncpy(up->atom.name, StringValuePtr(val), 4);
2289 } else rb_raise(rb_eMolbyError, "invalid member name");
2290 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2294 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2298 val = rb_Float(val);
2299 oldval = s_ParameterRef_GetWeight(self);
2300 up = s_UnionParFromValue(self, &tp, 1);
2301 oldsrc = up->bond.src;
2302 if (tp == kElementParType)
2303 up->atom.weight = NUM2DBL(val);
2304 else if (tp == kVdwParType)
2305 up->vdw.weight = NUM2DBL(val);
2306 else rb_raise(rb_eMolbyError, "invalid member weight");
2307 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2311 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2315 up = s_UnionParFromValue(self, &tp, 1);
2316 oldval = s_ParameterRef_GetFullName(self);
2317 oldsrc = up->bond.src;
2318 if (tp == kElementParType) {
2319 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2320 up->atom.fullname[15] = 0;
2321 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2322 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2326 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2328 Int tp, com, oldsrc;
2330 up = s_UnionParFromValue(self, &tp, 1);
2331 oldval = s_ParameterRef_GetComment(self);
2332 oldsrc = up->bond.src;
2336 com = ParameterCommentIndex(StringValuePtr(val));
2339 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2343 /* Only false (undefined) and nil (local) can be set */
2344 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2348 up = s_UnionParFromValue(self, &tp, 1);
2349 if (val != Qfalse && val != Qnil)
2350 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2351 oldval = s_ParameterRef_GetSource(self);
2352 oldsrc = up->bond.src;
2353 if (oldsrc != 0 && oldsrc != -1)
2354 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2355 up->bond.src = (val == Qfalse ? -1 : 0);
2356 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2360 static struct s_ParameterAttrDef {
2362 VALUE *symref; /* Address of s_IndexSymbol etc. */
2363 ID id; /* Will be set within InitMolby() */
2364 VALUE (*getter)(VALUE);
2365 VALUE (*setter)(VALUE, VALUE);
2366 } s_ParameterAttrDefTable[] = {
2367 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2368 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2369 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2370 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2371 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2372 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2373 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2374 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2375 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2376 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2377 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2378 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2379 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2380 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2381 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2382 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2383 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2384 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2385 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2386 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2387 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2388 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2389 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2390 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2391 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2392 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2393 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2394 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2395 {NULL} /* Sentinel */
2399 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2403 if (TYPE(key) != T_SYMBOL) {
2404 kid = rb_intern(StringValuePtr(key));
2406 } else kid = SYM2ID(key);
2407 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2408 if (s_ParameterAttrDefTable[i].id == kid) {
2409 if (value == Qundef)
2410 return (*(s_ParameterAttrDefTable[i].getter))(self);
2411 else if (s_ParameterAttrDefTable[i].setter == NULL)
2412 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2414 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2417 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2418 return Qnil; /* not reached */
2422 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2424 return s_ParameterRef_SetAttr(self, key, Qundef);
2429 * keys(idx) -> array of valid parameter attributes
2431 * Returns an array of valid parameter attributes (as Symbols).
2434 s_ParameterRef_Keys(VALUE self)
2437 Data_Get_Struct(self, ParameterRef, pref);
2438 switch (pref->parType) {
2440 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2442 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2443 case kDihedralParType:
2444 case kImproperParType:
2445 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2447 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);
2448 case kVdwPairParType:
2449 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2450 case kVdwCutoffParType:
2451 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2452 case kElementParType:
2453 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);
2455 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2457 return Qnil; /* Not reached */
2462 * to_hash(idx) -> Hash
2464 * Returns a hash containing valid parameter names and values
2467 s_ParameterRef_ToHash(VALUE self)
2469 VALUE keys = s_ParameterRef_Keys(self);
2474 retval = rb_hash_new();
2475 for (i = 0; i < RARRAY_LEN(keys); i++) {
2476 VALUE key = RARRAY_PTR(keys)[i];
2477 VALUE val = s_ParameterRef_GetAttr(self, key);
2478 rb_hash_aset(retval, key, val);
2485 * parameter.to_s(idx) -> String
2487 * Returns a string representation of the given parameter
2490 s_ParameterRef_ToString(VALUE self)
2493 char buf[1024], types[4][8];
2494 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2497 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);
2500 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);
2502 case kDihedralParType:
2503 case kImproperParType:
2504 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]));
2506 for (i = 0; i < up->torsion.mult; i++) {
2507 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);
2512 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);
2514 case kVdwPairParType:
2515 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);
2517 case kVdwCutoffParType:
2518 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);
2520 case kElementParType:
2521 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);
2524 return rb_str_new2(buf);
2529 * self == parameterRef -> boolean
2531 * True if the parameters point to the same parameter record.
2534 s_ParameterRef_Equal(VALUE self, VALUE val)
2537 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2538 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2539 } else return Qfalse;
2542 #pragma mark ====== Parameter Class ======
2544 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2545 * is NULL, then the global parameters are looked for. */
2547 /* Rebuild the MD parameter record if necessary: may throw an exception */
2548 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2550 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2553 Data_Get_Struct(val, Molecule, mol);
2555 rb_raise(rb_eMolbyError, "the molecule is empty");
2556 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2557 /* Do self.md_arena.prepare */
2558 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2560 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2565 s_NewParameterValueFromValue(VALUE val)
2568 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2569 Data_Get_Struct(val, Molecule, mol);
2570 s_RebuildMDParameterIfNecessary(val, Qtrue);
2571 MoleculeRetain(mol);
2572 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2575 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2580 s_MoleculeFromParameterValue(VALUE val)
2583 Data_Get_Struct(val, Molecule, mol);
2588 s_ParameterFromParameterValue(VALUE val)
2591 Data_Get_Struct(val, Molecule, mol);
2594 return gBuiltinParameters;
2597 /* Forward declarations */
2598 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2599 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2602 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2604 if (val == rb_cParameter) {
2605 return NULL; /* Parameter class method: builtin parameters */
2606 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2607 return s_MoleculeFromParameterValue(val);
2608 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2609 return s_MoleculeFromParEnumerableValue(val);
2615 * builtin -> Parameter
2617 * Returns a parameter value that points to the global (builtin) parameters.
2618 * Equivalent to Parameter::Builtin (constant).
2621 s_Parameter_Builtin(VALUE self)
2623 static ID s_builtin_id = 0;
2624 if (s_builtin_id == 0)
2625 s_builtin_id = rb_intern("Builtin");
2626 return rb_const_get(rb_cParameter, s_builtin_id);
2631 * bond(idx) -> ParameterRef
2633 * The index-th bond parameter record is returned.
2636 s_Parameter_Bond(VALUE self, VALUE ival)
2640 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2641 idx = NUM2INT(rb_Integer(ival));
2643 n = gBuiltinParameters->nbondPars;
2644 else if (mol->par != NULL)
2645 n = mol->par->nbondPars;
2647 if (idx < -n || idx >= n)
2648 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2651 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2656 * angle(idx) -> ParameterRef
2658 * The index-th angle parameter record is returned.
2661 s_Parameter_Angle(VALUE self, VALUE ival)
2665 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2666 idx = NUM2INT(rb_Integer(ival));
2668 n = gBuiltinParameters->nanglePars;
2669 else if (mol->par != NULL)
2670 n = mol->par->nanglePars;
2672 if (idx < -n || idx >= n)
2673 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2676 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2681 * dihedral(idx) -> ParameterRef
2683 * The index-th dihedral parameter record is returned.
2686 s_Parameter_Dihedral(VALUE self, VALUE ival)
2690 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2691 idx = NUM2INT(rb_Integer(ival));
2693 n = gBuiltinParameters->ndihedralPars;
2694 else if (mol->par != NULL)
2695 n = mol->par->ndihedralPars;
2697 if (idx < -n || idx >= n)
2698 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2701 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2706 * improper(idx) -> ParameterRef
2708 * The index-th improper parameter record is returned.
2711 s_Parameter_Improper(VALUE self, VALUE ival)
2715 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2716 idx = NUM2INT(rb_Integer(ival));
2718 n = gBuiltinParameters->nimproperPars;
2719 else if (mol->par != NULL)
2720 n = mol->par->nimproperPars;
2722 if (idx < -n || idx >= n)
2723 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2726 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2731 * vdw(idx) -> ParameterRef
2733 * The index-th vdw parameter record is returned.
2736 s_Parameter_Vdw(VALUE self, VALUE ival)
2740 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2741 idx = NUM2INT(rb_Integer(ival));
2743 n = gBuiltinParameters->nvdwPars;
2744 else if (mol->par != NULL)
2745 n = mol->par->nvdwPars;
2747 if (idx < -n || idx >= n)
2748 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2751 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2756 * vdw_pair(idx) -> ParameterRef
2758 * The index-th vdw pair parameter record is returned.
2761 s_Parameter_VdwPair(VALUE self, VALUE ival)
2765 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2766 idx = NUM2INT(rb_Integer(ival));
2768 n = gBuiltinParameters->nvdwpPars;
2769 else if (mol->par != NULL)
2770 n = mol->par->nvdwpPars;
2772 if (idx < -n || idx >= n)
2773 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2776 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2781 * vdw_cutoff(idx) -> ParameterRef
2783 * The index-th vdw cutoff parameter record is returned.
2786 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2790 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2791 idx = NUM2INT(rb_Integer(ival));
2793 n = gBuiltinParameters->nvdwCutoffPars;
2794 else if (mol->par != NULL)
2795 n = mol->par->nvdwCutoffPars;
2797 if (idx < -n || idx >= n)
2798 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2801 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2806 * element(idx) -> ParameterRef
2807 * element(t1) -> ParameterRef
2809 * In the first form, the index-th element parameter record is returned. In the second
2810 * form, the element parameter for t1 is looked up (the last index first). t1
2811 * is the element name string (up to 4 characters).
2812 * Unlike other Parameter methods, this is used only for the global parameter.
2815 s_Parameter_Element(VALUE self, VALUE ival)
2818 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2819 int n = gCountElementParameters;
2820 idx1 = NUM2INT(rb_Integer(ival));
2821 if (idx1 < -n || idx1 >= n)
2822 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2825 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2830 strncpy(name, StringValuePtr(ival), 4);
2832 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2833 if (strncmp(ep->name, name, 4) == 0)
2834 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2844 * Returns the number of bond parameters.
2847 s_Parameter_Nbonds(VALUE self)
2850 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2852 n = gBuiltinParameters->nbondPars;
2853 else if (mol->par != NULL)
2854 n = mol->par->nbondPars;
2861 * nangles -> Integer
2863 * Returns the number of angle parameters.
2866 s_Parameter_Nangles(VALUE self)
2869 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2871 n = gBuiltinParameters->nanglePars;
2872 else if (mol->par != NULL)
2873 n = mol->par->nanglePars;
2880 * ndihedrals -> Integer
2882 * Returns the number of dihedral parameters.
2885 s_Parameter_Ndihedrals(VALUE self)
2888 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2890 n = gBuiltinParameters->ndihedralPars;
2891 else if (mol->par != NULL)
2892 n = mol->par->ndihedralPars;
2899 * nimpropers -> Integer
2901 * Returns the number of improper parameters.
2904 s_Parameter_Nimpropers(VALUE self)
2907 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2909 n = gBuiltinParameters->nimproperPars;
2910 else if (mol->par != NULL)
2911 n = mol->par->nimproperPars;
2920 * Returns the number of vdw parameters.
2923 s_Parameter_Nvdws(VALUE self)
2926 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2928 n = gBuiltinParameters->nvdwPars;
2929 else if (mol->par != NULL)
2930 n = mol->par->nvdwPars;
2937 * nvdw_pairs -> Integer
2939 * Returns the number of vdw pair parameters.
2942 s_Parameter_NvdwPairs(VALUE self)
2945 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2947 n = gBuiltinParameters->nvdwpPars;
2948 else if (mol->par != NULL)
2949 n = mol->par->nvdwpPars;
2956 * nvdw_cutoffs -> Integer
2958 * Returns the number of vdw cutoff parameters.
2961 s_Parameter_NvdwCutoffs(VALUE self)
2964 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2966 n = gBuiltinParameters->nvdwCutoffPars;
2967 else if (mol->par != NULL)
2968 n = mol->par->nvdwCutoffPars;
2975 * nelements -> Integer
2977 * Returns the number of element parameters.
2980 s_Parameter_Nelements(VALUE self)
2982 return INT2NUM(gCountElementParameters);
2987 * bonds -> ParEnumerable
2989 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2990 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2991 * useful when all accessible parameters should be examined by use of 'each' method.
2994 s_Parameter_Bonds(VALUE self)
2996 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2997 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3002 * angles -> ParEnumerable
3004 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3005 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3006 * useful when all accessible parameters should be examined by use of 'each' method.
3009 s_Parameter_Angles(VALUE self)
3011 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3012 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3017 * dihedrals -> ParEnumerable
3019 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3020 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3021 * useful when all accessible parameters should be examined by use of 'each' method.
3024 s_Parameter_Dihedrals(VALUE self)
3026 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3027 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3032 * impropers -> ParEnumerable
3034 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3035 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3036 * useful when all accessible parameters should be examined by use of 'each' method.
3039 s_Parameter_Impropers(VALUE self)
3041 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3042 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3047 * vdws -> ParEnumerable
3049 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3050 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3051 * useful when all accessible parameters should be examined by use of 'each' method.
3054 s_Parameter_Vdws(VALUE self)
3056 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3057 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3062 * vdw_pairs -> ParEnumerable
3064 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3065 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3066 * useful when all accessible parameters should be examined by use of 'each' method.
3069 s_Parameter_VdwPairs(VALUE self)
3071 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3072 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3077 * vdw_cutoffs -> ParEnumerable
3079 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3080 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3081 * useful when all accessible parameters should be examined by use of 'each' method.
3084 s_Parameter_VdwCutoffs(VALUE self)
3086 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3087 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3092 * elements -> ParEnumerable
3094 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3095 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3096 * useful when all accessible parameters should be examined by use of 'each' method.
3099 s_Parameter_Elements(VALUE self)
3101 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3102 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3106 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3108 VALUE atval, optval;
3111 int i, n, idx, flags, is_global;
3113 rb_scan_args(argc, argv, "1*", &atval, &optval);
3115 /* Get the atom types */
3117 case kBondParType: n = 2; break;
3118 case kAngleParType: n = 3; break;
3119 case kDihedralParType: n = 4; break;
3120 case kImproperParType: n = 4; break;
3121 case kVdwParType: n = 1; break;
3122 case kVdwPairParType: n = 2; break;
3123 default: return Qnil;
3125 s_ScanAtomTypes(atval, n, t);
3126 for (i = 0; i < n; i++) {
3127 if (t[i] < kAtomTypeMinimum) {
3128 /* Explicit atom index */
3130 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3131 if (t[i] >= mol->natoms)
3132 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3134 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3138 /* Analyze options */
3140 n = RARRAY_LEN(optval);
3141 for (i = 0; i < n; i++) {
3142 VALUE oval = RARRAY_PTR(optval)[i];
3143 if (oval == ID2SYM(rb_intern("global")))
3144 flags |= kParameterLookupGlobal;
3145 else if (oval == ID2SYM(rb_intern("local")))
3146 flags |= kParameterLookupLocal;
3147 else if (oval == ID2SYM(rb_intern("missing")))
3148 flags |= kParameterLookupMissing;
3149 else if (oval == ID2SYM(rb_intern("nowildcard")))
3150 flags |= kParameterLookupNoWildcard;
3151 else if (oval == ID2SYM(rb_intern("nobasetype")))
3152 flags |= kParameterLookupNoBaseAtomType;
3153 else if (oval == ID2SYM(rb_intern("create")))
3157 flags = kParameterLookupGlobal | kParameterLookupLocal;
3162 case kBondParType: {
3165 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3167 idx = bp - mol->par->bondPars;
3171 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3173 idx = bp - gBuiltinParameters->bondPars;
3178 case kAngleParType: {
3181 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3183 idx = ap - mol->par->anglePars;
3187 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3189 idx = ap - gBuiltinParameters->anglePars;
3194 case kDihedralParType: {
3197 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3199 idx = tp - mol->par->dihedralPars;
3203 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3205 idx = tp - gBuiltinParameters->dihedralPars;
3210 case kImproperParType: {
3213 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3215 idx = tp - mol->par->improperPars;
3219 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3221 idx = tp - gBuiltinParameters->improperPars;
3229 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3231 idx = vp - mol->par->vdwPars;
3235 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3237 idx = vp - gBuiltinParameters->vdwPars;
3242 case kVdwPairParType: {
3245 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3247 idx = vp - mol->par->vdwpPars;
3251 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3253 idx = vp - gBuiltinParameters->vdwpPars;
3262 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3265 /* Insert a new parameter record */
3267 Int count = ParameterGetCountForType(mol->par, parType);
3268 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3269 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3270 IntGroupRelease(ig);
3273 /* Set atom types */
3274 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3279 up->bond.type1 = t[0];
3280 up->bond.type2 = t[1];
3283 up->angle.type1 = t[0];
3284 up->angle.type2 = t[1];
3285 up->angle.type3 = t[2];
3287 case kDihedralParType:
3288 case kImproperParType:
3289 up->torsion.type1 = t[0];
3290 up->torsion.type2 = t[1];
3291 up->torsion.type3 = t[2];
3292 up->torsion.type4 = t[3];
3295 up->vdw.type1 = t[0];
3297 case kVdwPairParType:
3298 up->vdwp.type1 = t[0];
3299 up->vdwp.type2 = t[1];
3306 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3311 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3312 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3314 * Find the parameter record that matches the given atom types. The atom types are given
3315 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3316 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3317 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3318 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3321 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3324 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3326 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3327 parType = s_ParTypeFromValue(argv[0]);
3328 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3333 * self == parameter -> boolean
3335 * True if the parameters point to the same parameter table.
3338 s_Parameter_Equal(VALUE self, VALUE val)
3340 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3341 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3342 } else return Qfalse;
3345 #pragma mark ====== ParEnumerable Class ======
3347 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3348 and the parameter type. If the Molecule is NULL, then it refers to the
3349 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3350 the global parameters are always accessible. */
3352 typedef struct ParEnumerable {
3354 Int parType; /* Same as parType in ParameterRef */
3357 static ParEnumerable *
3358 s_ParEnumerableNew(Molecule *mol, Int parType)
3360 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3364 MoleculeRetain(mol);
3365 pen->parType = parType;
3371 s_ParEnumerableRelease(ParEnumerable *pen)
3374 if (pen->mol != NULL)
3375 MoleculeRelease(pen->mol);
3381 s_MoleculeFromParEnumerableValue(VALUE val)
3384 Data_Get_Struct(val, ParEnumerable, pen);
3389 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3391 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3393 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3394 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3399 * par_type -> String
3401 * Get the parameter type, like "bond", "angle", etc.
3404 s_ParEnumerable_ParType(VALUE self) {
3407 Data_Get_Struct(self, ParEnumerable, pen);
3409 if (tp == kElementParType)
3410 return rb_str_new2("element");
3411 tp -= kFirstParType;
3412 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3413 return rb_str_new2(s_ParameterTypeNames[tp]);
3414 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3419 * self[idx] -> ParameterRef
3421 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3422 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3423 * parent Parameter object of self.
3425 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3426 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3429 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3432 Data_Get_Struct(self, ParEnumerable, pen);
3433 switch (pen->parType) {
3434 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3435 case kBondParType: return s_Parameter_Bond(self, ival);
3436 case kAngleParType: return s_Parameter_Angle(self, ival);
3437 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3438 case kImproperParType: return s_Parameter_Improper(self, ival);
3439 case kVdwParType: return s_Parameter_Vdw(self, ival);
3440 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3441 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3442 case kElementParType: return s_Parameter_Element(self, ival);
3444 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3446 return Qnil; /* Not reached */
3453 * Returns the number of parameters included in this enumerable.
3456 s_ParEnumerable_Length(VALUE self)
3459 Data_Get_Struct(self, ParEnumerable, pen);
3460 switch (pen->parType) {
3461 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3462 case kBondParType: return s_Parameter_Nbonds(self);
3463 case kAngleParType: return s_Parameter_Nangles(self);
3464 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3465 case kImproperParType: return s_Parameter_Nimpropers(self);
3466 case kVdwParType: return s_Parameter_Nvdws(self);
3467 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3468 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3469 case kElementParType: return s_Parameter_Nelements(self);
3471 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3473 return Qnil; /* Not reached */
3480 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3483 s_ParEnumerable_Each(VALUE self)
3489 Data_Get_Struct(self, ParEnumerable, pen);
3490 if (pen->parType == kElementParType)
3491 n = gCountElementParameters;
3493 switch (pen->parType) {
3494 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3495 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3496 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3497 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3498 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3499 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3500 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3502 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3504 if (pen->mol == NULL)
3505 n = *((Int *)((char *)gBuiltinParameters + ofs));
3506 else if (pen->mol->par != NULL)
3507 n = *((Int *)((char *)(pen->mol->par) + ofs));
3510 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3511 Data_Get_Struct(aval, ParameterRef, pref);
3512 for (i = 0; i < n; i++) {
3521 * reverse_each {|pref| ...}
3523 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3526 s_ParEnumerable_ReverseEach(VALUE self)
3532 Data_Get_Struct(self, ParEnumerable, pen);
3533 if (pen->parType == kElementParType)
3534 n = gCountElementParameters;
3536 switch (pen->parType) {
3537 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3538 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3539 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3540 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3541 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3542 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3543 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3545 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3547 if (pen->mol == NULL)
3548 n = *((Int *)((char *)gBuiltinParameters + ofs));
3549 else if (pen->mol->par != NULL)
3550 n = *((Int *)((char *)(pen->mol->par) + ofs));
3553 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3554 Data_Get_Struct(aval, ParameterRef, pref);
3555 for (i = n - 1; i >= 0; i--) {
3564 * insert(idx = nil, pref = nil) -> ParameterRef
3566 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3567 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3568 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3569 * parameter is left undefined.
3570 * Throws an exception if ParEnumerable points to the global parameter.
3573 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3581 Data_Get_Struct(self, ParEnumerable, pen);
3582 if (pen->mol == NULL)
3583 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3584 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3585 rb_scan_args(argc, argv, "02", &ival, &pval);
3587 i = NUM2INT(rb_Integer(ival));
3589 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3594 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3595 if (up == NULL || type != pen->parType)
3596 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3597 ParameterCopyOneWithType(&u, up, pen->parType);
3600 memset(&u, 0, sizeof(u));
3603 ig = IntGroupNewWithPoints(n, 1, -1);
3604 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3606 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3607 MolActionCallback_registerUndo(pen->mol, act);
3608 MolActionRelease(act);
3610 IntGroupRelease(ig);
3611 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3619 * Delete the parameter(s) specified by the argument.
3622 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3627 Data_Get_Struct(self, ParEnumerable, pen);
3628 if (pen->mol == NULL)
3629 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3630 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3631 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3632 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3635 ig = IntGroupFromValue(ival);
3636 if ((i = IntGroupGetCount(ig)) == 0) {
3637 IntGroupRelease(ig);
3641 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3642 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3645 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3646 IntGroupRelease(ig);
3652 * lookup(atom_types, options, ...) -> ParameterRef
3653 * lookup(atom_type_string, options, ...) -> ParameterRef
3655 * Find the parameter record that matches the given atom types. The arguments are
3656 * the same as Parameter#lookup, except for the parameter type which is implicitly
3660 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3663 Data_Get_Struct(self, ParEnumerable, pen);
3664 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3669 * self == parEnumerable -> boolean
3671 * True if the arguments point to the same parameter table and type.
3674 s_ParEnumerable_Equal(VALUE self, VALUE val)
3676 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3677 ParEnumerable *pen1, *pen2;
3678 Data_Get_Struct(self, ParEnumerable, pen1);
3679 Data_Get_Struct(val, ParEnumerable, pen2);
3680 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3681 } else return Qfalse;
3684 #pragma mark ====== AtomRef Class ======
3686 /* Forward declaration for register undo */
3687 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3689 /* Ruby string "set_atom_attr" */
3690 static VALUE s_SetAtomAttrString;
3693 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3697 Data_Get_Struct(self, AtomRef, aref);
3698 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3699 if (idx < 0 || idx >= aref->mol->natoms)
3700 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3702 *app = aref->mol->atoms + idx;
3709 s_AtomFromValue(VALUE self)
3712 s_AtomIndexFromValue(self, &ap, NULL);
3717 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3720 s_AtomIndexFromValue(self, &ap, mpp);
3725 s_NotifyModificationForAtomRef(VALUE self)
3728 Data_Get_Struct(self, AtomRef, aref);
3729 MoleculeIncrementModifyCount(aref->mol);
3733 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3736 Data_Get_Struct(self, AtomRef, aref);
3737 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3740 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3741 MolActionCallback_registerUndo(aref->mol, act);
3742 MoleculeCallback_notifyModification(aref->mol, 0);
3743 /* Request MD rebuilt if necessary */
3744 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3745 aref->mol->needsMDRebuild = 1;
3750 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3753 aref = AtomRefNew(mol, idx);
3754 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3758 s_AtomRef_GetMolecule(VALUE self)
3761 s_AtomIndexFromValue(self, NULL, &mpp);
3762 return ValueFromMolecule(mpp);
3765 static VALUE s_AtomRef_GetIndex(VALUE self) {
3766 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3769 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3770 return INT2NUM(s_AtomFromValue(self)->segSeq);
3773 static VALUE s_AtomRef_GetSegName(VALUE self) {
3774 char *p = s_AtomFromValue(self)->segName;
3775 return rb_str_new(p, strlen_limit(p, 4));
3778 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3779 return INT2NUM(s_AtomFromValue(self)->resSeq);
3782 static VALUE s_AtomRef_GetResName(VALUE self) {
3783 char *p = s_AtomFromValue(self)->resName;
3784 return rb_str_new(p, strlen_limit(p, 4));
3787 static VALUE s_AtomRef_GetName(VALUE self) {
3788 char *p = s_AtomFromValue(self)->aname;
3789 return rb_str_new(p, strlen_limit(p, 4));
3792 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3793 int type = s_AtomFromValue(self)->type;
3794 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3795 return rb_str_new(p, strlen_limit(p, 6));
3798 static VALUE s_AtomRef_GetCharge(VALUE self) {
3799 return rb_float_new(s_AtomFromValue(self)->charge);
3802 static VALUE s_AtomRef_GetWeight(VALUE self) {
3803 return rb_float_new(s_AtomFromValue(self)->weight);
3806 static VALUE s_AtomRef_GetElement(VALUE self) {
3807 char *p = s_AtomFromValue(self)->element;
3808 return rb_str_new(p, strlen_limit(p, 4));
3811 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3812 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3815 static VALUE s_AtomRef_GetConnects(VALUE self) {
3818 Atom *ap = s_AtomFromValue(self);
3819 retval = rb_ary_new();
3820 cp = AtomConnectData(&ap->connect);
3821 for (i = 0; i < ap->connect.count; i++)
3822 rb_ary_push(retval, INT2NUM(cp[i]));
3826 static VALUE s_AtomRef_GetR(VALUE self) {
3827 return ValueFromVector(&(s_AtomFromValue(self)->r));
3830 static VALUE s_AtomRef_GetX(VALUE self) {
3831 return rb_float_new(s_AtomFromValue(self)->r.x);
3834 static VALUE s_AtomRef_GetY(VALUE self) {
3835 return rb_float_new(s_AtomFromValue(self)->r.y);
3838 static VALUE s_AtomRef_GetZ(VALUE self) {
3839 return rb_float_new(s_AtomFromValue(self)->r.z);
3842 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3846 s_AtomIndexFromValue(self, &ap, &mp);
3848 if (mp->cell != NULL)
3849 TransformVec(&r1, mp->cell->rtr, &r1);
3853 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3854 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3855 return ValueFromVector(&r1);
3858 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3859 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3862 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3863 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3866 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3867 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3870 static VALUE s_AtomRef_GetSigma(VALUE self) {
3871 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3874 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3875 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3878 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3879 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3882 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3883 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3886 static VALUE s_AtomRef_GetV(VALUE self) {
3887 return ValueFromVector(&(s_AtomFromValue(self)->v));
3890 static VALUE s_AtomRef_GetF(VALUE self) {
3891 Vector v = s_AtomFromValue(self)->f;
3892 VecScaleSelf(v, INTERNAL2KCAL);
3893 return ValueFromVector(&v);
3896 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3897 return rb_float_new(s_AtomFromValue(self)->occupancy);
3900 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3901 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3904 static VALUE s_AtomRef_GetAniso(VALUE self) {
3907 Atom *ap = s_AtomFromValue(self);
3908 if (ap->aniso == NULL)
3910 retval = rb_ary_new();
3911 for (i = 0; i < 6; i++)
3912 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3913 if (ap->aniso->has_bsig) {
3914 rb_ary_push(retval, INT2NUM(0));
3915 for (i = 0; i < 6; i++)
3916 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3921 static VALUE s_AtomRef_GetSymop(VALUE self) {
3923 Atom *ap = s_AtomFromValue(self);
3924 if (!ap->symop.alive)
3926 retval = rb_ary_new();
3927 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3928 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3929 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3930 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3931 rb_ary_push(retval, INT2NUM(ap->symbase));
3935 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3936 return INT2NUM(s_AtomFromValue(self)->intCharge);
3939 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3940 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3943 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3944 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3947 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3951 MDExclusion *exinfo;
3954 idx = s_AtomIndexFromValue(self, &ap, &mol);
3955 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3956 VALUE mval = ValueFromMolecule(mol);
3957 s_RebuildMDParameterIfNecessary(mval, Qnil);
3959 if (mol->arena->exinfo == NULL)
3961 exinfo = mol->arena->exinfo + idx;
3962 exlist = mol->arena->exlist;
3963 retval = rb_ary_new();
3964 aval = rb_ary_new();
3965 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3966 rb_ary_push(aval, INT2NUM(exlist[i]));
3967 rb_ary_push(retval, aval);
3968 aval = rb_ary_new();
3969 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3970 rb_ary_push(aval, INT2NUM(exlist[i]));
3971 rb_ary_push(retval, aval);
3972 aval = rb_ary_new();
3973 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3974 rb_ary_push(aval, INT2NUM(exlist[i]));
3975 rb_ary_push(retval, aval);
3979 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3980 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3983 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3984 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3987 static VALUE s_AtomRef_GetHidden(VALUE self) {
3988 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3991 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3994 Atom *ap = s_AtomFromValue(self);
3995 if (ap->anchor == NULL)
3997 count = ap->anchor->connect.count;
3998 retval = rb_ary_new2(count * 2);
3999 cp = AtomConnectData(&ap->anchor->connect);
4000 for (i = 0; i < count; i++) {
4001 rb_ary_store(retval, i, INT2NUM(cp[i]));
4002 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4007 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4008 char *p = s_AtomFromValue(self)->uff_type;
4009 return rb_str_new(p, strlen_limit(p, 5));
4012 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4013 rb_raise(rb_eMolbyError, "index cannot be directly set");
4017 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4018 VALUE oval = s_AtomRef_GetSegSeq(self);
4019 val = rb_Integer(val);
4020 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4021 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4025 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4026 char *p = StringValuePtr(val);
4027 VALUE oval = s_AtomRef_GetSegName(self);
4028 strncpy(s_AtomFromValue(self)->segName, p, 4);
4029 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4033 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4034 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4035 return val; /* Not reached */
4038 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4039 Atom *ap = s_AtomFromValue(self);
4040 char *p = StringValuePtr(val);
4041 VALUE oval = s_AtomRef_GetName(self);
4042 if (ap->anchor != NULL && p[0] == '_')
4043 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4044 strncpy(ap->aname, p, 4);
4045 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4049 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4051 char *p = StringValuePtr(val);
4052 VALUE oval = s_AtomRef_GetAtomType(self);
4053 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4054 if (type != 0 && type < kAtomTypeMinimum)
4055 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4056 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4057 mp->needsMDRebuild = 1;
4058 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4062 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4064 VALUE oval = s_AtomRef_GetCharge(self);
4065 val = rb_Float(val);
4066 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4067 mp->needsMDRebuild = 1;
4068 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4072 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4074 VALUE oval = s_AtomRef_GetWeight(self);
4075 val = rb_Float(val);
4076 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4077 mp->needsMDRebuild = 1;
4078 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4082 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4085 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4086 char *p = StringValuePtr(val);
4087 VALUE oval = s_AtomRef_GetElement(self);
4088 ap->atomicNumber = ElementToInt(p);
4089 ElementToString(ap->atomicNumber, ap->element);
4090 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4092 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4093 mp->needsMDRebuild = 1;
4097 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4100 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4101 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4102 val = rb_Integer(val);
4103 ap->atomicNumber = NUM2INT(val);
4104 ElementToString(ap->atomicNumber, ap->element);
4105 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4107 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4108 mp->needsMDRebuild = 1;
4112 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4113 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4114 return val; /* Not reached */
4117 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4120 VALUE oval = s_AtomRef_GetR(self);
4121 VectorFromValue(val, &v);
4122 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4123 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4124 mp->needsMDCopyCoordinates = 1;
4128 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4131 VALUE oval = s_AtomRef_GetX(self);
4132 val = rb_Float(val);
4134 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4135 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4136 mp->needsMDCopyCoordinates = 1;
4140 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4143 VALUE oval = s_AtomRef_GetY(self);
4144 val = rb_Float(val);
4146 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4147 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4148 mp->needsMDCopyCoordinates = 1;
4152 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4155 VALUE oval = s_AtomRef_GetZ(self);
4156 val = rb_Float(val);
4158 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4159 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4160 mp->needsMDCopyCoordinates = 1;
4164 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4168 s_AtomIndexFromValue(self, &ap, &mp);
4170 VectorFromValue(val, &v);
4171 if (mp->cell != NULL)
4172 TransformVec(&v, mp->cell->tr, &v);
4174 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4175 mp->needsMDCopyCoordinates = 1;
4179 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4184 s_AtomIndexFromValue(self, &ap, &mp);
4186 val = rb_Float(val);
4188 if (mp->cell != NULL) {
4189 TransformVec(&v, mp->cell->rtr, &v);
4191 TransformVec(&v, mp->cell->tr, &v);
4194 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4195 mp->needsMDCopyCoordinates = 1;
4199 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4204 s_AtomIndexFromValue(self, &ap, &mp);
4206 val = rb_Float(val);
4208 if (mp->cell != NULL) {
4209 TransformVec(&v, mp->cell->rtr, &v);
4211 TransformVec(&v, mp->cell->tr, &v);
4214 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4215 mp->needsMDCopyCoordinates = 1;
4219 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4224 s_AtomIndexFromValue(self, &ap, &mp);
4226 val = rb_Float(val);
4228 if (mp->cell != NULL) {
4229 TransformVec(&v, mp->cell->rtr, &v);
4231 TransformVec(&v, mp->cell->tr, &v);
4234 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4235 mp->needsMDCopyCoordinates = 1;
4239 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4242 VALUE oval = s_AtomRef_GetSigma(self);
4243 VectorFromValue(val, &v);
4244 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4245 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4246 mp->needsMDCopyCoordinates = 1;
4250 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4253 VALUE oval = s_AtomRef_GetSigmaX(self);
4254 val = rb_Float(val);
4256 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4257 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4258 mp->needsMDCopyCoordinates = 1;
4262 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4265 VALUE oval = s_AtomRef_GetSigmaY(self);
4266 val = rb_Float(val);
4268 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4269 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4270 mp->needsMDCopyCoordinates = 1;
4274 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4277 VALUE oval = s_AtomRef_GetSigmaZ(self);
4278 val = rb_Float(val);
4280 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4281 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4282 mp->needsMDCopyCoordinates = 1;
4286 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4290 VALUE oval = s_AtomRef_GetV(self);
4291 VectorFromValue(val, &v);
4292 s_AtomIndexFromValue(self, &ap, &mp);
4294 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4295 mp->needsMDCopyCoordinates = 1;
4299 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4302 VALUE oval = s_AtomRef_GetF(self);
4303 VectorFromValue(val, &v);
4304 VecScaleSelf(v, KCAL2INTERNAL);
4305 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4306 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4307 mp->needsMDCopyCoordinates = 1;
4311 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4312 VALUE oval = s_AtomRef_GetOccupancy(self);
4314 val = rb_Float(val);
4315 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4316 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4317 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4321 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4322 VALUE oval = s_AtomRef_GetTempFactor(self);
4323 val = rb_Float(val);
4324 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4325 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4329 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4334 VALUE oval = s_AtomRef_GetAniso(self);
4335 Data_Get_Struct(self, AtomRef, aref);
4336 val = rb_funcall(val, rb_intern("to_a"), 0);
4337 n = RARRAY_LEN(val);
4338 valp = RARRAY_PTR(val);
4339 for (i = 0; i < 6; i++) {
4341 f[i] = NUM2DBL(rb_Float(valp[i]));
4345 type = NUM2INT(rb_Integer(valp[6]));
4348 for (i = 0; i < 6; i++)
4349 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4351 for (i = 0; i < 6; i++)
4354 i = s_AtomIndexFromValue(self, NULL, NULL);
4355 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4356 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4360 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4366 VALUE oval = s_AtomRef_GetSymop(self);
4367 i = s_AtomIndexFromValue(self, &ap, &mol);
4369 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4371 val = rb_funcall(val, rb_intern("to_a"), 0);
4372 n = RARRAY_LEN(val);
4373 valp = RARRAY_PTR(val);
4374 for (i = 0; i < 5; i++) {
4376 if (valp[i] == Qnil)
4379 ival[i] = NUM2INT(rb_Integer(valp[i]));
4380 } else ival[i] = -100000;
4383 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4384 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));
4385 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4386 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4387 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4388 return val; /* No need to change */
4389 if (ival[0] != -100000)
4390 ap->symop.sym = ival[0];
4391 if (ival[1] != -100000)
4392 ap->symop.dx = ival[1];
4393 if (ival[2] != -100000)
4394 ap->symop.dy = ival[2];
4395 if (ival[3] != -100000)
4396 ap->symop.dz = ival[3];
4397 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4398 if (ival[4] != -100000)
4399 ap->symbase = ival[4];
4400 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4401 /* The anisotropic parameters should be recalculated */
4402 VALUE oaval = s_AtomRef_GetAniso(self);
4403 MoleculeSetAnisoBySymop(mol, i);
4404 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4406 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4410 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4411 VALUE oval = s_AtomRef_GetIntCharge(self);
4412 val = rb_Integer(val);
4413 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4414 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4418 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4420 VALUE oval = s_AtomRef_GetFixForce(self);
4421 val = rb_Float(val);
4422 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4423 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4424 mp->needsMDRebuild = 1;
4428 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4431 VALUE oval = s_AtomRef_GetFixPos(self);
4432 VectorFromValue(val, &v);
4433 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4434 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4435 mp->needsMDRebuild = 1;
4439 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4440 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4441 return val; /* Not reached */
4444 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4445 VALUE oval = s_AtomRef_GetIntCharge(self);
4446 val = rb_Integer(val);
4447 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4448 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4452 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4453 VALUE oval = s_AtomRef_GetIntCharge(self);
4454 val = rb_Integer(val);
4455 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4456 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4460 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4461 Atom *ap = s_AtomFromValue(self);
4462 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4464 ap->exflags |= kAtomHiddenFlag;
4466 ap->exflags &= ~kAtomHiddenFlag;
4468 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4472 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4473 Int idx, i, j, k, n, *ip;
4480 MolAction **undoActions;
4481 memset(&ac, 0, sizeof(ac));
4482 idx = s_AtomIndexFromValue(self, &ap, &mol);
4483 oval = s_AtomRef_GetAnchorList(self);
4485 val = rb_ary_to_ary(val);
4486 n = RARRAY_LEN(val);
4489 if (ap->anchor != NULL) {
4490 AtomConnectResize(&ap->anchor->connect, 0);
4491 free(ap->anchor->coeffs);
4494 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4499 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4500 if (ap->aname[0] == '_')
4501 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4502 ip = (Int *)malloc(sizeof(Int) * n);
4504 for (i = 0; i < n; i++) {
4505 v = RARRAY_PTR(val)[i];
4506 if (rb_obj_is_kind_of(v, rb_cFloat))
4508 j = NUM2INT(rb_Integer(v));
4509 if (j < 0 || j >= mol->natoms)
4510 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4511 for (k = 0; k < i; k++) {
4513 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4519 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4520 else if (i * 2 != n)
4521 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4522 dp = (Double *)malloc(sizeof(Double) * n / 2);
4523 for (i = 0; i < n / 2; i++) {
4524 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4526 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4532 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4536 rb_raise(rb_eMolbyError, "invalid argument");
4537 if (nUndoActions > 0) {
4538 for (i = 0; i < nUndoActions; i++) {
4539 MolActionCallback_registerUndo(mol, undoActions[i]);
4540 MolActionRelease(undoActions[i]);
4544 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4548 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4549 Atom *ap = s_AtomFromValue(self);
4550 char *p = StringValuePtr(val);
4551 VALUE oval = s_AtomRef_GetUFFType(self);
4552 strncpy(ap->uff_type, p, 5);
4553 ap->uff_type[5] = 0;
4554 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4558 static struct s_AtomAttrDef {
4560 VALUE *symref; /* Address of s_IndexSymbol etc. */
4561 ID id; /* Will be set within InitMolby() */
4562 VALUE (*getter)(VALUE);
4563 VALUE (*setter)(VALUE, VALUE);
4564 } s_AtomAttrDefTable[] = {
4565 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4566 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4567 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4568 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4569 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4570 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4571 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4572 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4573 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4574 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4575 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4576 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4577 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4578 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4579 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4580 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4581 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4582 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4583 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4584 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4585 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4586 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4587 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4588 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4589 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4590 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4591 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4592 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4593 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4594 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4595 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4596 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4597 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4598 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4599 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4600 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4601 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4602 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4603 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4604 {NULL} /* Sentinel */
4608 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4612 if (TYPE(key) != T_SYMBOL) {
4613 kid = rb_intern(StringValuePtr(key));
4615 } else kid = SYM2ID(key);
4616 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4617 if (s_AtomAttrDefTable[i].id == kid) {
4618 if (value == Qundef)
4619 return (*(s_AtomAttrDefTable[i].getter))(self);
4621 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4624 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4625 return Qnil; /* not reached */
4629 s_AtomRef_GetAttr(VALUE self, VALUE key)
4631 return s_AtomRef_SetAttr(self, key, Qundef);
4636 * self == atomRef -> boolean
4638 * True if the two references point to the same atom.
4641 s_AtomRef_Equal(VALUE self, VALUE val)
4643 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4644 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4645 } else return Qfalse;
4648 #pragma mark ====== MolEnumerable Class ======
4650 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4654 * self[idx] -> AtomRef or Array of Integers
4656 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4657 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4658 * value is a String. Otherwise, the return value is an Array of Integers.
4661 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4663 MolEnumerable *mseq;
4666 Data_Get_Struct(self, MolEnumerable, mseq);
4668 if (mseq->kind == kAtomKind) {
4669 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4671 idx1 = NUM2INT(arg1);
4672 switch (mseq->kind) {
4674 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4675 if (idx2 < 0 || idx2 >= mol->nbonds)
4676 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4677 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4680 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4681 if (idx2 < 0 || idx2 >= mol->nangles)
4682 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4683 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4685 case kDihedralKind: {
4686 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4687 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4688 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4689 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]));
4691 case kImproperKind: {
4692 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4693 if (idx2 < 0 || idx2 >= mol->nimpropers)
4694 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4695 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]));
4697 case kResidueKind: {
4699 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4700 if (idx2 < 0 || idx2 >= mol->nresidues)
4701 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4702 p = mol->residues[idx2];
4703 return rb_str_new(p, strlen_limit(p, 4));
4713 * Returns the number of objects included in this enumerable.
4716 s_MolEnumerable_Length(VALUE self)
4718 MolEnumerable *mseq;
4719 Data_Get_Struct(self, MolEnumerable, mseq);
4720 switch (mseq->kind) {
4722 return INT2NUM(mseq->mol->natoms);
4724 return INT2NUM(mseq->mol->nbonds);
4726 return INT2NUM(mseq->mol->nangles);
4728 return INT2NUM(mseq->mol->ndihedrals);
4730 return INT2NUM(mseq->mol->nimpropers);
4732 return INT2NUM(mseq->mol->nresidues);
4741 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4742 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4743 * For the atoms, a same AtomRef object is passed (with different internal information)
4744 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4745 * for each iteration.
4748 s_MolEnumerable_Each(VALUE self)
4750 MolEnumerable *mseq;
4752 int len = NUM2INT(s_MolEnumerable_Length(self));
4753 Data_Get_Struct(self, MolEnumerable, mseq);
4754 if (mseq->kind == kAtomKind) {
4755 /* The same AtomRef object will be used during the loop */
4756 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4757 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4758 for (i = 0; i < len; i++) {
4763 /* A new ruby object will be created at each iteration (not very efficient) */
4764 for (i = 0; i < len; i++) {
4765 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4773 * self == molEnumerable -> boolean
4775 * True if the two arguments point to the same molecule and enumerable type.
4778 s_MolEnumerable_Equal(VALUE self, VALUE val)
4780 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4781 MolEnumerable *mseq1, *mseq2;
4782 Data_Get_Struct(self, MolEnumerable, mseq1);
4783 Data_Get_Struct(val, MolEnumerable, mseq2);
4784 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4785 } else return Qfalse;
4789 #pragma mark ====== Molecule Class ======
4791 #pragma mark ------ Allocate/Release/Accessor ------
4793 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method. */
4794 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4795 char *gLoadSaveErrorMessage = NULL;
4797 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4800 MoleculeFromValue(VALUE val)
4803 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4804 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4805 Data_Get_Struct(val, Molecule, mol);
4809 static VALUE sMoleculeRetainArray = Qnil;
4811 /* The function is called from MoleculeRelease() */
4812 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4813 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4814 /* object is always returned for the same Molecule. */
4815 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4816 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4817 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4818 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4819 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4821 /* Register/unregister the exmolobj Ruby object */
4823 MoleculeReleaseExternalObj(Molecule *mol)
4825 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4826 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4827 mol->exmolobjProtected = 0;
4832 MoleculeRetainExternalObj(Molecule *mol)
4834 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4835 if (sMoleculeRetainArray == Qnil) {
4836 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4837 sMoleculeRetainArray = rb_ary_new();
4840 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4841 mol->exmolobjProtected = 1;
4845 /* Release hook function for Ruby */
4847 MoleculeReleaseHook(Molecule *mol)
4849 if (mol->exmolobj != NULL) {
4850 /* No need to remove from sMoleculeRetainArray */
4851 mol->exmolobj = NULL;
4852 mol->exmolobjProtected = 0;
4854 MoleculeRelease(mol);
4858 ValueFromMolecule(Molecule *mol)
4862 if (mol->exmolobj != NULL)
4863 return (VALUE)mol->exmolobj;
4864 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4865 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4866 return (VALUE)mol->exmolobj;
4871 s_Molecule_Alloc(VALUE klass)
4874 Molecule *mol = MoleculeNew();
4875 val = ValueFromMolecule(mol);
4876 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4881 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4885 if (FIXNUM_P(val)) {
4887 if (n >= 0 && n < mol->natoms)
4889 n = -1; /* No such atom */
4890 val = rb_inspect(val);
4892 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4894 if (n >= 0 && n < mol->natoms)
4896 p = StringValuePtr(val);
4898 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4900 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4902 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4903 return 0; /* Not reached */
4907 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4910 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4911 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4912 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4913 Data_Get_Struct(val, IntGroup, ig);
4922 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4923 * created object does not affect the old object in any sense.
4926 s_Molecule_InitCopy(VALUE self, VALUE arg)
4928 Molecule *mp1, *mp2;
4929 Data_Get_Struct(self, Molecule, mp1);
4930 mp2 = MoleculeFromValue(arg);
4931 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4932 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4938 * atom_index(val) -> Integer
4940 * Returns the atom index represented by val. val can be either a non-negative integer
4941 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4942 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
4943 * where resname, resid, name are the residue name, residue id, and atom name respectively.
4944 * If val is a string and multiple atoms match the description, the atom with the lowest index
4948 s_Molecule_AtomIndex(VALUE self, VALUE val)
4951 Data_Get_Struct(self, Molecule, mol);
4952 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4957 * self == Molecule -> boolean
4959 * True if the two arguments point to the same molecule.
4962 s_Molecule_Equal(VALUE self, VALUE val)
4964 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4965 Molecule *mol1, *mol2;
4966 Data_Get_Struct(self, Molecule, mol1);
4967 Data_Get_Struct(val, Molecule, mol2);
4968 return (mol1 == mol2 ? Qtrue : Qfalse);
4969 } else return Qfalse;
4972 #pragma mark ------ Load/Save ------
4975 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4977 if (gLoadSaveErrorMessage != NULL) {
4978 MyAppCallback_setConsoleColor(1);
4979 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4980 MyAppCallback_setConsoleColor(0);
4983 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4988 * loadmbsf(file) -> bool
4990 * Read a structure from a mbsf file.
4991 * Return true if successful.
4994 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5000 MoleculeClearLoadSaveErrorMessage();
5001 Data_Get_Struct(self, Molecule, mol);
5002 rb_scan_args(argc, argv, "1", &fname);
5003 fstr = FileStringValuePtr(fname);
5004 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5005 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5011 * loadpsf(file, pdbfile = nil) -> bool
5013 * Read a structure from a psf file. molecule must be empty. The psf may be
5014 * an "extended" version, which also contains coordinates. If pdbfile
5015 * is given, then atomic coordinates are read from that file.
5016 * Return true if successful.
5019 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5021 VALUE fname, pdbname;
5022 char *fstr, *pdbstr;
5025 Data_Get_Struct(self, Molecule, mol);
5026 if (mol->natoms > 0)
5027 return Qnil; /* Must be a new molecule */
5028 MoleculeClearLoadSaveErrorMessage();
5029 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5030 fstr = FileStringValuePtr(fname);
5031 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5032 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5034 if (!NIL_P(pdbname)) {
5035 pdbstr = strdup(FileStringValuePtr(pdbname));
5036 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5038 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5045 * loadpdb(file) -> bool
5047 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5048 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5049 * Return true if successful.
5052 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5058 Data_Get_Struct(self, Molecule, mol);
5059 rb_scan_args(argc, argv, "1", &fname);
5060 MoleculeClearLoadSaveErrorMessage();
5061 fstr = FileStringValuePtr(fname);
5062 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5063 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5069 * loaddcd(file) -> bool
5071 * Read coordinates from a dcd file. The molecule should not empty.
5072 * Return true if successful.
5075 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5081 Data_Get_Struct(self, Molecule, mol);
5082 rb_scan_args(argc, argv, "1", &fname);
5083 MoleculeClearLoadSaveErrorMessage();
5084 fstr = FileStringValuePtr(fname);
5085 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5086 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5092 * loadtep(file) -> bool
5094 * Read coordinates from an ortep .tep file.
5095 * Return true if successful.
5098 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5104 Data_Get_Struct(self, Molecule, mol);
5105 rb_scan_args(argc, argv, "1", &fname);
5106 MoleculeClearLoadSaveErrorMessage();
5107 fstr = FileStringValuePtr(fname);
5108 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5109 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5115 * loadres(file) -> bool
5117 * Read coordinates from a shelx .res file.
5118 * Return true if successful.
5121 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5127 Data_Get_Struct(self, Molecule, mol);
5128 rb_scan_args(argc, argv, "1", &fname);
5129 MoleculeClearLoadSaveErrorMessage();
5130 fstr = FileStringValuePtr(fname);
5131 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5132 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5138 * loadfchk(file) -> bool
5140 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5141 * Return true if successful.
5144 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5150 Data_Get_Struct(self, Molecule, mol);
5151 rb_scan_args(argc, argv, "1", &fname);
5152 MoleculeClearLoadSaveErrorMessage();
5153 fstr = FileStringValuePtr(fname);
5154 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5155 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5161 * loaddat(file) -> bool
5163 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5164 * Return true if successful.
5167 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5173 Data_Get_Struct(self, Molecule, mol);
5174 rb_scan_args(argc, argv, "1", &fname);
5175 MoleculeClearLoadSaveErrorMessage();
5176 fstr = FileStringValuePtr(fname);
5177 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5178 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5179 MyAppCallback_hideProgressPanel();
5180 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5186 * savembsf(file) -> bool
5188 * Write structure as a mbsf file. Returns true if successful.
5191 s_Molecule_Savembsf(VALUE self, VALUE fname)
5196 Data_Get_Struct(self, Molecule, mol);
5197 MoleculeClearLoadSaveErrorMessage();
5198 fstr = FileStringValuePtr(fname);
5199 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5200 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5206 * savepsf(file) -> bool
5208 * Write structure as a psf file. Returns true if successful.
5211 s_Molecule_Savepsf(VALUE self, VALUE fname)
5216 Data_Get_Struct(self, Molecule, mol);
5217 MoleculeClearLoadSaveErrorMessage();
5218 fstr = FileStringValuePtr(fname);
5219 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5220 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5226 * savepdb(file) -> bool
5228 * Write coordinates as a pdb file. Returns true if successful.
5231 s_Molecule_Savepdb(VALUE self, VALUE fname)
5236 Data_Get_Struct(self, Molecule, mol);
5237 MoleculeClearLoadSaveErrorMessage();
5238 fstr = FileStringValuePtr(fname);
5239 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5240 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5246 * savedcd(file) -> bool
5248 * Write coordinates as a dcd file. Returns true if successful.
5251 s_Molecule_Savedcd(VALUE self, VALUE fname)
5256 Data_Get_Struct(self, Molecule, mol);
5257 MoleculeClearLoadSaveErrorMessage();
5258 fstr = FileStringValuePtr(fname);
5259 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5260 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5264 /* load([ftype, ] fname, ...) */
5266 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5269 char *argstr, *methname, *p, *type = "";
5272 const char *ls = (loadFlag ? "load" : "save");
5273 int lslen = strlen(ls);
5278 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5279 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5280 if (argstr[0] == ':') {
5281 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5282 methname = ALLOC_N(char, lslen + strlen(argstr));
5283 strcpy(methname, ls);
5284 strcat(methname, argstr + 1);
5286 for (i = lslen; methname[i] != 0; i++)
5287 methname[i] = tolower(methname[i]);
5288 mid = rb_intern(methname);
5292 rval = rb_funcall2(self, mid, argc, argv);
5298 /* Guess file type from extension */
5299 p = strrchr(argstr, '.');
5303 for (methname = p; *methname != 0; methname++) {
5304 if (!isalpha(*methname))
5307 if (*methname == 0) {
5308 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5309 if (methname == NULL)
5310 rb_raise(rb_eMolbyError, "Low memory");
5311 strcpy(methname, ls);
5312 strcat(methname, p);
5313 for (i = lslen; methname[i] != 0; i++)
5314 methname[i] = tolower(methname[i]);
5315 mid = rb_intern(methname);
5318 if (rb_respond_to(self, mid)) {
5319 /* Load: try to call the load procedure only if it is available */
5320 rval = rb_funcall2(self, mid, argc, argv);
5325 /* Save: call the save procedure, and if not found then call 'method_missing' */
5326 rval = rb_funcall2(self, mid, argc, argv);
5333 rval = rb_str_to_str(argv[0]);
5334 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5335 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5336 return Qnil; /* Does not reach here */
5340 /* Register the path */
5343 Data_Get_Struct(self, Molecule, mol);
5344 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5346 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5347 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5348 if (ap->occupancy != 0.0)
5351 if (i == mol->natoms) {
5352 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5353 ap->occupancy = 1.0;
5362 * molload(file, *args) -> bool
5364 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5365 * file type given by the extension). If this method fails, then all defined (public)
5366 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5369 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5371 return s_Molecule_LoadSave(argc, argv, self, 1);
5376 * molsave(file, *args) -> bool
5378 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5379 * (XXX is the file type given by the extension).
5382 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5384 return s_Molecule_LoadSave(argc, argv, self, 0);
5390 * open(file) -> Molecule
5392 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5395 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5403 rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5406 rb_scan_args(argc, argv, "01", &fname);
5410 p = FileStringValuePtr(fname);
5411 iflag = Ruby_SetInterruptFlag(Qfalse);
5412 mp = MoleculeCallback_openNewMolecule(p);
5413 Ruby_SetInterruptFlag(iflag);
5416 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5418 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5420 return ValueFromMolecule(mp);
5426 * new(file, *args) -> Molecule
5428 * Create a new molecule and call "load" method with the same arguments.
5431 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5434 return s_Molecule_Load(argc, argv, self);
5435 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5440 * error_message -> String
5442 * Get the error_message from the last load/save method. If no error, returns nil.
5445 s_Molecule_ErrorMessage(VALUE klass)
5447 if (gLoadSaveErrorMessage == NULL)
5449 else return rb_str_new2(gLoadSaveErrorMessage);
5454 * set_error_message(String)
5455 * Molecule.error_message = String
5457 * Set the error_message for the present load/save method.
5460 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5462 if (gLoadSaveErrorMessage != NULL) {
5463 free(gLoadSaveErrorMessage);
5464 gLoadSaveErrorMessage = NULL;
5467 sval = rb_str_to_str(sval);
5468 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5475 * set_molecule(Molecule)
5477 * Duplicate the given molecule and set to self. The present molecule must be empty.
5478 * This method is exclusively used for associating a new document with an existing molecule.
5481 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5483 Molecule *mp1, *mp2;
5484 Data_Get_Struct(self, Molecule, mp1);
5485 mp2 = MoleculeFromValue(mval);
5486 MoleculeInitWithMolecule(mp1, mp2);
5487 MoleculeCallback_notifyModification(mp1, 1);
5491 #pragma mark ------ Name attributes ------
5497 * Returns the display name of the molecule. If the molecule has no associated
5498 * document, then returns nil.
5501 s_Molecule_Name(VALUE self)
5505 Data_Get_Struct(self, Molecule, mol);
5506 MoleculeCallback_displayName(mol, buf, sizeof buf);
5510 return rb_str_new2(buf);
5515 * set_name(string) -> self
5517 * Set the name of an untitled molecule. If the molecule is not associated with window
5518 * or it already has an associated file, then exception is thrown.
5521 s_Molecule_SetName(VALUE self, VALUE nval)
5524 Data_Get_Struct(self, Molecule, mol);
5525 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5526 rb_raise(rb_eMolbyError, "Cannot change the window title");
5535 * Returns the full path name of the molecule, if it is associated with a file.
5536 * If the molecule has no associated file, then returns nil.
5539 s_Molecule_Path(VALUE self)
5543 Data_Get_Struct(self, Molecule, mol);
5544 MoleculeCallback_pathName(mol, buf, sizeof buf);
5548 return Ruby_NewFileStringValue(buf);
5555 * Returns the full path name of the directory in which the file associated with the
5556 * molecule is located. If the molecule has no associated file, then returns nil.
5559 s_Molecule_Dir(VALUE self)
5563 Data_Get_Struct(self, Molecule, mol);
5564 MoleculeCallback_pathName(mol, buf, sizeof buf);
5566 translate_char(buf, '\\', '/');
5571 p = strrchr(buf, '/');
5574 return rb_str_new2(buf);
5582 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5583 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5584 * the Molecule structure) is returned.
5587 s_Molecule_Inspect(VALUE self)
5591 Data_Get_Struct(self, Molecule, mol);
5592 MoleculeCallback_displayName(mol, buf, sizeof buf);
5594 /* No associated document */
5595 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5596 return rb_str_new2(buf);
5598 /* Check whether the document name is duplicate */
5602 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5603 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5604 if (strcmp(buf, buf2) == 0) {
5611 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5613 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5615 return rb_str_new2(buf2);
5619 #pragma mark ------ MolEnumerables ------
5622 s_Molecule_MolEnumerable(VALUE self, int kind)
5625 MolEnumerable *mseq;
5626 Data_Get_Struct(self, Molecule, mol);
5627 mseq = MolEnumerableNew(mol, kind);
5628 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5633 * atoms -> MolEnumerable
5635 * Returns a MolEnumerable object representing the array of atoms.
5638 s_Molecule_Atoms(VALUE self)
5640 return s_Molecule_MolEnumerable(self, kAtomKind);
5645 * bonds -> MolEnumerable
5647 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5648 * by an array of two atom indices.
5651 s_Molecule_Bonds(VALUE self)
5653 return s_Molecule_MolEnumerable(self, kBondKind);
5658 * angles -> MolEnumerable
5660 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5661 * by an array of three atom indices.
5664 s_Molecule_Angles(VALUE self)
5666 return s_Molecule_MolEnumerable(self, kAngleKind);
5671 * dihedrals -> MolEnumerable
5673 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5674 * by an array of four atom indices.
5677 s_Molecule_Dihedrals(VALUE self)
5679 return s_Molecule_MolEnumerable(self, kDihedralKind);
5684 * impropers -> MolEnumerable
5686 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5687 * by an array of four atom indices.
5690 s_Molecule_Impropers(VALUE self)
5692 return s_Molecule_MolEnumerable(self, kImproperKind);
5697 * residues -> MolEnumerable
5699 * Returns a MolEnumerable object representing the array of residue names.
5702 s_Molecule_Residues(VALUE self)
5704 return s_Molecule_MolEnumerable(self, kResidueKind);
5711 * Returns the number of atoms.
5714 s_Molecule_Natoms(VALUE self)
5717 Data_Get_Struct(self, Molecule, mol);
5718 return INT2NUM(mol->natoms);
5725 * Returns the number of bonds.
5728 s_Molecule_Nbonds(VALUE self)
5731 Data_Get_Struct(self, Molecule, mol);
5732 return INT2NUM(mol->nbonds);
5737 * nangles -> Integer
5739 * Returns the number of angles.
5742 s_Molecule_Nangles(VALUE self)
5745 Data_Get_Struct(self, Molecule, mol);
5746 return INT2NUM(mol->nangles);
5751 * ndihedrals -> Integer
5753 * Returns the number of dihedrals.
5756 s_Molecule_Ndihedrals(VALUE self)
5759 Data_Get_Struct(self, Molecule, mol);
5760 return INT2NUM(mol->ndihedrals);
5765 * nimpropers -> Integer
5767 * Returns the number of impropers.
5770 s_Molecule_Nimpropers(VALUE self)
5773 Data_Get_Struct(self, Molecule, mol);
5774 return INT2NUM(mol->nimpropers);
5779 * nresidues -> Integer
5781 * Returns the number of residues.
5784 s_Molecule_Nresidues(VALUE self)
5787 Data_Get_Struct(self, Molecule, mol);
5788 return INT2NUM(mol->nresidues);
5793 * nresidues = Integer
5795 * Change the number of residues.
5798 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5801 int ival = NUM2INT(val);
5802 Data_Get_Struct(self, Molecule, mol);
5803 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5804 if (ival != mol->nresidues)
5805 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5811 * max_residue_number(atom_group = nil) -> Integer
5813 * Returns the maximum residue number actually used. If an atom group is given, only
5814 * these atoms are examined. If no atom is present, nil is returned.
5817 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5823 Data_Get_Struct(self, Molecule, mol);
5824 rb_scan_args(argc, argv, "01", &gval);
5825 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5826 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5827 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5832 * min_residue_number(atom_group = nil) -> Integer
5834 * Returns the minimum residue number actually used. If an atom group is given, only
5835 * these atoms are examined. If no atom is present, nil is returned.
5838 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5844 Data_Get_Struct(self, Molecule, mol);
5845 rb_scan_args(argc, argv, "01", &gval);
5846 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5847 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5848 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5853 * each_atom(atom_group = nil) {|aref| ...}
5855 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5856 * group is given, only these atoms are processed.
5857 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5858 * is self (a Molecule object).
5861 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5869 Data_Get_Struct(self, Molecule, mol);
5870 rb_scan_args(argc, argv, "01", &gval);
5871 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5872 arval = ValueFromMoleculeAndIndex(mol, 0);
5873 Data_Get_Struct(arval, AtomRef, aref);
5874 for (i = 0; i < mol->natoms; i++) {
5876 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5880 IntGroupRelease(ig);
5884 #pragma mark ------ Atom Group ------
5887 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5889 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5890 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5891 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5892 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5899 * atom_group {|aref| ...}
5900 * atom_group(arg1, arg2, ...)
5901 * atom_group(arg1, arg2, ...) {|aref| ...}
5903 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5904 * If arguments are given, then the atoms reprensented by the arguments are added to the
5905 * group. For a conversion of a string to an atom index, see the description
5906 * of Molecule#atom_index.
5907 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5908 * representing each atom, and the atoms are removed from the result if the block returns false.
5912 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5914 IntGroup *ig1, *ig2;
5916 Int i, startPt, interval;
5917 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5918 Data_Get_Struct(retval, IntGroup, ig1);
5919 Data_Get_Struct(self, Molecule, mol);
5921 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5924 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5925 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5926 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5927 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5928 ig2 = IntGroupFromValue(*argv);
5929 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5930 interval = IntGroupGetInterval(ig2, i);
5931 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5933 IntGroupRelease(ig2);
5934 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5936 values[0] = (VALUE)mol;
5937 values[1] = (VALUE)ig1;
5938 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5940 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5945 if (rb_block_given_p()) {
5946 /* Evaluate the given block with an AtomRef as the argument, and delete
5947 the index if the block returns false */
5948 AtomRef *aref = AtomRefNew(mol, 0);
5949 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5950 ig2 = IntGroupNew();
5951 IntGroupCopy(ig2, ig1);
5952 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5954 if (startPt >= mol->natoms)
5956 aref->idx = startPt;
5957 resval = rb_yield(arval);
5959 IntGroupRemove(ig1, startPt, 1);
5961 IntGroupRelease(ig2);
5964 /* Remove points that are out of bounds */
5965 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5972 * selection -> IntGroup
5974 * Returns the current selection.
5977 s_Molecule_Selection(VALUE self)
5982 Data_Get_Struct(self, Molecule, mol);
5983 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5984 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
5985 val = ValueFromIntGroup(ig);
5986 IntGroupRelease(ig);
5988 val = IntGroup_Alloc(rb_cIntGroup);
5994 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5998 Data_Get_Struct(self, Molecule, mol);
6002 ig = s_Molecule_AtomGroupFromValue(self, val);
6004 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6006 MoleculeSetSelection(mol, ig);
6008 IntGroupRelease(ig);
6014 * selection = IntGroup
6016 * Set the current selection. The right-hand operand may be nil.
6017 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6020 s_Molecule_SetSelection(VALUE self, VALUE val)
6022 return s_Molecule_SetSelectionSub(self, val, 0);
6027 * set_undoable_selection(IntGroup)
6029 * Set the current selection with undo registration. The right-hand operand may be nil.
6030 * This operation is undoable.
6033 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6035 return s_Molecule_SetSelectionSub(self, val, 1);
6038 #pragma mark ------ Editing ------
6042 * extract(group, dummy_flag = nil) -> Molecule
6044 * Extract the atoms given by group and return as a new molecule object.
6045 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6046 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6047 * names beginning with an underscore) and included in the new molecule object.
6050 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6052 Molecule *mol1, *mol2;
6054 VALUE group, dummy_flag, retval;
6055 Data_Get_Struct(self, Molecule, mol1);
6056 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6057 ig = s_Molecule_AtomGroupFromValue(self, group);
6058 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6061 retval = ValueFromMolecule(mol2);
6063 IntGroupRelease(ig);
6069 * add(molecule2) -> self
6071 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6073 This operation is undoable.
6076 s_Molecule_Add(VALUE self, VALUE val)
6078 Molecule *mol1, *mol2;
6079 Data_Get_Struct(self, Molecule, mol1);
6080 mol2 = MoleculeFromValue(val);
6081 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6087 * remove(group) -> Molecule
6089 * The atoms designated by the given group are removed from the molecule.
6090 * This operation is undoable.
6093 s_Molecule_Remove(VALUE self, VALUE group)
6098 IntGroupIterator iter;
6100 Data_Get_Struct(self, Molecule, mol1);
6101 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6102 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6103 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6104 Data_Get_Struct(group, IntGroup, ig);
6106 /* Remove the bonds between the two fragments */
6107 /* (This is necessary for undo to work correctly) */
6108 IntGroupIteratorInit(ig, &iter);
6110 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6111 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6113 cp = AtomConnectData(&ap->connect);
6114 for (j = 0; j < ap->connect.count; j++) {
6116 if (!IntGroupLookup(ig, n, NULL)) {
6117 /* bond i-n, i is in ig and n is not */
6118 int k = MoleculeLookupBond(mol1, i, n);
6122 IntGroupAdd(bg, k, 1);
6127 IntGroupIteratorRelease(&iter);
6130 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6131 IntGroupRelease(bg);
6134 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6141 * create_atom(name, pos = -1) -> AtomRef
6143 * Create a new atom with the specified name (may contain residue
6144 * information) and position (if position is out of range, the atom is appended at
6145 * the end). Returns the reference to the new atom.
6146 * This operation is undoable.
6149 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6156 char *p, resName[6], atomName[6];
6158 Data_Get_Struct(self, Molecule, mol);
6159 rb_scan_args(argc, argv, "02", &name, &ival);
6161 pos = NUM2INT(rb_Integer(ival));
6164 p = StringValuePtr(name);
6166 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6167 if (atomName[0] == 0)
6168 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6171 if (p == NULL || p[0] == 0) {
6172 memset(atomName, 0, 4);
6175 memset(&arec, 0, sizeof(arec));
6176 strncpy(arec.aname, atomName, 4);
6178 strncpy(arec.resName, resName, 4);
6179 arec.resSeq = resSeq;
6181 arec.occupancy = 1.0;
6182 // i = MoleculeCreateAnAtom(mol, &arec);
6183 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6185 aref = AtomRefNew(mol, pos);
6186 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6191 * duplicate_atom(atomref, pos = -1) -> AtomRef
6193 * Create a new atom with the same attributes (but no bonding information)
6194 * with the specified atom. Returns the reference to the new atom.
6195 * This operation is undoable.
6198 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6204 VALUE retval, aval, ival;
6206 Data_Get_Struct(self, Molecule, mol);
6207 rb_scan_args(argc, argv, "11", &aval, &ival);
6208 if (FIXNUM_P(aval)) {
6209 int idx = NUM2INT(aval);
6210 if (idx < 0 || idx >= mol->natoms)
6211 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6212 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6214 apsrc = s_AtomFromValue(aval);
6217 rb_raise(rb_eMolbyError, "bad atom specification");
6219 pos = NUM2INT(rb_Integer(ival));
6221 AtomDuplicate(&arec, apsrc);
6222 arec.connect.count = 0;
6223 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6226 aref = AtomRefNew(mol, pos);
6227 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6235 * create_bond(n1, n2, ...) -> Integer
6237 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6238 * do nothing for that pair. Returns the number of bonds actually created.
6239 * This operation is undoable.
6242 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6245 Int i, j, k, *ip, old_nbonds;
6247 rb_raise(rb_eMolbyError, "missing arguments");
6249 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6250 Data_Get_Struct(self, Molecule, mol);
6251 ip = ALLOC_N(Int, argc + 1);
6252 for (i = j = 0; i < argc; i++, j++) {
6253 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6255 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6256 j -= 2; /* This bond is already present: skip it */
6258 for (k = 0; k < j - 1; k += 2) {
6259 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6260 j -= 2; /* The same entry is already in the argument */
6267 old_nbonds = mol->nbonds;
6269 ip[j] = kInvalidIndex;
6270 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6274 rb_raise(rb_eMolbyError, "atom index out of range");
6276 rb_raise(rb_eMolbyError, "too many bonds");
6278 rb_raise(rb_eMolbyError, "duplicate bonds");
6280 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6282 rb_raise(rb_eMolbyError, "error in creating bonds");
6283 return INT2NUM(mol->nbonds - old_nbonds);
6288 * molecule.remove_bonds(n1, n2, ...) -> Integer
6290 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6291 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6292 * This operation is undoable.
6295 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6301 rb_raise(rb_eMolbyError, "missing arguments");
6303 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6304 Data_Get_Struct(self, Molecule, mol);
6306 for (i = j = 0; i < argc; i++, j = 1 - j) {
6307 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6309 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6313 IntGroupAdd(bg, k, 1);
6318 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6319 i = IntGroupGetCount(bg);
6320 IntGroupRelease(bg);
6327 * assign_bond_order(idx, d1)
6328 * assign_bond_orders(group, [d1, d2, ...])
6330 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6331 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6332 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6333 * (This may change in the future)
6334 * This operation is undoable.
6337 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6341 Data_Get_Struct(self, Molecule, mol);
6342 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6343 /* The first form */
6344 Int idx = NUM2INT(rb_Integer(idxval));
6345 Double d1 = NUM2DBL(rb_Float(dval));
6346 if (idx < 0 || idx >= mol->nbonds)
6347 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6348 ig = IntGroupNewWithPoints(idx, 1, -1);
6349 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6350 IntGroupRelease(ig);
6354 ig = IntGroupFromValue(idxval);
6355 n = IntGroupGetCount(ig);
6357 rb_raise(rb_eMolbyError, "the bond index is empty");
6358 dval = rb_ary_to_ary(dval);
6359 dp = (Double *)calloc(sizeof(Double), n);
6360 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6361 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6363 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6365 IntGroupRelease(ig);
6372 * get_bond_order(idx) -> Float
6373 * get_bond_orders(group) -> Array
6375 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6376 * In the second form, the bond orders at the indices in the group are returned as an array.
6377 * If no bond order information have been assigned, returns nil (the first form)
6378 * or an empty array (the second form).
6381 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6387 Int i, n, numericArg;
6388 Data_Get_Struct(self, Molecule, mol);
6389 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6390 /* The first form */
6391 Int idx = NUM2INT(rb_Integer(idxval));
6392 if (idx < 0 || idx >= mol->nbonds)
6393 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6394 if (mol->bondOrders == NULL)
6396 ig = IntGroupNewWithPoints(idx, 1, -1);
6400 if (mol->bondOrders == NULL)
6401 return rb_ary_new();
6402 ig = IntGroupFromValue(idxval);
6403 n = IntGroupGetCount(ig);
6405 rb_raise(rb_eMolbyError, "the bond index is empty");
6408 dp = (Double *)calloc(sizeof(Double), n);
6409 MoleculeGetBondOrders(mol, dp, ig);
6411 retval = rb_float_new(dp[0]);
6413 retval = rb_ary_new();
6414 for (i = 0; i < n; i++)
6415 rb_ary_push(retval, rb_float_new(dp[i]));
6418 IntGroupRelease(ig);
6424 * bond_exist?(idx1, idx2) -> bool
6426 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6427 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6430 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6436 Data_Get_Struct(self, Molecule, mol);
6437 idx1 = NUM2INT(rb_Integer(ival1));
6438 idx2 = NUM2INT(rb_Integer(ival2));
6439 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6440 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6441 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6442 cp = AtomConnectData(&ap->connect);
6443 for (i = 0; i < ap->connect.count; i++) {
6452 * add_angle(n1, n2, n3) -> Molecule
6454 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6455 * when a bond is created, so it is rarely necessary to use this method explicitly.
6456 * This operation is undoable.
6459 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6463 Data_Get_Struct(self, Molecule, mol);
6464 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6465 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6466 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6467 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6468 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6469 n[3] = kInvalidIndex;
6470 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6476 * remove_angle(n1, n2, n3) -> Molecule
6478 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6479 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6480 * This operation is undoable.
6483 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6488 Data_Get_Struct(self, Molecule, mol);
6489 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6490 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6491 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6492 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6493 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6494 ig = IntGroupNewWithPoints(n[3], 1, -1);
6495 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6496 IntGroupRelease(ig);
6502 * add_dihedral(n1, n2, n3, n4) -> Molecule
6504 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6505 * when a bond is created, so it is rarely necessary to use this method explicitly.
6506 * This operation is undoable.
6509 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6513 Data_Get_Struct(self, Molecule, mol);
6514 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6515 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6516 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6517 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6518 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6519 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6520 n[4] = kInvalidIndex;
6521 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6527 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6529 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6530 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6531 * This operation is undoable.
6534 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6539 Data_Get_Struct(self, Molecule, mol);
6540 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6541 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6542 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6543 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6544 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6545 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6546 ig = IntGroupNewWithPoints(n[4], 1, -1);
6547 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6548 IntGroupRelease(ig);
6554 * add_improper(n1, n2, n3, n4) -> Molecule
6556 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6557 * not automatically added when a new bond is created, so this method is more useful than
6558 * the angle/dihedral counterpart.
6559 * This operation is undoable.
6562 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6566 Data_Get_Struct(self, Molecule, mol);
6567 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6568 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6569 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6570 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6571 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6572 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6573 n[4] = kInvalidIndex;
6574 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6580 * remove_improper(n1, n2, n3, n4) -> Molecule
6581 * remove_improper(intgroup) -> Molecule
6583 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6584 * Returns self. Unlike angles and dihedrals, impropers are
6585 * not automatically added when a new bond is created, so this method is more useful than
6586 * the angle/dihedral counterpart.
6587 * This operation is undoable.
6590 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6593 VALUE v1, v2, v3, v4;
6596 Data_Get_Struct(self, Molecule, mol);
6598 ig = IntGroupFromValue(argv[0]);
6600 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6601 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6602 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6603 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6604 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6605 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6606 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6607 ig = IntGroupNewWithPoints(n[4], 1, -1);
6609 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6610 IntGroupRelease(ig);
6616 * assign_residue(group, res) -> Molecule
6618 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6619 * or "resname.resno". When the residue number is not specified, the residue number of
6620 * the first atom in the group is used.
6621 * This operation is undoable.
6624 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6628 char *p, *pp, buf[16];
6631 Data_Get_Struct(self, Molecule, mol);
6633 /* Parse the argument res */
6634 if (FIXNUM_P(res)) {
6635 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6636 resid = NUM2INT(res);
6639 p = StringValuePtr(res);
6640 pp = strchr(p, '.');
6642 resid = atoi(pp + 1);
6648 if (n > sizeof buf - 1)
6653 ig = s_Molecule_AtomGroupFromValue(self, range);
6654 if (ig == NULL || IntGroupGetCount(ig) == 0)
6658 /* Use the residue number of the first specified atom */
6659 n = IntGroupGetNthPoint(ig, 0);
6660 if (n >= mol->natoms)
6661 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6662 ap = ATOM_AT_INDEX(mol->atoms, n);
6665 /* Change the residue number */
6666 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6667 /* Change the residue name if necessary */
6671 seqs[1] = kInvalidIndex; */
6672 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6674 IntGroupRelease(ig);
6680 * offset_residue(group, offset) -> Molecule
6682 * Offset the residue number of the specified atoms. If any of the residue number gets
6683 * negative, then exception is thrown.
6684 * This operation is undoable.
6687 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6692 Data_Get_Struct(self, Molecule, mol);
6693 ig = s_Molecule_AtomGroupFromValue(self, range);
6694 ofs = NUM2INT(offset);
6695 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6697 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6698 IntGroupRelease(ig);
6704 * renumber_atoms(array) -> IntGroup
6706 * Change the order of atoms so that the atoms specified in the array argument appear
6707 * in this order from the top of the molecule. The atoms that are not included in array
6708 * are placed after these atoms, and these atoms are returned as an intGroup.
6709 * This operation is undoable.
6712 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6718 VALUE *valp, retval;
6719 Data_Get_Struct(self, Molecule, mol);
6720 if (TYPE(array) != T_ARRAY)
6721 array = rb_funcall(array, rb_intern("to_a"), 0);
6722 n = RARRAY_LEN(array);
6723 valp = RARRAY_PTR(array);
6724 new2old = ALLOC_N(Int, n + 1);
6725 for (i = 0; i < n; i++)
6726 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6727 new2old[i] = kInvalidIndex;
6728 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6730 rb_raise(rb_eMolbyError, "Atom index out of range");
6732 rb_raise(rb_eMolbyError, "Duplicate entry");
6734 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6735 retval = IntGroup_Alloc(rb_cIntGroup);
6736 Data_Get_Struct(retval, IntGroup, ig);
6737 if (mol->natoms > n)
6738 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6745 * set_atom_attr(index, key, value)
6747 * Set the atom attribute for the specified atom.
6748 * This operation is undoable.
6751 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6755 Data_Get_Struct(self, Molecule, mol);
6756 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6757 oldval = s_AtomRef_GetAttr(aref, key);
6760 s_AtomRef_SetAttr(aref, key, val);
6766 * get_atom_attr(index, key)
6768 * Get the atom attribute for the specified atom.
6771 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6773 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6776 #pragma mark ------ Undo Support ------
6780 * register_undo(script, *args)
6782 * Register an undo operation with the current molecule.
6785 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6790 Data_Get_Struct(self, Molecule, mol);
6791 rb_scan_args(argc, argv, "1*", &script, &args);
6792 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6793 MolActionCallback_registerUndo(mol, act);
6799 * undo_enabled? -> bool
6801 * Returns true if undo is enabled for this molecule; otherwise no.
6804 s_Molecule_UndoEnabled(VALUE self)
6807 Data_Get_Struct(self, Molecule, mol);
6808 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6815 * undo_enabled = bool
6817 * Enable or disable undo.
6820 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6823 Data_Get_Struct(self, Molecule, mol);
6824 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6828 #pragma mark ------ Measure ------
6831 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6833 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6834 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6835 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6837 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6843 * center_of_mass(group = nil) -> Vector3D
6845 * Calculate the center of mass for the given set of atoms. The argument
6846 * group is null, then all atoms are considered.
6849 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6855 Data_Get_Struct(self, Molecule, mol);
6856 rb_scan_args(argc, argv, "01", &group);
6857 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6858 s_Molecule_DoCenterOfMass(mol, &v, ig);
6860 IntGroupRelease(ig);
6861 return ValueFromVector(&v);
6866 * centralize(group = nil) -> self
6868 * Translate the molecule so that the center of mass of the given group is located
6869 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6872 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6878 Data_Get_Struct(self, Molecule, mol);
6879 rb_scan_args(argc, argv, "01", &group);
6880 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6881 s_Molecule_DoCenterOfMass(mol, &v, ig);
6883 IntGroupRelease(ig);
6887 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6893 * bounds(group = nil) -> [min, max]
6895 * Calculate the boundary. The return value is an array of two Vector3D objects.
6898 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6906 Data_Get_Struct(self, Molecule, mol);
6907 rb_scan_args(argc, argv, "01", &group);
6908 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6909 if (ig != NULL && IntGroupGetCount(ig) == 0)
6910 rb_raise(rb_eMolbyError, "atom group is empty");
6911 vmin.x = vmin.y = vmin.z = 1e30;
6912 vmax.x = vmax.y = vmax.z = -1e30;
6913 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6915 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6931 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6934 /* Get atom position or a vector */
6936 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6938 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6939 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6940 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6942 VectorFromValue(val, vp);
6948 * measure_bond(n1, n2) -> Float
6950 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
6951 * or Vector3D values.
6952 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6955 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6959 Data_Get_Struct(self, Molecule, mol);
6960 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6961 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6962 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6967 * measure_angle(n1, n2, n3) -> Float
6969 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
6970 * or Vector3D values. The return value is in degree.
6971 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6974 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6979 Data_Get_Struct(self, Molecule, mol);
6980 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6981 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6982 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
6983 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6985 return Qnil; /* Cannot define */
6986 else return rb_float_new(d);
6991 * measure_dihedral(n1, n2, n3, n4) -> Float
6993 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
6994 * or Vector3D values. The return value is in degree.
6995 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6998 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7001 Vector v1, v2, v3, v4;
7003 Data_Get_Struct(self, Molecule, mol);
7004 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7005 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7006 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7007 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7008 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7010 return Qnil; /* Cannot define */
7011 else return rb_float_new(d);
7016 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7018 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7019 * first and second atom in the pair should belong to group1 and group2, respectively.
7020 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7023 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7026 VALUE limval, gval1, gval2, rval, igval;
7027 IntGroup *ig1, *ig2;
7028 IntGroupIterator iter1, iter2;
7034 MDExclusion *exinfo;
7037 Data_Get_Struct(self, Molecule, mol);
7038 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7039 lim = NUM2DBL(rb_Float(limval));
7041 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7043 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7045 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7047 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7049 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7051 if (!RTEST(igval)) {
7052 /* Use the exclusion table in MDArena */
7053 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7054 VALUE mval = ValueFromMolecule(mol);
7055 s_RebuildMDParameterIfNecessary(mval, Qnil);
7057 exinfo = mol->arena->exinfo; /* May be NULL */
7058 exlist = mol->arena->exlist;
7063 IntGroupIteratorInit(ig1, &iter1);
7064 IntGroupIteratorInit(ig2, &iter2);
7067 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7069 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7071 if (exinfo != NULL) {
7072 exn1 = exinfo[n[0]].index1;
7073 exn2 = exinfo[n[0] + 1].index1;
7074 } else exn1 = exn2 = -1;
7075 IntGroupIteratorReset(&iter2);
7076 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7077 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7079 continue; /* Same atom */
7080 if (exinfo != NULL) {
7081 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7082 for (i = exn1; i < exn2; i++) {
7083 if (exlist[i] == n[1])
7087 continue; /* Should be excluded */
7089 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7090 /* Is this pair already registered? */
7092 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7093 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7097 /* Not registered yet */
7098 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7103 IntGroupIteratorRelease(&iter2);
7104 IntGroupIteratorRelease(&iter1);
7105 IntGroupRelease(ig2);
7106 IntGroupRelease(ig1);
7107 rval = rb_ary_new2(npairs);
7108 if (pairs != NULL) {
7109 for (i = 0; i < npairs; i++) {
7110 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7119 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7121 * Find atoms that are within the threshold distance from the given atom.
7122 * (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.)
7123 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7124 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7125 * If limit is not given, a default value of 1.2 is used.
7126 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7129 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7132 VALUE aval, limval, radval;
7133 double limit, radius;
7134 Int n1, nbonds, *bonds, an;
7136 Data_Get_Struct(self, Molecule, mol);
7137 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7138 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)) {
7139 VectorFromValue(aval, &v);
7141 radius = gElementParameters[6].radius;
7143 radius = NUM2DBL(rb_Float(radval));
7146 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7147 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7148 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7149 if (an >= 0 && an < gCountElementParameters)
7150 radius = gElementParameters[an].radius;
7151 else radius = gElementParameters[6].radius;
7156 limit = NUM2DBL(rb_Float(limval));
7157 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7159 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7160 aval = rb_ary_new();
7162 for (n1 = 0; n1 < nbonds; n1++)
7163 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7171 * guess_bonds(limit = 1.2) -> Integer
7173 * Create bonds between atoms that are within the threshold distance.
7174 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7175 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7176 * If limit is not given, a default value of 1.2 is used.
7177 * The number of the newly created bonds is returned.
7178 * This operation is undoable.
7181 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7187 Data_Get_Struct(self, Molecule, mol);
7188 rb_scan_args(argc, argv, "01", &limval);
7192 limit = NUM2DBL(rb_Float(limval));
7193 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7195 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7198 return INT2NUM(nbonds);
7201 #pragma mark ------ Cell and Symmetry ------
7205 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7207 * Returns the unit cell parameters. If cell is not set, returns nil.
7210 s_Molecule_Cell(VALUE self)
7215 Data_Get_Struct(self, Molecule, mol);
7216 if (mol->cell == NULL)
7218 val = rb_ary_new2(6);
7219 for (i = 0; i < 6; i++)
7220 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7221 if (mol->cell->has_sigma) {
7222 for (i = 0; i < 6; i++) {
7223 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7231 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7232 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7234 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7235 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7236 This operation is undoable.
7237 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7240 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7244 int i, convert_coord, n;
7246 Data_Get_Struct(self, Molecule, mol);
7247 rb_scan_args(argc, argv, "11", &val, &cval);
7252 val = rb_ary_to_ary(val);
7253 len = RARRAY_LEN(val);
7256 } else if (len >= 6) {
7258 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7259 for (i = 0; i < n; i++)
7260 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7262 convert_coord = (RTEST(cval) ? 1 : 0);
7263 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7269 * box -> [avec, bvec, cvec, origin, flags]
7271 * Get the unit cell information in the form of a periodic bounding box.
7272 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7273 * Integers which define whether the system is periodic along the axis.
7274 * If no unit cell is defined, nil is returned.
7277 s_Molecule_Box(VALUE self)
7281 Data_Get_Struct(self, Molecule, mol);
7282 if (mol == NULL || mol->cell == NULL)
7284 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7285 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7286 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7287 v[3] = ValueFromVector(&(mol->cell->origin));
7288 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7289 val = rb_ary_new4(5, v);
7295 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7296 * set_box(d, origin = [0, 0, 0])
7299 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7300 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7302 Flags, if present, is a 3-member array of Integers defining whether the system is
7303 periodic along the axis.
7304 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7305 In the second form, an isotropic box with cell-length d is set.
7306 In the third form, the existing box is cleared.
7307 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7310 s_Molecule_SetBox(VALUE self, VALUE aval)
7314 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7316 Vector origin = {0, 0, 0};
7319 int i, convertCoordinates = 0;
7320 Data_Get_Struct(self, Molecule, mol);
7322 MolActionCreateAndPerform(mol, gMolActionClearBox);
7325 aval = rb_ary_to_ary(aval);
7326 for (i = 0; i < 6; i++) {
7327 if (i < RARRAY_LEN(aval))
7328 v[i] = (RARRAY_PTR(aval))[i];
7332 MolActionCreateAndPerform(mol, gMolActionClearBox);
7335 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7336 d = NUM2DBL(rb_Float(v[0]));
7337 for (i = 0; i < 3; i++)
7338 VecScale(vv[i], ax[i], d);
7340 VectorFromValue(v[1], &origin);
7341 flags[0] = flags[1] = flags[2] = 1;
7343 for (i = 0; i < 3; i++) {
7346 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7347 d = NUM2DBL(rb_Float(v[i]));
7348 VecScale(vv[i], ax[i], d);
7350 VectorFromValue(v[i], &vv[i]);
7352 flags[i] = (VecLength2(vv[i]) > 0.0);
7355 VectorFromValue(v[3], &origin);
7357 for (i = 0; i < 3; i++) {
7358 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7359 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7363 convertCoordinates = 1;
7365 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7371 * cell_periodicity -> [n1, n2, n3]
7373 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7377 s_Molecule_CellPeriodicity(VALUE self)
7380 Data_Get_Struct(self, Molecule, mol);
7381 if (mol->cell == NULL)
7383 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7388 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7389 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7391 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7392 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7393 * If cell is not defined, exception is raised.
7394 * This operation is undoable.
7397 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7401 Data_Get_Struct(self, Molecule, mol);
7402 if (mol->cell == NULL)
7403 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7406 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7407 flag = NUM2INT(rb_Integer(arg));
7411 arg = rb_ary_to_ary(arg);
7413 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7414 arg0 = RARRAY_PTR(arg)[i];
7415 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7416 flag |= (1 << (2 - i));
7419 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7425 * cell_flexibility -> bool
7427 * Returns the unit cell is flexible or not
7430 s_Molecule_CellFlexibility(VALUE self)
7432 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7435 Data_Get_Struct(self, Molecule, mol);
7436 if (mol->cell == NULL)
7438 if (mol->useFlexibleCell)
7440 else return Qfalse; */
7445 * self.cell_flexibility = bool
7446 * set_cell_flexibility(bool)
7448 * Change the unit cell is flexible or not
7451 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7453 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7456 Data_Get_Struct(self, Molecule, mol);
7457 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7463 * cell_transform -> Transform
7465 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7466 * If cell is not defined, nil is returned.
7469 s_Molecule_CellTransform(VALUE self)
7472 Data_Get_Struct(self, Molecule, mol);
7473 if (mol == NULL || mol->cell == NULL)
7475 return ValueFromTransform(&(mol->cell->tr));
7480 * symmetry -> Array of Transforms
7481 * symmetries -> Array of Transforms
7483 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7484 * returns an empty array.
7487 s_Molecule_Symmetry(VALUE self)
7492 Data_Get_Struct(self, Molecule, mol);
7493 if (mol->nsyms <= 0)
7494 return rb_ary_new();
7495 val = rb_ary_new2(mol->nsyms);
7496 for (i = 0; i < mol->nsyms; i++) {
7497 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7504 * nsymmetries -> Integer
7506 * Get the number of currently defined symmetry operations.
7509 s_Molecule_Nsymmetries(VALUE self)
7512 Data_Get_Struct(self, Molecule, mol);
7513 return INT2NUM(mol->nsyms);
7518 * add_symmetry(Transform) -> Integer
7520 * Add a new symmetry operation. If no symmetry operation is defined and the
7521 * given argument is not an identity transform, then also add an identity
7522 * transform at the index 0.
7523 * Returns the total number of symmetries after operation.
7526 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7530 Data_Get_Struct(self, Molecule, mol);
7531 TransformFromValue(trans, &tr);
7532 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7533 return INT2NUM(mol->nsyms);
7538 * remove_symmetry(count = nil) -> Integer
7539 * remove_symmetries(count = nil) -> Integer
7541 * Remove the specified number of symmetry operations. The last added ones are removed
7542 * first. If count is nil, then all symmetry operations are removed. Returns the
7543 * number of leftover symmetries.
7546 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7551 Data_Get_Struct(self, Molecule, mol);
7552 rb_scan_args(argc, argv, "01", &cval);
7556 n = NUM2INT(rb_Integer(cval));
7557 if (n < 0 || n > mol->nsyms)
7558 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7559 if (n == mol->nsyms)
7562 for (i = 0; i < n; i++)
7563 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7564 return INT2NUM(mol->nsyms);
7569 * wrap_unit_cell(group) -> Vector3D
7571 * Move the specified group so that the center of mass of the group is within the
7572 * unit cell. The offset vector is returned. If no periodic box is defined,
7573 * exception is raised.
7576 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7581 Data_Get_Struct(self, Molecule, mol);
7582 if (mol->cell == NULL)
7583 rb_raise(rb_eMolbyError, "no unit cell is defined");
7584 ig = s_Molecule_AtomGroupFromValue(self, gval);
7585 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7586 TransformVec(&v, mol->cell->rtr, &cv);
7587 if (mol->cell->flags[0])
7589 if (mol->cell->flags[1])
7591 if (mol->cell->flags[2])
7593 TransformVec(&dv, mol->cell->tr, &v);
7595 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7596 IntGroupRelease(ig);
7597 return ValueFromVector(&dv);
7602 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7604 * Expand the specified part of the molecule by the given symmetry operation.
7605 * Returns the array of atom indices corresponding to the expanded atoms.
7606 * If allow_overlap is true, then new atoms are created even when the
7607 * coordinates coincide with the some other atom (special position) of the
7608 * same element; otherwise, such atom will not be created and the index of the
7609 * existing atom is given in the returned array.
7612 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7615 VALUE gval, sval, xval, yval, zval, rval, oval;
7617 Int n[4], allow_overlap;
7621 Data_Get_Struct(self, Molecule, mol);
7622 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7623 n[0] = NUM2INT(rb_Integer(sval));
7624 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7625 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7626 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7627 allow_overlap = (RTEST(oval) ? 1 : 0);
7628 ig = s_Molecule_AtomGroupFromValue(self, gval);
7629 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7630 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7631 natoms = mol->natoms;
7633 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7635 rval = rb_ary_new2(nidx);
7636 while (--nidx >= 0) {
7637 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7639 /* if (natoms == mol->natoms)
7642 rval = IntGroup_Alloc(rb_cIntGroup);
7643 Data_Get_Struct(rval, IntGroup, ig);
7644 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7651 * amend_by_symmetry(group = nil) -> IntGroup
7653 * Expand the specified part of the molecule by the given symmetry operation.
7654 * Returns an IntGroup containing the added atoms.
7657 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7662 Data_Get_Struct(self, Molecule, mol);
7663 rb_scan_args(argc, argv, "01", &gval);
7665 ig = s_Molecule_AtomGroupFromValue(self, gval);
7667 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7668 rval = ValueFromIntGroup(ig2);
7669 IntGroupRelease(ig2);
7673 #pragma mark ------ Transforms ------
7677 * translate(vec, group = nil) -> Molecule
7679 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7680 * This operation is undoable.
7683 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7689 Data_Get_Struct(self, Molecule, mol);
7690 rb_scan_args(argc, argv, "11", &vec, &group);
7691 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7692 VectorFromValue(vec, &v);
7693 // MoleculeTranslate(mol, &v, ig);
7694 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7696 IntGroupRelease(ig);
7702 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7704 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7705 * If group is given, only atoms in the group are moved.
7706 * This operation is undoable.
7709 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7712 volatile VALUE aval, anval, cval, gval;
7717 Data_Get_Struct(self, Molecule, mol);
7718 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7719 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7720 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7721 VectorFromValue(aval, &av);
7723 cv.x = cv.y = cv.z = 0.0;
7725 VectorFromValue(cval, &cv);
7726 if (TransformForRotation(tr, &av, angle, &cv))
7727 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7728 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7730 IntGroupRelease(ig);
7736 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7738 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7739 * axis must not be a zero vector.
7740 * If group is given, only atoms in the group are moved.
7741 * This operation is undoable.
7744 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7747 volatile VALUE aval, cval, gval;
7751 Data_Get_Struct(self, Molecule, mol);
7752 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7753 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7754 VectorFromValue(aval, &av);
7756 cv.x = cv.y = cv.z = 0.0;
7758 VectorFromValue(cval, &cv);
7759 if (TransformForReflection(tr, &av, &cv))
7760 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7761 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7763 IntGroupRelease(ig);
7769 * invert(center = [0,0,0], group = nil) -> Molecule
7771 * Invert the molecule with the given center.
7772 * If group is given, only atoms in the group are moved.
7773 * This operation is undoable.
7776 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7779 volatile VALUE cval, gval;
7783 Data_Get_Struct(self, Molecule, mol);
7784 rb_scan_args(argc, argv, "02", &cval, &gval);
7785 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7787 cv.x = cv.y = cv.z = 0.0;
7789 VectorFromValue(cval, &cv);
7790 TransformForInversion(tr, &cv);
7791 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7793 IntGroupRelease(ig);
7799 * transform(transform, group = nil) -> Molecule
7801 * Transform the molecule by the given Transform object.
7802 * If group is given, only atoms in the group are moved.
7803 * This operation is undoable.
7806 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7812 Data_Get_Struct(self, Molecule, mol);
7813 rb_scan_args(argc, argv, "11", &trans, &group);
7814 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7815 TransformFromValue(trans, &tr);
7816 /* MoleculeTransform(mol, tr, ig); */
7817 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7819 IntGroupRelease(ig);
7825 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7827 * Get the transform corresponding to the symmetry operation. The symop can either be
7828 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7829 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7830 * Otherwise, the returned transform is for fractional coordinates.
7831 * Raises exception when no cell or no transform are defined.
7834 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7840 Data_Get_Struct(self, Molecule, mol);
7841 if (mol->cell == NULL)
7842 rb_raise(rb_eMolbyError, "no unit cell is defined");
7843 if (mol->nsyms == 0)
7844 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7845 rb_scan_args(argc, argv, "11", &sval, &fval);
7846 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7847 symop.sym = NUM2INT(rb_Integer(sval));
7848 symop.dx = symop.dy = symop.dz = 0;
7850 sval = rb_ary_to_ary(sval);
7851 if (RARRAY_LEN(sval) < 4)
7852 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7853 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7854 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7855 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7856 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7858 if (symop.sym >= mol->nsyms)
7859 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7860 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7861 return ValueFromTransform(&tr);
7866 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7868 * Get the symmetry operation corresponding to the given transform.
7869 * If is_cartesian is true, the given transform is for cartesian coordinates.
7870 * Otherwise, the given transform is for fractional coordinates.
7871 * Raises exception when no cell or no transform are defined.
7874 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7881 Data_Get_Struct(self, Molecule, mol);
7882 if (mol->cell == NULL)
7883 rb_raise(rb_eMolbyError, "no unit cell is defined");
7884 if (mol->nsyms == 0)
7885 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7886 rb_scan_args(argc, argv, "11", &tval, &fval);
7887 TransformFromValue(tval, &tr);
7888 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7890 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7892 return Qnil; /* Not found */
7896 #pragma mark ------ Frames ------
7900 * select_frame(index)
7903 * Select the specified frame. If successful, returns true, otherwise returns false.
7906 s_Molecule_SelectFrame(VALUE self, VALUE val)
7909 int ival = NUM2INT(val);
7910 Data_Get_Struct(self, Molecule, mol);
7911 ival = MoleculeSelectFrame(mol, ival, 1);
7921 * Get the current frame.
7924 s_Molecule_Frame(VALUE self)
7927 Data_Get_Struct(self, Molecule, mol);
7928 return INT2NUM(mol->cframe);
7933 * nframes -> Integer
7935 * Get the number of frames.
7938 s_Molecule_Nframes(VALUE self)
7941 Data_Get_Struct(self, Molecule, mol);
7942 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7947 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7948 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7950 * Insert new frames at the indices specified by the intGroup. If the first argument is
7951 * an integer, a single new frame is inserted at that index. If the first argument is
7952 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7953 * should be an array of arrays of Vector3Ds, then those coordinates are set
7954 * to the new frame. Otherwise, the coordinates of current molecule are copied
7956 * Returns an intGroup representing the inserted frames if successful, nil if not.
7959 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7961 VALUE val, coords, cells;
7964 int count, ival, i, j, len, len_c, len2, nframes;
7967 Data_Get_Struct(self, Molecule, mol);
7968 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7969 if (coords != Qnil) {
7970 if (TYPE(coords) != T_ARRAY)
7971 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7972 len = RARRAY_LEN(coords);
7974 if (cells != Qnil) {
7975 if (mol->cell == NULL)
7976 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7977 if (TYPE(cells) != T_ARRAY)
7978 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7979 len_c = RARRAY_LEN(cells);
7981 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7982 nframes = MoleculeGetNumberOfFrames(mol);
7984 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7985 val = ValueFromIntGroup(ig);
7987 ig = IntGroupFromValue(val);
7989 count = IntGroupGetCount(ig); /* Count is updated here */
7990 vp = ALLOC_N(Vector, mol->natoms * count);
7992 vp2 = ALLOC_N(Vector, 4 * count);
7996 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7997 ptr = RARRAY_PTR(coords);
7998 for (i = 0; i < count; i++) {
7999 if (TYPE(ptr[i]) != T_ARRAY)
8000 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8001 len2 = RARRAY_LEN(ptr[i]);
8002 if (len2 < mol->natoms)
8003 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8004 ptr2 = RARRAY_PTR(ptr[i]);
8005 for (j = 0; j < mol->natoms; j++)
8006 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8010 for (i = 0; i < count; i++) {
8011 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8012 vp[i * mol->natoms + j] = ap->r;
8018 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8019 ptr = RARRAY_PTR(cells);
8020 for (i = 0; i < count; i++) {
8021 if (TYPE(ptr[i]) != T_ARRAY)
8022 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8023 len2 = RARRAY_LEN(ptr[i]);
8025 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8026 ptr2 = RARRAY_PTR(ptr[i]);
8027 for (j = 0; j < 4; j++)
8028 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8031 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8032 IntGroupRelease(ig);
8036 return (ival >= 0 ? val : Qnil);
8041 * create_frame(coordinates = nil) -> Integer
8042 * create_frames(coordinates = nil) -> Integer
8044 * Same as molecule.insert_frames(nil, coordinates).
8047 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8050 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8052 return s_Molecule_InsertFrames(3, vals, self);
8057 * remove_frames(IntGroup, wantCoordinates = false)
8059 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8060 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8061 * removed frames is returned if operation is successful.
8064 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8071 Data_Get_Struct(self, Molecule, mol);
8072 rb_scan_args(argc, argv, "11", &val, &flag);
8073 ig = IntGroupFromValue(val);
8074 count = IntGroupGetCount(ig);
8076 /* Create return value before removing frames */
8081 retval = rb_ary_new2(count);
8082 for (i = 0; i < count; i++) {
8083 n = IntGroupGetNthPoint(ig, i);
8084 coords = rb_ary_new2(mol->natoms);
8085 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8086 if (n < ap->nframes && n != mol->cframe)
8089 rb_ary_push(coords, ValueFromVector(&v));
8091 rb_ary_push(retval, coords);
8093 } else retval = Qtrue;
8094 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8101 * each_frame {|n| ...}
8103 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8104 * the frame number. After completion, the original frame number is restored.
8107 s_Molecule_EachFrame(VALUE self)
8109 int i, cframe, nframes;
8111 Data_Get_Struct(self, Molecule, mol);
8112 cframe = mol->cframe;
8113 nframes = MoleculeGetNumberOfFrames(mol);
8115 for (i = 0; i < nframes; i++) {
8116 MoleculeSelectFrame(mol, i, 1);
8117 rb_yield(INT2NUM(i));
8119 MoleculeSelectFrame(mol, cframe, 1);
8126 * get_coord_from_frame(index, group = nil)
8128 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8129 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8130 * copied; now they are always copied)
8133 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8136 VALUE ival, gval, cval;
8137 Int index, i, j, n, nn;
8139 IntGroupIterator iter;
8142 Data_Get_Struct(self, Molecule, mol);
8143 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8145 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8146 index = NUM2INT(rb_Integer(ival));
8147 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8149 rb_raise(rb_eMolbyError, "No frame is present");
8151 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8154 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8156 ig = s_Molecule_AtomGroupFromValue(self, gval);
8158 n = IntGroupGetCount(ig);
8160 vp = (Vector *)calloc(sizeof(Vector), n);
8161 IntGroupIteratorInit(ig, &iter);
8164 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8165 ap = ATOM_AT_INDEX(mol->atoms, i);
8166 if (index < ap->nframes) {
8167 vp[j] = ap->frames[index];
8175 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8177 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8178 vp = mol->frame_cells + index * 4;
8179 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8181 IntGroupIteratorRelease(&iter);
8183 /* Copy the extra properties */
8184 IntGroupRelease(ig);
8185 for (i = 0; i < mol->nmolprops; i++) {
8186 Double *dp = (Double *)malloc(sizeof(Double));
8188 IntGroupAdd(ig, mol->cframe, 1);
8189 *dp = mol->molprops[i].propvals[index];
8190 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8192 IntGroupRelease(ig);
8200 * reorder_frames(old_indices)
8202 * Reorder the frames. The argument is an array of integers that specify the 'old'
8203 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8204 * same as the old frames 2/0/1, respectively.
8205 * The argument must have the same number of integers as the number of frames.
8208 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8211 Int *ip, *ip2, i, n, nframes;
8212 Data_Get_Struct(self, Molecule, mol);
8213 aval = rb_ary_to_ary(aval);
8214 nframes = MoleculeGetNumberOfFrames(mol);
8215 if (RARRAY_LEN(aval) != nframes)
8216 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8217 ip2 = (Int *)calloc(sizeof(Int), nframes);
8218 ip = (Int *)calloc(sizeof(Int), nframes);
8219 for (i = 0; i < nframes; i++) {
8220 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8221 if (n < 0 || n >= nframes || ip2[n] != 0) {
8224 if (n < 0 || n >= nframes)
8225 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8227 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8233 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8238 #pragma mark ------ Fragments ------
8242 * fragment(n1, *exatoms) -> IntGroup
8243 * fragment(group, *exatoms) -> IntGroup
8245 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8246 * those atoms will not be counted during the search.
8249 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8252 IntGroup *baseg, *ig, *exatoms;
8254 volatile VALUE nval, exval;
8255 Data_Get_Struct(self, Molecule, mol);
8256 rb_scan_args(argc, argv, "1*", &nval, &exval);
8257 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8259 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8261 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8263 if (RARRAY_LEN(exval) == 0) {
8266 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8267 Data_Get_Struct(exval, IntGroup, exatoms);
8269 if (baseg == NULL) {
8270 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8272 IntGroupIterator iter;
8273 IntGroupIteratorInit(baseg, &iter);
8274 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8277 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8279 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8281 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8283 IntGroupAddIntGroup(ig, subg);
8284 IntGroupRelease(subg);
8289 IntGroupIteratorRelease(&iter);
8290 IntGroupRelease(baseg);
8293 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8294 nval = ValueFromIntGroup(ig);
8295 IntGroupRelease(ig);
8301 * fragments(exclude = nil)
8303 * Returns the fragments as an array of IntGroups.
8304 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8305 * in defining the fragment.
8308 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8311 IntGroup *ag, *fg, *eg;
8312 VALUE gval, exval, retval;
8313 Data_Get_Struct(self, Molecule, mol);
8316 if (mol->natoms == 0)
8317 return rb_ary_new();
8318 rb_scan_args(argc, argv, "01", &exval);
8322 eg = IntGroupFromValue(exval);
8323 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8325 IntGroupRemoveIntGroup(ag, eg);
8326 retval = rb_ary_new();
8327 while (IntGroupGetCount(ag) > 0) {
8328 int n = IntGroupGetNthPoint(ag, 0);
8329 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8331 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8332 gval = ValueFromIntGroup(fg);
8333 rb_ary_push(retval, gval);
8334 IntGroupRemoveIntGroup(ag, fg);
8335 IntGroupRelease(fg);
8337 IntGroupRelease(ag);
8339 IntGroupRelease(eg);
8345 * each_fragment(exclude = nil) {|group| ...}
8347 * Execute the block, with the IntGroup object for each fragment as the argument.
8348 * Atoms or bonds should not be added or removed during the execution of the block.
8349 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8350 * in defining the fragment.
8353 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8356 IntGroup *ag, *fg, *eg;
8358 Data_Get_Struct(self, Molecule, mol);
8359 if (mol == NULL || mol->natoms == 0)
8361 rb_scan_args(argc, argv, "01", &exval);
8365 eg = IntGroupFromValue(exval);
8366 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8368 IntGroupRemoveIntGroup(ag, eg);
8369 while (IntGroupGetCount(ag) > 0) {
8370 int n = IntGroupGetNthPoint(ag, 0);
8371 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8373 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8374 gval = ValueFromIntGroup(fg);
8376 IntGroupRemoveIntGroup(ag, fg);
8377 IntGroupRelease(fg);
8379 IntGroupRelease(ag);
8381 IntGroupRelease(eg);
8387 * detachable?(group) -> [n1, n2]
8389 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8390 * of the molecule via only one bond. If it is, then the indices of the atoms
8391 * belonging to the bond is returned, the first element being the atom included
8392 * in the fragment. Otherwise, Qnil is returned.
8395 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8401 Data_Get_Struct(self, Molecule, mol);
8402 ig = s_Molecule_AtomGroupFromValue(self, gval);
8403 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8404 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8405 } else retval = Qnil;
8406 IntGroupRelease(ig);
8412 * bonds_on_border(group = selection) -> Array of Array of two Integers
8414 * Returns an array of bonds that connect an atom in the group and an atom out
8415 * of the group. The first atom in the bond always belongs to the group. If no
8416 * such bonds are present, an empty array is returned.
8419 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8424 Data_Get_Struct(self, Molecule, mol);
8425 rb_scan_args(argc, argv, "01", &gval);
8427 ig = MoleculeGetSelection(mol);
8431 ig = s_Molecule_AtomGroupFromValue(self, gval);
8433 retval = rb_ary_new();
8436 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8438 IntGroupIterator iter;
8440 IntGroupIteratorInit(bg, &iter);
8441 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8442 /* The atoms at the border */
8444 n1 = mol->bonds[i * 2];
8445 n2 = mol->bonds[i * 2 + 1];
8446 if (IntGroupLookupPoint(ig, n1) < 0) {
8450 if (IntGroupLookupPoint(ig, n1) < 0)
8451 continue; /* Actually this is an internal error */
8453 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8455 IntGroupIteratorRelease(&iter);
8457 IntGroupRelease(bg);
8458 IntGroupRelease(ig);
8462 /* Calculate the transform that moves the current coordinates to the reference
8463 coordinates with least displacements. */
8465 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8473 Double eigen_val[3];
8474 Vector eigen_vec[3];
8476 IntGroupIterator iter;
8478 natoms = mol->natoms;
8480 IntGroupIteratorInit(ig, &iter);
8482 /* Calculate the weighted center */
8486 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8487 ap1 = ATOM_AT_INDEX(ap, in);
8488 w1 = (weights != NULL ? weights[i] : ap1->weight);
8489 VecScaleInc(org1, ap1->r, w1);
8490 VecScaleInc(org2, ref[i], w1);
8494 VecScaleSelf(org1, w);
8495 VecScaleSelf(org2, w);
8497 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8498 /* Matrix to diagonalize = R * tR */
8499 memset(r, 0, sizeof(Mat33));
8500 memset(q, 0, sizeof(Mat33));
8501 memset(u, 0, sizeof(Mat33));
8503 IntGroupIteratorReset(&iter);
8504 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8506 ap1 = ATOM_AT_INDEX(ap, in);
8507 w1 = (weights != NULL ? weights[i] : ap1->weight);
8509 VecSub(v1, ap1->r, org1);
8510 VecSub(v2, ref[i], org2);
8511 r[0] += w1 * v1.x * v2.x;
8512 r[1] += w1 * v1.y * v2.x;
8513 r[2] += w1 * v1.z * v2.x;
8514 r[3] += w1 * v1.x * v2.y;
8515 r[4] += w1 * v1.y * v2.y;
8516 r[5] += w1 * v1.z * v2.y;
8517 r[6] += w1 * v1.x * v2.z;
8518 r[7] += w1 * v1.y * v2.z;
8519 r[8] += w1 * v1.z * v2.z;
8522 for (i = 0; i < 9; i++)
8524 for (i = 0; i < 3; i++) {
8525 for (j = 0; j < 3; j++) {
8526 for (k = 0; k < 3; k++) {
8527 q[i+j*3] += r[i+k*3] * r[j+k*3];
8532 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8533 IntGroupIteratorRelease(&iter);
8534 return -1.0; /* Cannot determine the eigenvector */
8537 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8538 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8539 MatrixTranspose(r, r);
8540 for (i = 0; i < 3; i++) {
8541 MatrixVec(&s[i], r, &eigen_vec[i]);
8542 w1 = 1.0 / sqrt(eigen_val[i]);
8543 VecScaleSelf(s[i], w1);
8545 for (k = 0; k < 3; k++) {
8546 u[0] += s[k].x * eigen_vec[k].x;
8547 u[1] += s[k].y * eigen_vec[k].x;
8548 u[2] += s[k].z * eigen_vec[k].x;
8549 u[3] += s[k].x * eigen_vec[k].y;
8550 u[4] += s[k].y * eigen_vec[k].y;
8551 u[5] += s[k].z * eigen_vec[k].y;
8552 u[6] += s[k].x * eigen_vec[k].z;
8553 u[7] += s[k].y * eigen_vec[k].z;
8554 u[8] += s[k].z * eigen_vec[k].z;
8557 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8558 MatrixVec(&org1, u, &org1);
8560 for (i = 0; i < 9; i++)
8566 /* Calculate rmsd */
8567 IntGroupIteratorReset(&iter);
8569 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8571 ap1 = ATOM_AT_INDEX(ap, in);
8572 TransformVec(&tv, trans, &ap1->r);
8574 w += VecLength2(tv);
8577 IntGroupIteratorRelease(&iter);
8583 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8585 * Calculate the transform to fit the given group to the set of reference coordinates.
8586 * The reference coordinates ref is given as either a frame number, an array of
8587 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8588 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8589 * Return values are the transform (that converts the present coordinates to the
8590 * target coordinates) and root mean square deviation (without weight).
8593 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8597 VALUE gval, rval, wval;
8599 IntGroupIterator iter;
8600 int nn, errno, i, j, in, status;
8602 Double *weights, dval[3];
8605 Data_Get_Struct(self, Molecule, mol);
8606 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8608 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8610 ig = IntGroupFromValue(gval);
8611 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8612 IntGroupRelease(ig);
8613 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8615 ref = (Vector *)calloc(sizeof(Vector), nn);
8616 weights = (Double *)calloc(sizeof(Double), nn);
8617 IntGroupIteratorInit(ig, &iter);
8618 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8619 int fn = NUM2INT(rb_Integer(rval));
8620 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8625 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8626 ap = ATOM_AT_INDEX(mol->atoms, in);
8627 if (fn < ap->nframes)
8628 ref[i] = ap->frames[fn];
8629 else ref[i] = ap->r;
8631 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8632 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8633 if (m->row * m->column < nn * 3) {
8637 for (i = 0; i < nn; i++) {
8638 ref[i].x = m->data[i * 3];
8639 ref[i].y = m->data[i * 3 + 1];
8640 ref[i].z = m->data[i * 3 + 2];
8644 rval = rb_protect(rb_ary_to_ary, rval, &status);
8649 if (RARRAY_LEN(rval) < nn) {
8653 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8654 /* Array of 3*nn numbers */
8655 if (RARRAY_LEN(rval) < nn * 3) {
8659 for (i = 0; i < nn; i++) {
8660 for (j = 0; j < 3; j++) {
8661 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8666 dval[j] = NUM2DBL(aval);
8673 /* Array of nn Vector3Ds or Arrays */
8674 for (i = 0; i < nn; i++) {
8675 aval = (RARRAY_PTR(rval))[i];
8676 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8677 VectorFromValue(aval, &ref[i]);
8679 aval = rb_protect(rb_ary_to_ary, aval, &status);
8684 if (RARRAY_LEN(aval) < 3) {
8689 for (j = 0; j < 3; j++) {
8690 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8695 dval[j] = NUM2DBL(aaval);
8705 /* Use atomic weights */
8706 IntGroupIteratorReset(&iter);
8707 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8708 ap = ATOM_AT_INDEX(mol->atoms, in);
8709 weights[i] = ap->weight;
8712 wval = rb_protect(rb_ary_to_ary, wval, &status);
8717 if (RARRAY_LEN(wval) < nn) {
8721 for (i = 0; i < nn; i++) {
8722 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8727 weights[i] = NUM2DBL(wwval);
8730 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8737 IntGroupIteratorRelease(&iter);
8741 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8742 } else if (errno == 1) {
8743 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8744 } else if (errno == 2) {
8745 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8746 } else if (errno == 3) {
8747 rb_jump_tag(status);
8748 } else if (errno == 4) {
8749 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8750 } else if (errno == 5) {
8751 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8752 } else if (errno == 6) {
8753 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8755 return Qnil; /* Not reached */
8758 #pragma mark ------ Screen Display ------
8764 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8767 s_Molecule_Display(VALUE self)
8770 Data_Get_Struct(self, Molecule, mol);
8771 if (mol->mview != NULL)
8772 MainViewCallback_display(mol->mview);
8780 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8783 s_Molecule_MakeFront(VALUE self)
8786 Data_Get_Struct(self, Molecule, mol);
8787 if (mol->mview != NULL)
8788 MainViewCallback_makeFront(mol->mview);
8794 * update_enabled? -> bool
8796 * Returns true if screen update is enabled; otherwise no.
8799 s_Molecule_UpdateEnabled(VALUE self)
8802 Data_Get_Struct(self, Molecule, mol);
8803 if (mol->mview != NULL && !mol->mview->freezeScreen)
8810 * update_enabled = bool
8812 * Enable or disable screen update. This is effective for automatic update on modification.
8813 * Explicit call to molecule.display() always updates the screen.
8816 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8819 Data_Get_Struct(self, Molecule, mol);
8820 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8821 if (mol->mview != NULL)
8822 mol->mview->freezeScreen = (val == Qfalse);
8830 * show_unitcell(bool)
8831 * show_unitcell = bool
8833 * Set the flag whether to show the unit cell. If no argument is given, the
8834 * current flag is returned.
8837 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8840 Data_Get_Struct(self, Molecule, mol);
8841 if (mol->mview == NULL)
8844 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8845 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8847 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8853 * show_hydrogens(bool)
8854 * show_hydrogens = bool
8856 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8857 * current flag is returned.
8860 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8863 Data_Get_Struct(self, Molecule, mol);
8864 if (mol->mview == NULL)
8867 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8868 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8870 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8876 * show_dummy_atoms(bool)
8877 * show_dummy_atoms = bool
8879 * Set the flag whether to show the dummy atoms. If no argument is given, the
8880 * current flag is returned.
8883 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8886 Data_Get_Struct(self, Molecule, mol);
8887 if (mol->mview == NULL)
8890 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8891 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8893 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8899 * show_expanded(bool)
8900 * show_expanded = bool
8902 * Set the flag whether to show the expanded atoms. If no argument is given, the
8903 * current flag is returned.
8906 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8909 Data_Get_Struct(self, Molecule, mol);
8910 if (mol->mview == NULL)
8913 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8914 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8916 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8922 * show_ellipsoids(bool)
8923 * show_ellipsoids = bool
8925 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8926 * current flag is returned.
8929 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8932 Data_Get_Struct(self, Molecule, mol);
8933 if (mol->mview == NULL)
8936 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8937 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8939 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8944 * is_atom_visible(index) -> Boolean
8946 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8947 * as well as the molecule attributes (showHydrogens, etc.)
8950 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8955 Data_Get_Struct(self, Molecule, mol);
8956 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8957 if (idx < 0 || idx >= mol->natoms)
8959 ap = ATOM_AT_INDEX(mol->atoms, idx);
8960 if (mol->mview != NULL) {
8961 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8963 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8965 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8968 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8973 * hidden_atoms -> IntGroup
8975 * Returns the currently hidden atoms.
8978 s_Molecule_HiddenAtoms(VALUE self)
8980 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8981 return Qnil; /* Not reached */
8986 * set_hidden_atoms(IntGroup)
8987 * self.hidden_atoms = IntGroup
8989 * Hide the specified atoms. This operation is _not_ undoable.
8992 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8994 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8995 return Qnil; /* Not reached */
9000 * show_graphite -> Integer
9001 * show_graphite = Integer
9002 * show_graphite = boolean
9004 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9005 * number of rings to display for each direction.
9006 * If the argument is boolean, only the show/hide flag is set.
9009 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9012 Data_Get_Struct(self, Molecule, mol);
9013 if (mol->mview == NULL)
9016 if (argv[0] == Qnil || argv[0] == Qfalse)
9017 mol->mview->showGraphiteFlag = 0;
9018 else if (argv[0] == Qtrue)
9019 mol->mview->showGraphiteFlag = 1;
9021 int n = NUM2INT(rb_Integer(argv[0]));
9023 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9024 mol->mview->showGraphite = n;
9026 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9028 return INT2NUM(mol->mview->showGraphite);
9033 * show_graphite? -> boolean
9035 * Return whether the graphite is set visible or not.
9038 s_Molecule_ShowGraphiteFlag(VALUE self)
9041 Data_Get_Struct(self, Molecule, mol);
9042 if (mol->mview == NULL)
9044 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9049 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9050 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9051 * show_periodic_image = boolean
9053 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9054 * set but no visual effects are observed.
9055 * If the argument is boolean, only the show/hide flag is modified.
9058 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9064 Data_Get_Struct(self, Molecule, mol);
9065 if (mol->mview == NULL)
9067 rb_scan_args(argc, argv, "01", &val);
9069 /* Change current settings */
9070 if (val == Qnil || val == Qfalse)
9071 mol->mview->showPeriodicImageFlag = 0;
9072 else if (val == Qtrue)
9073 mol->mview->showPeriodicImageFlag = 1;
9075 val = rb_ary_to_ary(val);
9076 for (i = 0; i < 6; i++) {
9077 if (i < RARRAY_LEN(val))
9078 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9080 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9081 rb_raise(rb_eMolbyError, "bad arguments");
9082 for (i = 0; i < 6; i++)
9083 mol->mview->showPeriodicImage[i] = ival[i];
9085 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9088 for (i = 0; i < 6; i++)
9089 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9095 * show_periodic_image? -> boolean
9097 * Return whether the periodic images are set to visible or not. This flag is
9098 * independent from the show_periodic_image settings.
9101 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9104 Data_Get_Struct(self, Molecule, mol);
9105 if (mol->mview == NULL)
9107 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9112 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9113 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9114 * show_rotation_center = boolean
9116 * Set to show the rotation center of the screen.
9117 * If the argument is boolean, only the show/hide flag is modified.
9120 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9123 Data_Get_Struct(self, Molecule, mol);
9124 if (mol->mview == NULL)
9127 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9128 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9130 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9139 * Set the flag whether to draw the model in line mode. If no argument is given, the
9140 * current flag is returned.
9143 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9146 Data_Get_Struct(self, Molecule, mol);
9147 if (mol->mview == NULL)
9150 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9151 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9153 return (mol->mview->lineMode ? Qtrue : Qfalse);
9158 * atom_radius = float
9161 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9163 * If no argument is given, the current value is returned.
9166 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9169 Data_Get_Struct(self, Molecule, mol);
9170 if (mol->mview == NULL)
9173 double rad = NUM2DBL(rb_Float(argv[0]));
9175 mol->mview->atomRadius = rad;
9176 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9180 return rb_float_new(mol->mview->atomRadius);
9185 * bond_radius = float
9188 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9189 * If no argument is given, the current value is returned.
9192 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9195 Data_Get_Struct(self, Molecule, mol);
9196 if (mol->mview == NULL)
9199 double rad = NUM2DBL(rb_Float(argv[0]));
9201 mol->mview->bondRadius = rad;
9202 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9206 return rb_float_new(mol->mview->bondRadius);
9211 * atom_resolution = integer
9214 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9215 * (Default = 12; minimum = 6)
9216 * If no argument is given, the current value is returned.
9219 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9222 Data_Get_Struct(self, Molecule, mol);
9223 if (mol->mview == NULL)
9226 int res = NUM2INT(rb_Integer(argv[0]));
9228 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9229 mol->mview->atomResolution = res;
9230 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9231 return INT2NUM(res);
9233 return INT2NUM(mol->mview->atomResolution);
9238 * bond_resolution = integer
9241 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9242 * (Default = 8; minimum = 4)
9243 * If no argument is given, the current value is returned.
9246 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9249 Data_Get_Struct(self, Molecule, mol);
9250 if (mol->mview == NULL)
9253 int res = NUM2INT(rb_Integer(argv[0]));
9255 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9256 mol->mview->bondResolution = res;
9257 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9258 return INT2NUM(res);
9260 return INT2NUM(mol->mview->bondResolution);
9267 * Resize the model drawing to fit in the window.
9270 s_Molecule_ResizeToFit(VALUE self)
9273 Data_Get_Struct(self, Molecule, mol);
9274 if (mol->mview != NULL)
9275 MainView_resizeToFit(mol->mview);
9281 * get_view_rotation -> [[ax, ay, az], angle]
9283 * Get the current rotation for the view. Angle is in degree, not radian.
9286 s_Molecule_GetViewRotation(VALUE self)
9291 Data_Get_Struct(self, Molecule, mol);
9292 if (mol->mview == NULL)
9294 TrackballGetRotate(mol->mview->track, f);
9295 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9299 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9304 * get_view_scale -> float
9306 * Get the current scale for the view.
9309 s_Molecule_GetViewScale(VALUE self)
9312 Data_Get_Struct(self, Molecule, mol);
9313 if (mol->mview == NULL)
9315 return rb_float_new(TrackballGetScale(mol->mview->track));
9320 * get_view_center -> Vector
9322 * Get the current center point of the view.
9325 s_Molecule_GetViewCenter(VALUE self)
9330 Data_Get_Struct(self, Molecule, mol);
9331 if (mol->mview == NULL)
9333 TrackballGetTranslate(mol->mview->track, f);
9334 v.x = -f[0] * mol->mview->dimension;
9335 v.y = -f[1] * mol->mview->dimension;
9336 v.z = -f[2] * mol->mview->dimension;
9337 return ValueFromVector(&v);
9342 * set_view_rotation([ax, ay, az], angle) -> self
9344 * Set the current rotation for the view. Angle is in degree, not radian.
9347 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9352 Data_Get_Struct(self, Molecule, mol);
9353 if (mol->mview == NULL)
9355 VectorFromValue(aval, &v);
9356 if (NormalizeVec(&v, &v))
9357 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9361 f[0] = -NUM2DBL(rb_Float(angval));
9362 TrackballSetRotate(mol->mview->track, f);
9363 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9369 * set_view_scale(scale) -> self
9371 * Set the current scale for the view.
9374 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9377 Data_Get_Struct(self, Molecule, mol);
9378 if (mol->mview == NULL)
9380 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9381 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9387 * set_view_center(vec) -> self
9389 * Set the current center point of the view.
9392 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9397 Data_Get_Struct(self, Molecule, mol);
9398 if (mol->mview == NULL)
9400 VectorFromValue(aval, &v);
9401 f[0] = -v.x / mol->mview->dimension;
9402 f[1] = -v.y / mol->mview->dimension;
9403 f[2] = -v.z / mol->mview->dimension;
9404 TrackballSetTranslate(mol->mview->track, f);
9405 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9411 * set_background_color(red, green, blue)
9413 * Set the background color of the model window.
9416 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9419 Data_Get_Struct(self, Molecule, mol);
9420 if (mol->mview != NULL) {
9421 VALUE rval, gval, bval;
9422 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9423 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9430 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9432 * Export the current graphic to a PNG or TIF file (determined by the extension).
9433 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9434 * If either width or height is not specified, then the screen width/height is used instead.
9437 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9440 VALUE fval, sval, bval, wval, hval;
9443 int bg_color, width, height;
9444 Data_Get_Struct(self, Molecule, mol);
9445 if (mol->mview == NULL)
9446 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9447 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9448 fname = FileStringValuePtr(fval);
9451 else scale = NUM2DBL(rb_Float(sval));
9454 else bg_color = NUM2INT(rb_Integer(bval));
9457 else width = NUM2INT(rb_Integer(wval));
9460 else height = NUM2INT(rb_Integer(hval));
9461 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9466 #pragma mark ------ Graphics ------
9469 s_CalculateGraphicNormals(MainViewGraphic *gp)
9473 if (gp == NULL || gp->npoints < 3)
9475 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9476 v1.x = gp->points[3] - gp->points[0];
9477 v1.y = gp->points[4] - gp->points[1];
9478 v1.z = gp->points[5] - gp->points[2];
9479 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9480 for (i = 2; i < gp->npoints; i++) {
9481 v2.x = gp->points[i * 3] - gp->points[0];
9482 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9483 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9484 VecCross(v3, v1, v2);
9485 NormalizeVec(&v3, &v3);
9486 gp->normals[i * 3] = v3.x;
9487 gp->normals[i * 3 + 1] = v3.y;
9488 gp->normals[i * 3 + 2] = v3.z;
9491 /* normals[0] = average of all nv[i] (i=2..n-1) */
9493 for (i = 2; i < gp->npoints; i++) {
9494 v1.x += gp->normals[i * 3];
9495 v1.y += gp->normals[i * 3 + 1];
9496 v1.z += gp->normals[i * 3 + 2];
9498 NormalizeVec(&v1, &v1);
9499 gp->normals[0] = v1.x;
9500 gp->normals[1] = v1.y;
9501 gp->normals[2] = v1.z;
9502 /* normals[1] = nv[2].normalize */
9503 v2.x = gp->normals[6];
9504 v2.y = gp->normals[7];
9505 v2.z = gp->normals[8];
9506 NormalizeVec(&v1, &v2);
9507 gp->normals[3] = v1.x;
9508 gp->normals[4] = v1.y;
9509 gp->normals[5] = v1.z;
9510 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9511 for (i = 2; i < gp->npoints; i++) {
9512 if (i == gp->npoints - 1)
9515 v3.x = gp->normals[i * 3 + 3];
9516 v3.y = gp->normals[i * 3 + 4];
9517 v3.z = gp->normals[i * 3 + 5];
9520 NormalizeVec(&v1, &v2);
9521 gp->normals[i * 3] = v1.x;
9522 gp->normals[i * 3 + 1] = v1.y;
9523 gp->normals[i * 3 + 2] = v1.z;
9530 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9532 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9533 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9534 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9535 * points: an array of Vectors
9539 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9545 VALUE kval, cval, pval, fval, ival;
9546 Data_Get_Struct(self, Molecule, mol);
9547 if (mol->mview == NULL)
9548 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9549 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9550 idx = NUM2INT(rb_Integer(ival));
9552 idx = mol->mview->ngraphics;
9553 else if (idx < 0 || idx > mol->mview->ngraphics)
9554 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9555 memset(&g, 0, sizeof(g));
9557 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9558 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9560 kval = rb_obj_as_string(kval);
9561 p = StringValuePtr(kval);
9562 if (strcmp(p, "line") == 0)
9563 g.kind = kMainViewGraphicLine;
9564 else if (strcmp(p, "poly") == 0)
9565 g.kind = kMainViewGraphicPoly;
9566 else if (strcmp(p, "cylinder") == 0)
9567 g.kind = kMainViewGraphicCylinder;
9568 else if (strcmp(p, "cone") == 0)
9569 g.kind = kMainViewGraphicCone;
9570 else if (strcmp(p, "ellipsoid") == 0)
9571 g.kind = kMainViewGraphicEllipsoid;
9572 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9574 g.closed = (RTEST(fval) ? 1 : 0);
9575 cval = rb_ary_to_ary(cval);
9576 n = RARRAY_LEN(cval);
9577 if (n < 3 || n >= 5)
9578 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9581 for (i = 0; i < n; i++)
9582 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9583 pval = rb_ary_to_ary(pval);
9584 n = RARRAY_LEN(pval);
9585 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9587 rb_raise(rb_eArgError, "no control points are given");
9589 case kMainViewGraphicLine:
9591 rb_raise(rb_eArgError, "the line object must have at least two control points");
9593 case kMainViewGraphicPoly:
9595 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9597 case kMainViewGraphicCylinder:
9598 case kMainViewGraphicCone:
9600 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9603 case kMainViewGraphicEllipsoid:
9607 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9610 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9611 for (i = 0; i < n; i++) {
9613 VALUE rval = RARRAY_PTR(pval)[i];
9615 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9616 /* The float argument can also be given as a vector (for simplify undo registration) */
9617 VectorFromValue(rval, &v);
9619 v.x = NUM2DBL(rb_Float(rval));
9623 VectorFromValue(rval, &v);
9625 g.points[i * 3] = v.x;
9626 g.points[i * 3 + 1] = v.y;
9627 g.points[i * 3 + 2] = v.z;
9629 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9631 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9632 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9633 g.points[7] = g.points[11] = g.points[3];
9635 if (g.kind == kMainViewGraphicPoly) {
9636 /* Calculate normals */
9637 s_CalculateGraphicNormals(&g);
9639 MainView_insertGraphic(mol->mview, idx, &g);
9644 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9645 MolActionCallback_registerUndo(mol, act);
9646 MolActionRelease(act);
9649 return INT2NUM(idx);
9654 * create_graphic(kind, color, points, fill = nil) -> integer
9656 * Create a new graphic object. The arguments are similar as insert_graphic.
9659 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9662 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9663 args[0] = INT2NUM(-1);
9664 return s_Molecule_InsertGraphic(argc + 1, args, self);
9669 * remove_graphic(index) -> integer
9671 * Remove a graphic object.
9674 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9678 Data_Get_Struct(self, Molecule, mol);
9679 if (mol->mview == NULL)
9680 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9681 i = NUM2INT(rb_Integer(ival));
9682 if (i < 0 || i >= mol->mview->ngraphics)
9683 rb_raise(rb_eArgError, "graphic index is out of range");
9685 /* Prepare data for undo */
9686 MainViewGraphic *gp;
9691 gp = mol->mview->graphics + i;
9692 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9693 for (n = 0; n < gp->npoints; n++) {
9694 vp[n].x = gp->points[n * 3];
9695 vp[n].y = gp->points[n * 3 + 1];
9696 vp[n].z = gp->points[n * 3 + 2];
9698 col[0] = gp->rgba[0];
9699 col[1] = gp->rgba[1];
9700 col[2] = gp->rgba[2];
9701 col[3] = gp->rgba[3];
9702 if (gp->visible == 0) {
9703 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9704 MolActionCallback_registerUndo(mol, act);
9705 MolActionRelease(act);
9707 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9708 MolActionCallback_registerUndo(mol, act);
9710 MolActionRelease(act);
9712 MainView_removeGraphic(mol->mview, i);
9718 * ngraphics -> integer
9720 * Get the number of graphic objects.
9723 s_Molecule_NGraphics(VALUE self)
9726 Data_Get_Struct(self, Molecule, mol);
9727 if (mol->mview == NULL)
9728 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9729 return INT2NUM(mol->mview->ngraphics);
9734 * get_graphic_point(graphic_index, point_index) -> value
9735 * get_graphic_points(graphic_index) -> values
9737 * Get the point_index-th control point of graphic_index-th graphic object.
9738 * Get an array of all control points with the given values.
9742 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9744 MainViewGraphic *gp;
9749 Data_Get_Struct(self, Molecule, mol);
9750 if (mol->mview == NULL)
9751 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9752 rb_scan_args(argc, argv, "11", &gval, &pval);
9753 index = NUM2INT(rb_Integer(gval));
9754 if (index < 0 || index >= mol->mview->ngraphics)
9755 rb_raise(rb_eArgError, "the graphic index is out of range");
9756 gp = mol->mview->graphics + index;
9758 pindex = NUM2INT(rb_Integer(pval));
9759 if (pindex < 0 || pindex >= gp->npoints)
9760 rb_raise(rb_eArgError, "the point index is out of range");
9761 v.x = gp->points[pindex * 3];
9762 v.y = gp->points[pindex * 3 + 1];
9763 v.z = gp->points[pindex * 3 + 2];
9764 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9765 return rb_float_new(v.x);
9767 return ValueFromVector(&v);
9770 pval = rb_ary_new();
9771 for (pindex = 0; pindex < gp->npoints; pindex++) {
9772 v.x = gp->points[pindex * 3];
9773 v.y = gp->points[pindex * 3 + 1];
9774 v.z = gp->points[pindex * 3 + 2];
9775 rb_ary_push(pval, ValueFromVector(&v));
9783 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9784 * set_graphic_points(graphic_index, new_values) -> new_values
9786 * Change the point_index-th control point of graphic_index-th graphic object.
9787 * Replace the control points with the given values.
9791 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9793 MainViewGraphic *gp;
9797 VALUE gval, pval, nval;
9799 Data_Get_Struct(self, Molecule, mol);
9800 if (mol->mview == NULL)
9801 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9802 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9803 index = NUM2INT(rb_Integer(gval));
9804 if (index < 0 || index >= mol->mview->ngraphics)
9805 rb_raise(rb_eArgError, "the graphic index is out of range");
9806 gp = mol->mview->graphics + index;
9808 pindex = NUM2INT(rb_Integer(pval));
9809 if (pindex < 0 || pindex >= gp->npoints)
9810 rb_raise(rb_eArgError, "the point index is out of range");
9811 v0.x = gp->points[pindex * 3];
9812 v0.y = gp->points[pindex * 3 + 1];
9813 v0.z = gp->points[pindex * 3 + 2];
9814 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9815 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9816 v.x = NUM2DBL(rb_Float(nval));
9818 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9819 v.x = NUM2DBL(rb_Float(nval));
9821 gp->points[7] = gp->points[11] = v.x;
9822 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9823 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9826 v.x = kInvalidFloat;
9828 } else VectorFromValue(nval, &v);
9830 gp->points[pindex * 3] = v.x;
9831 gp->points[pindex * 3 + 1] = v.y;
9832 gp->points[pindex * 3 + 2] = v.z;
9833 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9837 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9838 for (pindex = 0; pindex < gp->npoints; pindex++) {
9839 vp[pindex].x = gp->points[pindex * 3];
9840 vp[pindex].y = gp->points[pindex * 3 + 1];
9841 vp[pindex].z = gp->points[pindex * 3 + 2];
9843 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9845 pval = rb_ary_to_ary(pval);
9846 len = RARRAY_LEN(pval);
9847 if (gp->npoints < len) {
9848 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9850 } else if (gp->npoints > len) {
9853 case kMainViewGraphicLine: len2 = 2; break;
9854 case kMainViewGraphicPoly: len2 = 3; break;
9855 case kMainViewGraphicCylinder: len2 = 3; break;
9856 case kMainViewGraphicCone: len2 = 3; break;
9857 case kMainViewGraphicEllipsoid: len2 = 4; break;
9863 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9864 aval = RARRAY_PTR(pval)[pindex];
9865 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9866 v.x = NUM2DBL(rb_Float(aval));
9868 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9869 v.x = NUM2DBL(rb_Float(aval));
9871 gp->points[7] = gp->points[11] = v.x;
9872 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9874 } else VectorFromValue(aval, &v);
9875 gp->points[pindex * 3] = v.x;
9876 gp->points[pindex * 3 + 1] = v.y;
9877 gp->points[pindex * 3 + 2] = v.z;
9880 if (gp->kind == kMainViewGraphicPoly) {
9881 /* Calculate normals */
9882 s_CalculateGraphicNormals(gp);
9884 MolActionCallback_registerUndo(mol, act);
9885 MolActionRelease(act);
9886 MoleculeCallback_notifyModification(mol, 0);
9892 * get_graphic_color(graphic_index) -> value
9894 * Get the color of graphic_index-th graphic object
9897 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9899 MainViewGraphic *gp;
9902 Data_Get_Struct(self, Molecule, mol);
9903 if (mol->mview == NULL)
9904 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9905 index = NUM2INT(rb_Integer(gval));
9906 if (index < 0 || index >= mol->mview->ngraphics)
9907 rb_raise(rb_eArgError, "the graphic index is out of range");
9908 gp = mol->mview->graphics + index;
9909 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]));
9914 * set_graphic_color(graphic_index, new_value) -> new_value
9916 * Change the color of graphic_index-th graphic object
9920 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9922 MainViewGraphic *gp;
9927 Data_Get_Struct(self, Molecule, mol);
9928 if (mol->mview == NULL)
9929 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9930 index = NUM2INT(rb_Integer(gval));
9931 if (index < 0 || index >= mol->mview->ngraphics)
9932 rb_raise(rb_eArgError, "the graphic index is out of range");
9933 gp = mol->mview->graphics + index;
9934 for (i = 0; i < 4; i++)
9936 cval = rb_ary_to_ary(cval);
9937 n = RARRAY_LEN(cval);
9938 if (n != 3 && n != 4)
9939 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9941 for (i = 0; i < n; i++) {
9942 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9946 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9947 MolActionCallback_registerUndo(mol, act);
9948 MolActionRelease(act);
9949 MoleculeCallback_notifyModification(mol, 0);
9955 * show_graphic(graphic_index) -> self
9957 * Enable the visible flag of the graphic_index-th graphic object
9961 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9963 MainViewGraphic *gp;
9966 Data_Get_Struct(self, Molecule, mol);
9967 if (mol->mview == NULL)
9968 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9969 index = NUM2INT(rb_Integer(gval));
9970 if (index < 0 || index >= mol->mview->ngraphics)
9971 rb_raise(rb_eArgError, "the graphic index is out of range");
9972 gp = mol->mview->graphics + index;
9974 MoleculeCallback_notifyModification(mol, 0);
9980 * hide_graphic(graphic_index) -> self
9982 * Disable the visible flag of the graphic_index-th graphic object
9986 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9988 MainViewGraphic *gp;
9991 Data_Get_Struct(self, Molecule, mol);
9992 if (mol->mview == NULL)
9993 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9994 index = NUM2INT(rb_Integer(gval));
9995 if (index < 0 || index >= mol->mview->ngraphics)
9996 rb_raise(rb_eArgError, "the graphic index is out of range");
9997 gp = mol->mview->graphics + index;
9999 MoleculeCallback_notifyModification(mol, 0);
10005 * show_text(string)
10007 * Show the string in the info text box.
10010 s_Molecule_ShowText(VALUE self, VALUE arg)
10013 Data_Get_Struct(self, Molecule, mol);
10014 if (mol->mview != NULL)
10015 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10019 #pragma mark ------ MD Support ------
10023 * md_arena -> MDArena
10025 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10026 * this molecule, a new arena is created.
10029 s_Molecule_MDArena(VALUE self)
10033 Data_Get_Struct(self, Molecule, mol);
10034 if (mol->arena == NULL)
10036 retval = ValueFromMDArena(mol->arena);
10042 * set_parameter_attr(type, index, key, value, src) -> value
10044 * This method is used only internally.
10047 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10049 /* This method is called from MolAction to change a MM parameter attribute. */
10052 ParameterRef *pref;
10054 Data_Get_Struct(self, Molecule, mol);
10055 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10056 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10058 /* This is the special part of this method; it allows modification of the src field. */
10059 /* (ParameterRef#set_attr sets 0 to the src field) */
10060 Data_Get_Struct(pval, ParameterRef, pref);
10061 up = ParameterRefGetPar(pref);
10062 up->bond.src = FIX2INT(sval);
10069 * parameter -> Parameter
10071 * Get the local parameter of this molecule. If not defined, returns nil.
10074 s_Molecule_Parameter(VALUE self)
10077 Data_Get_Struct(self, Molecule, mol);
10078 /* if (mol->par == NULL)
10080 return s_NewParameterValueFromValue(self);
10085 * start_step -> Integer
10087 * Returns the start step (defined by dcd format).
10090 s_Molecule_StartStep(VALUE self)
10093 Data_Get_Struct(self, Molecule, mol);
10094 return INT2NUM(mol->startStep);
10099 * start_step = Integer
10101 * Set the start step (defined by dcd format).
10104 s_Molecule_SetStartStep(VALUE self, VALUE val)
10107 Data_Get_Struct(self, Molecule, mol);
10108 mol->startStep = NUM2INT(rb_Integer(val));
10114 * steps_per_frame -> Integer
10116 * Returns the number of steps between frames (defined by dcd format).
10119 s_Molecule_StepsPerFrame(VALUE self)
10122 Data_Get_Struct(self, Molecule, mol);
10123 return INT2NUM(mol->stepsPerFrame);
10128 * steps_per_frame = Integer
10130 * Set the number of steps between frames (defined by dcd format).
10133 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10136 Data_Get_Struct(self, Molecule, mol);
10137 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10143 * ps_per_step -> Float
10145 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10148 s_Molecule_PsPerStep(VALUE self)
10151 Data_Get_Struct(self, Molecule, mol);
10152 return rb_float_new(mol->psPerStep);
10157 * ps_per_step = Float
10159 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10162 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10165 Data_Get_Struct(self, Molecule, mol);
10166 mol->psPerStep = NUM2DBL(rb_Float(val));
10171 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10173 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.");
10176 #pragma mark ------ MO Handling ------
10180 * selectedMO -> IntGroup
10182 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10183 * is not selected, returns nil. If the MO info table is selected but no MOs
10184 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10187 s_Molecule_SelectedMO(VALUE self)
10192 Data_Get_Struct(self, Molecule, mol);
10193 if (mol->mview == NULL)
10195 ig = MainView_selectedMO(mol->mview);
10198 IntGroupOffset(ig, 1);
10199 val = ValueFromIntGroup(ig);
10200 IntGroupRelease(ig);
10206 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10208 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10209 * If the molecule does not contain a basis set information, then returns nil.
10212 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10215 Vector o, dx, dy, dz;
10218 Int npoints = 80 * 80 * 80;
10219 Data_Get_Struct(self, Molecule, mol);
10220 if (mol->bset == NULL)
10222 rb_scan_args(argc, argv, "01", &nval);
10224 npoints = NUM2INT(rb_Integer(nval));
10225 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10227 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));
10231 s_Cubegen_callback(double progress, void *ref)
10233 MyAppCallback_setProgressValue(progress);
10234 if (MyAppCallback_checkInterrupt())
10241 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10242 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10244 * Calculate the molecular orbital with number mo and create a 'cube' file.
10245 * In the first form, the cube size is estimated from the atomic coordinates. In the
10246 * second form, the cube dimension is explicitly given.
10247 * Returns fname when successful, nil otherwise.
10248 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10249 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10250 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10253 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10255 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10257 Int mono, nx, ny, nz, npoints;
10258 Vector o, dx, dy, dz;
10261 Data_Get_Struct(self, Molecule, mol);
10262 if (mol->bset == NULL)
10263 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10264 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10266 /* Set up parameters */
10267 mono = NUM2INT(rb_Integer(mval));
10268 if (mono < 0 || mono > mol->bset->ncomps)
10269 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d, or 0 as 'arbitrary vector')", mono, mol->bset->ncomps);
10271 if (mol->bset->rflag != 0)
10272 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10273 mono += mol->bset->ncomps;
10276 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10277 /* Automatic grid formation */
10279 npoints = NUM2INT(rb_Integer(oval));
10283 else if (npoints < 8)
10284 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10285 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10286 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10290 VectorFromValue(oval, &o);
10291 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10292 VectorFromValue(dxval, &dx);
10294 dx.x = NUM2DBL(rb_Float(dxval));
10297 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10298 VectorFromValue(dyval, &dy);
10300 dy.y = NUM2DBL(rb_Float(dyval));
10303 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10304 VectorFromValue(dzval, &dz);
10306 dz.z = NUM2DBL(rb_Float(dzval));
10309 nx = NUM2INT(rb_Integer(nxval));
10310 ny = NUM2INT(rb_Integer(nyval));
10311 nz = NUM2INT(rb_Integer(nzval));
10312 if (nx <= 0 || ny <= 0 || nz <= 0)
10313 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10314 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10315 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);
10319 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10322 else if (index < 0)
10323 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10325 /* Output to file */
10326 MoleculeCallback_displayName(mol, buf, sizeof buf);
10327 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10329 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10331 /* Discard the cube */
10332 MoleculeClearCubeAtIndex(mol, index);
10340 * Clear the MO surface if present.
10343 s_Molecule_ClearSurface(VALUE self)
10346 Data_Get_Struct(self, Molecule, mol);
10347 if (mol->mcube != NULL)
10348 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10356 * Hide the MO surface if present.
10359 s_Molecule_HideSurface(VALUE self)
10362 Data_Get_Struct(self, Molecule, mol);
10363 if (mol->mcube != NULL) {
10364 mol->mcube->hidden = 1;
10365 MoleculeCallback_notifyModification(mol, 0);
10374 * Show the MO surface if present.
10377 s_Molecule_ShowSurface(VALUE self)
10380 Data_Get_Struct(self, Molecule, mol);
10381 if (mol->mcube != NULL) {
10382 mol->mcube->hidden = 0;
10383 MoleculeCallback_notifyModification(mol, 0);
10390 * create_surface(mo, attr = nil)
10392 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10393 * then it denotes the beta orbital.
10394 * If mo is nil, then the attributes of the current surface are modified.
10396 * :npoints : the approximate number of grid points
10397 * :expand : the scale factor to expand/shrink the display box size for each atom,
10398 * :thres : the threshold for the isovalue surface
10399 * If the molecule does not contain MO information, raises exception.
10402 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10405 Vector o, dx, dy, dz;
10406 Int nmo, nx, ny, nz, i;
10407 Int need_recalc = 0;
10408 VALUE nval, hval, aval;
10413 Data_Get_Struct(self, Molecule, mol);
10414 rb_scan_args(argc, argv, "11", &nval, &hval);
10415 if (mol->bset == NULL)
10416 rb_raise(rb_eMolbyError, "No MO information is given");
10417 if (nval == Qnil) {
10419 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10420 nmo = mol->bset->nmos + 1;
10422 nmo = NUM2INT(rb_Integer(nval));
10423 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10424 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);
10426 nmo = -nmo + mol->bset->ncomps;
10428 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10429 npoints = NUM2INT(rb_Integer(aval));
10431 } else if (mol->mcube != NULL) {
10432 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10433 } else npoints = 80 * 80 * 80;
10434 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10435 expand = NUM2DBL(rb_Float(aval));
10436 } else if (mol->mcube != NULL) {
10437 expand = mol->mcube->expand;
10438 } else expand = 1.0;
10439 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10440 thres = NUM2DBL(rb_Float(aval));
10441 } else if (mol->mcube != NULL) {
10442 thres = mol->mcube->thres;
10443 } else thres = 0.05;
10444 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10445 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10446 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10447 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10448 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10450 for (nx = 0; nx < 2; nx++) {
10451 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10452 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10453 aval = rb_ary_to_ary(aval);
10454 if (RARRAY_LEN(aval) < 3) {
10456 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10458 for (i = 0; i < 4; i++)
10459 d[i] = mol->mcube->c[nx].rgba[i];
10460 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10461 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10462 if (d[i] < 0.0 && d[i] > 1.0)
10465 for (i = 0; i < 4; i++)
10466 mol->mcube->c[nx].rgba[i] = d[i];
10469 if (mol->mcube->expand != expand)
10471 mol->mcube->thres = thres;
10472 mol->mcube->expand = expand;
10474 if (mol->mcube->idn < 0)
10475 return self; /* Only set attributes for now */
10477 nmo = mol->mcube->idn; /* Force recalculation */
10479 if (MoleculeUpdateMCube(mol, nmo) != 0)
10480 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10486 * set_surface_attr(attr = nil)
10488 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10491 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10496 return s_Molecule_CreateSurface(2, args, self);
10503 * Get the number of electrostatic potential info.
10506 s_Molecule_NElpots(VALUE self)
10509 Data_Get_Struct(self, Molecule, mol);
10510 return INT2NUM(mol->nelpots);
10517 * Get the electrostatic potential info at the given index. If present, then the
10518 * return value is [Vector, Float] (position and potential). If not present, then
10522 s_Molecule_Elpot(VALUE self, VALUE ival)
10526 Data_Get_Struct(self, Molecule, mol);
10527 idx = NUM2INT(rb_Integer(ival));
10528 if (idx < 0 || idx >= mol->nelpots)
10530 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10537 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10538 * cube and marching cube information are discarded. This operation is _not_ undoable!
10541 s_Molecule_ClearBasisSet(VALUE self)
10544 Data_Get_Struct(self, Molecule, mol);
10546 if (mol->bset != NULL) {
10547 BasisSetRelease(mol->bset);
10550 if (mol->mcube != NULL) {
10551 MoleculeDeallocateMCube(mol->mcube);
10560 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10562 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10563 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10567 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10570 int sym, nprims, a_idx, n;
10571 Data_Get_Struct(self, Molecule, mol);
10572 a_idx = NUM2INT(rb_Integer(aval));
10573 sym = NUM2INT(rb_Integer(symval));
10574 nprims = NUM2INT(rb_Integer(npval));
10575 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10577 rb_raise(rb_eMolbyError, "Molecule is emptry");
10579 rb_raise(rb_eMolbyError, "Low memory");
10581 rb_raise(rb_eMolbyError, "Unknown orbital type");
10583 rb_raise(rb_eMolbyError, "Unknown error");
10589 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10591 * To be used internally. Add a gaussian primitive coefficients.
10594 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10598 Double exponent, contraction, contraction_sp;
10599 Data_Get_Struct(self, Molecule, mol);
10600 exponent = NUM2DBL(rb_Float(expval));
10601 contraction = NUM2DBL(rb_Float(cval));
10602 contraction_sp = NUM2DBL(rb_Float(cspval));
10603 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10605 rb_raise(rb_eMolbyError, "Molecule is emptry");
10607 rb_raise(rb_eMolbyError, "Low memory");
10609 rb_raise(rb_eMolbyError, "Unknown error");
10615 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10617 * Get the Gaussian shell information for the given MO coefficient index.
10618 * The symmetry code is the same as in add_gaussian_orbital_shell.
10619 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10620 * is the number of MO component belonging to this shell.
10623 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10628 Data_Get_Struct(self, Molecule, mol);
10629 if (mol->bset == NULL)
10630 rb_raise(rb_eMolbyError, "No basis set information is defined");
10631 s_idx = NUM2INT(rb_Integer(sval));
10632 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10634 sp = mol->bset->shells + s_idx;
10637 case kGTOType_S: sym = 0; break;
10638 case kGTOType_SP: sym = -1; break;
10639 case kGTOType_P: sym = 1; break;
10640 case kGTOType_D: sym = 2; break;
10641 case kGTOType_D5: sym = -2; break;
10642 case kGTOType_F: sym = 3; break;
10643 case kGTOType_F7: sym = -3; break;
10644 case kGTOType_G: sym = 4; break;
10645 case kGTOType_G9: sym = -4; break;
10647 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10649 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10654 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10656 * Get the Gaussian primitive coefficients for the given MO component.
10659 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10665 VALUE retval, aval;
10666 Data_Get_Struct(self, Molecule, mol);
10667 if (mol->bset == NULL)
10668 rb_raise(rb_eMolbyError, "No basis set information is defined");
10669 s_idx = NUM2INT(rb_Integer(sval));
10670 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10672 sp = mol->bset->shells + s_idx;
10673 pp = mol->bset->priminfos + sp->p_idx;
10674 retval = rb_ary_new2(sp->nprim);
10675 for (i = 0; i < sp->nprim; i++) {
10676 if (sp->sym == kGTOType_SP) {
10677 /* With P contraction coefficient */
10678 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10680 /* Without P contraction coefficient */
10681 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10683 rb_ary_store(retval, i, aval);
10690 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10692 * Get the Gaussian shell information for the given MO coefficient index.
10695 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10698 Int n, c, atom_idx, shell_idx;
10700 Data_Get_Struct(self, Molecule, mol);
10701 if (mol->bset == NULL)
10702 rb_raise(rb_eMolbyError, "No basis set information is defined");
10703 c = NUM2INT(rb_Integer(cval));
10704 if (c < 0 || c >= mol->bset->ncomps)
10706 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10708 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10709 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10714 * clear_mo_coefficients
10716 * Clear the existing MO coefficients.
10719 s_Molecule_ClearMOCoefficients(VALUE self)
10722 Data_Get_Struct(self, Molecule, mol);
10723 if (mol->bset != NULL) {
10724 if (mol->bset->moenergies != NULL) {
10725 free(mol->bset->moenergies);
10726 mol->bset->moenergies = NULL;
10728 if (mol->bset->mo != NULL) {
10729 free(mol->bset->mo);
10730 mol->bset->mo = NULL;
10732 mol->bset->nmos = 0;
10739 * set_mo_coefficients(idx, energy, coefficients)
10741 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10742 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10743 * Energy is the MO energy, and coefficients is an array
10744 * of MO coefficients.
10747 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10750 Int idx, ncomps, i;
10753 Data_Get_Struct(self, Molecule, mol);
10754 idx = NUM2INT(rb_Integer(ival));
10755 energy = NUM2DBL(rb_Float(eval));
10756 aval = rb_ary_to_ary(aval);
10757 ncomps = RARRAY_LEN(aval);
10758 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10759 if (coeffs == NULL) {
10763 for (i = 0; i < ncomps; i++)
10764 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10765 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10768 rb_raise(rb_eMolbyError, "Molecule is emptry");
10770 rb_raise(rb_eMolbyError, "Low memory");
10772 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10774 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10776 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10778 rb_raise(rb_eMolbyError, "Unknown error");
10784 * get_mo_coefficients(idx)
10786 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10789 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10792 Int idx, ncomps, n;
10796 Data_Get_Struct(self, Molecule, mol);
10797 idx = NUM2INT(rb_Integer(ival));
10800 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10802 rb_raise(rb_eMolbyError, "Molecule is emptry");
10804 rb_raise(rb_eMolbyError, "No basis set information is present");
10806 return Qnil; /* Silently returns nil */
10807 retval = rb_ary_new2(ncomps);
10808 for (n = 0; n < ncomps; n++)
10809 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10816 * get_mo_energy(idx)
10818 * To be used internally. Get the MO energy for the given MO index (1-based).
10821 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10826 Data_Get_Struct(self, Molecule, mol);
10827 idx = NUM2INT(rb_Integer(ival));
10828 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10830 rb_raise(rb_eMolbyError, "Molecule is emptry");
10832 rb_raise(rb_eMolbyError, "No basis set information is present");
10835 return rb_float_new(energy);
10838 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10841 s_InitMOInfoKeys(void)
10843 if (sTypeSym == 0) {
10844 sTypeSym = ID2SYM(rb_intern("type"));
10845 sAlphaSym = ID2SYM(rb_intern("alpha"));
10846 sBetaSym = ID2SYM(rb_intern("beta"));
10847 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10848 sNshellsSym = ID2SYM(rb_intern("nshells"));
10854 * set_mo_info(hash)
10856 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10857 * :alpha=>integer, :beta=>integer
10860 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10864 Int rflag, na, nb, n;
10866 Data_Get_Struct(self, Molecule, mol);
10867 if (mol->bset != NULL) {
10868 rflag = mol->bset->rflag;
10869 na = mol->bset->ne_alpha;
10870 nb = mol->bset->ne_beta;
10876 if (hval != Qnil) {
10877 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10878 s = StringValuePtr(aval);
10879 if (strcasecmp(s, "RHF") == 0)
10881 else if (strcasecmp(s, "UHF") == 0)
10883 else if (strcasecmp(s, "ROHF") == 0)
10886 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10887 n = NUM2INT(rb_Integer(aval));
10891 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10892 n = NUM2INT(rb_Integer(aval));
10896 MoleculeSetMOInfo(mol, rflag, na, nb);
10905 * Get the MO info. The key is as described in set_mo_info.
10906 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10909 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10912 Data_Get_Struct(self, Molecule, mol);
10913 if (mol->bset == NULL)
10915 if (kval == sTypeSym) {
10916 switch (mol->bset->rflag) {
10917 case 0: return rb_str_new2("UHF");
10918 case 1: return rb_str_new2("RHF");
10919 case 2: return rb_str_new2("ROHF");
10920 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10922 } else if (kval == sAlphaSym) {
10923 return INT2NUM(mol->bset->ne_alpha);
10924 } else if (kval == sBetaSym) {
10925 return INT2NUM(mol->bset->ne_beta);
10926 } else if (kval == sNcompsSym) {
10927 return INT2NUM(mol->bset->ncomps);
10928 } else if (kval == sNshellsSym) {
10929 return INT2NUM(mol->bset->nshells);
10931 kval = rb_inspect(kval);
10932 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10933 return Qnil; /* Does not reach here */
10941 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10944 s_Molecule_MOType(VALUE self)
10946 return s_Molecule_GetMOInfo(self, sTypeSym);
10949 #pragma mark ------ Molecular Topology ------
10953 * search_equivalent_atoms(ig = nil)
10955 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10958 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10964 Data_Get_Struct(self, Molecule, mol);
10965 if (mol->natoms == 0)
10967 rb_scan_args(argc, argv, "01", &val);
10969 ig = IntGroupFromValue(val);
10971 result = MoleculeSearchEquivalentAtoms(mol, ig);
10972 if (result == NULL)
10973 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10975 IntGroupRelease(ig);
10976 val = rb_ary_new2(mol->natoms);
10977 for (i = 0; i < mol->natoms; i++)
10978 rb_ary_push(val, INT2NUM(result[i]));
10985 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10987 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10988 * Name is the name of the new pi anchor, and group is the atoms that define
10989 * the pi system. Type (a String) is an atom type for MM implementation.
10990 * Weights represent the relative significance of the component atoms; if omitted, then
10991 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10992 * The weight values will be normalized so that the sum of the weights is 1.0.
10993 * The weight values must be positive.
10994 * Index is the atom index where the created pi-anchor is inserted in the
10995 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10996 * having the largest index.
10997 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10998 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11001 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11006 Int i, n, idx, last_component;
11010 if (argc < 2 || argc >= 6)
11011 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11015 Data_Get_Struct(self, Molecule, mol);
11016 ig = IntGroupFromValue(gval);
11017 memset(&a, 0, sizeof(a));
11018 memset(&an, 0, sizeof(an));
11019 strncpy(a.aname, StringValuePtr(nval), 4);
11020 if (a.aname[0] == '_')
11021 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11022 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11023 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11024 if (n >= mol->natoms) {
11025 AtomConnectResize(&an.connect, 0);
11026 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11028 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11029 last_component = n;
11031 if (an.connect.count == 0)
11032 rb_raise(rb_eMolbyError, "no atoms are specified");
11033 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11034 for (i = 0; i < an.connect.count; i++) {
11035 an.coeffs[i] = 1.0 / an.connect.count;
11037 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11039 if (argv[0] != Qnil)
11040 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11044 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11045 if (argv[0] != Qnil) {
11046 VALUE aval = rb_ary_to_ary(argv[0]);
11048 if (RARRAY_LEN(aval) != an.connect.count)
11049 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11050 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11051 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11053 rb_raise(rb_eMolbyError, "the weight value must be positive");
11057 for (i = 0; i < an.connect.count; i++)
11058 an.coeffs[i] /= sum;
11063 if (argc > 0 && argv[0] != Qnil) {
11065 idx = NUM2INT(rb_Integer(argv[0]));
11067 if (idx < 0 || idx > mol->natoms) {
11068 /* Immediately after the last specified atom */
11069 idx = last_component + 1;
11071 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11072 memmove(a.anchor, &an, sizeof(PiAnchor));
11073 /* Use residue information of the last specified atom */
11074 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11075 a.resSeq = ap->resSeq;
11076 strncpy(a.resName, ap->resName, 4);
11077 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11079 MoleculeCalculatePiAnchorPosition(mol, idx);
11080 aref = AtomRefNew(mol, idx);
11081 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11084 #pragma mark ------ Molecular Properties ------
11088 * set_property(name, value[, index]) -> value
11089 * set_property(name, values, group) -> values
11091 * Set molecular property. A property is a floating-point number with a specified name,
11092 * and can be set for each frame separately. The name of the property is given as a String.
11093 * The value can be a single floating point number, which is set to the current frame.
11097 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11100 VALUE nval, vval, ival;
11103 Int i, n, idx, fidx;
11105 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11106 Data_Get_Struct(self, Molecule, mol);
11107 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11108 idx = NUM2INT(rb_Integer(nval));
11109 if (idx < 0 || idx >= mol->nmolprops)
11110 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11112 name = StringValuePtr(nval);
11113 idx = MoleculeLookUpProperty(mol, name);
11115 idx = MoleculeCreateProperty(mol, name);
11117 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11120 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11122 fidx = mol->cframe;
11124 fidx = NUM2INT(rb_Integer(ival));
11125 n = MoleculeGetNumberOfFrames(mol);
11126 if (fidx < 0 || fidx >= n)
11127 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11129 ig = IntGroupNewWithPoints(fidx, 1, -1);
11130 dp = (Double *)malloc(sizeof(Double));
11131 *dp = NUM2DBL(rb_Float(vval));
11134 vval = rb_ary_to_ary(vval);
11135 ig = IntGroupFromValue(ival);
11136 n = IntGroupGetCount(ig);
11138 rb_raise(rb_eMolbyError, "No frames are specified");
11139 if (RARRAY_LEN(vval) < n)
11140 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11141 dp = (Double *)calloc(sizeof(Double), n);
11142 for (i = 0; i < n; i++)
11143 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11146 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11148 IntGroupRelease(ig);
11154 * get_property(name[, index]) -> value
11155 * get_property(name, group) -> values
11157 * Get molecular property. In the first form, a property value for a single frame is returned.
11158 * (If index is omitted, then the value for the current frame is given)
11159 * In the second form, an array of property values for the given frames is returned.
11160 * If name is not one of known properties or a valid index integer, exception is raised.
11163 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11169 Int i, n, idx, fidx;
11171 rb_scan_args(argc, argv, "11", &nval, &ival);
11172 Data_Get_Struct(self, Molecule, mol);
11173 if (mol->nmolprops == 0)
11174 rb_raise(rb_eMolbyError, "The molecule has no properties");
11175 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11176 idx = NUM2INT(rb_Integer(nval));
11177 if (idx < 0 || idx >= mol->nmolprops)
11178 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11180 name = StringValuePtr(nval);
11181 idx = MoleculeLookUpProperty(mol, name);
11183 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11185 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11187 fidx = mol->cframe;
11189 fidx = NUM2INT(rb_Integer(ival));
11190 n = MoleculeGetNumberOfFrames(mol);
11191 if (fidx < 0 || fidx >= n)
11192 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11194 ig = IntGroupNewWithPoints(fidx, 1, -1);
11195 ival = INT2FIX(fidx);
11198 ig = IntGroupFromValue(ival);
11199 n = IntGroupGetCount(ig);
11201 return rb_ary_new();
11203 dp = (Double *)calloc(sizeof(Double), n);
11204 MoleculeGetProperty(mol, idx, ig, dp);
11205 if (FIXNUM_P(ival))
11206 ival = rb_float_new(dp[0]);
11208 ival = rb_ary_new();
11209 for (i = n - 1; i >= 0; i--) {
11210 nval = rb_float_new(dp[i]);
11211 rb_ary_store(ival, i, nval);
11215 IntGroupRelease(ig);
11221 * property_names -> Array
11223 * Get an array of property names.
11226 s_Molecule_PropertyNames(VALUE self)
11231 Data_Get_Struct(self, Molecule, mol);
11232 rval = rb_ary_new();
11233 for (i = mol->nmolprops - 1; i >= 0; i--) {
11234 nval = rb_str_new2(mol->molprops[i].propname);
11235 rb_ary_store(rval, i, nval);
11240 #pragma mark ------ Class methods ------
11244 * current -> Molecule
11246 * Get the currently "active" molecule.
11249 s_Molecule_Current(VALUE klass)
11251 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11256 * Molecule[] -> Molecule
11257 * Molecule[n] -> Molecule
11258 * Molecule[name] -> Molecule
11259 * Molecule[name, k] -> Molecule
11260 * Molecule[regex] -> Molecule
11261 * Molecule[regex, k] -> Molecule
11263 * Molecule[] is equivalent to Molecule.current.
11264 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11265 * Molecule[name] gives the first document (in the order of creation time) that has
11266 * the given name. If a second argument (k) is given, the k-th document that has the
11267 * given name is returned.
11268 * Molecule[regex] gives the first document (in the order of creation time) that
11269 * has a name matching the regular expression. If a second argument (k) is given,
11270 * the k-th document that has a name matching the re is returned.
11273 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11279 rb_scan_args(argc, argv, "02", &val, &kval);
11281 return s_Molecule_Current(klass);
11282 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11283 idx = NUM2INT(val);
11284 mol = MoleculeCallback_moleculeAtIndex(idx);
11285 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11286 char *p = StringValuePtr(val);
11287 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11288 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11289 MoleculeCallback_displayName(mol, buf, sizeof buf);
11290 if (strcmp(buf, p) == 0 && --k == 0)
11293 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11294 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11295 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11297 MoleculeCallback_displayName(mol, buf, sizeof buf);
11298 name = rb_str_new2(buf);
11299 if (rb_reg_match(val, name) != Qnil && --k == 0)
11302 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11306 else return ValueFromMolecule(mol);
11311 * list -> array of Molecules
11313 * Get the list of molecules associated to the documents, in the order of creation
11314 * time of the document. If no document is open, returns an empry array.
11317 s_Molecule_List(VALUE klass)
11323 ary = rb_ary_new();
11324 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11325 rb_ary_push(ary, ValueFromMolecule(mol));
11333 * ordered_list -> array of Molecules
11335 * Get the list of molecules associated to the documents, in the order of front-to-back
11336 * ordering of the associated window. If no document is open, returns an empry array.
11339 s_Molecule_OrderedList(VALUE klass)
11345 ary = rb_ary_new();
11346 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11347 rb_ary_push(ary, ValueFromMolecule(mol));
11353 #pragma mark ------ Call Subprocess ------
11355 /* The callback functions for call_subprocess_async */
11357 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11360 VALUE procval, retval, args[2];
11361 args[0] = ValueFromMolecule(mol);
11362 args[1] = INT2NUM(status);
11363 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11364 if (procval != Qnil) {
11365 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11366 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11373 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11376 VALUE procval, retval, args[2];
11377 args[0] = ValueFromMolecule(mol);
11378 args[1] = INT2NUM(tcount);
11379 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11380 if (procval != Qnil) {
11381 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11382 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11390 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11392 * Call subprocess asynchronically.
11393 * If end_callback is given, it will be called (with two arguments self and termination status)
11394 * when the subprocess terminated.
11395 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11396 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11397 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11398 * filename begins with ">>", then the message will be appended to the file.
11399 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11400 * If the argument is nil, then the message will be sent to the Ruby console.
11401 * Returns the process ID as an integer.
11404 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11406 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11410 FILE *fpout, *fperr;
11411 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11412 Data_Get_Struct(self, Molecule, mol);
11414 if (stdout_val == Qnil) {
11417 sout = StringValuePtr(stdout_val);
11418 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11421 if (strncmp(sout, ">>", 2) == 0) {
11423 fpout = fopen(sout, "a");
11427 fpout = fopen(sout, "w");
11430 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11433 if (stderr_val == Qnil) {
11436 serr = StringValuePtr(stderr_val);
11437 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11440 if (strncmp(serr, ">>", 2) == 0) {
11442 fpout = fopen(serr, "a");
11446 fperr = fopen(serr, "w");
11449 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11453 /* Register procs as instance variables */
11454 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11455 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11456 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11457 if (fpout != NULL && fpout != (FILE *)1)
11459 if (fperr != NULL && fperr != (FILE *)1)
11464 #pragma mark ====== Define Molby Classes ======
11471 /* Define module Molby */
11472 rb_mMolby = rb_define_module("Molby");
11474 /* Define Vector3D, Transform, IntGroup */
11477 /* Define MDArena */
11478 Init_MolbyMDTypes();
11480 /* class Molecule */
11481 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11483 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11484 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11485 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11486 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11487 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11489 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11490 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11491 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11492 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11493 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11494 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11495 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11496 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11497 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11498 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11499 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11500 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11501 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11502 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11503 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11504 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11505 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11506 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11507 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11508 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11509 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11510 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11512 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11513 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11514 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11515 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11516 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11518 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11519 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11520 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11521 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11522 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11523 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11524 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11525 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11526 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11527 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11528 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11529 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11530 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11531 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11532 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11534 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11535 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11536 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11537 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11538 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11540 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11541 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11542 rb_define_alias(rb_cMolecule, "+", "add");
11543 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11544 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11545 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11546 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11547 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11548 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11549 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11550 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11551 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11552 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11553 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11554 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11555 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11556 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11557 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11558 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11559 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11560 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11561 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11562 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11563 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11565 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11566 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11567 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11568 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11569 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11571 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11572 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11573 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11574 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11575 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11576 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11577 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11578 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11579 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11581 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11582 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11583 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11584 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11585 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11586 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11587 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11588 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11589 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11590 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11591 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11592 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11593 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11594 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11595 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11596 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11597 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11598 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11599 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11600 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11601 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11602 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11604 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11605 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11606 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11607 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11608 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11609 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11610 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11612 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11613 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11614 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11615 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11616 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11617 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11618 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11619 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11620 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11621 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11622 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11623 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11624 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11626 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11627 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11628 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11629 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11630 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11631 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11633 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11634 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11635 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11636 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11637 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11638 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11639 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11640 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11641 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11642 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11643 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11644 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11645 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11646 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11647 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11648 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11649 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11650 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11651 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11652 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11653 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11654 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11655 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11656 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11657 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11658 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11659 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11660 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11661 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11662 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11663 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11664 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11665 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11666 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11667 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11668 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11669 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11670 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11671 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11672 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11673 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11674 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11675 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11676 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11677 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11678 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11679 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11680 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11681 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11682 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11683 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11684 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11685 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11686 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11688 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11689 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11690 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11691 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11692 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11693 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11694 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11695 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11696 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11697 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11698 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11699 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11700 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11702 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11703 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11704 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11705 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11706 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11707 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11708 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11709 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11710 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11711 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11712 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11713 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11714 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11715 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11717 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11718 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11719 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11720 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11721 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11722 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11723 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11724 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11725 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11726 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11727 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11728 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11729 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11730 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11731 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11732 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11733 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11734 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11735 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11736 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11737 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11738 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11739 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11741 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11742 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11744 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11745 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11746 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11748 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11749 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11750 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11751 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11753 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11755 /* class MolEnumerable */
11756 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11757 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11758 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11759 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11760 rb_define_alias(rb_cMolEnumerable, "size", "length");
11761 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11762 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11764 /* class AtomRef */
11765 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11766 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11768 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11769 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11770 s_AtomAttrDefTable[i].id = rb_intern(buf);
11771 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11773 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11775 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11776 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11777 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11778 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11779 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11780 rb_global_variable(&s_SetAtomAttrString);
11781 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11782 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11784 /* class Parameter */
11785 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11786 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11787 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11788 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11789 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11790 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11791 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11792 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11793 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11794 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11795 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11796 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11797 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11798 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11799 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11800 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11801 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11802 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11803 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11804 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11805 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11806 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11807 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11808 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11809 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11810 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11811 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11812 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11813 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11814 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11815 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11816 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11817 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11818 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11819 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11820 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11821 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11822 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11823 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11824 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11825 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11826 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11827 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11828 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11829 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11830 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11831 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11832 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11833 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11834 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11835 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11836 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11837 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11838 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11840 /* class ParEnumerable */
11841 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11842 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11843 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11844 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11845 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11846 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11847 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11848 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11849 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11850 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11851 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11853 /* class ParameterRef */
11854 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11855 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11857 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11858 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11859 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11860 if (s_ParameterAttrDefTable[i].symref != NULL)
11861 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11862 if (s_ParameterAttrDefTable[i].setter != NULL) {
11864 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11867 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11868 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11869 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11870 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11871 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11872 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11873 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11874 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11876 /* class MolbyError */
11877 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11879 /* module Kernel */
11880 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11881 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11882 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11883 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11884 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11885 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11886 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11887 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11888 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11889 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11890 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11891 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11892 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11893 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11894 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11895 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11896 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11897 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11898 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11899 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11900 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11901 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11902 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11903 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11906 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11908 s_ID_equal = rb_intern("==");
11909 g_RubyID_call = rb_intern("call");
11911 s_InitMOInfoKeys();
11913 /* Symbols for graphics */
11914 s_LineSym = ID2SYM(rb_intern("line"));
11915 s_PolySym = ID2SYM(rb_intern("poly"));
11916 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11917 s_ConeSym = ID2SYM(rb_intern("cone"));
11918 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11921 #pragma mark ====== Interface with RubyDialog class ======
11924 RubyDialogCallback_parentModule(void)
11926 return (RubyValue)rb_mMolby;
11929 #pragma mark ====== External functions ======
11931 static VALUE s_ruby_top_self = Qfalse;
11932 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11933 static VALUE s_ruby_export_local_variables = Qfalse;
11936 s_evalRubyScriptOnMoleculeSub(VALUE val)
11938 void **ptr = (void **)val;
11939 Molecule *mol = (Molecule *)ptr[1];
11940 VALUE sval, fnval, lnval, retval;
11943 /* Clear the error information (store in the history array if necessary) */
11944 sval = rb_errinfo();
11945 if (sval != Qnil) {
11946 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11947 rb_set_errinfo(Qnil);
11950 if (s_ruby_top_self == Qfalse) {
11951 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11953 if (s_ruby_get_binding_for_molecule == Qfalse) {
11955 "lambda { |_mol_, _bind_| \n"
11956 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11957 " _proc_.call(_mol_) } ";
11958 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11959 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11961 if (s_ruby_export_local_variables == Qfalse) {
11963 "lambda { |_bind_| \n"
11964 " # find local variables newly defined in _bind_ \n"
11965 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11966 " _a_.each { |_vsym_| \n"
11967 " _vname_ = _vsym_.to_s \n"
11968 " _vval_ = _bind_.eval(_vname_) \n"
11969 " # Define local variable \n"
11970 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11971 " # Then set value \n"
11972 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11975 s_ruby_export_local_variables = rb_eval_string(s2);
11976 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11978 if (ptr[2] == NULL) {
11980 /* String literal: we need to specify string encoding */
11981 #if defined(__WXMSW__)
11982 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11984 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11986 sval = rb_str_new2(scr);
11988 fnval = rb_str_new2("(eval)");
11989 lnval = INT2FIX(0);
11991 sval = rb_str_new2((char *)ptr[0]);
11992 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11993 lnval = INT2FIX(1);
11995 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11997 VALUE mval = ValueFromMolecule(mol);
11998 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12000 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12002 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12008 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12012 VALUE save_interrupt_flag;
12013 /* char *save_ruby_sourcefile;
12014 int save_ruby_sourceline; */
12015 if (gMolbyIsCheckingInterrupt) {
12016 MolActionAlertRubyIsRunning();
12018 return (RubyValue)Qnil;
12021 args[0] = (void *)script;
12022 args[1] = (void *)mol;
12023 args[2] = (void *)fname;
12024 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12025 /* save_ruby_sourcefile = ruby_sourcefile;
12026 save_ruby_sourceline = ruby_sourceline; */
12027 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12028 if (*status != 0) {
12029 /* Is this 'exit' exception? */
12030 VALUE last_exception = rb_gv_get("$!");
12031 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12032 /* Capture exit and return the status value */
12033 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12035 rb_set_errinfo(Qnil);
12038 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12039 /* ruby_sourcefile = save_ruby_sourcefile;
12040 ruby_sourceline = save_ruby_sourceline; */
12046 Ruby_showValue(RubyValue value, char **outValueString)
12048 VALUE val = (VALUE)value;
12049 if (gMolbyIsCheckingInterrupt) {
12050 MolActionAlertRubyIsRunning();
12057 val = rb_protect(rb_inspect, val, &status);
12061 str = StringValuePtr(val);
12062 if (outValueString != NULL)
12063 *outValueString = strdup(str);
12064 MyAppCallback_showScriptMessage("%s", str);
12066 if (outValueString != NULL)
12067 *outValueString = NULL;
12073 Ruby_showError(int status)
12075 static const int tag_raise = 6;
12076 char *main_message = "Molby script error";
12077 char *msg = NULL, *msg2;
12078 VALUE val, backtrace;
12079 int interrupted = 0;
12080 int exit_status = -1;
12081 if (status == tag_raise) {
12082 VALUE errinfo = rb_errinfo();
12083 VALUE eclass = CLASS_OF(errinfo);
12084 if (eclass == rb_eInterrupt) {
12085 main_message = "Molby script interrupted";
12088 } else if (eclass == rb_eSystemExit) {
12089 main_message = "Molby script exit";
12091 val = rb_eval_string_protect("$!.status", &status);
12093 exit_status = NUM2INT(rb_Integer(val));
12094 asprintf(&msg, "Molby script exit with status %d", exit_status);
12096 asprintf(&msg, "Molby script exit with unknown status");
12101 if (exit_status != 0) {
12102 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12104 val = rb_eval_string_protect("$!.to_s", &status);
12106 msg = RSTRING_PTR(val);
12108 msg = "(message not available)";
12110 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12112 msg2 = strdup(msg);
12114 MyAppCallback_messageBox(msg2, main_message, 0, 3);
12116 if (interrupted == 2) {
12118 if (!gUseGUI && exit_status == 0)
12119 exit(0); // Capture exit(0) here and force exit
12124 /* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
12126 Molby_loadScript(const char *script, int from_file)
12131 rb_load_protect(rb_str_new2(script), 0, &status);
12133 rb_eval_string_protect(script, &status);
12139 Molby_getDescription(char **versionString, char **auxString)
12141 extern const char *gVersionString, *gCopyrightString;
12142 extern int gRevisionNumber;
12143 extern char *gLastBuildString;
12145 char *revisionString;
12146 if (gRevisionNumber > 0) {
12147 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12148 } else revisionString = "";
12150 asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12151 #if defined(__WXMSW__)
12152 #if TARGET_ARCH == 64
12160 gVersionString, revisionString, gCopyrightString, gLastBuildString);
12165 "ruby %s, http://www.ruby-lang.org/\n"
12167 "FFTW 3.3.2, http://www.fftw.org/\n"
12168 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12169 " and Massachusetts Institute of Technology",
12170 MyAppCallback_getGUIDescriptionString(),
12171 gRubyVersion, gRubyCopyright);
12175 "ruby %s, http://www.ruby-lang.org/\n"
12177 "FFTW 3.3.2, http://www.fftw.org/\n"
12178 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12179 " and Massachusetts Institute of Technology",
12180 gRubyVersion, gRubyCopyright);
12183 if (revisionString[0] != 0)
12184 free(revisionString);
12185 if (versionString != NULL)
12186 *versionString = s1;
12187 if (auxString != NULL)
12192 Molby_startup(const char *script, const char *dir)
12197 char *respath, *p, *wbuf;
12199 /* Get version/copyright string from Ruby interpreter */
12201 gRubyVersion = strdup(ruby_version);
12202 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12203 " ", /* Indent for displaying in About dialog */
12204 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12207 /* Read build and revision information for Molby */
12210 extern int gRevisionNumber;
12211 extern char *gLastBuildString;
12212 FILE *fp = fopen("../buildInfo.txt", "r");
12213 gLastBuildString = "";
12215 if (fgets(buf, sizeof(buf), fp) != NULL) {
12216 char *p1 = strchr(buf, '\"');
12217 char *p2 = strrchr(buf, '\"');
12218 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12219 memmove(buf, p1 + 1, p2 - p1 - 1);
12220 buf[p2 - p1 - 1] = 0;
12221 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12226 fp = fopen("../revisionInfo.txt", "r");
12227 gRevisionNumber = 0;
12229 if (fgets(buf, sizeof(buf), fp) != NULL) {
12230 gRevisionNumber = strtol(buf, NULL, 0);
12238 Molby_getDescription(&wbuf, &wbuf2);
12239 MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12244 /* Read atom display parameters */
12245 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12246 MyAppCallback_setConsoleColor(1);
12247 MyAppCallback_showScriptMessage("%s", wbuf);
12248 MyAppCallback_setConsoleColor(0);
12252 /* Read default parameters */
12253 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12254 if (wbuf != NULL) {
12255 MyAppCallback_setConsoleColor(1);
12256 MyAppCallback_showScriptMessage("%s", wbuf);
12257 MyAppCallback_setConsoleColor(0);
12261 /* Initialize Ruby interpreter */
12264 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12265 it causes rb_bug() (= fatal error) during ruby_init().
12266 As a workaround, these standard streams are reopend as
12268 freopen("NUL", "r", stdin);
12269 freopen("NUL", "w", stdout);
12270 freopen("NUL", "w", stderr);
12276 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12278 Init_trans_japanese_sjis();
12281 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12283 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12284 ruby_incpush(libpath);
12288 ruby_script("Molby");
12290 /* Find the resource path (the parent directory of the given directory) */
12291 respath = strdup(dir);
12292 p = strrchr(respath, '/');
12293 if (p == NULL && PATH_SEPARATOR != '/')
12294 p = strrchr(respath, PATH_SEPARATOR);
12297 val = Ruby_NewFileStringValue(respath);
12298 rb_define_global_const("MolbyResourcePath", val);
12301 /* Define Molby classes */
12304 RubyDialogInitClass();
12306 rb_define_const(rb_mMolby, "ResourcePath", val);
12307 val = Ruby_NewFileStringValue(dir);
12308 rb_define_const(rb_mMolby, "ScriptPath", val);
12309 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12310 val = Ruby_NewFileStringValue(p);
12311 rb_define_const(rb_mMolby, "MbsfPath", val);
12314 p = MyAppCallback_getHomeDir();
12315 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12316 rb_define_const(rb_mMolby, "HomeDirectory", val);
12318 p = MyAppCallback_getDocumentHomeDir();
12319 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12320 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12324 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12326 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12329 /* Create objects for stdout and stderr */
12330 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12331 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12332 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12333 rb_gv_set("$stdout", val);
12334 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12335 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12336 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12337 rb_gv_set("$stderr", val);
12339 /* Create objects for stdin */
12340 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12341 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12342 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12343 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12344 rb_gv_set("$stdin", val);
12347 /* Global variable to hold error information */
12348 rb_define_variable("$backtrace", &gMolbyBacktrace);
12349 rb_define_variable("$error_history", &gMolbyErrorHistory);
12350 gMolbyErrorHistory = rb_ary_new();
12352 /* Global variables for script menus */
12353 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12354 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12355 gScriptMenuCommands = rb_ary_new();
12356 gScriptMenuEnablers = rb_ary_new();
12359 /* Register interrupt check code */
12360 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12361 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12362 s_SetIntervalTimer(0, 50);
12365 /* Read the startup script */
12366 if (script != NULL && script[0] != 0) {
12367 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12369 rb_load_protect(rb_str_new2(script), 0, &status);
12372 Ruby_showError(status);
12374 MyAppCallback_showScriptMessage("Done.\n");
12379 Molby_buildARGV(int argc, const char **argv)
12382 rb_ary_clear(rb_argv);
12383 for (i = 0; i < argc; i++) {
12384 VALUE arg = rb_tainted_str_new2(argv[i]);
12386 rb_ary_push(rb_argv, arg);