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_AnisoEigvalSym, s_SymopSym, s_IntChargeSym,
80 s_FixForceSym, s_FixPosSym, s_ExclusionSym, s_MMExcludeSym,
81 s_PeriodicExcludeSym, 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 = Ruby_NewEncodedStringValue2(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 = Ruby_NewEncodedStringValue2(p);
142 return Ruby_NewEncodedStringValue2(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_NewEncodedStringValue2(const char *str)
165 return Ruby_NewEncodedStringValue(str, -1);
169 Ruby_ObjToStringObj(VALUE val)
175 return rb_str_new2(rb_id2name(SYM2ID(val)));
177 return rb_str_to_str(val);
181 #pragma mark ====== Message input/output ======
185 * message_box(str, title, button = nil, icon = :info)
187 * Show a message box.
188 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
189 * Icon: :info, :warning, :error
192 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
194 char *str, *title, *s;
196 VALUE sval, tval, bval, ival;
197 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
198 str = StringValuePtr(sval);
199 title = StringValuePtr(tval);
201 bval = Ruby_ObjToStringObj(bval);
202 s = RSTRING_PTR(bval);
203 if (strncmp(s, "ok", 2) == 0)
205 else if (strncmp(s, "cancel", 6) == 0)
208 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
211 ival = Ruby_ObjToStringObj(ival);
212 s = RSTRING_PTR(ival);
213 if (strncmp(s, "info", 4) == 0)
215 else if (strncmp(s, "warn", 4) == 0)
217 else if (strncmp(s, "err", 3) == 0)
220 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
222 MyAppCallback_messageBox(str, title, buttons, icon);
228 * error_message_box(str)
230 * Show an error message box.
233 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
235 char *str = StringValuePtr(sval);
236 MyAppCallback_errorMessageBox("%s", str);
242 * ask(prompt, default = nil) -> string
244 * Open a modal dialog and get a line of text.
247 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
249 volatile VALUE prompt, message;
252 rb_scan_args(argc, argv, "11", &prompt, &message);
253 if (message != Qnil) {
254 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
255 buf[sizeof buf - 1] = 0;
257 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
259 return Ruby_NewEncodedStringValue2(buf);
266 * show_console_window
268 * Show the console window and bring to the front.
271 s_Kernel_ShowConsoleWindow(VALUE self)
273 MyAppCallback_showConsoleWindow();
279 * hide_console_window
281 * Hide the console window.
284 s_Kernel_HideConsoleWindow(VALUE self)
286 MyAppCallback_hideConsoleWindow();
294 * Ring the system bell.
297 s_Kernel_Bell(VALUE self)
299 MyAppCallback_bell();
305 * play_sound(filename, flag = 0)
307 * Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
308 * 1, play the sound asynchronously; 3, play the sound with loop asynchronously
311 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
316 rb_scan_args(argc, argv, "11", &fnval, &flval);
319 else flag = NUM2INT(rb_Integer(flval));
320 fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
321 fname = StringValuePtr(fnval);
322 retval = MyAppCallback_playSound(fname, flag);
323 return (retval ? Qtrue : Qnil);
330 * Stop the sound if playing.
333 s_Kernel_StopSound(VALUE self)
335 MyAppCallback_stopSound();
341 * export_to_clipboard(str)
343 * Export the given string to clipboard.
346 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
348 #if !defined(__CMDMAC__)
353 s = StringValuePtr(sval);
355 /* Convert the end-of-line characters */
356 { const char *p; int nc; char *np;
358 for (p = s; *p != 0; p++) {
362 ns = (char *)malloc(strlen(s) + nc + 1);
363 for (np = ns, p = s; *p != 0; p++, np++) {
371 ns = (char *)malloc(strlen(s) + 1);
375 /* wxMac still has Carbon code. Oops. */
376 for (np = ns; *np != 0; np++) {
383 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
384 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
393 * Put the message in the main text view in black color.
396 s_StandardOutput(VALUE self, VALUE str)
399 MyAppCallback_setConsoleColor(0);
400 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
408 * Put the message in the main text view in red color.
411 s_StandardErrorOutput(VALUE self, VALUE str)
414 MyAppCallback_setConsoleColor(1);
415 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
416 MyAppCallback_setConsoleColor(0);
425 * Flush the standard (error) output. Actually do nothing.
428 s_FlushConsoleOutput(VALUE self)
435 * stdin.gets(rs = $/)
437 * Read one line message via dialog box.
440 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
443 pval = Ruby_NewEncodedStringValue2("Enter a line:");
444 rval = s_Kernel_Ask(1, &pval, self);
447 rb_str_cat2(rval, "\n");
453 * stdin.method_missing(name, args, ...)
455 * Throw an exception, noting only gets and readline are defined.
458 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
461 rb_scan_args(argc, argv, "10", &nval);
462 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
463 return Qnil; /* Not reached */
466 #pragma mark ====== Track key events ======
468 /* User interrupt handling
469 * User interrupt (command-period on Mac OS) is handled by periodic polling of
470 * key events. This polling should only be enabled during "normal" execution
471 * of scripts and must be disabled when the rest of the application (or Ruby
472 * script itself) is handling GUI. This is ensured by appropriate calls to
473 * enable_interrupt and disable_interrupt. */
475 static VALUE s_interrupt_flag = Qfalse;
478 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
480 volatile VALUE message;
482 if (Ruby_GetInterruptFlag() == Qtrue) {
483 rb_scan_args(argc, argv, "01", &message);
485 p = StringValuePtr(message);
488 MyAppCallback_showProgressPanel(p);
494 s_HideProgressPanel(VALUE self)
496 MyAppCallback_hideProgressPanel();
501 s_SetProgressValue(VALUE self, VALUE val)
503 double dval = NUM2DBL(rb_Float(val));
504 MyAppCallback_setProgressValue(dval);
509 s_SetProgressMessage(VALUE self, VALUE msg)
514 else p = StringValuePtr(msg);
515 MyAppCallback_setProgressMessage(p);
520 s_SetInterruptFlag(VALUE self, VALUE val)
524 if (val == Qfalse || val == Qnil)
528 oldval = s_interrupt_flag;
530 s_interrupt_flag = val;
536 s_GetInterruptFlag(VALUE self)
538 return s_SetInterruptFlag(self, Qundef);
542 Ruby_SetInterruptFlag(VALUE val)
544 return s_SetInterruptFlag(Qnil, val);
548 Ruby_GetInterruptFlag(void)
550 return s_SetInterruptFlag(Qnil, Qundef);
555 * check_interrupt -> integer
557 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
560 s_Kernel_CheckInterrupt(VALUE self)
562 if (Ruby_GetInterruptFlag() == Qfalse)
564 else if (MyAppCallback_checkInterrupt())
566 else return INT2NUM(0);
569 static volatile unsigned long sITimerCount = 0;
572 static HANDLE sITimerEvent;
573 static HANDLE sITimerThread;
574 static int sITimerInterval;
576 static __stdcall unsigned
577 s_ITimerThreadFunc(void *p)
579 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
585 #elif USE_PTHREAD_FOR_TIMER
588 static pthread_t sTimerThread;
590 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
591 static volatile signed char sTimerFlag = -1;
592 static volatile int sTimerIntervalMicrosec = 0;
595 s_TimerThreadEntry(void *param)
598 usleep(sTimerIntervalMicrosec);
601 else if (sTimerFlag == -2)
610 s_SignalAction(int n)
616 s_SetIntervalTimer(int n, int msec)
620 /* Start interval timer */
621 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
622 sITimerInterval = msec;
624 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
627 /* Stop interval timer */
629 SetEvent(sITimerEvent); /* Tell thread to terminate */
631 WaitForSingleObject(sITimerThread, 1000);
632 CloseHandle(sITimerThread);
635 CloseHandle(sITimerEvent);
637 sITimerThread = NULL;
639 #elif USE_PTHREAD_FOR_TIMER
641 if (sTimerFlag == -1) {
642 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
644 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
647 sTimerFlag = 0; /* Active */
648 sTimerIntervalMicrosec = msec * 1000;
649 } else if (sTimerFlag != -1)
650 sTimerFlag = 1; /* Inactive */
652 static struct itimerval sOldValue;
653 static struct sigaction sOldAction;
654 struct itimerval val;
655 struct sigaction act;
658 act.sa_handler = s_SignalAction;
661 sigaction(SIGALRM, &act, &sOldAction);
662 val.it_value.tv_sec = 0;
663 val.it_value.tv_usec = msec * 1000;
664 val.it_interval.tv_sec = 0;
665 val.it_interval.tv_usec = msec * 1000;
666 setitimer(ITIMER_REAL, &val, &sOldValue);
668 setitimer(ITIMER_REAL, &sOldValue, &val);
669 sigaction(SIGALRM, &sOldAction, &act);
675 s_GetTimerCount(void)
681 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
682 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
684 if (s_interrupt_flag != Qfalse) {
685 static unsigned long sLastTime = 0;
686 unsigned long currentTime;
688 currentTime = s_GetTimerCount();
689 if (currentTime != sLastTime) {
690 sLastTime = currentTime;
691 gMolbyIsCheckingInterrupt = 1;
692 flag = MyAppCallback_checkInterrupt();
693 gMolbyIsCheckingInterrupt = 0;
695 s_SetInterruptFlag(Qnil, Qfalse);
702 #pragma mark ====== Menu handling ======
706 * register_menu(title, method, enable_proc = nil)
708 * Register the method (specified as a symbol) in the script menu.
709 * The method must be either an instance method of Molecule with no argument,
710 * or a class method of Molecule with one argument (the current molecule),
711 * or a proc object with one argument (the current molecule).
712 * The menu associated with the class method can be invoked even when no document
713 * is open (the argument is set to Qnil in this case). On the other hand, the
714 * menu associated with the instance method can only be invoked when at least one
715 * document is active.
716 * If enable_proc is non-nil, then it is called whenever the availability of
717 * the menu command is tested. It is usually a proc object with one argument
718 * (the current molecule or nil). As a special case, the following symbols can
719 * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
720 * the top-level molecule has at least one atom), :selection (enabled when
721 * the top-level molecule has one or more selected atoms).
724 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
727 VALUE tval, mval, pval;
728 static VALUE sMolSym, sNonEmptySym, sSelectionSym;
729 static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
730 rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
731 tval = rb_str_to_str(tval);
732 n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
735 if (TYPE(mval) == T_SYMBOL) {
736 /* Create an appropriate proc object */
737 const char *name = rb_id2name(SYM2ID(mval));
739 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
740 /* Defined as a Molecule method */
741 asprintf(&s, "lambda { |m| m.%s }", name);
743 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
744 /* Defined as a Molecule class method */
745 asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
747 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
748 mval = rb_eval_string(s);
751 if (sMolSym == Qfalse) {
752 sMolSym = ID2SYM(rb_intern("mol"));
753 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
754 sSelectionSym = ID2SYM(rb_intern("selection"));
755 sMolProc = rb_eval_string("lambda { |m| m != nil }");
756 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
757 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
758 sTrueProc = rb_eval_string("lambda { |m| true }");
759 rb_global_variable(&sMolProc);
760 rb_global_variable(&sNonEmptyProc);
761 rb_global_variable(&sSelectionProc);
762 rb_global_variable(&sTrueProc);
770 } else if (pval == sMolSym)
772 else if (pval == sNonEmptySym)
773 pval = sNonEmptyProc;
774 else if (pval == sSelectionSym)
775 pval = sSelectionProc;
776 rb_ary_store(gScriptMenuCommands, n, mval);
777 rb_ary_store(gScriptMenuEnablers, n, pval);
782 s_Kernel_LookupMenu(VALUE self, VALUE title)
784 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
789 s_Ruby_UpdateUI_handler(VALUE data)
791 void **p = (void **)data;
792 int index = (int)p[0];
793 Molecule *mol = (Molecule *)p[1];
794 int *outChecked = (int *)p[2];
795 char **outTitle = (char **)p[3];
796 VALUE mval = ValueFromMolecule(mol);
797 VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
798 static ID call_id = 0;
800 call_id = rb_intern("call");
803 pval = rb_funcall(pval, call_id, 1, mval);
804 if (rb_obj_is_kind_of(pval, rb_cArray)) {
806 if (outChecked != NULL) {
807 val = rb_ary_entry(pval, 1); /* Checked or not */
808 *outChecked = (RTEST(val) ? 1 : 0);
810 if (outTitle != NULL) {
811 val = rb_ary_entry(pval, 2); /* Text */
812 if (TYPE(val) == T_STRING) {
813 *outTitle = strdup(StringValuePtr(val));
816 pval = rb_ary_entry(pval, 0);
822 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
827 p[0] = (void *)(intptr_t)index;
831 retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
832 return (RTEST(retval) ? 1 : 0);
837 s_Ruby_methodType_sub(VALUE data)
839 const char **p = (const char **)data;
840 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
841 ID mid = rb_intern(p[1]);
843 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
845 else if (rb_respond_to(klass, mid))
848 return INT2FIX(ival);
851 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
852 has the singleton method (class method) with the given name, 0 otherwise. */
854 Ruby_methodType(const char *className, const char *methodName)
861 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
863 return FIX2INT(retval);
870 * execute_script_file(fname)
872 * Execute the script in the given file. If a molecule is active, then
873 * the script is evaluated as Molecule.current.instance_eval(script).
874 * Before entering the script, the current directory is set to the parent
875 * directory of the script.
878 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
881 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
882 if (retval == (VALUE)6 && status == -1)
883 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
893 * Get the directory suitable for storing user documents. On Windows
894 * it is the home directory + "My Documents". On other platforms
895 * it is the home directory.
898 s_Kernel_DocumentHome(VALUE self)
900 char *s = MyAppCallback_getDocumentHomeDir();
901 VALUE retval = Ruby_NewFileStringValue(s);
906 /* The callback function for call_subprocess */
908 s_Kernel_CallSubProcess_Callback(void *data)
911 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
912 if (status != 0 || retval == Qnil || retval == Qfalse)
919 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
921 * Call subprocess. A progress dialog window is displayed, with a message
922 * "Running #{process_name}...".
923 * A callback proc can be given, which is called periodically during execution. If the proc returns
924 * nil or false, then the execution will be interrupted.
925 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
926 * filename begins with ">>", then the message will be appended to the file.
927 * If the filename is "/dev/null" or "NUL", then the message will be lost.
928 * If the argument is nil, then the message will be sent to the Ruby console.
931 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
933 VALUE cmd, procname, cproc, stdout_val, stderr_val;
934 VALUE save_interruptFlag;
935 int n, exitstatus, pid;
939 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
941 if (stdout_val == Qnil) {
944 sout = StringValuePtr(stdout_val);
945 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
948 if (strncmp(sout, ">>", 2) == 0) {
950 fpout = fopen(sout, "a");
954 fpout = fopen(sout, "w");
957 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
960 if (stderr_val == Qnil) {
963 serr = StringValuePtr(stderr_val);
964 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
967 if (strncmp(serr, ">>", 2) == 0) {
969 fpout = fopen(serr, "a");
973 fperr = fopen(serr, "w");
976 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
980 save_interruptFlag = s_SetInterruptFlag(self, Qnil);
981 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
982 s_SetInterruptFlag(self, save_interruptFlag);
984 if (fpout != NULL && fpout != (FILE *)1)
986 if (fperr != NULL && fperr != (FILE *)1)
998 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
1001 s_Kernel_Backquote(VALUE self, VALUE cmd)
1004 int n, exitstatus, pid;
1006 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1007 /* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1008 if (n >= 0 && buf != NULL) {
1009 val = Ruby_NewEncodedStringValue(buf, 0);
1012 val = Ruby_NewEncodedStringValue("", 0);
1014 rb_last_status_set(exitstatus, pid);
1018 #pragma mark ====== User defaults ======
1022 * get_global_settings(key)
1024 * Get a setting data for key from the application preferences.
1027 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1029 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1031 VALUE retval = rb_eval_string(p);
1039 * set_global_settings(key, value)
1041 * Set a setting data for key to the application preferences.
1044 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1046 VALUE sval = rb_inspect(value);
1047 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1051 #pragma mark ====== IO extension ======
1054 s_Ruby_str_encode_protected(VALUE val)
1056 return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1057 ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1064 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1067 s_IO_gets_any_eol(VALUE self)
1069 VALUE val, val2, cval;
1072 static ID id_getbyte = 0, id_ungetbyte;
1073 if (id_getbyte == 0) {
1074 id_getbyte = rb_intern("getbyte");
1075 id_ungetbyte = rb_intern("ungetbyte");
1079 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1080 c = NUM2INT(rb_Integer(cval));
1082 cval = rb_funcall(self, id_getbyte, 0);
1084 c = NUM2INT(rb_Integer(cval));
1086 rb_funcall(self, id_ungetbyte, 1, cval);
1089 } else if (c != 0x0a) {
1094 val = rb_str_new(buf, i);
1096 rb_str_append(val, rb_str_new(buf, i));
1101 if (cval == Qnil && i == 0 && val == Qnil)
1102 return Qnil; /* End of file */
1105 val = rb_str_new(buf, i);
1107 rb_str_append(val, rb_str_new(buf, i));
1108 val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
1112 /* Needs a end-of-line mark */
1113 cval = rb_gv_get("$/");
1114 rb_str_append(val, cval);
1116 rb_gv_set("$_", val);
1120 #pragma mark ====== Utility functions (protected funcall) ======
1122 struct Ruby_funcall2_record {
1130 s_Ruby_funcall2_sub(VALUE data)
1132 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1133 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1137 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1139 struct Ruby_funcall2_record rec;
1144 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1148 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1150 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1153 #pragma mark ====== ParameterRef Class ======
1156 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1160 Data_Get_Struct(self, ParameterRef, pref);
1162 *typep = pref->parType;
1163 if (pref->parType == kElementParType) {
1164 up = (UnionPar *)&gElementParameters[pref->idx];
1166 up = ParameterRefGetPar(pref);
1167 if (checkEditable) {
1169 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1170 if (up->bond.src != 0 && up->bond.src != -1)
1171 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1178 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1182 Data_Get_Struct(self, ParameterRef, pref);
1183 if (pref->mol == NULL)
1185 up = ParameterRefGetPar(pref);
1186 if (key != s_SourceSym)
1187 up->bond.src = 0; /* Becomes automatically molecule-local */
1188 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1191 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1192 MolActionCallback_registerUndo(pref->mol, act);
1193 MoleculeCallback_notifyModification(pref->mol, 0);
1194 pref->mol->needsMDRebuild = 1;
1199 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1201 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1203 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1205 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1209 s_AtomTypeIndexFromValue(VALUE val)
1211 if (rb_obj_is_kind_of(val, rb_cNumeric))
1212 return NUM2INT(val);
1214 return AtomTypeEncodeToUInt(StringValuePtr(val));
1217 static const char *s_ParameterTypeNames[] = {
1218 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1220 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1223 s_ParTypeFromValue(VALUE val)
1227 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1228 if (s_ParameterTypeIDs[0] == 0) {
1229 for (i = 0; i < n; i++)
1230 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1232 valid = rb_to_id(val);
1233 for (i = 0; i < n; i++) {
1234 if (valid == s_ParameterTypeIDs[i]) {
1236 return kElementParType;
1237 else return kFirstParType + i;
1240 return kInvalidParType;
1247 * Get the index in the parameter list.
1249 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1251 Data_Get_Struct(self, ParameterRef, pref);
1252 return INT2NUM(pref->idx);
1257 * par_type -> String
1259 * Get the parameter type, like "bond", "angle", etc.
1261 static VALUE s_ParameterRef_GetParType(VALUE self) {
1263 s_UnionParFromValue(self, &tp, 0);
1264 if (tp == kElementParType)
1265 return Ruby_NewEncodedStringValue2("element");
1266 tp -= kFirstParType;
1267 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1268 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1269 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1274 * atom_type -> String or Array of String
1275 * atom_types -> String or Array of String
1277 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1278 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1279 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1280 * The atom type may be "X", which is a wildcard that matches any atom type.
1282 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1287 up = s_UnionParFromValue(self, &tp, 0);
1288 n = ParameterGetAtomTypes(tp, up, types);
1290 rb_raise(rb_eMolbyError, "invalid member atom_types");
1291 for (i = 0; i < n; i++) {
1292 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1293 vals[i] = INT2NUM(types[i]);
1295 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1300 return rb_ary_new4(n, vals);
1307 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1309 static VALUE s_ParameterRef_GetK(VALUE self) {
1313 up = s_UnionParFromValue(self, &tp, 0);
1316 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1318 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1319 case kDihedralParType:
1320 case kImproperParType:
1321 if (up->torsion.mult == 1)
1322 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1323 n = up->torsion.mult;
1326 for (i = 0; i < n; i++)
1327 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1328 return rb_ary_new4(n, vals);
1330 rb_raise(rb_eMolbyError, "invalid member k");
1338 * Get the equilibrium bond length. Only available for bond parameters.
1340 static VALUE s_ParameterRef_GetR0(VALUE self) {
1343 up = s_UnionParFromValue(self, &tp, 0);
1344 if (tp == kBondParType)
1345 return rb_float_new(up->bond.r0);
1346 else rb_raise(rb_eMolbyError, "invalid member r0");
1353 * Get the equilibrium angle (in degree). Only available for angle parameters.
1355 static VALUE s_ParameterRef_GetA0(VALUE self) {
1358 up = s_UnionParFromValue(self, &tp, 0);
1359 if (tp == kAngleParType)
1360 return rb_float_new(up->angle.a0 * kRad2Deg);
1361 else rb_raise(rb_eMolbyError, "invalid member a0");
1368 * Get the multiplicity. Only available for dihedral and improper parameters.
1369 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1371 static VALUE s_ParameterRef_GetMult(VALUE self) {
1374 up = s_UnionParFromValue(self, &tp, 0);
1375 if (tp == kDihedralParType || tp == kImproperParType)
1376 return rb_float_new(up->torsion.mult);
1377 else rb_raise(rb_eMolbyError, "invalid member mult");
1382 * period -> Integer or Array of Integers
1384 * Get the periodicity. Only available for dihedral and improper parameters.
1385 * If the multiplicity is larger than 1, then an array of integers is returned.
1386 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1388 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1392 up = s_UnionParFromValue(self, &tp, 0);
1393 if (tp == kDihedralParType || tp == kImproperParType) {
1394 if (up->torsion.mult == 1)
1395 return INT2NUM(up->torsion.period[0]);
1396 n = up->torsion.mult;
1399 for (i = 0; i < n; i++)
1400 vals[i] = INT2NUM(up->torsion.period[i]);
1401 return rb_ary_new4(n, vals);
1402 } else rb_raise(rb_eMolbyError, "invalid member period");
1407 * phi0 -> Float or Array of Floats
1409 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1410 * If the multiplicity is larger than 1, then an array of floats is returned.
1411 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1413 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1417 up = s_UnionParFromValue(self, &tp, 0);
1418 if (tp == kDihedralParType || tp == kImproperParType) {
1419 if (up->torsion.mult == 1)
1420 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1421 n = up->torsion.mult;
1424 for (i = 0; i < n; i++)
1425 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1426 return rb_ary_new4(n, vals);
1427 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1434 * Get the "A" value for the van der Waals parameter.
1437 static VALUE s_ParameterRef_GetA(VALUE self) {
1440 up = s_UnionParFromValue(self, &tp, 0);
1441 if (tp == kVdwParType)
1442 return rb_float_new(up->vdw.A);
1443 else if (tp == kVdwPairParType)
1444 return rb_float_new(up->vdwp.A);
1445 else rb_raise(rb_eMolbyError, "invalid member A");
1453 * Get the "B" value for the van der Waals parameter.
1456 static VALUE s_ParameterRef_GetB(VALUE self) {
1459 up = s_UnionParFromValue(self, &tp, 0);
1460 if (tp == kVdwParType)
1461 return rb_float_new(up->vdw.B);
1462 else if (tp == kVdwPairParType)
1463 return rb_float_new(up->vdwp.B);
1464 else rb_raise(rb_eMolbyError, "invalid member B");
1472 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1474 static VALUE s_ParameterRef_GetReq(VALUE self) {
1477 /* Double a, b, r; */
1479 up = s_UnionParFromValue(self, &tp, 0);
1480 if (tp == kVdwParType) {
1484 } else if (tp == kVdwPairParType) {
1488 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1489 /* if (a == 0.0 || b == 0.0) */
1490 return rb_float_new(r);
1491 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1498 * Get the minimum energy for the van der Waals parameter.
1500 static VALUE s_ParameterRef_GetEps(VALUE self) {
1505 up = s_UnionParFromValue(self, &tp, 0);
1506 if (tp == kVdwParType) {
1510 } else if (tp == kVdwPairParType) {
1514 } else rb_raise(rb_eMolbyError, "invalid member eps");
1515 /* if (a == 0.0 || b == 0.0) */
1516 return rb_float_new(eps * INTERNAL2KCAL);
1517 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1524 * Get the "A" value for the 1-4 van der Waals parameter.
1527 static VALUE s_ParameterRef_GetA14(VALUE self) {
1530 up = s_UnionParFromValue(self, &tp, 0);
1531 if (tp == kVdwParType)
1532 return rb_float_new(up->vdw.A14);
1533 else if (tp == kVdwPairParType)
1534 return rb_float_new(up->vdwp.A14);
1535 else rb_raise(rb_eMolbyError, "invalid member A14");
1543 * Get the "B" value for the 1-4 van der Waals parameter.
1546 static VALUE s_ParameterRef_GetB14(VALUE self) {
1549 up = s_UnionParFromValue(self, &tp, 0);
1550 if (tp == kVdwParType)
1551 return rb_float_new(up->vdw.B14);
1552 else if (tp == kVdwPairParType)
1553 return rb_float_new(up->vdwp.B14);
1554 else rb_raise(rb_eMolbyError, "invalid member B14");
1562 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1564 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1567 /* Double a, b, r; */
1569 up = s_UnionParFromValue(self, &tp, 0);
1570 if (tp == kVdwParType) {
1574 } else if (tp == kVdwPairParType) {
1575 /* a = up->vdwp.A14;
1576 b = up->vdwp.B14; */
1577 r = up->vdwp.r_eq14;
1578 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1579 /* if (a == 0.0 || b == 0.0) */
1580 return rb_float_new(r);
1581 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1588 * Get the minimum energy for the 1-4 van der Waals parameter.
1590 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1595 up = s_UnionParFromValue(self, &tp, 0);
1596 if (tp == kVdwParType) {
1599 eps = up->vdw.eps14;
1600 } else if (tp == kVdwPairParType) {
1601 /* a = up->vdwp.A14;
1602 b = up->vdwp.B14; */
1603 eps = up->vdwp.eps14;
1604 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1605 /* if (a == 0.0 || b == 0.0) */
1606 return rb_float_new(eps * INTERNAL2KCAL);
1607 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1614 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1616 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1619 up = s_UnionParFromValue(self, &tp, 0);
1620 if (tp == kVdwCutoffParType)
1621 return rb_float_new(up->vdwcutoff.cutoff);
1622 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1629 * Get the atomic (covalent) radius for the element parameter.
1631 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1634 up = s_UnionParFromValue(self, &tp, 0);
1635 if (tp == kElementParType)
1636 return rb_float_new(up->atom.radius);
1637 else rb_raise(rb_eMolbyError, "invalid member radius");
1642 * vdw_radius -> Float
1644 * Get the van der Waals radius for the element parameter. (0 if not given)
1646 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1649 up = s_UnionParFromValue(self, &tp, 0);
1650 if (tp == kElementParType)
1651 return rb_float_new(up->atom.vdw_radius);
1652 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1657 * color -> [Float, Float, Float]
1659 * Get the rgb color for the element parameter.
1661 static VALUE s_ParameterRef_GetColor(VALUE self) {
1664 up = s_UnionParFromValue(self, &tp, 0);
1665 if (tp == kElementParType)
1666 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));
1667 else rb_raise(rb_eMolbyError, "invalid member color");
1672 * atomic_number -> Integer
1674 * Get the atomic number for the vdw or element parameter.
1676 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1679 up = s_UnionParFromValue(self, &tp, 0);
1680 if (tp == kElementParType)
1681 return INT2NUM(up->atom.number);
1682 else if (tp == kVdwParType)
1683 return INT2NUM(up->vdw.atomicNumber);
1684 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1691 * Get the name for the element parameter.
1693 static VALUE s_ParameterRef_GetName(VALUE self) {
1696 up = s_UnionParFromValue(self, &tp, 0);
1697 if (tp == kElementParType) {
1699 strncpy(name, up->atom.name, 4);
1701 return Ruby_NewEncodedStringValue2(name);
1702 } else rb_raise(rb_eMolbyError, "invalid member name");
1709 * Get the atomic weight for the element parameter.
1711 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1714 up = s_UnionParFromValue(self, &tp, 0);
1715 if (tp == kElementParType)
1716 return rb_float_new(up->atom.weight);
1717 else if (tp == kVdwParType)
1718 return rb_float_new(up->vdw.weight);
1719 else rb_raise(rb_eMolbyError, "invalid member weight");
1724 * fullname -> String
1726 * Get the full name for the element parameter.
1728 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1731 up = s_UnionParFromValue(self, &tp, 0);
1732 if (tp == kElementParType) {
1734 strncpy(fullname, up->atom.fullname, 15);
1736 return Ruby_NewEncodedStringValue2(fullname);
1737 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1744 * Get the comment for the parameter.
1746 static VALUE s_ParameterRef_GetComment(VALUE self) {
1749 up = s_UnionParFromValue(self, &tp, 0);
1753 else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1760 * Get the source string for the parameter. Returns false for undefined parameter,
1761 * and nil for "local" parameter that is specific for the molecule.
1763 static VALUE s_ParameterRef_GetSource(VALUE self) {
1766 up = s_UnionParFromValue(self, &tp, 0);
1769 return Qfalse; /* undefined */
1771 return Qnil; /* local */
1772 else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1776 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1783 if (rb_obj_is_kind_of(val, rb_cString)) {
1784 char *s = StringValuePtr(val);
1786 for (i = 0; i < n; i++) {
1789 /* Skip leading separaters */
1790 while (*s == '-' || *s == ' ' || *s == '\t')
1792 for (p = s; *p != 0; p++) {
1793 if (*p == '-' || *p == ' ' || *p == '\t')
1797 if (len >= sizeof(buf))
1798 len = sizeof(buf) - 1;
1799 strncpy(buf, s, len);
1801 /* Skip trailing blanks */
1802 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1805 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1806 if (buf[0] >= '0' && buf[0] <= '9')
1807 types[i] = atoi(buf);
1809 types[i] = AtomTypeEncodeToUInt(buf);
1810 if (p == NULL || *p == 0) {
1816 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1819 val = rb_ary_to_ary(val);
1820 if (RARRAY_LEN(val) != n)
1821 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1822 valp = RARRAY_PTR(val);
1824 for (i = 0; i < n; i++) {
1825 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1826 types[i] = NUM2INT(rb_Integer(valp[i]));
1828 VALUE sval = valp[i];
1829 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1834 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1839 up = s_UnionParFromValue(self, &tp, 1);
1840 oldval = s_ParameterRef_GetAtomTypes(self);
1841 oldsrc = up->bond.src;
1844 s_ScanAtomTypes(val, 2, types);
1845 up->bond.type1 = types[0];
1846 up->bond.type2 = types[1];
1849 s_ScanAtomTypes(val, 3, types);
1850 up->angle.type1 = types[0];
1851 up->angle.type2 = types[1];
1852 up->angle.type3 = types[2];
1854 case kDihedralParType:
1855 case kImproperParType:
1856 s_ScanAtomTypes(val, 4, types);
1857 up->torsion.type1 = types[0];
1858 up->torsion.type2 = types[1];
1859 up->torsion.type3 = types[2];
1860 up->torsion.type4 = types[3];
1863 s_ScanAtomTypes(val, 1, types);
1864 up->vdw.type1 = types[0];
1866 case kVdwPairParType:
1867 s_ScanAtomTypes(val, 2, types);
1868 up->vdwp.type1 = types[0];
1869 up->vdwp.type2 = types[1];
1871 case kVdwCutoffParType:
1872 s_ScanAtomTypes(val, 2, types);
1873 up->vdwcutoff.type1 = types[0];
1874 up->vdwcutoff.type2 = types[1];
1879 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1883 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1885 Int tp, i, n, oldsrc;
1886 VALUE *valp, oldval;
1887 up = s_UnionParFromValue(self, &tp, 1);
1888 oldval = s_ParameterRef_GetK(self);
1889 oldsrc = up->bond.src;
1892 val = rb_Float(val);
1893 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1896 val = rb_Float(val);
1897 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1899 case kDihedralParType:
1900 case kImproperParType:
1901 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1902 up->torsion.mult = 1;
1903 val = rb_Float(val);
1904 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1907 n = up->torsion.mult;
1910 val = rb_ary_to_ary(val);
1911 if (RARRAY_LEN(val) != n)
1912 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1913 valp = RARRAY_PTR(val);
1914 for (i = 0; i < n; i++) {
1915 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1919 rb_raise(rb_eMolbyError, "invalid member k");
1921 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1925 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1929 up = s_UnionParFromValue(self, &tp, 1);
1930 oldval = s_ParameterRef_GetR0(self);
1931 oldsrc = up->bond.src;
1932 if (tp == kBondParType) {
1933 val = rb_Float(val);
1934 up->bond.r0 = NUM2DBL(val);
1935 } else rb_raise(rb_eMolbyError, "invalid member r0");
1936 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1940 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1944 up = s_UnionParFromValue(self, &tp, 1);
1945 oldval = s_ParameterRef_GetA0(self);
1946 oldsrc = up->bond.src;
1947 if (tp == kAngleParType) {
1948 val = rb_Float(val);
1949 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1950 } else rb_raise(rb_eMolbyError, "invalid member a0");
1951 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1955 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1959 up = s_UnionParFromValue(self, &tp, 1);
1960 oldval = s_ParameterRef_GetMult(self);
1961 oldsrc = up->bond.src;
1962 if (tp == kDihedralParType || tp == kImproperParType) {
1964 val = rb_Integer(val);
1967 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1968 up->torsion.mult = i;
1969 } else rb_raise(rb_eMolbyError, "invalid member mult");
1970 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1974 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1976 Int tp, i, n, oldsrc;
1977 VALUE *valp, oldval;
1978 up = s_UnionParFromValue(self, &tp, 1);
1979 oldval = s_ParameterRef_GetPeriod(self);
1980 oldsrc = up->bond.src;
1981 if (tp == kDihedralParType || tp == kImproperParType) {
1982 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1983 up->torsion.mult = 1;
1984 val = rb_Integer(val);
1985 up->torsion.period[0] = NUM2INT(val);
1987 n = up->torsion.mult;
1990 val = rb_ary_to_ary(val);
1991 if (RARRAY_LEN(val) != n)
1992 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1993 valp = RARRAY_PTR(val);
1994 for (i = 0; i < n; i++) {
1995 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1998 } else rb_raise(rb_eMolbyError, "invalid member period");
1999 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2003 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2005 Int tp, i, n, oldsrc;
2006 VALUE *valp, oldval;
2007 up = s_UnionParFromValue(self, &tp, 1);
2008 oldval = s_ParameterRef_GetPhi0(self);
2009 oldsrc = up->bond.src;
2010 if (tp == kDihedralParType || tp == kImproperParType) {
2011 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2012 up->torsion.mult = 1;
2013 val = rb_Float(val);
2014 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2016 n = up->torsion.mult;
2019 val = rb_ary_to_ary(val);
2020 if (RARRAY_LEN(val) != n)
2021 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2022 valp = RARRAY_PTR(val);
2023 for (i = 0; i < n; i++)
2024 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2026 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2027 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2032 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2037 up = s_UnionParFromValue(self, &tp, 1);
2038 oldval = s_ParameterRef_GetA(self);
2039 oldsrc = up->bond.src;
2040 val = rb_Float(val);
2042 if (tp == kVdwParType)
2044 else if (tp == kVdwPairParType)
2046 else rb_raise(rb_eMolbyError, "invalid member A");
2047 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2051 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2056 up = s_UnionParFromValue(self, &tp, 1);
2057 oldval = s_ParameterRef_GetB(self);
2058 oldsrc = up->bond.src;
2059 val = rb_Float(val);
2061 if (tp == kVdwParType)
2063 else if (tp == kVdwPairParType)
2065 else rb_raise(rb_eMolbyError, "invalid member B");
2066 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2071 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2076 up = s_UnionParFromValue(self, &tp, 1);
2077 oldval = s_ParameterRef_GetReq(self);
2078 oldsrc = up->bond.src;
2079 val = rb_Float(val);
2081 if (tp == kVdwParType) {
2083 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2084 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2085 } else if (tp == kVdwPairParType) {
2087 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2088 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2089 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2090 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2094 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2099 up = s_UnionParFromValue(self, &tp, 1);
2100 oldval = s_ParameterRef_GetEps(self);
2101 oldsrc = up->bond.src;
2102 val = rb_Float(val);
2103 e = NUM2DBL(val) * KCAL2INTERNAL;
2104 if (tp == kVdwParType) {
2106 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2107 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2108 } else if (tp == kVdwPairParType) {
2110 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2111 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2112 } else rb_raise(rb_eMolbyError, "invalid member eps");
2113 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2118 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2123 up = s_UnionParFromValue(self, &tp, 1);
2124 oldval = s_ParameterRef_GetA14(self);
2125 oldsrc = up->bond.src;
2126 val = rb_Float(val);
2128 if (tp == kVdwParType)
2130 else if (tp == kVdwPairParType)
2132 else rb_raise(rb_eMolbyError, "invalid member A14");
2133 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2137 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2142 up = s_UnionParFromValue(self, &tp, 1);
2143 oldval = s_ParameterRef_GetB14(self);
2144 oldsrc = up->bond.src;
2145 val = rb_Float(val);
2147 if (tp == kVdwParType)
2149 else if (tp == kVdwPairParType)
2151 else rb_raise(rb_eMolbyError, "invalid member B14");
2152 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2157 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2162 up = s_UnionParFromValue(self, &tp, 1);
2163 oldval = s_ParameterRef_GetReq14(self);
2164 oldsrc = up->bond.src;
2165 val = rb_Float(val);
2167 if (tp == kVdwParType) {
2169 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2170 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2171 } else if (tp == kVdwPairParType) {
2172 up->vdwp.r_eq14 = r;
2173 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2174 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2175 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2176 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2180 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2185 up = s_UnionParFromValue(self, &tp, 1);
2186 oldval = s_ParameterRef_GetEps14(self);
2187 oldsrc = up->bond.src;
2188 val = rb_Float(val);
2189 e = NUM2DBL(val) * KCAL2INTERNAL;
2190 if (tp == kVdwParType) {
2192 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2193 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2194 } else if (tp == kVdwPairParType) {
2196 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2197 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2198 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2199 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2203 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2207 oldval = s_ParameterRef_GetCutoff(self);
2208 oldsrc = up->bond.src;
2209 up = s_UnionParFromValue(self, &tp, 1);
2210 val = rb_Float(val);
2211 if (tp == kVdwCutoffParType) {
2212 up->vdwcutoff.cutoff = NUM2DBL(val);
2213 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2214 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2218 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2222 up = s_UnionParFromValue(self, &tp, 1);
2223 oldval = s_ParameterRef_GetRadius(self);
2224 oldsrc = up->bond.src;
2225 val = rb_Float(val);
2226 if (tp == kElementParType) {
2227 up->atom.radius = NUM2DBL(val);
2228 } else rb_raise(rb_eMolbyError, "invalid member radius");
2229 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2233 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2237 up = s_UnionParFromValue(self, &tp, 1);
2238 oldval = s_ParameterRef_GetVdwRadius(self);
2239 oldsrc = up->bond.src;
2240 val = rb_Float(val);
2241 if (tp == kElementParType) {
2242 up->atom.vdw_radius = NUM2DBL(val);
2243 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2244 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2248 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2251 VALUE *valp, oldval;
2252 up = s_UnionParFromValue(self, &tp, 1);
2253 oldval = s_ParameterRef_GetColor(self);
2254 oldsrc = up->bond.src;
2255 val = rb_ary_to_ary(val);
2256 if (RARRAY_LEN(val) != 3)
2257 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2258 valp = RARRAY_PTR(val);
2259 if (tp == kElementParType) {
2260 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2261 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2262 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2263 } else rb_raise(rb_eMolbyError, "invalid member color");
2264 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2268 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2272 up = s_UnionParFromValue(self, &tp, 1);
2273 oldval = s_ParameterRef_GetAtomicNumber(self);
2274 oldsrc = up->bond.src;
2275 val = rb_Integer(val);
2276 if (tp == kElementParType)
2277 up->atom.number = NUM2INT(val);
2278 else if (tp == kVdwParType) {
2279 up->vdw.atomicNumber = NUM2INT(val);
2280 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2281 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2282 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2286 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2290 up = s_UnionParFromValue(self, &tp, 1);
2291 oldval = s_ParameterRef_GetName(self);
2292 oldsrc = up->bond.src;
2293 if (tp == kElementParType) {
2294 strncpy(up->atom.name, StringValuePtr(val), 4);
2295 } else rb_raise(rb_eMolbyError, "invalid member name");
2296 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2300 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2304 val = rb_Float(val);
2305 oldval = s_ParameterRef_GetWeight(self);
2306 up = s_UnionParFromValue(self, &tp, 1);
2307 oldsrc = up->bond.src;
2308 if (tp == kElementParType)
2309 up->atom.weight = NUM2DBL(val);
2310 else if (tp == kVdwParType)
2311 up->vdw.weight = NUM2DBL(val);
2312 else rb_raise(rb_eMolbyError, "invalid member weight");
2313 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2317 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2321 up = s_UnionParFromValue(self, &tp, 1);
2322 oldval = s_ParameterRef_GetFullName(self);
2323 oldsrc = up->bond.src;
2324 if (tp == kElementParType) {
2325 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2326 up->atom.fullname[15] = 0;
2327 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2328 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2332 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2334 Int tp, com, oldsrc;
2336 up = s_UnionParFromValue(self, &tp, 1);
2337 oldval = s_ParameterRef_GetComment(self);
2338 oldsrc = up->bond.src;
2342 com = ParameterCommentIndex(StringValuePtr(val));
2345 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2349 /* Only false (undefined) and nil (local) can be set */
2350 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2354 up = s_UnionParFromValue(self, &tp, 1);
2355 if (val != Qfalse && val != Qnil)
2356 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2357 oldval = s_ParameterRef_GetSource(self);
2358 oldsrc = up->bond.src;
2359 if (oldsrc != 0 && oldsrc != -1)
2360 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2361 up->bond.src = (val == Qfalse ? -1 : 0);
2362 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2366 static struct s_ParameterAttrDef {
2368 VALUE *symref; /* Address of s_IndexSymbol etc. */
2369 ID id; /* Will be set within InitMolby() */
2370 VALUE (*getter)(VALUE);
2371 VALUE (*setter)(VALUE, VALUE);
2372 } s_ParameterAttrDefTable[] = {
2373 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2374 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2375 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2376 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2377 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2378 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2379 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2380 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2381 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2382 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2383 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2384 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2385 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2386 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2387 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2388 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2389 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2390 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2391 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2392 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2393 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2394 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2395 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2396 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2397 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2398 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2399 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2400 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2401 {NULL} /* Sentinel */
2405 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2409 if (TYPE(key) != T_SYMBOL) {
2410 kid = rb_intern(StringValuePtr(key));
2412 } else kid = SYM2ID(key);
2413 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2414 if (s_ParameterAttrDefTable[i].id == kid) {
2415 if (value == Qundef)
2416 return (*(s_ParameterAttrDefTable[i].getter))(self);
2417 else if (s_ParameterAttrDefTable[i].setter == NULL)
2418 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2420 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2423 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2424 return Qnil; /* not reached */
2428 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2430 return s_ParameterRef_SetAttr(self, key, Qundef);
2435 * keys(idx) -> array of valid parameter attributes
2437 * Returns an array of valid parameter attributes (as Symbols).
2440 s_ParameterRef_Keys(VALUE self)
2443 Data_Get_Struct(self, ParameterRef, pref);
2444 switch (pref->parType) {
2446 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2448 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2449 case kDihedralParType:
2450 case kImproperParType:
2451 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2453 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);
2454 case kVdwPairParType:
2455 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2456 case kVdwCutoffParType:
2457 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2458 case kElementParType:
2459 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);
2461 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2463 return Qnil; /* Not reached */
2468 * to_hash(idx) -> Hash
2470 * Returns a hash containing valid parameter names and values
2473 s_ParameterRef_ToHash(VALUE self)
2475 VALUE keys = s_ParameterRef_Keys(self);
2480 retval = rb_hash_new();
2481 for (i = 0; i < RARRAY_LEN(keys); i++) {
2482 VALUE key = RARRAY_PTR(keys)[i];
2483 VALUE val = s_ParameterRef_GetAttr(self, key);
2484 rb_hash_aset(retval, key, val);
2491 * parameter.to_s(idx) -> String
2493 * Returns a string representation of the given parameter
2496 s_ParameterRef_ToString(VALUE self)
2499 char buf[1024], types[4][8];
2500 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2503 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);
2506 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);
2508 case kDihedralParType:
2509 case kImproperParType:
2510 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]));
2512 for (i = 0; i < up->torsion.mult; i++) {
2513 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);
2518 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);
2520 case kVdwPairParType:
2521 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);
2523 case kVdwCutoffParType:
2524 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);
2526 case kElementParType:
2527 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);
2530 return Ruby_NewEncodedStringValue2(buf);
2535 * self == parameterRef -> boolean
2537 * True if the parameters point to the same parameter record.
2540 s_ParameterRef_Equal(VALUE self, VALUE val)
2543 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2544 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2545 } else return Qfalse;
2548 #pragma mark ====== Parameter Class ======
2550 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2551 * is NULL, then the global parameters are looked for. */
2553 /* Rebuild the MD parameter record if necessary: may throw an exception */
2554 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2556 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2559 Data_Get_Struct(val, Molecule, mol);
2561 rb_raise(rb_eMolbyError, "the molecule is empty");
2562 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2563 /* Do self.md_arena.prepare */
2564 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2566 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2571 s_NewParameterValueFromValue(VALUE val)
2574 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2575 Data_Get_Struct(val, Molecule, mol);
2576 s_RebuildMDParameterIfNecessary(val, Qtrue);
2577 MoleculeRetain(mol);
2578 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2581 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2586 s_MoleculeFromParameterValue(VALUE val)
2589 Data_Get_Struct(val, Molecule, mol);
2594 s_ParameterFromParameterValue(VALUE val)
2597 Data_Get_Struct(val, Molecule, mol);
2600 return gBuiltinParameters;
2603 /* Forward declarations */
2604 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2605 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2608 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2610 if (val == rb_cParameter) {
2611 return NULL; /* Parameter class method: builtin parameters */
2612 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2613 return s_MoleculeFromParameterValue(val);
2614 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2615 return s_MoleculeFromParEnumerableValue(val);
2621 * builtin -> Parameter
2623 * Returns a parameter value that points to the global (builtin) parameters.
2624 * Equivalent to Parameter::Builtin (constant).
2627 s_Parameter_Builtin(VALUE self)
2629 static ID s_builtin_id = 0;
2630 if (s_builtin_id == 0)
2631 s_builtin_id = rb_intern("Builtin");
2632 return rb_const_get(rb_cParameter, s_builtin_id);
2637 * bond(idx) -> ParameterRef
2639 * The index-th bond parameter record is returned.
2642 s_Parameter_Bond(VALUE self, VALUE ival)
2646 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2647 idx = NUM2INT(rb_Integer(ival));
2649 n = gBuiltinParameters->nbondPars;
2650 else if (mol->par != NULL)
2651 n = mol->par->nbondPars;
2653 if (idx < -n || idx >= n)
2654 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2657 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2662 * angle(idx) -> ParameterRef
2664 * The index-th angle parameter record is returned.
2667 s_Parameter_Angle(VALUE self, VALUE ival)
2671 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2672 idx = NUM2INT(rb_Integer(ival));
2674 n = gBuiltinParameters->nanglePars;
2675 else if (mol->par != NULL)
2676 n = mol->par->nanglePars;
2678 if (idx < -n || idx >= n)
2679 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2682 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2687 * dihedral(idx) -> ParameterRef
2689 * The index-th dihedral parameter record is returned.
2692 s_Parameter_Dihedral(VALUE self, VALUE ival)
2696 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2697 idx = NUM2INT(rb_Integer(ival));
2699 n = gBuiltinParameters->ndihedralPars;
2700 else if (mol->par != NULL)
2701 n = mol->par->ndihedralPars;
2703 if (idx < -n || idx >= n)
2704 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2707 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2712 * improper(idx) -> ParameterRef
2714 * The index-th improper parameter record is returned.
2717 s_Parameter_Improper(VALUE self, VALUE ival)
2721 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2722 idx = NUM2INT(rb_Integer(ival));
2724 n = gBuiltinParameters->nimproperPars;
2725 else if (mol->par != NULL)
2726 n = mol->par->nimproperPars;
2728 if (idx < -n || idx >= n)
2729 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2732 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2737 * vdw(idx) -> ParameterRef
2739 * The index-th vdw parameter record is returned.
2742 s_Parameter_Vdw(VALUE self, VALUE ival)
2746 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2747 idx = NUM2INT(rb_Integer(ival));
2749 n = gBuiltinParameters->nvdwPars;
2750 else if (mol->par != NULL)
2751 n = mol->par->nvdwPars;
2753 if (idx < -n || idx >= n)
2754 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2757 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2762 * vdw_pair(idx) -> ParameterRef
2764 * The index-th vdw pair parameter record is returned.
2767 s_Parameter_VdwPair(VALUE self, VALUE ival)
2771 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2772 idx = NUM2INT(rb_Integer(ival));
2774 n = gBuiltinParameters->nvdwpPars;
2775 else if (mol->par != NULL)
2776 n = mol->par->nvdwpPars;
2778 if (idx < -n || idx >= n)
2779 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2782 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2787 * vdw_cutoff(idx) -> ParameterRef
2789 * The index-th vdw cutoff parameter record is returned.
2792 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2796 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2797 idx = NUM2INT(rb_Integer(ival));
2799 n = gBuiltinParameters->nvdwCutoffPars;
2800 else if (mol->par != NULL)
2801 n = mol->par->nvdwCutoffPars;
2803 if (idx < -n || idx >= n)
2804 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2807 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2812 * element(idx) -> ParameterRef
2813 * element(t1) -> ParameterRef
2815 * In the first form, the index-th element parameter record is returned. In the second
2816 * form, the element parameter for t1 is looked up (the last index first). t1
2817 * is the element name string (up to 4 characters).
2818 * Unlike other Parameter methods, this is used only for the global parameter.
2821 s_Parameter_Element(VALUE self, VALUE ival)
2824 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2825 int n = gCountElementParameters;
2826 idx1 = NUM2INT(rb_Integer(ival));
2827 if (idx1 < -n || idx1 >= n)
2828 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2831 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2836 strncpy(name, StringValuePtr(ival), 4);
2838 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2839 if (strncmp(ep->name, name, 4) == 0)
2840 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2850 * Returns the number of bond parameters.
2853 s_Parameter_Nbonds(VALUE self)
2856 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2858 n = gBuiltinParameters->nbondPars;
2859 else if (mol->par != NULL)
2860 n = mol->par->nbondPars;
2867 * nangles -> Integer
2869 * Returns the number of angle parameters.
2872 s_Parameter_Nangles(VALUE self)
2875 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2877 n = gBuiltinParameters->nanglePars;
2878 else if (mol->par != NULL)
2879 n = mol->par->nanglePars;
2886 * ndihedrals -> Integer
2888 * Returns the number of dihedral parameters.
2891 s_Parameter_Ndihedrals(VALUE self)
2894 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2896 n = gBuiltinParameters->ndihedralPars;
2897 else if (mol->par != NULL)
2898 n = mol->par->ndihedralPars;
2905 * nimpropers -> Integer
2907 * Returns the number of improper parameters.
2910 s_Parameter_Nimpropers(VALUE self)
2913 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2915 n = gBuiltinParameters->nimproperPars;
2916 else if (mol->par != NULL)
2917 n = mol->par->nimproperPars;
2926 * Returns the number of vdw parameters.
2929 s_Parameter_Nvdws(VALUE self)
2932 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2934 n = gBuiltinParameters->nvdwPars;
2935 else if (mol->par != NULL)
2936 n = mol->par->nvdwPars;
2943 * nvdw_pairs -> Integer
2945 * Returns the number of vdw pair parameters.
2948 s_Parameter_NvdwPairs(VALUE self)
2951 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2953 n = gBuiltinParameters->nvdwpPars;
2954 else if (mol->par != NULL)
2955 n = mol->par->nvdwpPars;
2962 * nvdw_cutoffs -> Integer
2964 * Returns the number of vdw cutoff parameters.
2967 s_Parameter_NvdwCutoffs(VALUE self)
2970 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2972 n = gBuiltinParameters->nvdwCutoffPars;
2973 else if (mol->par != NULL)
2974 n = mol->par->nvdwCutoffPars;
2981 * nelements -> Integer
2983 * Returns the number of element parameters.
2986 s_Parameter_Nelements(VALUE self)
2988 return INT2NUM(gCountElementParameters);
2993 * bonds -> ParEnumerable
2995 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2996 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2997 * useful when all accessible parameters should be examined by use of 'each' method.
3000 s_Parameter_Bonds(VALUE self)
3002 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3003 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3008 * angles -> ParEnumerable
3010 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3011 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3012 * useful when all accessible parameters should be examined by use of 'each' method.
3015 s_Parameter_Angles(VALUE self)
3017 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3018 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3023 * dihedrals -> ParEnumerable
3025 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3026 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3027 * useful when all accessible parameters should be examined by use of 'each' method.
3030 s_Parameter_Dihedrals(VALUE self)
3032 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3033 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3038 * impropers -> ParEnumerable
3040 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3041 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3042 * useful when all accessible parameters should be examined by use of 'each' method.
3045 s_Parameter_Impropers(VALUE self)
3047 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3048 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3053 * vdws -> ParEnumerable
3055 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3056 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3057 * useful when all accessible parameters should be examined by use of 'each' method.
3060 s_Parameter_Vdws(VALUE self)
3062 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3063 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3068 * vdw_pairs -> ParEnumerable
3070 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3071 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3072 * useful when all accessible parameters should be examined by use of 'each' method.
3075 s_Parameter_VdwPairs(VALUE self)
3077 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3078 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3083 * vdw_cutoffs -> ParEnumerable
3085 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3086 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3087 * useful when all accessible parameters should be examined by use of 'each' method.
3090 s_Parameter_VdwCutoffs(VALUE self)
3092 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3093 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3098 * elements -> ParEnumerable
3100 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3101 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3102 * useful when all accessible parameters should be examined by use of 'each' method.
3105 s_Parameter_Elements(VALUE self)
3107 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3108 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3112 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3114 VALUE atval, optval;
3117 int i, n, idx, flags, is_global;
3119 rb_scan_args(argc, argv, "1*", &atval, &optval);
3121 /* Get the atom types */
3123 case kBondParType: n = 2; break;
3124 case kAngleParType: n = 3; break;
3125 case kDihedralParType: n = 4; break;
3126 case kImproperParType: n = 4; break;
3127 case kVdwParType: n = 1; break;
3128 case kVdwPairParType: n = 2; break;
3129 default: return Qnil;
3131 s_ScanAtomTypes(atval, n, t);
3132 for (i = 0; i < n; i++) {
3133 if (t[i] < kAtomTypeMinimum) {
3134 /* Explicit atom index */
3136 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3137 if (t[i] >= mol->natoms)
3138 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3140 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3144 /* Analyze options */
3146 n = RARRAY_LEN(optval);
3147 for (i = 0; i < n; i++) {
3148 VALUE oval = RARRAY_PTR(optval)[i];
3149 if (oval == ID2SYM(rb_intern("global")))
3150 flags |= kParameterLookupGlobal;
3151 else if (oval == ID2SYM(rb_intern("local")))
3152 flags |= kParameterLookupLocal;
3153 else if (oval == ID2SYM(rb_intern("missing")))
3154 flags |= kParameterLookupMissing;
3155 else if (oval == ID2SYM(rb_intern("nowildcard")))
3156 flags |= kParameterLookupNoWildcard;
3157 else if (oval == ID2SYM(rb_intern("nobasetype")))
3158 flags |= kParameterLookupNoBaseAtomType;
3159 else if (oval == ID2SYM(rb_intern("create")))
3163 flags = kParameterLookupGlobal | kParameterLookupLocal;
3168 case kBondParType: {
3171 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3173 idx = bp - mol->par->bondPars;
3177 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3179 idx = bp - gBuiltinParameters->bondPars;
3184 case kAngleParType: {
3187 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3189 idx = ap - mol->par->anglePars;
3193 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3195 idx = ap - gBuiltinParameters->anglePars;
3200 case kDihedralParType: {
3203 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3205 idx = tp - mol->par->dihedralPars;
3209 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3211 idx = tp - gBuiltinParameters->dihedralPars;
3216 case kImproperParType: {
3219 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3221 idx = tp - mol->par->improperPars;
3225 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3227 idx = tp - gBuiltinParameters->improperPars;
3235 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3237 idx = vp - mol->par->vdwPars;
3241 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3243 idx = vp - gBuiltinParameters->vdwPars;
3248 case kVdwPairParType: {
3251 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3253 idx = vp - mol->par->vdwpPars;
3257 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3259 idx = vp - gBuiltinParameters->vdwpPars;
3268 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3271 /* Insert a new parameter record */
3273 Int count = ParameterGetCountForType(mol->par, parType);
3274 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3275 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3276 IntGroupRelease(ig);
3279 /* Set atom types */
3280 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3285 up->bond.type1 = t[0];
3286 up->bond.type2 = t[1];
3289 up->angle.type1 = t[0];
3290 up->angle.type2 = t[1];
3291 up->angle.type3 = t[2];
3293 case kDihedralParType:
3294 case kImproperParType:
3295 up->torsion.type1 = t[0];
3296 up->torsion.type2 = t[1];
3297 up->torsion.type3 = t[2];
3298 up->torsion.type4 = t[3];
3301 up->vdw.type1 = t[0];
3303 case kVdwPairParType:
3304 up->vdwp.type1 = t[0];
3305 up->vdwp.type2 = t[1];
3312 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3317 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3318 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3320 * Find the parameter record that matches the given atom types. The atom types are given
3321 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3322 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3323 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3324 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3327 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3330 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3332 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3333 parType = s_ParTypeFromValue(argv[0]);
3334 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3339 * self == parameter -> boolean
3341 * True if the parameters point to the same parameter table.
3344 s_Parameter_Equal(VALUE self, VALUE val)
3346 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3347 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3348 } else return Qfalse;
3351 #pragma mark ====== ParEnumerable Class ======
3353 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3354 and the parameter type. If the Molecule is NULL, then it refers to the
3355 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3356 the global parameters are always accessible. */
3358 typedef struct ParEnumerable {
3360 Int parType; /* Same as parType in ParameterRef */
3363 static ParEnumerable *
3364 s_ParEnumerableNew(Molecule *mol, Int parType)
3366 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3370 MoleculeRetain(mol);
3371 pen->parType = parType;
3377 s_ParEnumerableRelease(ParEnumerable *pen)
3380 if (pen->mol != NULL)
3381 MoleculeRelease(pen->mol);
3387 s_MoleculeFromParEnumerableValue(VALUE val)
3390 Data_Get_Struct(val, ParEnumerable, pen);
3395 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3397 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3399 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3400 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3405 * par_type -> String
3407 * Get the parameter type, like "bond", "angle", etc.
3410 s_ParEnumerable_ParType(VALUE self) {
3413 Data_Get_Struct(self, ParEnumerable, pen);
3415 if (tp == kElementParType)
3416 return Ruby_NewEncodedStringValue2("element");
3417 tp -= kFirstParType;
3418 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3419 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3420 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3425 * self[idx] -> ParameterRef
3427 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3428 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3429 * parent Parameter object of self.
3431 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3432 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3435 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3438 Data_Get_Struct(self, ParEnumerable, pen);
3439 switch (pen->parType) {
3440 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3441 case kBondParType: return s_Parameter_Bond(self, ival);
3442 case kAngleParType: return s_Parameter_Angle(self, ival);
3443 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3444 case kImproperParType: return s_Parameter_Improper(self, ival);
3445 case kVdwParType: return s_Parameter_Vdw(self, ival);
3446 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3447 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3448 case kElementParType: return s_Parameter_Element(self, ival);
3450 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3452 return Qnil; /* Not reached */
3459 * Returns the number of parameters included in this enumerable.
3462 s_ParEnumerable_Length(VALUE self)
3465 Data_Get_Struct(self, ParEnumerable, pen);
3466 switch (pen->parType) {
3467 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3468 case kBondParType: return s_Parameter_Nbonds(self);
3469 case kAngleParType: return s_Parameter_Nangles(self);
3470 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3471 case kImproperParType: return s_Parameter_Nimpropers(self);
3472 case kVdwParType: return s_Parameter_Nvdws(self);
3473 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3474 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3475 case kElementParType: return s_Parameter_Nelements(self);
3477 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3479 return Qnil; /* Not reached */
3486 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3489 s_ParEnumerable_Each(VALUE self)
3495 Data_Get_Struct(self, ParEnumerable, pen);
3496 if (pen->parType == kElementParType)
3497 n = gCountElementParameters;
3499 switch (pen->parType) {
3500 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3501 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3502 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3503 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3504 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3505 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3506 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3508 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3510 if (pen->mol == NULL)
3511 n = *((Int *)((char *)gBuiltinParameters + ofs));
3512 else if (pen->mol->par != NULL)
3513 n = *((Int *)((char *)(pen->mol->par) + ofs));
3516 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3517 Data_Get_Struct(aval, ParameterRef, pref);
3518 for (i = 0; i < n; i++) {
3527 * reverse_each {|pref| ...}
3529 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3532 s_ParEnumerable_ReverseEach(VALUE self)
3538 Data_Get_Struct(self, ParEnumerable, pen);
3539 if (pen->parType == kElementParType)
3540 n = gCountElementParameters;
3542 switch (pen->parType) {
3543 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3544 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3545 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3546 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3547 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3548 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3549 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3551 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3553 if (pen->mol == NULL)
3554 n = *((Int *)((char *)gBuiltinParameters + ofs));
3555 else if (pen->mol->par != NULL)
3556 n = *((Int *)((char *)(pen->mol->par) + ofs));
3559 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3560 Data_Get_Struct(aval, ParameterRef, pref);
3561 for (i = n - 1; i >= 0; i--) {
3570 * insert(idx = nil, pref = nil) -> ParameterRef
3572 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3573 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3574 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3575 * parameter is left undefined.
3576 * Throws an exception if ParEnumerable points to the global parameter.
3579 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3587 Data_Get_Struct(self, ParEnumerable, pen);
3588 if (pen->mol == NULL)
3589 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3590 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3591 rb_scan_args(argc, argv, "02", &ival, &pval);
3593 i = NUM2INT(rb_Integer(ival));
3595 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3600 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3601 if (up == NULL || type != pen->parType)
3602 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3603 ParameterCopyOneWithType(&u, up, pen->parType);
3606 memset(&u, 0, sizeof(u));
3609 ig = IntGroupNewWithPoints(n, 1, -1);
3610 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3612 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3613 MolActionCallback_registerUndo(pen->mol, act);
3614 MolActionRelease(act);
3616 IntGroupRelease(ig);
3617 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3625 * Delete the parameter(s) specified by the argument.
3628 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3633 Data_Get_Struct(self, ParEnumerable, pen);
3634 if (pen->mol == NULL)
3635 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3636 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3637 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3638 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3641 ig = IntGroupFromValue(ival);
3642 if ((i = IntGroupGetCount(ig)) == 0) {
3643 IntGroupRelease(ig);
3647 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3648 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3651 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3652 IntGroupRelease(ig);
3658 * lookup(atom_types, options, ...) -> ParameterRef
3659 * lookup(atom_type_string, options, ...) -> ParameterRef
3661 * Find the parameter record that matches the given atom types. The arguments are
3662 * the same as Parameter#lookup, except for the parameter type which is implicitly
3666 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3669 Data_Get_Struct(self, ParEnumerable, pen);
3670 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3675 * self == parEnumerable -> boolean
3677 * True if the arguments point to the same parameter table and type.
3680 s_ParEnumerable_Equal(VALUE self, VALUE val)
3682 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3683 ParEnumerable *pen1, *pen2;
3684 Data_Get_Struct(self, ParEnumerable, pen1);
3685 Data_Get_Struct(val, ParEnumerable, pen2);
3686 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3687 } else return Qfalse;
3690 #pragma mark ====== AtomRef Class ======
3692 /* Forward declaration for register undo */
3693 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3695 /* Ruby string "set_atom_attr" */
3696 static VALUE s_SetAtomAttrString;
3699 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3703 Data_Get_Struct(self, AtomRef, aref);
3704 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3705 if (idx < 0 || idx >= aref->mol->natoms)
3706 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3708 *app = aref->mol->atoms + idx;
3715 s_AtomFromValue(VALUE self)
3718 s_AtomIndexFromValue(self, &ap, NULL);
3723 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3726 s_AtomIndexFromValue(self, &ap, mpp);
3731 s_NotifyModificationForAtomRef(VALUE self)
3734 Data_Get_Struct(self, AtomRef, aref);
3735 MoleculeIncrementModifyCount(aref->mol);
3739 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3742 Data_Get_Struct(self, AtomRef, aref);
3743 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3746 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3747 MolActionCallback_registerUndo(aref->mol, act);
3748 MoleculeCallback_notifyModification(aref->mol, 0);
3749 /* Request MD rebuilt if necessary */
3750 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3751 aref->mol->needsMDRebuild = 1;
3756 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3759 aref = AtomRefNew(mol, idx);
3760 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3764 s_AtomRef_GetMolecule(VALUE self)
3767 s_AtomIndexFromValue(self, NULL, &mpp);
3768 return ValueFromMolecule(mpp);
3771 static VALUE s_AtomRef_GetIndex(VALUE self) {
3772 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3775 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3776 return INT2NUM(s_AtomFromValue(self)->segSeq);
3779 static VALUE s_AtomRef_GetSegName(VALUE self) {
3780 char *p = s_AtomFromValue(self)->segName;
3781 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3784 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3785 return INT2NUM(s_AtomFromValue(self)->resSeq);
3788 static VALUE s_AtomRef_GetResName(VALUE self) {
3789 char *p = s_AtomFromValue(self)->resName;
3790 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3793 static VALUE s_AtomRef_GetName(VALUE self) {
3794 char *p = s_AtomFromValue(self)->aname;
3795 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3798 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3799 int type = s_AtomFromValue(self)->type;
3800 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3801 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3804 static VALUE s_AtomRef_GetCharge(VALUE self) {
3805 return rb_float_new(s_AtomFromValue(self)->charge);
3808 static VALUE s_AtomRef_GetWeight(VALUE self) {
3809 return rb_float_new(s_AtomFromValue(self)->weight);
3812 static VALUE s_AtomRef_GetElement(VALUE self) {
3813 char *p = s_AtomFromValue(self)->element;
3814 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3817 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3818 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3821 static VALUE s_AtomRef_GetConnects(VALUE self) {
3824 Atom *ap = s_AtomFromValue(self);
3825 retval = rb_ary_new();
3826 cp = AtomConnectData(&ap->connect);
3827 for (i = 0; i < ap->connect.count; i++)
3828 rb_ary_push(retval, INT2NUM(cp[i]));
3832 static VALUE s_AtomRef_GetR(VALUE self) {
3833 return ValueFromVector(&(s_AtomFromValue(self)->r));
3836 static VALUE s_AtomRef_GetX(VALUE self) {
3837 return rb_float_new(s_AtomFromValue(self)->r.x);
3840 static VALUE s_AtomRef_GetY(VALUE self) {
3841 return rb_float_new(s_AtomFromValue(self)->r.y);
3844 static VALUE s_AtomRef_GetZ(VALUE self) {
3845 return rb_float_new(s_AtomFromValue(self)->r.z);
3848 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3852 s_AtomIndexFromValue(self, &ap, &mp);
3854 if (mp->cell != NULL)
3855 TransformVec(&r1, mp->cell->rtr, &r1);
3859 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3860 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3861 return ValueFromVector(&r1);
3864 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3865 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3868 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3869 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3872 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3873 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3876 static VALUE s_AtomRef_GetSigma(VALUE self) {
3877 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3880 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3881 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3884 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3885 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3888 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3889 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3892 static VALUE s_AtomRef_GetV(VALUE self) {
3893 return ValueFromVector(&(s_AtomFromValue(self)->v));
3896 static VALUE s_AtomRef_GetF(VALUE self) {
3897 Vector v = s_AtomFromValue(self)->f;
3898 VecScaleSelf(v, INTERNAL2KCAL);
3899 return ValueFromVector(&v);
3902 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3903 return rb_float_new(s_AtomFromValue(self)->occupancy);
3906 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3907 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3910 static VALUE s_AtomRef_GetAniso(VALUE self) {
3913 Atom *ap = s_AtomFromValue(self);
3914 if (ap->aniso == NULL)
3916 retval = rb_ary_new();
3917 for (i = 0; i < 6; i++)
3918 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3919 if (ap->aniso->has_bsig) {
3920 rb_ary_push(retval, INT2NUM(0));
3921 for (i = 0; i < 6; i++)
3922 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3927 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
3930 Atom *ap = s_AtomFromValue(self);
3931 if (ap->aniso == NULL)
3933 retval = rb_ary_new();
3934 for (i = 0; i < 3; i++)
3935 rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
3939 static VALUE s_AtomRef_GetSymop(VALUE self) {
3941 Atom *ap = s_AtomFromValue(self);
3942 if (!ap->symop.alive)
3944 retval = rb_ary_new();
3945 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3946 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3947 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3948 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3949 rb_ary_push(retval, INT2NUM(ap->symbase));
3953 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3954 return INT2NUM(s_AtomFromValue(self)->intCharge);
3957 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3958 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3961 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3962 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3965 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3969 MDExclusion *exinfo;
3972 idx = s_AtomIndexFromValue(self, &ap, &mol);
3973 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3974 VALUE mval = ValueFromMolecule(mol);
3975 s_RebuildMDParameterIfNecessary(mval, Qnil);
3977 if (mol->arena->exinfo == NULL)
3979 exinfo = mol->arena->exinfo + idx;
3980 exlist = mol->arena->exlist;
3981 retval = rb_ary_new();
3982 aval = rb_ary_new();
3983 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3984 rb_ary_push(aval, INT2NUM(exlist[i]));
3985 rb_ary_push(retval, aval);
3986 aval = rb_ary_new();
3987 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3988 rb_ary_push(aval, INT2NUM(exlist[i]));
3989 rb_ary_push(retval, aval);
3990 aval = rb_ary_new();
3991 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3992 rb_ary_push(aval, INT2NUM(exlist[i]));
3993 rb_ary_push(retval, aval);
3997 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3998 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4001 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4002 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4005 static VALUE s_AtomRef_GetHidden(VALUE self) {
4006 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4009 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4012 Atom *ap = s_AtomFromValue(self);
4013 if (ap->anchor == NULL)
4015 count = ap->anchor->connect.count;
4016 retval = rb_ary_new2(count * 2);
4017 cp = AtomConnectData(&ap->anchor->connect);
4018 for (i = 0; i < count; i++) {
4019 rb_ary_store(retval, i, INT2NUM(cp[i]));
4020 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4025 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4026 char *p = s_AtomFromValue(self)->uff_type;
4027 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4030 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4031 rb_raise(rb_eMolbyError, "index cannot be directly set");
4035 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4036 VALUE oval = s_AtomRef_GetSegSeq(self);
4037 val = rb_Integer(val);
4038 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4039 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4043 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4044 char *p = StringValuePtr(val);
4045 VALUE oval = s_AtomRef_GetSegName(self);
4046 strncpy(s_AtomFromValue(self)->segName, p, 4);
4047 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4051 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4052 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4053 return val; /* Not reached */
4056 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4057 Atom *ap = s_AtomFromValue(self);
4058 char *p = StringValuePtr(val);
4059 VALUE oval = s_AtomRef_GetName(self);
4060 if (ap->anchor != NULL && p[0] == '_')
4061 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4062 strncpy(ap->aname, p, 4);
4063 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4067 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4069 char *p = StringValuePtr(val);
4070 VALUE oval = s_AtomRef_GetAtomType(self);
4071 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4072 if (type != 0 && type < kAtomTypeMinimum)
4073 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4074 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4075 mp->needsMDRebuild = 1;
4076 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4080 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4082 VALUE oval = s_AtomRef_GetCharge(self);
4083 val = rb_Float(val);
4084 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4085 mp->needsMDRebuild = 1;
4086 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4090 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4092 VALUE oval = s_AtomRef_GetWeight(self);
4093 val = rb_Float(val);
4094 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4095 mp->needsMDRebuild = 1;
4096 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4100 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4103 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4104 char *p = StringValuePtr(val);
4105 VALUE oval = s_AtomRef_GetElement(self);
4106 ap->atomicNumber = ElementToInt(p);
4107 ElementToString(ap->atomicNumber, ap->element);
4108 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4110 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4111 mp->needsMDRebuild = 1;
4115 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4118 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4119 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4120 val = rb_Integer(val);
4121 ap->atomicNumber = NUM2INT(val);
4122 ElementToString(ap->atomicNumber, ap->element);
4123 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4125 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4126 mp->needsMDRebuild = 1;
4130 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4131 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4132 return val; /* Not reached */
4135 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4138 VALUE oval = s_AtomRef_GetR(self);
4139 VectorFromValue(val, &v);
4140 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4141 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4142 mp->needsMDCopyCoordinates = 1;
4146 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4149 VALUE oval = s_AtomRef_GetX(self);
4150 val = rb_Float(val);
4152 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4153 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4154 mp->needsMDCopyCoordinates = 1;
4158 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4161 VALUE oval = s_AtomRef_GetY(self);
4162 val = rb_Float(val);
4164 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4165 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4166 mp->needsMDCopyCoordinates = 1;
4170 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4173 VALUE oval = s_AtomRef_GetZ(self);
4174 val = rb_Float(val);
4176 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4177 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4178 mp->needsMDCopyCoordinates = 1;
4182 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4186 s_AtomIndexFromValue(self, &ap, &mp);
4188 VectorFromValue(val, &v);
4189 if (mp->cell != NULL)
4190 TransformVec(&v, mp->cell->tr, &v);
4192 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4193 mp->needsMDCopyCoordinates = 1;
4197 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4202 s_AtomIndexFromValue(self, &ap, &mp);
4204 val = rb_Float(val);
4206 if (mp->cell != NULL) {
4207 TransformVec(&v, mp->cell->rtr, &v);
4209 TransformVec(&v, mp->cell->tr, &v);
4212 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4213 mp->needsMDCopyCoordinates = 1;
4217 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4222 s_AtomIndexFromValue(self, &ap, &mp);
4224 val = rb_Float(val);
4226 if (mp->cell != NULL) {
4227 TransformVec(&v, mp->cell->rtr, &v);
4229 TransformVec(&v, mp->cell->tr, &v);
4232 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4233 mp->needsMDCopyCoordinates = 1;
4237 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4242 s_AtomIndexFromValue(self, &ap, &mp);
4244 val = rb_Float(val);
4246 if (mp->cell != NULL) {
4247 TransformVec(&v, mp->cell->rtr, &v);
4249 TransformVec(&v, mp->cell->tr, &v);
4252 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4253 mp->needsMDCopyCoordinates = 1;
4257 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4260 VALUE oval = s_AtomRef_GetSigma(self);
4261 VectorFromValue(val, &v);
4262 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4263 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4264 mp->needsMDCopyCoordinates = 1;
4268 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4271 VALUE oval = s_AtomRef_GetSigmaX(self);
4272 val = rb_Float(val);
4274 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4275 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4276 mp->needsMDCopyCoordinates = 1;
4280 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4283 VALUE oval = s_AtomRef_GetSigmaY(self);
4284 val = rb_Float(val);
4286 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4287 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4288 mp->needsMDCopyCoordinates = 1;
4292 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4295 VALUE oval = s_AtomRef_GetSigmaZ(self);
4296 val = rb_Float(val);
4298 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4299 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4300 mp->needsMDCopyCoordinates = 1;
4304 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4308 VALUE oval = s_AtomRef_GetV(self);
4309 VectorFromValue(val, &v);
4310 s_AtomIndexFromValue(self, &ap, &mp);
4312 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4313 mp->needsMDCopyCoordinates = 1;
4317 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4320 VALUE oval = s_AtomRef_GetF(self);
4321 VectorFromValue(val, &v);
4322 VecScaleSelf(v, KCAL2INTERNAL);
4323 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4324 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4325 mp->needsMDCopyCoordinates = 1;
4329 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4330 VALUE oval = s_AtomRef_GetOccupancy(self);
4332 val = rb_Float(val);
4333 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4334 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4335 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4339 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4340 VALUE oval = s_AtomRef_GetTempFactor(self);
4341 val = rb_Float(val);
4342 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4343 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4347 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4352 VALUE oval = s_AtomRef_GetAniso(self);
4353 Data_Get_Struct(self, AtomRef, aref);
4354 val = rb_funcall(val, rb_intern("to_a"), 0);
4355 n = RARRAY_LEN(val);
4356 valp = RARRAY_PTR(val);
4357 for (i = 0; i < 6; i++) {
4359 f[i] = NUM2DBL(rb_Float(valp[i]));
4363 type = NUM2INT(rb_Integer(valp[6]));
4366 for (i = 0; i < 6; i++)
4367 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4369 for (i = 0; i < 6; i++)
4372 i = s_AtomIndexFromValue(self, NULL, NULL);
4373 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4374 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4378 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4379 rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4380 return val; /* Not reached */
4383 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4389 VALUE oval = s_AtomRef_GetSymop(self);
4390 i = s_AtomIndexFromValue(self, &ap, &mol);
4392 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4394 val = rb_funcall(val, rb_intern("to_a"), 0);
4395 n = RARRAY_LEN(val);
4396 valp = RARRAY_PTR(val);
4397 for (i = 0; i < 5; i++) {
4399 if (valp[i] == Qnil)
4402 ival[i] = NUM2INT(rb_Integer(valp[i]));
4403 } else ival[i] = -100000;
4406 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4407 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));
4408 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4409 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4410 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4411 return val; /* No need to change */
4412 if (ival[0] != -100000)
4413 ap->symop.sym = ival[0];
4414 if (ival[1] != -100000)
4415 ap->symop.dx = ival[1];
4416 if (ival[2] != -100000)
4417 ap->symop.dy = ival[2];
4418 if (ival[3] != -100000)
4419 ap->symop.dz = ival[3];
4420 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4421 if (ival[4] != -100000)
4422 ap->symbase = ival[4];
4423 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4424 /* The anisotropic parameters should be recalculated */
4425 VALUE oaval = s_AtomRef_GetAniso(self);
4426 MoleculeSetAnisoBySymop(mol, i);
4427 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4429 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4433 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4434 VALUE oval = s_AtomRef_GetIntCharge(self);
4435 val = rb_Integer(val);
4436 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4437 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4441 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4443 VALUE oval = s_AtomRef_GetFixForce(self);
4444 val = rb_Float(val);
4445 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4446 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4447 mp->needsMDRebuild = 1;
4451 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4454 VALUE oval = s_AtomRef_GetFixPos(self);
4455 VectorFromValue(val, &v);
4456 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4457 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4458 mp->needsMDRebuild = 1;
4462 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4463 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4464 return val; /* Not reached */
4467 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4468 VALUE oval = s_AtomRef_GetIntCharge(self);
4469 val = rb_Integer(val);
4470 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4471 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4475 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4476 VALUE oval = s_AtomRef_GetIntCharge(self);
4477 val = rb_Integer(val);
4478 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4479 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4483 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4484 Atom *ap = s_AtomFromValue(self);
4485 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4487 ap->exflags |= kAtomHiddenFlag;
4489 ap->exflags &= ~kAtomHiddenFlag;
4491 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4495 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4496 Int idx, i, j, k, n, *ip;
4503 MolAction **undoActions;
4504 memset(&ac, 0, sizeof(ac));
4505 idx = s_AtomIndexFromValue(self, &ap, &mol);
4506 oval = s_AtomRef_GetAnchorList(self);
4508 val = rb_ary_to_ary(val);
4509 n = RARRAY_LEN(val);
4512 if (ap->anchor != NULL) {
4513 AtomConnectResize(&ap->anchor->connect, 0);
4514 free(ap->anchor->coeffs);
4517 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4522 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4523 if (ap->aname[0] == '_')
4524 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4525 ip = (Int *)malloc(sizeof(Int) * n);
4527 for (i = 0; i < n; i++) {
4528 v = RARRAY_PTR(val)[i];
4529 if (rb_obj_is_kind_of(v, rb_cFloat))
4531 j = NUM2INT(rb_Integer(v));
4532 if (j < 0 || j >= mol->natoms)
4533 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4534 for (k = 0; k < i; k++) {
4536 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4542 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4543 else if (i * 2 != n)
4544 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4545 dp = (Double *)malloc(sizeof(Double) * n / 2);
4546 for (i = 0; i < n / 2; i++) {
4547 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4549 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4555 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4559 rb_raise(rb_eMolbyError, "invalid argument");
4560 if (nUndoActions > 0) {
4561 for (i = 0; i < nUndoActions; i++) {
4562 MolActionCallback_registerUndo(mol, undoActions[i]);
4563 MolActionRelease(undoActions[i]);
4567 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4571 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4572 Atom *ap = s_AtomFromValue(self);
4573 char *p = StringValuePtr(val);
4574 VALUE oval = s_AtomRef_GetUFFType(self);
4575 strncpy(ap->uff_type, p, 5);
4576 ap->uff_type[5] = 0;
4577 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4581 static struct s_AtomAttrDef {
4583 VALUE *symref; /* Address of s_IndexSymbol etc. */
4584 ID id; /* Will be set within InitMolby() */
4585 VALUE (*getter)(VALUE);
4586 VALUE (*setter)(VALUE, VALUE);
4587 } s_AtomAttrDefTable[] = {
4588 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4589 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4590 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4591 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4592 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4593 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4594 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4595 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4596 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4597 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4598 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4599 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4600 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4601 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4602 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4603 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4604 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4605 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4606 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4607 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4608 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4609 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4610 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4611 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4612 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4613 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4614 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4615 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4616 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4617 {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues, s_AtomRef_SetAnisoEigenValues},
4618 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4619 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4620 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4621 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4622 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4623 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4624 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4625 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4626 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4627 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4628 {NULL} /* Sentinel */
4632 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4636 if (TYPE(key) != T_SYMBOL) {
4637 kid = rb_intern(StringValuePtr(key));
4639 } else kid = SYM2ID(key);
4640 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4641 if (s_AtomAttrDefTable[i].id == kid) {
4642 if (value == Qundef)
4643 return (*(s_AtomAttrDefTable[i].getter))(self);
4645 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4648 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4649 return Qnil; /* not reached */
4653 s_AtomRef_GetAttr(VALUE self, VALUE key)
4655 return s_AtomRef_SetAttr(self, key, Qundef);
4660 * self == atomRef -> boolean
4662 * True if the two references point to the same atom.
4665 s_AtomRef_Equal(VALUE self, VALUE val)
4667 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4668 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4669 } else return Qfalse;
4672 #pragma mark ====== MolEnumerable Class ======
4674 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4678 * self[idx] -> AtomRef or Array of Integers
4680 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4681 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4682 * value is a String. Otherwise, the return value is an Array of Integers.
4685 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4687 MolEnumerable *mseq;
4690 Data_Get_Struct(self, MolEnumerable, mseq);
4692 if (mseq->kind == kAtomKind) {
4693 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4695 idx1 = NUM2INT(arg1);
4696 switch (mseq->kind) {
4698 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4699 if (idx2 < 0 || idx2 >= mol->nbonds)
4700 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4701 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4704 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4705 if (idx2 < 0 || idx2 >= mol->nangles)
4706 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4707 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4709 case kDihedralKind: {
4710 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4711 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4712 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4713 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]));
4715 case kImproperKind: {
4716 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4717 if (idx2 < 0 || idx2 >= mol->nimpropers)
4718 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4719 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]));
4721 case kResidueKind: {
4723 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4724 if (idx2 < 0 || idx2 >= mol->nresidues)
4725 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4726 p = mol->residues[idx2];
4727 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4737 * Returns the number of objects included in this enumerable.
4740 s_MolEnumerable_Length(VALUE self)
4742 MolEnumerable *mseq;
4743 Data_Get_Struct(self, MolEnumerable, mseq);
4744 switch (mseq->kind) {
4746 return INT2NUM(mseq->mol->natoms);
4748 return INT2NUM(mseq->mol->nbonds);
4750 return INT2NUM(mseq->mol->nangles);
4752 return INT2NUM(mseq->mol->ndihedrals);
4754 return INT2NUM(mseq->mol->nimpropers);
4756 return INT2NUM(mseq->mol->nresidues);
4765 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4766 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4767 * For the atoms, a same AtomRef object is passed (with different internal information)
4768 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4769 * for each iteration.
4772 s_MolEnumerable_Each(VALUE self)
4774 MolEnumerable *mseq;
4776 int len = NUM2INT(s_MolEnumerable_Length(self));
4777 Data_Get_Struct(self, MolEnumerable, mseq);
4778 if (mseq->kind == kAtomKind) {
4779 /* The same AtomRef object will be used during the loop */
4780 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4781 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4782 for (i = 0; i < len; i++) {
4787 /* A new ruby object will be created at each iteration (not very efficient) */
4788 for (i = 0; i < len; i++) {
4789 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4797 * self == molEnumerable -> boolean
4799 * True if the two arguments point to the same molecule and enumerable type.
4802 s_MolEnumerable_Equal(VALUE self, VALUE val)
4804 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4805 MolEnumerable *mseq1, *mseq2;
4806 Data_Get_Struct(self, MolEnumerable, mseq1);
4807 Data_Get_Struct(val, MolEnumerable, mseq2);
4808 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4809 } else return Qfalse;
4813 #pragma mark ====== Molecule Class ======
4815 #pragma mark ------ Allocate/Release/Accessor ------
4817 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method. */
4818 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4819 char *gLoadSaveErrorMessage = NULL;
4821 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4824 MoleculeFromValue(VALUE val)
4827 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4828 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4829 Data_Get_Struct(val, Molecule, mol);
4833 static VALUE sMoleculeRetainArray = Qnil;
4835 /* The function is called from MoleculeRelease() */
4836 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4837 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4838 /* object is always returned for the same Molecule. */
4839 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4840 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4841 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4842 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4843 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4845 /* Register/unregister the exmolobj Ruby object */
4847 MoleculeReleaseExternalObj(Molecule *mol)
4849 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4850 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4851 mol->exmolobjProtected = 0;
4856 MoleculeRetainExternalObj(Molecule *mol)
4858 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4859 if (sMoleculeRetainArray == Qnil) {
4860 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4861 sMoleculeRetainArray = rb_ary_new();
4864 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4865 mol->exmolobjProtected = 1;
4869 /* Release hook function for Ruby */
4871 MoleculeReleaseHook(Molecule *mol)
4873 if (mol->exmolobj != NULL) {
4874 /* No need to remove from sMoleculeRetainArray */
4875 mol->exmolobj = NULL;
4876 mol->exmolobjProtected = 0;
4878 MoleculeRelease(mol);
4882 ValueFromMolecule(Molecule *mol)
4886 if (mol->exmolobj != NULL)
4887 return (VALUE)mol->exmolobj;
4888 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4889 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4890 return (VALUE)mol->exmolobj;
4895 s_Molecule_Alloc(VALUE klass)
4898 Molecule *mol = MoleculeNew();
4899 val = ValueFromMolecule(mol);
4900 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4905 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4909 if (FIXNUM_P(val)) {
4911 if (n >= 0 && n < mol->natoms)
4913 n = -1; /* No such atom */
4914 val = rb_inspect(val);
4916 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4918 if (n >= 0 && n < mol->natoms)
4920 p = StringValuePtr(val);
4922 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4924 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4926 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4927 return 0; /* Not reached */
4931 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4935 Data_Get_Struct(self, Molecule, mp1);
4936 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4937 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4938 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4939 Data_Get_Struct(val, IntGroup, ig);
4940 IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
4949 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4950 * created object does not affect the old object in any sense.
4953 s_Molecule_InitCopy(VALUE self, VALUE arg)
4955 Molecule *mp1, *mp2;
4956 Data_Get_Struct(self, Molecule, mp1);
4957 mp2 = MoleculeFromValue(arg);
4958 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4959 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4965 * atom_index(val) -> Integer
4967 * Returns the atom index represented by val. val can be either a non-negative integer
4968 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4969 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
4970 * where resname, resid, name are the residue name, residue id, and atom name respectively.
4971 * If val is a string and multiple atoms match the description, the atom with the lowest index
4975 s_Molecule_AtomIndex(VALUE self, VALUE val)
4978 Data_Get_Struct(self, Molecule, mol);
4979 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4984 * self == Molecule -> boolean
4986 * True if the two arguments point to the same molecule.
4989 s_Molecule_Equal(VALUE self, VALUE val)
4991 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4992 Molecule *mol1, *mol2;
4993 Data_Get_Struct(self, Molecule, mol1);
4994 Data_Get_Struct(val, Molecule, mol2);
4995 return (mol1 == mol2 ? Qtrue : Qfalse);
4996 } else return Qfalse;
4999 #pragma mark ------ Load/Save ------
5002 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5004 if (gLoadSaveErrorMessage != NULL) {
5005 MyAppCallback_setConsoleColor(1);
5006 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5007 MyAppCallback_setConsoleColor(0);
5010 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5015 * loadmbsf(file) -> bool
5017 * Read a structure from a mbsf file.
5018 * Return true if successful.
5021 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5027 MoleculeClearLoadSaveErrorMessage();
5028 Data_Get_Struct(self, Molecule, mol);
5029 rb_scan_args(argc, argv, "1", &fname);
5030 fstr = FileStringValuePtr(fname);
5031 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5032 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5038 * loadpsf(file, pdbfile = nil) -> bool
5040 * Read a structure from a psf file. molecule must be empty. The psf may be
5041 * an "extended" version, which also contains coordinates. If pdbfile
5042 * is given, then atomic coordinates are read from that file.
5043 * Return true if successful.
5046 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5048 VALUE fname, pdbname;
5049 char *fstr, *pdbstr;
5052 Data_Get_Struct(self, Molecule, mol);
5053 if (mol->natoms > 0)
5054 return Qnil; /* Must be a new molecule */
5055 MoleculeClearLoadSaveErrorMessage();
5056 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5057 fstr = FileStringValuePtr(fname);
5058 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5059 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5061 if (!NIL_P(pdbname)) {
5062 pdbstr = strdup(FileStringValuePtr(pdbname));
5063 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5065 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5072 * loadpdb(file) -> bool
5074 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5075 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5076 * Return true if successful.
5079 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5085 Data_Get_Struct(self, Molecule, mol);
5086 rb_scan_args(argc, argv, "1", &fname);
5087 MoleculeClearLoadSaveErrorMessage();
5088 fstr = FileStringValuePtr(fname);
5089 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5090 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5096 * loaddcd(file) -> bool
5098 * Read coordinates from a dcd file. The molecule should not empty.
5099 * Return true if successful.
5102 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5108 Data_Get_Struct(self, Molecule, mol);
5109 rb_scan_args(argc, argv, "1", &fname);
5110 MoleculeClearLoadSaveErrorMessage();
5111 fstr = FileStringValuePtr(fname);
5112 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5113 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5119 * loadtep(file) -> bool
5121 * Read coordinates from an ortep .tep file.
5122 * Return true if successful.
5125 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5131 Data_Get_Struct(self, Molecule, mol);
5132 rb_scan_args(argc, argv, "1", &fname);
5133 MoleculeClearLoadSaveErrorMessage();
5134 fstr = FileStringValuePtr(fname);
5135 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5136 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5142 * loadres(file) -> bool
5144 * Read coordinates from a shelx .res file.
5145 * Return true if successful.
5148 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5154 Data_Get_Struct(self, Molecule, mol);
5155 rb_scan_args(argc, argv, "1", &fname);
5156 MoleculeClearLoadSaveErrorMessage();
5157 fstr = FileStringValuePtr(fname);
5158 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5159 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5165 * loadfchk(file) -> bool
5167 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5168 * Return true if successful.
5171 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5177 Data_Get_Struct(self, Molecule, mol);
5178 rb_scan_args(argc, argv, "1", &fname);
5179 MoleculeClearLoadSaveErrorMessage();
5180 fstr = FileStringValuePtr(fname);
5181 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5182 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5188 * loaddat(file) -> bool
5190 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5191 * Return true if successful.
5194 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5200 Data_Get_Struct(self, Molecule, mol);
5201 rb_scan_args(argc, argv, "1", &fname);
5202 MoleculeClearLoadSaveErrorMessage();
5203 fstr = FileStringValuePtr(fname);
5204 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5205 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5206 MyAppCallback_hideProgressPanel();
5207 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5213 * savembsf(file) -> bool
5215 * Write structure as a mbsf file. Returns true if successful.
5218 s_Molecule_Savembsf(VALUE self, VALUE fname)
5223 Data_Get_Struct(self, Molecule, mol);
5224 MoleculeClearLoadSaveErrorMessage();
5225 fstr = FileStringValuePtr(fname);
5226 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5227 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5233 * savepsf(file) -> bool
5235 * Write structure as a psf file. Returns true if successful.
5238 s_Molecule_Savepsf(VALUE self, VALUE fname)
5243 Data_Get_Struct(self, Molecule, mol);
5244 MoleculeClearLoadSaveErrorMessage();
5245 fstr = FileStringValuePtr(fname);
5246 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5247 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5253 * savepdb(file) -> bool
5255 * Write coordinates as a pdb file. Returns true if successful.
5258 s_Molecule_Savepdb(VALUE self, VALUE fname)
5263 Data_Get_Struct(self, Molecule, mol);
5264 MoleculeClearLoadSaveErrorMessage();
5265 fstr = FileStringValuePtr(fname);
5266 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5267 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5273 * savedcd(file) -> bool
5275 * Write coordinates as a dcd file. Returns true if successful.
5278 s_Molecule_Savedcd(VALUE self, VALUE fname)
5283 Data_Get_Struct(self, Molecule, mol);
5284 MoleculeClearLoadSaveErrorMessage();
5285 fstr = FileStringValuePtr(fname);
5286 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5287 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5291 /* load([ftype, ] fname, ...) */
5293 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5296 char *argstr, *methname, *p, *type = "";
5299 const char *ls = (loadFlag ? "load" : "save");
5300 int lslen = strlen(ls);
5305 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5306 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5307 if (argstr[0] == ':') {
5308 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5309 methname = ALLOC_N(char, lslen + strlen(argstr));
5310 strcpy(methname, ls);
5311 strcat(methname, argstr + 1);
5313 for (i = lslen; methname[i] != 0; i++)
5314 methname[i] = tolower(methname[i]);
5315 mid = rb_intern(methname);
5319 rval = rb_funcall2(self, mid, argc, argv);
5325 /* Guess file type from extension */
5326 p = strrchr(argstr, '.');
5330 for (methname = p; *methname != 0; methname++) {
5331 if (!isalpha(*methname))
5334 if (*methname == 0) {
5335 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5336 if (methname == NULL)
5337 rb_raise(rb_eMolbyError, "Low memory");
5338 strcpy(methname, ls);
5339 strcat(methname, p);
5340 for (i = lslen; methname[i] != 0; i++)
5341 methname[i] = tolower(methname[i]);
5342 mid = rb_intern(methname);
5345 if (rb_respond_to(self, mid)) {
5346 /* Load: try to call the load procedure only if it is available */
5347 rval = rb_funcall2(self, mid, argc, argv);
5352 /* Save: call the save procedure, and if not found then call 'method_missing' */
5353 rval = rb_funcall2(self, mid, argc, argv);
5360 rval = rb_str_to_str(argv[0]);
5361 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5362 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5363 return Qnil; /* Does not reach here */
5367 /* Register the path */
5370 Data_Get_Struct(self, Molecule, mol);
5371 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5373 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5374 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5375 if (ap->occupancy != 0.0)
5378 if (i == mol->natoms) {
5379 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5380 ap->occupancy = 1.0;
5389 * molload(file, *args) -> bool
5391 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5392 * file type given by the extension). If this method fails, then all defined (public)
5393 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5396 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5398 return s_Molecule_LoadSave(argc, argv, self, 1);
5403 * molsave(file, *args) -> bool
5405 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5406 * (XXX is the file type given by the extension).
5409 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5411 return s_Molecule_LoadSave(argc, argv, self, 0);
5417 * open(file) -> Molecule
5419 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5422 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5430 rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5433 rb_scan_args(argc, argv, "01", &fname);
5437 p = FileStringValuePtr(fname);
5438 iflag = Ruby_SetInterruptFlag(Qfalse);
5439 mp = MoleculeCallback_openNewMolecule(p);
5440 Ruby_SetInterruptFlag(iflag);
5443 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5445 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5447 return ValueFromMolecule(mp);
5453 * new(file, *args) -> Molecule
5455 * Create a new molecule and call "load" method with the same arguments.
5458 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5461 return s_Molecule_Load(argc, argv, self);
5462 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5467 * error_message -> String
5469 * Get the error_message from the last load/save method. If no error, returns nil.
5472 s_Molecule_ErrorMessage(VALUE klass)
5474 if (gLoadSaveErrorMessage == NULL)
5476 else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5481 * set_error_message(String)
5482 * Molecule.error_message = String
5484 * Set the error_message for the present load/save method.
5487 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5489 if (gLoadSaveErrorMessage != NULL) {
5490 free(gLoadSaveErrorMessage);
5491 gLoadSaveErrorMessage = NULL;
5494 sval = rb_str_to_str(sval);
5495 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5502 * set_molecule(Molecule)
5504 * Duplicate the given molecule and set to self. The present molecule must be empty.
5505 * This method is exclusively used for associating a new document with an existing molecule.
5508 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5510 Molecule *mp1, *mp2;
5511 Data_Get_Struct(self, Molecule, mp1);
5512 mp2 = MoleculeFromValue(mval);
5513 MoleculeInitWithMolecule(mp1, mp2);
5514 MoleculeCallback_notifyModification(mp1, 1);
5518 #pragma mark ------ Name attributes ------
5524 * Returns the display name of the molecule. If the molecule has no associated
5525 * document, then returns nil.
5528 s_Molecule_Name(VALUE self)
5532 Data_Get_Struct(self, Molecule, mol);
5533 MoleculeCallback_displayName(mol, buf, sizeof buf);
5537 return Ruby_NewEncodedStringValue2(buf);
5542 * set_name(string) -> self
5544 * Set the name of an untitled molecule. If the molecule is not associated with window
5545 * or it already has an associated file, then exception is thrown.
5548 s_Molecule_SetName(VALUE self, VALUE nval)
5551 Data_Get_Struct(self, Molecule, mol);
5552 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5553 rb_raise(rb_eMolbyError, "Cannot change the window title");
5562 * Returns the full path name of the molecule, if it is associated with a file.
5563 * If the molecule has no associated file, then returns nil.
5566 s_Molecule_Path(VALUE self)
5570 Data_Get_Struct(self, Molecule, mol);
5571 MoleculeCallback_pathName(mol, buf, sizeof buf);
5575 return Ruby_NewFileStringValue(buf);
5582 * Returns the full path name of the directory in which the file associated with the
5583 * molecule is located. If the molecule has no associated file, then returns nil.
5586 s_Molecule_Dir(VALUE self)
5590 Data_Get_Struct(self, Molecule, mol);
5591 MoleculeCallback_pathName(mol, buf, sizeof buf);
5593 translate_char(buf, '\\', '/');
5598 p = strrchr(buf, '/');
5601 return Ruby_NewEncodedStringValue2(buf);
5609 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5610 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5611 * the Molecule structure) is returned.
5614 s_Molecule_Inspect(VALUE self)
5618 Data_Get_Struct(self, Molecule, mol);
5619 MoleculeCallback_displayName(mol, buf, sizeof buf);
5621 /* No associated document */
5622 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5623 return Ruby_NewEncodedStringValue2(buf);
5625 /* Check whether the document name is duplicate */
5629 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5630 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5631 if (strcmp(buf, buf2) == 0) {
5638 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5640 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5642 return Ruby_NewEncodedStringValue2(buf2);
5646 #pragma mark ------ MolEnumerables ------
5649 s_Molecule_MolEnumerable(VALUE self, int kind)
5652 MolEnumerable *mseq;
5653 Data_Get_Struct(self, Molecule, mol);
5654 mseq = MolEnumerableNew(mol, kind);
5655 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5660 * atoms -> MolEnumerable
5662 * Returns a MolEnumerable object representing the array of atoms.
5665 s_Molecule_Atoms(VALUE self)
5667 return s_Molecule_MolEnumerable(self, kAtomKind);
5672 * bonds -> MolEnumerable
5674 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5675 * by an array of two atom indices.
5678 s_Molecule_Bonds(VALUE self)
5680 return s_Molecule_MolEnumerable(self, kBondKind);
5685 * angles -> MolEnumerable
5687 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5688 * by an array of three atom indices.
5691 s_Molecule_Angles(VALUE self)
5693 return s_Molecule_MolEnumerable(self, kAngleKind);
5698 * dihedrals -> MolEnumerable
5700 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5701 * by an array of four atom indices.
5704 s_Molecule_Dihedrals(VALUE self)
5706 return s_Molecule_MolEnumerable(self, kDihedralKind);
5711 * impropers -> MolEnumerable
5713 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5714 * by an array of four atom indices.
5717 s_Molecule_Impropers(VALUE self)
5719 return s_Molecule_MolEnumerable(self, kImproperKind);
5724 * residues -> MolEnumerable
5726 * Returns a MolEnumerable object representing the array of residue names.
5729 s_Molecule_Residues(VALUE self)
5731 return s_Molecule_MolEnumerable(self, kResidueKind);
5738 * Returns the number of atoms.
5741 s_Molecule_Natoms(VALUE self)
5744 Data_Get_Struct(self, Molecule, mol);
5745 return INT2NUM(mol->natoms);
5752 * Returns the number of bonds.
5755 s_Molecule_Nbonds(VALUE self)
5758 Data_Get_Struct(self, Molecule, mol);
5759 return INT2NUM(mol->nbonds);
5764 * nangles -> Integer
5766 * Returns the number of angles.
5769 s_Molecule_Nangles(VALUE self)
5772 Data_Get_Struct(self, Molecule, mol);
5773 return INT2NUM(mol->nangles);
5778 * ndihedrals -> Integer
5780 * Returns the number of dihedrals.
5783 s_Molecule_Ndihedrals(VALUE self)
5786 Data_Get_Struct(self, Molecule, mol);
5787 return INT2NUM(mol->ndihedrals);
5792 * nimpropers -> Integer
5794 * Returns the number of impropers.
5797 s_Molecule_Nimpropers(VALUE self)
5800 Data_Get_Struct(self, Molecule, mol);
5801 return INT2NUM(mol->nimpropers);
5806 * nresidues -> Integer
5808 * Returns the number of residues.
5811 s_Molecule_Nresidues(VALUE self)
5814 Data_Get_Struct(self, Molecule, mol);
5815 return INT2NUM(mol->nresidues);
5820 * nresidues = Integer
5822 * Change the number of residues.
5825 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5828 int ival = NUM2INT(val);
5829 Data_Get_Struct(self, Molecule, mol);
5830 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5831 if (ival != mol->nresidues)
5832 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5838 * max_residue_number(atom_group = nil) -> Integer
5840 * Returns the maximum residue number actually used. If an atom group is given, only
5841 * these atoms are examined. If no atom is present, nil is returned.
5844 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5850 Data_Get_Struct(self, Molecule, mol);
5851 rb_scan_args(argc, argv, "01", &gval);
5852 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5853 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5854 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5859 * min_residue_number(atom_group = nil) -> Integer
5861 * Returns the minimum residue number actually used. If an atom group is given, only
5862 * these atoms are examined. If no atom is present, nil is returned.
5865 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5871 Data_Get_Struct(self, Molecule, mol);
5872 rb_scan_args(argc, argv, "01", &gval);
5873 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5874 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5875 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5880 * each_atom(atom_group = nil) {|aref| ...}
5882 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5883 * group is given, only these atoms are processed.
5884 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5885 * is self (a Molecule object).
5888 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5896 Data_Get_Struct(self, Molecule, mol);
5897 rb_scan_args(argc, argv, "01", &gval);
5898 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5899 arval = ValueFromMoleculeAndIndex(mol, 0);
5900 Data_Get_Struct(arval, AtomRef, aref);
5901 for (i = 0; i < mol->natoms; i++) {
5903 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5907 IntGroupRelease(ig);
5911 #pragma mark ------ Atom Group ------
5914 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5916 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5917 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5918 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5919 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5926 * atom_group {|aref| ...}
5927 * atom_group(arg1, arg2, ...)
5928 * atom_group(arg1, arg2, ...) {|aref| ...}
5930 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5931 * If arguments are given, then the atoms reprensented by the arguments are added to the
5932 * group. For a conversion of a string to an atom index, see the description
5933 * of Molecule#atom_index.
5934 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5935 * representing each atom, and the atoms are removed from the result if the block returns false.
5939 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5941 IntGroup *ig1, *ig2;
5943 Int i, startPt, interval;
5944 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5945 Data_Get_Struct(retval, IntGroup, ig1);
5946 Data_Get_Struct(self, Molecule, mol);
5948 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5951 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5952 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5953 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5954 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5955 ig2 = IntGroupFromValue(*argv);
5956 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5957 interval = IntGroupGetInterval(ig2, i);
5958 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5960 IntGroupRelease(ig2);
5961 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5963 values[0] = (VALUE)mol;
5964 values[1] = (VALUE)ig1;
5965 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5967 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5972 if (rb_block_given_p()) {
5973 /* Evaluate the given block with an AtomRef as the argument, and delete
5974 the index if the block returns false */
5975 AtomRef *aref = AtomRefNew(mol, 0);
5976 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5977 ig2 = IntGroupNew();
5978 IntGroupCopy(ig2, ig1);
5979 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5981 if (startPt >= mol->natoms)
5983 aref->idx = startPt;
5984 resval = rb_yield(arval);
5986 IntGroupRemove(ig1, startPt, 1);
5988 IntGroupRelease(ig2);
5991 /* Remove points that are out of bounds */
5992 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5999 * selection -> IntGroup
6001 * Returns the current selection.
6004 s_Molecule_Selection(VALUE self)
6009 Data_Get_Struct(self, Molecule, mol);
6010 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6011 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6012 val = ValueFromIntGroup(ig);
6013 IntGroupRelease(ig);
6015 val = IntGroup_Alloc(rb_cIntGroup);
6021 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6025 Data_Get_Struct(self, Molecule, mol);
6029 ig = s_Molecule_AtomGroupFromValue(self, val);
6031 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6033 MoleculeSetSelection(mol, ig);
6035 IntGroupRelease(ig);
6041 * selection = IntGroup
6043 * Set the current selection. The right-hand operand may be nil.
6044 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6047 s_Molecule_SetSelection(VALUE self, VALUE val)
6049 return s_Molecule_SetSelectionSub(self, val, 0);
6054 * set_undoable_selection(IntGroup)
6056 * Set the current selection with undo registration. The right-hand operand may be nil.
6057 * This operation is undoable.
6060 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6062 return s_Molecule_SetSelectionSub(self, val, 1);
6065 #pragma mark ------ Editing ------
6069 * extract(group, dummy_flag = nil) -> Molecule
6071 * Extract the atoms given by group and return as a new molecule object.
6072 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6073 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6074 * names beginning with an underscore) and included in the new molecule object.
6077 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6079 Molecule *mol1, *mol2;
6081 VALUE group, dummy_flag, retval;
6082 Data_Get_Struct(self, Molecule, mol1);
6083 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6084 ig = s_Molecule_AtomGroupFromValue(self, group);
6085 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6088 retval = ValueFromMolecule(mol2);
6090 IntGroupRelease(ig);
6096 * add(molecule2) -> self
6098 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6100 This operation is undoable.
6103 s_Molecule_Add(VALUE self, VALUE val)
6105 Molecule *mol1, *mol2;
6106 Data_Get_Struct(self, Molecule, mol1);
6107 mol2 = MoleculeFromValue(val);
6108 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6114 * remove(group) -> Molecule
6116 * The atoms designated by the given group are removed from the molecule.
6117 * This operation is undoable.
6120 s_Molecule_Remove(VALUE self, VALUE group)
6125 IntGroupIterator iter;
6127 ig = s_Molecule_AtomGroupFromValue(self, group);
6128 /* Data_Get_Struct(self, Molecule, mol1);
6129 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6130 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6131 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6132 Data_Get_Struct(group, IntGroup, ig); */
6133 Data_Get_Struct(self, Molecule, mol1);
6135 /* Remove the bonds between the two fragments */
6136 /* (This is necessary for undo to work correctly) */
6137 IntGroupIteratorInit(ig, &iter);
6139 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6140 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6142 cp = AtomConnectData(&ap->connect);
6143 for (j = 0; j < ap->connect.count; j++) {
6145 if (!IntGroupLookup(ig, n, NULL)) {
6146 /* bond i-n, i is in ig and n is not */
6147 int k = MoleculeLookupBond(mol1, i, n);
6151 IntGroupAdd(bg, k, 1);
6156 IntGroupIteratorRelease(&iter);
6159 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6160 IntGroupRelease(bg);
6163 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6170 * create_atom(name, pos = -1) -> AtomRef
6172 * Create a new atom with the specified name (may contain residue
6173 * information) and position (if position is out of range, the atom is appended at
6174 * the end). Returns the reference to the new atom.
6175 * This operation is undoable.
6178 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6185 char *p, resName[6], atomName[6];
6187 Data_Get_Struct(self, Molecule, mol);
6188 rb_scan_args(argc, argv, "02", &name, &ival);
6190 pos = NUM2INT(rb_Integer(ival));
6193 p = StringValuePtr(name);
6195 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6196 if (atomName[0] == 0)
6197 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6200 if (p == NULL || p[0] == 0) {
6201 memset(atomName, 0, 4);
6204 memset(&arec, 0, sizeof(arec));
6205 strncpy(arec.aname, atomName, 4);
6207 strncpy(arec.resName, resName, 4);
6208 arec.resSeq = resSeq;
6210 arec.occupancy = 1.0;
6211 // i = MoleculeCreateAnAtom(mol, &arec);
6212 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6214 aref = AtomRefNew(mol, pos);
6215 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6220 * duplicate_atom(atomref, pos = -1) -> AtomRef
6222 * Create a new atom with the same attributes (but no bonding information)
6223 * with the specified atom. Returns the reference to the new atom.
6224 * This operation is undoable.
6227 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6233 VALUE retval, aval, ival;
6235 Data_Get_Struct(self, Molecule, mol);
6236 rb_scan_args(argc, argv, "11", &aval, &ival);
6237 if (FIXNUM_P(aval)) {
6238 int idx = NUM2INT(aval);
6239 if (idx < 0 || idx >= mol->natoms)
6240 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6241 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6243 apsrc = s_AtomFromValue(aval);
6246 rb_raise(rb_eMolbyError, "bad atom specification");
6248 pos = NUM2INT(rb_Integer(ival));
6250 AtomDuplicate(&arec, apsrc);
6251 arec.connect.count = 0;
6252 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6255 aref = AtomRefNew(mol, pos);
6256 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6264 * create_bond(n1, n2, ...) -> Integer
6266 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6267 * do nothing for that pair. Returns the number of bonds actually created.
6268 * This operation is undoable.
6271 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6274 Int i, j, k, *ip, old_nbonds;
6276 rb_raise(rb_eMolbyError, "missing arguments");
6278 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6279 Data_Get_Struct(self, Molecule, mol);
6280 ip = ALLOC_N(Int, argc + 1);
6281 for (i = j = 0; i < argc; i++, j++) {
6282 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6284 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6285 j -= 2; /* This bond is already present: skip it */
6287 for (k = 0; k < j - 1; k += 2) {
6288 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6289 j -= 2; /* The same entry is already in the argument */
6296 old_nbonds = mol->nbonds;
6298 ip[j] = kInvalidIndex;
6299 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6303 rb_raise(rb_eMolbyError, "atom index out of range");
6305 rb_raise(rb_eMolbyError, "too many bonds");
6307 rb_raise(rb_eMolbyError, "duplicate bonds");
6309 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6311 rb_raise(rb_eMolbyError, "error in creating bonds");
6312 return INT2NUM(mol->nbonds - old_nbonds);
6317 * molecule.remove_bonds(n1, n2, ...) -> Integer
6319 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6320 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6321 * This operation is undoable.
6324 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6330 rb_raise(rb_eMolbyError, "missing arguments");
6332 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6333 Data_Get_Struct(self, Molecule, mol);
6335 for (i = j = 0; i < argc; i++, j = 1 - j) {
6336 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6338 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6342 IntGroupAdd(bg, k, 1);
6347 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6348 i = IntGroupGetCount(bg);
6349 IntGroupRelease(bg);
6356 * assign_bond_order(idx, d1)
6357 * assign_bond_orders(group, [d1, d2, ...])
6359 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6360 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6361 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6362 * (This may change in the future)
6363 * This operation is undoable.
6366 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6370 Data_Get_Struct(self, Molecule, mol);
6371 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6372 /* The first form */
6373 Int idx = NUM2INT(rb_Integer(idxval));
6374 Double d1 = NUM2DBL(rb_Float(dval));
6375 if (idx < 0 || idx >= mol->nbonds)
6376 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6377 ig = IntGroupNewWithPoints(idx, 1, -1);
6378 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6379 IntGroupRelease(ig);
6383 ig = IntGroupFromValue(idxval);
6384 n = IntGroupGetCount(ig);
6386 rb_raise(rb_eMolbyError, "the bond index is empty");
6387 dval = rb_ary_to_ary(dval);
6388 dp = (Double *)calloc(sizeof(Double), n);
6389 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6390 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6392 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6394 IntGroupRelease(ig);
6401 * get_bond_order(idx) -> Float
6402 * get_bond_orders(group) -> Array
6404 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6405 * In the second form, the bond orders at the indices in the group are returned as an array.
6406 * If no bond order information have been assigned, returns nil (the first form)
6407 * or an empty array (the second form).
6410 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6416 Int i, n, numericArg;
6417 Data_Get_Struct(self, Molecule, mol);
6418 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6419 /* The first form */
6420 Int idx = NUM2INT(rb_Integer(idxval));
6421 if (idx < 0 || idx >= mol->nbonds)
6422 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6423 if (mol->bondOrders == NULL)
6425 ig = IntGroupNewWithPoints(idx, 1, -1);
6429 if (mol->bondOrders == NULL)
6430 return rb_ary_new();
6431 ig = IntGroupFromValue(idxval);
6432 n = IntGroupGetCount(ig);
6434 rb_raise(rb_eMolbyError, "the bond index is empty");
6437 dp = (Double *)calloc(sizeof(Double), n);
6438 MoleculeGetBondOrders(mol, dp, ig);
6440 retval = rb_float_new(dp[0]);
6442 retval = rb_ary_new();
6443 for (i = 0; i < n; i++)
6444 rb_ary_push(retval, rb_float_new(dp[i]));
6447 IntGroupRelease(ig);
6453 * bond_exist?(idx1, idx2) -> bool
6455 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6456 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6459 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6465 Data_Get_Struct(self, Molecule, mol);
6466 idx1 = NUM2INT(rb_Integer(ival1));
6467 idx2 = NUM2INT(rb_Integer(ival2));
6468 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6469 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6470 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6471 cp = AtomConnectData(&ap->connect);
6472 for (i = 0; i < ap->connect.count; i++) {
6481 * add_angle(n1, n2, n3) -> Molecule
6483 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6484 * when a bond is created, so it is rarely necessary to use this method explicitly.
6485 * This operation is undoable.
6488 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6492 Data_Get_Struct(self, Molecule, mol);
6493 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6494 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6495 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6496 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6497 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6498 n[3] = kInvalidIndex;
6499 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6505 * remove_angle(n1, n2, n3) -> Molecule
6507 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6508 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6509 * This operation is undoable.
6512 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6517 Data_Get_Struct(self, Molecule, mol);
6518 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6519 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6520 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6521 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6522 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6523 ig = IntGroupNewWithPoints(n[3], 1, -1);
6524 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6525 IntGroupRelease(ig);
6531 * add_dihedral(n1, n2, n3, n4) -> Molecule
6533 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6534 * when a bond is created, so it is rarely necessary to use this method explicitly.
6535 * This operation is undoable.
6538 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6542 Data_Get_Struct(self, Molecule, mol);
6543 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6544 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6545 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6546 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6547 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6548 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6549 n[4] = kInvalidIndex;
6550 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6556 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6558 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6559 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6560 * This operation is undoable.
6563 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6568 Data_Get_Struct(self, Molecule, mol);
6569 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6570 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6571 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6572 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6573 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6574 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6575 ig = IntGroupNewWithPoints(n[4], 1, -1);
6576 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6577 IntGroupRelease(ig);
6583 * add_improper(n1, n2, n3, n4) -> Molecule
6585 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6586 * not automatically added when a new bond is created, so this method is more useful than
6587 * the angle/dihedral counterpart.
6588 * This operation is undoable.
6591 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6595 Data_Get_Struct(self, Molecule, mol);
6596 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6597 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6598 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6599 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6600 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6601 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6602 n[4] = kInvalidIndex;
6603 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6609 * remove_improper(n1, n2, n3, n4) -> Molecule
6610 * remove_improper(intgroup) -> Molecule
6612 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6613 * Returns self. Unlike angles and dihedrals, impropers are
6614 * not automatically added when a new bond is created, so this method is more useful than
6615 * the angle/dihedral counterpart.
6616 * This operation is undoable.
6619 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6622 VALUE v1, v2, v3, v4;
6625 Data_Get_Struct(self, Molecule, mol);
6627 ig = IntGroupFromValue(argv[0]);
6629 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6630 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6631 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6632 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6633 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6634 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6635 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6636 ig = IntGroupNewWithPoints(n[4], 1, -1);
6638 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6639 IntGroupRelease(ig);
6645 * assign_residue(group, res) -> Molecule
6647 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6648 * or "resname.resno". When the residue number is not specified, the residue number of
6649 * the first atom in the group is used.
6650 * This operation is undoable.
6653 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6657 char *p, *pp, buf[16];
6660 Data_Get_Struct(self, Molecule, mol);
6662 /* Parse the argument res */
6663 if (FIXNUM_P(res)) {
6664 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6665 resid = NUM2INT(res);
6668 p = StringValuePtr(res);
6669 pp = strchr(p, '.');
6671 resid = atoi(pp + 1);
6677 if (n > sizeof buf - 1)
6682 ig = s_Molecule_AtomGroupFromValue(self, range);
6683 if (ig == NULL || IntGroupGetCount(ig) == 0)
6687 /* Use the residue number of the first specified atom */
6688 n = IntGroupGetNthPoint(ig, 0);
6689 if (n >= mol->natoms)
6690 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6691 ap = ATOM_AT_INDEX(mol->atoms, n);
6694 /* Change the residue number */
6695 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6696 /* Change the residue name if necessary */
6700 seqs[1] = kInvalidIndex; */
6701 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6703 IntGroupRelease(ig);
6709 * offset_residue(group, offset) -> Molecule
6711 * Offset the residue number of the specified atoms. If any of the residue number gets
6712 * negative, then exception is thrown.
6713 * This operation is undoable.
6716 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6721 Data_Get_Struct(self, Molecule, mol);
6722 ig = s_Molecule_AtomGroupFromValue(self, range);
6723 ofs = NUM2INT(offset);
6724 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6726 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6727 IntGroupRelease(ig);
6733 * renumber_atoms(array) -> IntGroup
6735 * Change the order of atoms so that the atoms specified in the array argument appear
6736 * in this order from the top of the molecule. The atoms that are not included in array
6737 * are placed after these atoms, and these atoms are returned as an intGroup.
6738 * This operation is undoable.
6741 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6747 VALUE *valp, retval;
6748 Data_Get_Struct(self, Molecule, mol);
6749 if (TYPE(array) != T_ARRAY)
6750 array = rb_funcall(array, rb_intern("to_a"), 0);
6751 n = RARRAY_LEN(array);
6752 valp = RARRAY_PTR(array);
6753 new2old = ALLOC_N(Int, n + 1);
6754 for (i = 0; i < n; i++)
6755 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6756 new2old[i] = kInvalidIndex;
6757 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6759 rb_raise(rb_eMolbyError, "Atom index out of range");
6761 rb_raise(rb_eMolbyError, "Duplicate entry");
6763 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6764 retval = IntGroup_Alloc(rb_cIntGroup);
6765 Data_Get_Struct(retval, IntGroup, ig);
6766 if (mol->natoms > n)
6767 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6774 * set_atom_attr(index, key, value)
6776 * Set the atom attribute for the specified atom.
6777 * This operation is undoable.
6780 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6784 Data_Get_Struct(self, Molecule, mol);
6785 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6786 oldval = s_AtomRef_GetAttr(aref, key);
6789 s_AtomRef_SetAttr(aref, key, val);
6795 * get_atom_attr(index, key)
6797 * Get the atom attribute for the specified atom.
6800 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6802 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6805 #pragma mark ------ Undo Support ------
6809 * register_undo(script, *args)
6811 * Register an undo operation with the current molecule.
6814 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6819 Data_Get_Struct(self, Molecule, mol);
6820 rb_scan_args(argc, argv, "1*", &script, &args);
6821 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6822 MolActionCallback_registerUndo(mol, act);
6828 * undo_enabled? -> bool
6830 * Returns true if undo is enabled for this molecule; otherwise no.
6833 s_Molecule_UndoEnabled(VALUE self)
6836 Data_Get_Struct(self, Molecule, mol);
6837 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6844 * undo_enabled = bool
6846 * Enable or disable undo.
6849 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6852 Data_Get_Struct(self, Molecule, mol);
6853 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6857 #pragma mark ------ Measure ------
6860 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6862 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6863 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6864 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6866 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6872 * center_of_mass(group = nil) -> Vector3D
6874 * Calculate the center of mass for the given set of atoms. The argument
6875 * group is null, then all atoms are considered.
6878 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6884 Data_Get_Struct(self, Molecule, mol);
6885 rb_scan_args(argc, argv, "01", &group);
6886 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6887 s_Molecule_DoCenterOfMass(mol, &v, ig);
6889 IntGroupRelease(ig);
6890 return ValueFromVector(&v);
6895 * centralize(group = nil) -> self
6897 * Translate the molecule so that the center of mass of the given group is located
6898 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6901 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6907 Data_Get_Struct(self, Molecule, mol);
6908 rb_scan_args(argc, argv, "01", &group);
6909 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6910 s_Molecule_DoCenterOfMass(mol, &v, ig);
6912 IntGroupRelease(ig);
6916 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6922 * bounds(group = nil) -> [min, max]
6924 * Calculate the boundary. The return value is an array of two Vector3D objects.
6927 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6935 Data_Get_Struct(self, Molecule, mol);
6936 rb_scan_args(argc, argv, "01", &group);
6937 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6938 if (ig != NULL && IntGroupGetCount(ig) == 0)
6939 rb_raise(rb_eMolbyError, "atom group is empty");
6940 vmin.x = vmin.y = vmin.z = 1e30;
6941 vmax.x = vmax.y = vmax.z = -1e30;
6942 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6944 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6960 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6963 /* Get atom position or a vector */
6965 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6967 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6968 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6969 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6971 VectorFromValue(val, vp);
6977 * measure_bond(n1, n2) -> Float
6979 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
6980 * or Vector3D values.
6981 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6984 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6988 Data_Get_Struct(self, Molecule, mol);
6989 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6990 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6991 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6996 * measure_angle(n1, n2, n3) -> Float
6998 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
6999 * or Vector3D values. The return value is in degree.
7000 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7003 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7008 Data_Get_Struct(self, Molecule, mol);
7009 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7010 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7011 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7012 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7014 return Qnil; /* Cannot define */
7015 else return rb_float_new(d);
7020 * measure_dihedral(n1, n2, n3, n4) -> Float
7022 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7023 * or Vector3D values. The return value is in degree.
7024 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7027 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7030 Vector v1, v2, v3, v4;
7032 Data_Get_Struct(self, Molecule, mol);
7033 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7034 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7035 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7036 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7037 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7039 return Qnil; /* Cannot define */
7040 else return rb_float_new(d);
7045 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7047 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7048 * first and second atom in the pair should belong to group1 and group2, respectively.
7049 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7052 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7055 VALUE limval, gval1, gval2, rval, igval;
7056 IntGroup *ig1, *ig2;
7057 IntGroupIterator iter1, iter2;
7063 MDExclusion *exinfo;
7066 Data_Get_Struct(self, Molecule, mol);
7067 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7068 lim = NUM2DBL(rb_Float(limval));
7070 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7072 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7074 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7076 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7078 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7080 if (!RTEST(igval)) {
7081 /* Use the exclusion table in MDArena */
7082 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7083 VALUE mval = ValueFromMolecule(mol);
7084 s_RebuildMDParameterIfNecessary(mval, Qnil);
7086 exinfo = mol->arena->exinfo; /* May be NULL */
7087 exlist = mol->arena->exlist;
7092 IntGroupIteratorInit(ig1, &iter1);
7093 IntGroupIteratorInit(ig2, &iter2);
7096 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7098 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7100 if (exinfo != NULL) {
7101 exn1 = exinfo[n[0]].index1;
7102 exn2 = exinfo[n[0] + 1].index1;
7103 } else exn1 = exn2 = -1;
7104 IntGroupIteratorReset(&iter2);
7105 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7106 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7108 continue; /* Same atom */
7109 if (exinfo != NULL) {
7110 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7111 for (i = exn1; i < exn2; i++) {
7112 if (exlist[i] == n[1])
7116 continue; /* Should be excluded */
7118 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7119 /* Is this pair already registered? */
7121 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7122 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7126 /* Not registered yet */
7127 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7132 IntGroupIteratorRelease(&iter2);
7133 IntGroupIteratorRelease(&iter1);
7134 IntGroupRelease(ig2);
7135 IntGroupRelease(ig1);
7136 rval = rb_ary_new2(npairs);
7137 if (pairs != NULL) {
7138 for (i = 0; i < npairs; i++) {
7139 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7148 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7150 * Find atoms that are within the threshold distance from the given atom.
7151 * (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.)
7152 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7153 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7154 * If limit is not given, a default value of 1.2 is used.
7155 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7158 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7161 VALUE aval, limval, radval;
7162 double limit, radius;
7163 Int n1, nbonds, *bonds, an;
7165 Data_Get_Struct(self, Molecule, mol);
7166 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7167 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)) {
7168 VectorFromValue(aval, &v);
7170 radius = gElementParameters[6].radius;
7172 radius = NUM2DBL(rb_Float(radval));
7175 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7176 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7177 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7178 if (an >= 0 && an < gCountElementParameters)
7179 radius = gElementParameters[an].radius;
7180 else radius = gElementParameters[6].radius;
7185 limit = NUM2DBL(rb_Float(limval));
7186 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7188 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7189 aval = rb_ary_new();
7191 for (n1 = 0; n1 < nbonds; n1++)
7192 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7200 * guess_bonds(limit = 1.2) -> Integer
7202 * Create bonds between atoms that are within the threshold distance.
7203 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7204 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7205 * If limit is not given, a default value of 1.2 is used.
7206 * The number of the newly created bonds is returned.
7207 * This operation is undoable.
7210 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7216 Data_Get_Struct(self, Molecule, mol);
7217 rb_scan_args(argc, argv, "01", &limval);
7221 limit = NUM2DBL(rb_Float(limval));
7222 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7224 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7227 return INT2NUM(nbonds);
7230 #pragma mark ------ Cell and Symmetry ------
7234 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7236 * Returns the unit cell parameters. If cell is not set, returns nil.
7239 s_Molecule_Cell(VALUE self)
7244 Data_Get_Struct(self, Molecule, mol);
7245 if (mol->cell == NULL)
7247 val = rb_ary_new2(6);
7248 for (i = 0; i < 6; i++)
7249 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7250 if (mol->cell->has_sigma) {
7251 for (i = 0; i < 6; i++) {
7252 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7260 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7261 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7263 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7264 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7265 This operation is undoable.
7266 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7269 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7273 int i, convert_coord, n;
7275 Data_Get_Struct(self, Molecule, mol);
7276 rb_scan_args(argc, argv, "11", &val, &cval);
7281 val = rb_ary_to_ary(val);
7282 len = RARRAY_LEN(val);
7285 } else if (len >= 6) {
7287 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7288 for (i = 0; i < n; i++)
7289 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7291 convert_coord = (RTEST(cval) ? 1 : 0);
7292 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7298 * box -> [avec, bvec, cvec, origin, flags]
7300 * Get the unit cell information in the form of a periodic bounding box.
7301 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7302 * Integers which define whether the system is periodic along the axis.
7303 * If no unit cell is defined, nil is returned.
7306 s_Molecule_Box(VALUE self)
7310 Data_Get_Struct(self, Molecule, mol);
7311 if (mol == NULL || mol->cell == NULL)
7313 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7314 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7315 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7316 v[3] = ValueFromVector(&(mol->cell->origin));
7317 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7318 val = rb_ary_new4(5, v);
7324 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7325 * set_box(d, origin = [0, 0, 0])
7328 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7329 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7331 Flags, if present, is a 3-member array of Integers defining whether the system is
7332 periodic along the axis.
7333 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7334 In the second form, an isotropic box with cell-length d is set.
7335 In the third form, the existing box is cleared.
7336 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7339 s_Molecule_SetBox(VALUE self, VALUE aval)
7343 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7345 Vector origin = {0, 0, 0};
7348 int i, convertCoordinates = 0;
7349 Data_Get_Struct(self, Molecule, mol);
7351 MolActionCreateAndPerform(mol, gMolActionClearBox);
7354 aval = rb_ary_to_ary(aval);
7355 for (i = 0; i < 6; i++) {
7356 if (i < RARRAY_LEN(aval))
7357 v[i] = (RARRAY_PTR(aval))[i];
7361 MolActionCreateAndPerform(mol, gMolActionClearBox);
7364 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7365 d = NUM2DBL(rb_Float(v[0]));
7366 for (i = 0; i < 3; i++)
7367 VecScale(vv[i], ax[i], d);
7369 VectorFromValue(v[1], &origin);
7370 flags[0] = flags[1] = flags[2] = 1;
7372 for (i = 0; i < 3; i++) {
7375 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7376 d = NUM2DBL(rb_Float(v[i]));
7377 VecScale(vv[i], ax[i], d);
7379 VectorFromValue(v[i], &vv[i]);
7381 flags[i] = (VecLength2(vv[i]) > 0.0);
7384 VectorFromValue(v[3], &origin);
7386 for (i = 0; i < 3; i++) {
7387 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7388 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7392 convertCoordinates = 1;
7394 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7400 * cell_periodicity -> [n1, n2, n3]
7402 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7406 s_Molecule_CellPeriodicity(VALUE self)
7409 Data_Get_Struct(self, Molecule, mol);
7410 if (mol->cell == NULL)
7412 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7417 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7418 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7420 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7421 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7422 * If cell is not defined, exception is raised.
7423 * This operation is undoable.
7426 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7430 Data_Get_Struct(self, Molecule, mol);
7431 if (mol->cell == NULL)
7432 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7435 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7436 flag = NUM2INT(rb_Integer(arg));
7440 arg = rb_ary_to_ary(arg);
7442 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7443 arg0 = RARRAY_PTR(arg)[i];
7444 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7445 flag |= (1 << (2 - i));
7448 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7454 * cell_flexibility -> bool
7456 * Returns the unit cell is flexible or not
7459 s_Molecule_CellFlexibility(VALUE self)
7461 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7464 Data_Get_Struct(self, Molecule, mol);
7465 if (mol->cell == NULL)
7467 if (mol->useFlexibleCell)
7469 else return Qfalse; */
7474 * self.cell_flexibility = bool
7475 * set_cell_flexibility(bool)
7477 * Change the unit cell is flexible or not
7480 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7482 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7485 Data_Get_Struct(self, Molecule, mol);
7486 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7492 * cell_transform -> Transform
7494 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7495 * If cell is not defined, nil is returned.
7498 s_Molecule_CellTransform(VALUE self)
7501 Data_Get_Struct(self, Molecule, mol);
7502 if (mol == NULL || mol->cell == NULL)
7504 return ValueFromTransform(&(mol->cell->tr));
7509 * symmetry -> Array of Transforms
7510 * symmetries -> Array of Transforms
7512 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7513 * returns an empty array.
7516 s_Molecule_Symmetry(VALUE self)
7521 Data_Get_Struct(self, Molecule, mol);
7522 if (mol->nsyms <= 0)
7523 return rb_ary_new();
7524 val = rb_ary_new2(mol->nsyms);
7525 for (i = 0; i < mol->nsyms; i++) {
7526 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7533 * nsymmetries -> Integer
7535 * Get the number of currently defined symmetry operations.
7538 s_Molecule_Nsymmetries(VALUE self)
7541 Data_Get_Struct(self, Molecule, mol);
7542 return INT2NUM(mol->nsyms);
7547 * add_symmetry(Transform) -> Integer
7549 * Add a new symmetry operation. If no symmetry operation is defined and the
7550 * given argument is not an identity transform, then also add an identity
7551 * transform at the index 0.
7552 * Returns the total number of symmetries after operation.
7555 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7559 Data_Get_Struct(self, Molecule, mol);
7560 TransformFromValue(trans, &tr);
7561 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7562 return INT2NUM(mol->nsyms);
7567 * remove_symmetry(count = nil) -> Integer
7568 * remove_symmetries(count = nil) -> Integer
7570 * Remove the specified number of symmetry operations. The last added ones are removed
7571 * first. If count is nil, then all symmetry operations are removed. Returns the
7572 * number of leftover symmetries.
7575 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7580 Data_Get_Struct(self, Molecule, mol);
7581 rb_scan_args(argc, argv, "01", &cval);
7585 n = NUM2INT(rb_Integer(cval));
7586 if (n < 0 || n > mol->nsyms)
7587 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7588 if (n == mol->nsyms)
7591 for (i = 0; i < n; i++)
7592 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7593 return INT2NUM(mol->nsyms);
7598 * wrap_unit_cell(group) -> Vector3D
7600 * Move the specified group so that the center of mass of the group is within the
7601 * unit cell. The offset vector is returned. If no periodic box is defined,
7602 * exception is raised.
7605 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7610 Data_Get_Struct(self, Molecule, mol);
7611 if (mol->cell == NULL)
7612 rb_raise(rb_eMolbyError, "no unit cell is defined");
7613 ig = s_Molecule_AtomGroupFromValue(self, gval);
7614 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7615 TransformVec(&v, mol->cell->rtr, &cv);
7616 if (mol->cell->flags[0])
7618 if (mol->cell->flags[1])
7620 if (mol->cell->flags[2])
7622 TransformVec(&dv, mol->cell->tr, &v);
7624 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7625 IntGroupRelease(ig);
7626 return ValueFromVector(&dv);
7631 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7633 * Expand the specified part of the molecule by the given symmetry operation.
7634 * Returns the array of atom indices corresponding to the expanded atoms.
7635 * If allow_overlap is true, then new atoms are created even when the
7636 * coordinates coincide with the some other atom (special position) of the
7637 * same element; otherwise, such atom will not be created and the index of the
7638 * existing atom is given in the returned array.
7641 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7644 VALUE gval, sval, xval, yval, zval, rval, oval;
7646 Int n[4], allow_overlap;
7650 Data_Get_Struct(self, Molecule, mol);
7651 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7652 n[0] = NUM2INT(rb_Integer(sval));
7653 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7654 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7655 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7656 allow_overlap = (RTEST(oval) ? 1 : 0);
7657 ig = s_Molecule_AtomGroupFromValue(self, gval);
7658 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7659 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7660 natoms = mol->natoms;
7662 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7664 rval = rb_ary_new2(nidx);
7665 while (--nidx >= 0) {
7666 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7668 /* if (natoms == mol->natoms)
7671 rval = IntGroup_Alloc(rb_cIntGroup);
7672 Data_Get_Struct(rval, IntGroup, ig);
7673 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7680 * amend_by_symmetry(group = nil) -> IntGroup
7682 * Expand the specified part of the molecule by the given symmetry operation.
7683 * Returns an IntGroup containing the added atoms.
7686 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7691 Data_Get_Struct(self, Molecule, mol);
7692 rb_scan_args(argc, argv, "01", &gval);
7694 ig = s_Molecule_AtomGroupFromValue(self, gval);
7696 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7697 rval = ValueFromIntGroup(ig2);
7698 IntGroupRelease(ig2);
7702 #pragma mark ------ Transforms ------
7706 * translate(vec, group = nil) -> Molecule
7708 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7709 * This operation is undoable.
7712 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7718 Data_Get_Struct(self, Molecule, mol);
7719 rb_scan_args(argc, argv, "11", &vec, &group);
7720 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7721 VectorFromValue(vec, &v);
7722 // MoleculeTranslate(mol, &v, ig);
7723 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7725 IntGroupRelease(ig);
7731 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7733 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7734 * If group is given, only atoms in the group are moved.
7735 * This operation is undoable.
7738 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7741 volatile VALUE aval, anval, cval, gval;
7746 Data_Get_Struct(self, Molecule, mol);
7747 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7748 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7749 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7750 VectorFromValue(aval, &av);
7752 cv.x = cv.y = cv.z = 0.0;
7754 VectorFromValue(cval, &cv);
7755 if (TransformForRotation(tr, &av, angle, &cv))
7756 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7757 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7759 IntGroupRelease(ig);
7765 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7767 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7768 * axis must not be a zero vector.
7769 * If group is given, only atoms in the group are moved.
7770 * This operation is undoable.
7773 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7776 volatile VALUE aval, cval, gval;
7780 Data_Get_Struct(self, Molecule, mol);
7781 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7782 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7783 VectorFromValue(aval, &av);
7785 cv.x = cv.y = cv.z = 0.0;
7787 VectorFromValue(cval, &cv);
7788 if (TransformForReflection(tr, &av, &cv))
7789 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7790 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7792 IntGroupRelease(ig);
7798 * invert(center = [0,0,0], group = nil) -> Molecule
7800 * Invert the molecule with the given center.
7801 * If group is given, only atoms in the group are moved.
7802 * This operation is undoable.
7805 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7808 volatile VALUE cval, gval;
7812 Data_Get_Struct(self, Molecule, mol);
7813 rb_scan_args(argc, argv, "02", &cval, &gval);
7814 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7816 cv.x = cv.y = cv.z = 0.0;
7818 VectorFromValue(cval, &cv);
7819 TransformForInversion(tr, &cv);
7820 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7822 IntGroupRelease(ig);
7828 * transform(transform, group = nil) -> Molecule
7830 * Transform the molecule by the given Transform object.
7831 * If group is given, only atoms in the group are moved.
7832 * This operation is undoable.
7835 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7841 Data_Get_Struct(self, Molecule, mol);
7842 rb_scan_args(argc, argv, "11", &trans, &group);
7843 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7844 TransformFromValue(trans, &tr);
7845 /* MoleculeTransform(mol, tr, ig); */
7846 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7848 IntGroupRelease(ig);
7854 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7856 * Get the transform corresponding to the symmetry operation. The symop can either be
7857 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7858 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7859 * Otherwise, the returned transform is for fractional coordinates.
7860 * Raises exception when no cell or no transform are defined.
7863 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7869 Data_Get_Struct(self, Molecule, mol);
7870 if (mol->cell == NULL)
7871 rb_raise(rb_eMolbyError, "no unit cell is defined");
7872 if (mol->nsyms == 0)
7873 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7874 rb_scan_args(argc, argv, "11", &sval, &fval);
7875 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7876 symop.sym = NUM2INT(rb_Integer(sval));
7877 symop.dx = symop.dy = symop.dz = 0;
7879 sval = rb_ary_to_ary(sval);
7880 if (RARRAY_LEN(sval) < 4)
7881 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7882 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7883 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7884 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7885 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7887 if (symop.sym >= mol->nsyms)
7888 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7889 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7890 return ValueFromTransform(&tr);
7895 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7897 * Get the symmetry operation corresponding to the given transform.
7898 * If is_cartesian is true, the given transform is for cartesian coordinates.
7899 * Otherwise, the given transform is for fractional coordinates.
7900 * Raises exception when no cell or no transform are defined.
7903 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7910 Data_Get_Struct(self, Molecule, mol);
7911 if (mol->cell == NULL)
7912 rb_raise(rb_eMolbyError, "no unit cell is defined");
7913 if (mol->nsyms == 0)
7914 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7915 rb_scan_args(argc, argv, "11", &tval, &fval);
7916 TransformFromValue(tval, &tr);
7917 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7919 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7921 return Qnil; /* Not found */
7925 #pragma mark ------ Frames ------
7929 * select_frame(index)
7932 * Select the specified frame. If successful, returns true, otherwise returns false.
7935 s_Molecule_SelectFrame(VALUE self, VALUE val)
7938 int ival = NUM2INT(val);
7939 Data_Get_Struct(self, Molecule, mol);
7940 ival = MoleculeSelectFrame(mol, ival, 1);
7950 * Get the current frame.
7953 s_Molecule_Frame(VALUE self)
7956 Data_Get_Struct(self, Molecule, mol);
7957 return INT2NUM(mol->cframe);
7962 * nframes -> Integer
7964 * Get the number of frames.
7967 s_Molecule_Nframes(VALUE self)
7970 Data_Get_Struct(self, Molecule, mol);
7971 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7976 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7977 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7979 * Insert new frames at the indices specified by the intGroup. If the first argument is
7980 * an integer, a single new frame is inserted at that index. If the first argument is
7981 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7982 * should be an array of arrays of Vector3Ds, then those coordinates are set
7983 * to the new frame. Otherwise, the coordinates of current molecule are copied
7985 * Returns an intGroup representing the inserted frames if successful, nil if not.
7988 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7990 VALUE val, coords, cells;
7993 int count, ival, i, j, len, len_c, len2, nframes;
7996 Data_Get_Struct(self, Molecule, mol);
7997 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7998 if (coords != Qnil) {
7999 if (TYPE(coords) != T_ARRAY)
8000 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8001 len = RARRAY_LEN(coords);
8003 if (cells != Qnil) {
8004 if (mol->cell == NULL)
8005 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8006 if (TYPE(cells) != T_ARRAY)
8007 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8008 len_c = RARRAY_LEN(cells);
8010 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
8011 nframes = MoleculeGetNumberOfFrames(mol);
8013 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8014 val = ValueFromIntGroup(ig);
8016 ig = IntGroupFromValue(val);
8018 count = IntGroupGetCount(ig); /* Count is updated here */
8019 vp = ALLOC_N(Vector, mol->natoms * count);
8021 vp2 = ALLOC_N(Vector, 4 * count);
8025 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8026 ptr = RARRAY_PTR(coords);
8027 for (i = 0; i < count; i++) {
8028 if (TYPE(ptr[i]) != T_ARRAY)
8029 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8030 len2 = RARRAY_LEN(ptr[i]);
8031 if (len2 < mol->natoms)
8032 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8033 ptr2 = RARRAY_PTR(ptr[i]);
8034 for (j = 0; j < mol->natoms; j++)
8035 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8039 for (i = 0; i < count; i++) {
8040 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8041 vp[i * mol->natoms + j] = ap->r;
8047 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8048 ptr = RARRAY_PTR(cells);
8049 for (i = 0; i < count; i++) {
8050 if (TYPE(ptr[i]) != T_ARRAY)
8051 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8052 len2 = RARRAY_LEN(ptr[i]);
8054 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8055 ptr2 = RARRAY_PTR(ptr[i]);
8056 for (j = 0; j < 4; j++)
8057 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8060 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8061 IntGroupRelease(ig);
8065 return (ival >= 0 ? val : Qnil);
8070 * create_frame(coordinates = nil) -> Integer
8071 * create_frames(coordinates = nil) -> Integer
8073 * Same as molecule.insert_frames(nil, coordinates).
8076 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8079 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8081 return s_Molecule_InsertFrames(3, vals, self);
8086 * remove_frames(IntGroup, wantCoordinates = false)
8088 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8089 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8090 * removed frames is returned if operation is successful.
8093 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8100 Data_Get_Struct(self, Molecule, mol);
8101 rb_scan_args(argc, argv, "11", &val, &flag);
8102 ig = IntGroupFromValue(val);
8103 count = IntGroupGetCount(ig);
8105 /* Create return value before removing frames */
8110 retval = rb_ary_new2(count);
8111 for (i = 0; i < count; i++) {
8112 n = IntGroupGetNthPoint(ig, i);
8113 coords = rb_ary_new2(mol->natoms);
8114 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8115 if (n < ap->nframes && n != mol->cframe)
8118 rb_ary_push(coords, ValueFromVector(&v));
8120 rb_ary_push(retval, coords);
8122 } else retval = Qtrue;
8123 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8130 * each_frame {|n| ...}
8132 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8133 * the frame number. After completion, the original frame number is restored.
8136 s_Molecule_EachFrame(VALUE self)
8138 int i, cframe, nframes;
8140 Data_Get_Struct(self, Molecule, mol);
8141 cframe = mol->cframe;
8142 nframes = MoleculeGetNumberOfFrames(mol);
8144 for (i = 0; i < nframes; i++) {
8145 MoleculeSelectFrame(mol, i, 1);
8146 rb_yield(INT2NUM(i));
8148 MoleculeSelectFrame(mol, cframe, 1);
8155 * get_coord_from_frame(index, group = nil)
8157 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8158 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8159 * copied; now they are always copied)
8162 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8165 VALUE ival, gval, cval;
8166 Int index, i, j, n, nn;
8168 IntGroupIterator iter;
8171 Data_Get_Struct(self, Molecule, mol);
8172 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8174 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8175 index = NUM2INT(rb_Integer(ival));
8176 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8178 rb_raise(rb_eMolbyError, "No frame is present");
8180 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8183 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8185 ig = s_Molecule_AtomGroupFromValue(self, gval);
8187 n = IntGroupGetCount(ig);
8189 vp = (Vector *)calloc(sizeof(Vector), n);
8190 IntGroupIteratorInit(ig, &iter);
8193 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8194 ap = ATOM_AT_INDEX(mol->atoms, i);
8195 if (index < ap->nframes) {
8196 vp[j] = ap->frames[index];
8204 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8206 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8207 vp = mol->frame_cells + index * 4;
8208 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8210 IntGroupIteratorRelease(&iter);
8212 /* Copy the extra properties */
8213 IntGroupRelease(ig);
8214 for (i = 0; i < mol->nmolprops; i++) {
8215 Double *dp = (Double *)malloc(sizeof(Double));
8217 IntGroupAdd(ig, mol->cframe, 1);
8218 *dp = mol->molprops[i].propvals[index];
8219 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8221 IntGroupRelease(ig);
8229 * reorder_frames(old_indices)
8231 * Reorder the frames. The argument is an array of integers that specify the 'old'
8232 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8233 * same as the old frames 2/0/1, respectively.
8234 * The argument must have the same number of integers as the number of frames.
8237 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8240 Int *ip, *ip2, i, n, nframes;
8241 Data_Get_Struct(self, Molecule, mol);
8242 aval = rb_ary_to_ary(aval);
8243 nframes = MoleculeGetNumberOfFrames(mol);
8244 if (RARRAY_LEN(aval) != nframes)
8245 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8246 ip2 = (Int *)calloc(sizeof(Int), nframes);
8247 ip = (Int *)calloc(sizeof(Int), nframes);
8248 for (i = 0; i < nframes; i++) {
8249 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8250 if (n < 0 || n >= nframes || ip2[n] != 0) {
8253 if (n < 0 || n >= nframes)
8254 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8256 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8262 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8267 #pragma mark ------ Fragments ------
8271 * fragment(n1, *exatoms) -> IntGroup
8272 * fragment(group, *exatoms) -> IntGroup
8274 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8275 * those atoms will not be counted during the search.
8278 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8281 IntGroup *baseg, *ig, *exatoms;
8283 volatile VALUE nval, exval;
8284 Data_Get_Struct(self, Molecule, mol);
8285 rb_scan_args(argc, argv, "1*", &nval, &exval);
8286 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8288 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8290 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8292 if (RARRAY_LEN(exval) == 0) {
8295 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8296 Data_Get_Struct(exval, IntGroup, exatoms);
8298 if (baseg == NULL) {
8299 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8301 IntGroupIterator iter;
8302 IntGroupIteratorInit(baseg, &iter);
8303 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8306 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8308 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8310 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8312 IntGroupAddIntGroup(ig, subg);
8313 IntGroupRelease(subg);
8318 IntGroupIteratorRelease(&iter);
8319 IntGroupRelease(baseg);
8322 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8323 nval = ValueFromIntGroup(ig);
8324 IntGroupRelease(ig);
8330 * fragments(exclude = nil)
8332 * Returns the fragments as an array of IntGroups.
8333 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8334 * in defining the fragment.
8337 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8340 IntGroup *ag, *fg, *eg;
8341 VALUE gval, exval, retval;
8342 Data_Get_Struct(self, Molecule, mol);
8345 if (mol->natoms == 0)
8346 return rb_ary_new();
8347 rb_scan_args(argc, argv, "01", &exval);
8351 eg = IntGroupFromValue(exval);
8352 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8354 IntGroupRemoveIntGroup(ag, eg);
8355 retval = rb_ary_new();
8356 while (IntGroupGetCount(ag) > 0) {
8357 int n = IntGroupGetNthPoint(ag, 0);
8358 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8360 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8361 gval = ValueFromIntGroup(fg);
8362 rb_ary_push(retval, gval);
8363 IntGroupRemoveIntGroup(ag, fg);
8364 IntGroupRelease(fg);
8366 IntGroupRelease(ag);
8368 IntGroupRelease(eg);
8374 * each_fragment(exclude = nil) {|group| ...}
8376 * Execute the block, with the IntGroup object for each fragment as the argument.
8377 * Atoms or bonds should not be added or removed during the execution of the block.
8378 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8379 * in defining the fragment.
8382 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8385 IntGroup *ag, *fg, *eg;
8387 Data_Get_Struct(self, Molecule, mol);
8388 if (mol == NULL || mol->natoms == 0)
8390 rb_scan_args(argc, argv, "01", &exval);
8394 eg = IntGroupFromValue(exval);
8395 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8397 IntGroupRemoveIntGroup(ag, eg);
8398 while (IntGroupGetCount(ag) > 0) {
8399 int n = IntGroupGetNthPoint(ag, 0);
8400 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8402 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8403 gval = ValueFromIntGroup(fg);
8405 IntGroupRemoveIntGroup(ag, fg);
8406 IntGroupRelease(fg);
8408 IntGroupRelease(ag);
8410 IntGroupRelease(eg);
8416 * detachable?(group) -> [n1, n2]
8418 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8419 * of the molecule via only one bond. If it is, then the indices of the atoms
8420 * belonging to the bond is returned, the first element being the atom included
8421 * in the fragment. Otherwise, Qnil is returned.
8424 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8430 Data_Get_Struct(self, Molecule, mol);
8431 ig = s_Molecule_AtomGroupFromValue(self, gval);
8432 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8433 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8434 } else retval = Qnil;
8435 IntGroupRelease(ig);
8441 * bonds_on_border(group = selection) -> Array of Array of two Integers
8443 * Returns an array of bonds that connect an atom in the group and an atom out
8444 * of the group. The first atom in the bond always belongs to the group. If no
8445 * such bonds are present, an empty array is returned.
8448 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8453 Data_Get_Struct(self, Molecule, mol);
8454 rb_scan_args(argc, argv, "01", &gval);
8456 ig = MoleculeGetSelection(mol);
8460 ig = s_Molecule_AtomGroupFromValue(self, gval);
8462 retval = rb_ary_new();
8465 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8467 IntGroupIterator iter;
8469 IntGroupIteratorInit(bg, &iter);
8470 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8471 /* The atoms at the border */
8473 n1 = mol->bonds[i * 2];
8474 n2 = mol->bonds[i * 2 + 1];
8475 if (IntGroupLookupPoint(ig, n1) < 0) {
8479 if (IntGroupLookupPoint(ig, n1) < 0)
8480 continue; /* Actually this is an internal error */
8482 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8484 IntGroupIteratorRelease(&iter);
8486 IntGroupRelease(bg);
8487 IntGroupRelease(ig);
8491 /* Calculate the transform that moves the current coordinates to the reference
8492 coordinates with least displacements. */
8494 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8502 Double eigen_val[3];
8503 Vector eigen_vec[3];
8505 IntGroupIterator iter;
8507 natoms = mol->natoms;
8509 IntGroupIteratorInit(ig, &iter);
8511 /* Calculate the weighted center */
8515 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8516 ap1 = ATOM_AT_INDEX(ap, in);
8517 w1 = (weights != NULL ? weights[i] : ap1->weight);
8518 VecScaleInc(org1, ap1->r, w1);
8519 VecScaleInc(org2, ref[i], w1);
8523 VecScaleSelf(org1, w);
8524 VecScaleSelf(org2, w);
8526 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8527 /* Matrix to diagonalize = R * tR */
8528 memset(r, 0, sizeof(Mat33));
8529 memset(q, 0, sizeof(Mat33));
8530 memset(u, 0, sizeof(Mat33));
8532 IntGroupIteratorReset(&iter);
8533 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8535 ap1 = ATOM_AT_INDEX(ap, in);
8536 w1 = (weights != NULL ? weights[i] : ap1->weight);
8538 VecSub(v1, ap1->r, org1);
8539 VecSub(v2, ref[i], org2);
8540 r[0] += w1 * v1.x * v2.x;
8541 r[1] += w1 * v1.y * v2.x;
8542 r[2] += w1 * v1.z * v2.x;
8543 r[3] += w1 * v1.x * v2.y;
8544 r[4] += w1 * v1.y * v2.y;
8545 r[5] += w1 * v1.z * v2.y;
8546 r[6] += w1 * v1.x * v2.z;
8547 r[7] += w1 * v1.y * v2.z;
8548 r[8] += w1 * v1.z * v2.z;
8551 for (i = 0; i < 9; i++)
8553 for (i = 0; i < 3; i++) {
8554 for (j = 0; j < 3; j++) {
8555 for (k = 0; k < 3; k++) {
8556 q[i+j*3] += r[i+k*3] * r[j+k*3];
8561 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8562 IntGroupIteratorRelease(&iter);
8563 return -1.0; /* Cannot determine the eigenvector */
8566 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8567 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8568 MatrixTranspose(r, r);
8569 for (i = 0; i < 3; i++) {
8570 MatrixVec(&s[i], r, &eigen_vec[i]);
8571 w1 = 1.0 / sqrt(eigen_val[i]);
8572 VecScaleSelf(s[i], w1);
8574 for (k = 0; k < 3; k++) {
8575 u[0] += s[k].x * eigen_vec[k].x;
8576 u[1] += s[k].y * eigen_vec[k].x;
8577 u[2] += s[k].z * eigen_vec[k].x;
8578 u[3] += s[k].x * eigen_vec[k].y;
8579 u[4] += s[k].y * eigen_vec[k].y;
8580 u[5] += s[k].z * eigen_vec[k].y;
8581 u[6] += s[k].x * eigen_vec[k].z;
8582 u[7] += s[k].y * eigen_vec[k].z;
8583 u[8] += s[k].z * eigen_vec[k].z;
8586 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8587 MatrixVec(&org1, u, &org1);
8589 for (i = 0; i < 9; i++)
8595 /* Calculate rmsd */
8596 IntGroupIteratorReset(&iter);
8598 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8600 ap1 = ATOM_AT_INDEX(ap, in);
8601 TransformVec(&tv, trans, &ap1->r);
8603 w += VecLength2(tv);
8606 IntGroupIteratorRelease(&iter);
8612 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8614 * Calculate the transform to fit the given group to the set of reference coordinates.
8615 * The reference coordinates ref is given as either a frame number, an array of
8616 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8617 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8618 * Return values are the transform (that converts the present coordinates to the
8619 * target coordinates) and root mean square deviation (without weight).
8622 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8626 VALUE gval, rval, wval;
8628 IntGroupIterator iter;
8629 int nn, errno, i, j, in, status;
8631 Double *weights, dval[3];
8634 Data_Get_Struct(self, Molecule, mol);
8635 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8637 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8639 ig = s_Molecule_AtomGroupFromValue(self, gval);
8640 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8641 IntGroupRelease(ig);
8642 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8644 ref = (Vector *)calloc(sizeof(Vector), nn);
8645 weights = (Double *)calloc(sizeof(Double), nn);
8646 IntGroupIteratorInit(ig, &iter);
8647 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8648 int fn = NUM2INT(rb_Integer(rval));
8649 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8654 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8655 ap = ATOM_AT_INDEX(mol->atoms, in);
8656 if (fn < ap->nframes)
8657 ref[i] = ap->frames[fn];
8658 else ref[i] = ap->r;
8660 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8661 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8662 if (m->row * m->column < nn * 3) {
8666 for (i = 0; i < nn; i++) {
8667 ref[i].x = m->data[i * 3];
8668 ref[i].y = m->data[i * 3 + 1];
8669 ref[i].z = m->data[i * 3 + 2];
8673 rval = rb_protect(rb_ary_to_ary, rval, &status);
8678 if (RARRAY_LEN(rval) < nn) {
8682 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8683 /* Array of 3*nn numbers */
8684 if (RARRAY_LEN(rval) < nn * 3) {
8688 for (i = 0; i < nn; i++) {
8689 for (j = 0; j < 3; j++) {
8690 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8695 dval[j] = NUM2DBL(aval);
8702 /* Array of nn Vector3Ds or Arrays */
8703 for (i = 0; i < nn; i++) {
8704 aval = (RARRAY_PTR(rval))[i];
8705 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8706 VectorFromValue(aval, &ref[i]);
8708 aval = rb_protect(rb_ary_to_ary, aval, &status);
8713 if (RARRAY_LEN(aval) < 3) {
8718 for (j = 0; j < 3; j++) {
8719 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8724 dval[j] = NUM2DBL(aaval);
8734 /* Use atomic weights */
8735 IntGroupIteratorReset(&iter);
8736 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8737 ap = ATOM_AT_INDEX(mol->atoms, in);
8738 weights[i] = ap->weight;
8741 wval = rb_protect(rb_ary_to_ary, wval, &status);
8746 if (RARRAY_LEN(wval) < nn) {
8750 for (i = 0; i < nn; i++) {
8751 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8756 weights[i] = NUM2DBL(wwval);
8759 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8766 IntGroupIteratorRelease(&iter);
8770 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8771 } else if (errno == 1) {
8772 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8773 } else if (errno == 2) {
8774 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8775 } else if (errno == 3) {
8776 rb_jump_tag(status);
8777 } else if (errno == 4) {
8778 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8779 } else if (errno == 5) {
8780 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8781 } else if (errno == 6) {
8782 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8784 return Qnil; /* Not reached */
8787 #pragma mark ------ Screen Display ------
8793 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8796 s_Molecule_Display(VALUE self)
8799 Data_Get_Struct(self, Molecule, mol);
8800 if (mol->mview != NULL)
8801 MainViewCallback_display(mol->mview);
8809 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8812 s_Molecule_MakeFront(VALUE self)
8815 Data_Get_Struct(self, Molecule, mol);
8816 if (mol->mview != NULL)
8817 MainViewCallback_makeFront(mol->mview);
8823 * update_enabled? -> bool
8825 * Returns true if screen update is enabled; otherwise no.
8828 s_Molecule_UpdateEnabled(VALUE self)
8831 Data_Get_Struct(self, Molecule, mol);
8832 if (mol->mview != NULL && !mol->mview->freezeScreen)
8839 * update_enabled = bool
8841 * Enable or disable screen update. This is effective for automatic update on modification.
8842 * Explicit call to molecule.display() always updates the screen.
8845 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8848 Data_Get_Struct(self, Molecule, mol);
8849 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8850 if (mol->mview != NULL)
8851 mol->mview->freezeScreen = (val == Qfalse);
8859 * show_unitcell(bool)
8860 * show_unitcell = bool
8862 * Set the flag whether to show the unit cell. If no argument is given, the
8863 * current flag is returned.
8866 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8869 Data_Get_Struct(self, Molecule, mol);
8870 if (mol->mview == NULL)
8873 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8874 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8876 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8882 * show_hydrogens(bool)
8883 * show_hydrogens = bool
8885 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8886 * current flag is returned.
8889 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8892 Data_Get_Struct(self, Molecule, mol);
8893 if (mol->mview == NULL)
8896 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8897 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8899 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8905 * show_dummy_atoms(bool)
8906 * show_dummy_atoms = bool
8908 * Set the flag whether to show the dummy atoms. If no argument is given, the
8909 * current flag is returned.
8912 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8915 Data_Get_Struct(self, Molecule, mol);
8916 if (mol->mview == NULL)
8919 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8920 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8922 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8928 * show_expanded(bool)
8929 * show_expanded = bool
8931 * Set the flag whether to show the expanded atoms. If no argument is given, the
8932 * current flag is returned.
8935 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8938 Data_Get_Struct(self, Molecule, mol);
8939 if (mol->mview == NULL)
8942 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8943 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8945 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8951 * show_ellipsoids(bool)
8952 * show_ellipsoids = bool
8954 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8955 * current flag is returned.
8958 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8961 Data_Get_Struct(self, Molecule, mol);
8962 if (mol->mview == NULL)
8965 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8966 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8968 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8973 * is_atom_visible(index) -> Boolean
8975 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8976 * as well as the molecule attributes (showHydrogens, etc.)
8979 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8984 Data_Get_Struct(self, Molecule, mol);
8985 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8986 if (idx < 0 || idx >= mol->natoms)
8988 ap = ATOM_AT_INDEX(mol->atoms, idx);
8989 if (mol->mview != NULL) {
8990 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8992 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8994 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8997 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9002 * hidden_atoms -> IntGroup
9004 * Returns the currently hidden atoms.
9007 s_Molecule_HiddenAtoms(VALUE self)
9009 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9010 return Qnil; /* Not reached */
9015 * set_hidden_atoms(IntGroup)
9016 * self.hidden_atoms = IntGroup
9018 * Hide the specified atoms. This operation is _not_ undoable.
9021 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9023 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9024 return Qnil; /* Not reached */
9029 * show_graphite -> Integer
9030 * show_graphite = Integer
9031 * show_graphite = boolean
9033 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9034 * number of rings to display for each direction.
9035 * If the argument is boolean, only the show/hide flag is set.
9038 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9041 Data_Get_Struct(self, Molecule, mol);
9042 if (mol->mview == NULL)
9045 if (argv[0] == Qnil || argv[0] == Qfalse)
9046 mol->mview->showGraphiteFlag = 0;
9047 else if (argv[0] == Qtrue)
9048 mol->mview->showGraphiteFlag = 1;
9050 int n = NUM2INT(rb_Integer(argv[0]));
9052 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9053 mol->mview->showGraphite = n;
9055 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9057 return INT2NUM(mol->mview->showGraphite);
9062 * show_graphite? -> boolean
9064 * Return whether the graphite is set visible or not.
9067 s_Molecule_ShowGraphiteFlag(VALUE self)
9070 Data_Get_Struct(self, Molecule, mol);
9071 if (mol->mview == NULL)
9073 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9078 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9079 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9080 * show_periodic_image = boolean
9082 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9083 * set but no visual effects are observed.
9084 * If the argument is boolean, only the show/hide flag is modified.
9087 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9093 Data_Get_Struct(self, Molecule, mol);
9094 if (mol->mview == NULL)
9096 rb_scan_args(argc, argv, "01", &val);
9098 /* Change current settings */
9099 if (val == Qnil || val == Qfalse)
9100 mol->mview->showPeriodicImageFlag = 0;
9101 else if (val == Qtrue)
9102 mol->mview->showPeriodicImageFlag = 1;
9104 val = rb_ary_to_ary(val);
9105 for (i = 0; i < 6; i++) {
9106 if (i < RARRAY_LEN(val))
9107 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9109 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9110 rb_raise(rb_eMolbyError, "bad arguments");
9111 for (i = 0; i < 6; i++)
9112 mol->mview->showPeriodicImage[i] = ival[i];
9114 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9117 for (i = 0; i < 6; i++)
9118 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9124 * show_periodic_image? -> boolean
9126 * Return whether the periodic images are set to visible or not. This flag is
9127 * independent from the show_periodic_image settings.
9130 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9133 Data_Get_Struct(self, Molecule, mol);
9134 if (mol->mview == NULL)
9136 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9141 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9142 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9143 * show_rotation_center = boolean
9145 * Set to show the rotation center of the screen.
9146 * If the argument is boolean, only the show/hide flag is modified.
9149 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9152 Data_Get_Struct(self, Molecule, mol);
9153 if (mol->mview == NULL)
9156 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9157 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9159 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9168 * Set the flag whether to draw the model in line mode. If no argument is given, the
9169 * current flag is returned.
9172 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9175 Data_Get_Struct(self, Molecule, mol);
9176 if (mol->mview == NULL)
9179 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9180 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9182 return (mol->mview->lineMode ? Qtrue : Qfalse);
9187 * atom_radius = float
9190 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9192 * If no argument is given, the current value is returned.
9195 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9198 Data_Get_Struct(self, Molecule, mol);
9199 if (mol->mview == NULL)
9202 double rad = NUM2DBL(rb_Float(argv[0]));
9204 mol->mview->atomRadius = rad;
9205 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9209 return rb_float_new(mol->mview->atomRadius);
9214 * bond_radius = float
9217 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9218 * If no argument is given, the current value is returned.
9221 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9224 Data_Get_Struct(self, Molecule, mol);
9225 if (mol->mview == NULL)
9228 double rad = NUM2DBL(rb_Float(argv[0]));
9230 mol->mview->bondRadius = rad;
9231 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9235 return rb_float_new(mol->mview->bondRadius);
9240 * atom_resolution = integer
9243 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9244 * (Default = 12; minimum = 6)
9245 * If no argument is given, the current value is returned.
9248 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9251 Data_Get_Struct(self, Molecule, mol);
9252 if (mol->mview == NULL)
9255 int res = NUM2INT(rb_Integer(argv[0]));
9257 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9258 mol->mview->atomResolution = res;
9259 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9260 return INT2NUM(res);
9262 return INT2NUM(mol->mview->atomResolution);
9267 * bond_resolution = integer
9270 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9271 * (Default = 8; minimum = 4)
9272 * If no argument is given, the current value is returned.
9275 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9278 Data_Get_Struct(self, Molecule, mol);
9279 if (mol->mview == NULL)
9282 int res = NUM2INT(rb_Integer(argv[0]));
9284 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9285 mol->mview->bondResolution = res;
9286 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9287 return INT2NUM(res);
9289 return INT2NUM(mol->mview->bondResolution);
9296 * Resize the model drawing to fit in the window.
9299 s_Molecule_ResizeToFit(VALUE self)
9302 Data_Get_Struct(self, Molecule, mol);
9303 if (mol->mview != NULL)
9304 MainView_resizeToFit(mol->mview);
9310 * get_view_rotation -> [[ax, ay, az], angle]
9312 * Get the current rotation for the view. Angle is in degree, not radian.
9315 s_Molecule_GetViewRotation(VALUE self)
9320 Data_Get_Struct(self, Molecule, mol);
9321 if (mol->mview == NULL)
9323 TrackballGetRotate(mol->mview->track, f);
9324 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9328 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9333 * get_view_scale -> float
9335 * Get the current scale for the view.
9338 s_Molecule_GetViewScale(VALUE self)
9341 Data_Get_Struct(self, Molecule, mol);
9342 if (mol->mview == NULL)
9344 return rb_float_new(TrackballGetScale(mol->mview->track));
9349 * get_view_center -> Vector
9351 * Get the current center point of the view.
9354 s_Molecule_GetViewCenter(VALUE self)
9359 Data_Get_Struct(self, Molecule, mol);
9360 if (mol->mview == NULL)
9362 TrackballGetTranslate(mol->mview->track, f);
9363 v.x = -f[0] * mol->mview->dimension;
9364 v.y = -f[1] * mol->mview->dimension;
9365 v.z = -f[2] * mol->mview->dimension;
9366 return ValueFromVector(&v);
9371 * set_view_rotation([ax, ay, az], angle) -> self
9373 * Set the current rotation for the view. Angle is in degree, not radian.
9376 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9381 Data_Get_Struct(self, Molecule, mol);
9382 if (mol->mview == NULL)
9384 VectorFromValue(aval, &v);
9385 if (NormalizeVec(&v, &v))
9386 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9390 f[0] = -NUM2DBL(rb_Float(angval));
9391 TrackballSetRotate(mol->mview->track, f);
9392 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9398 * set_view_scale(scale) -> self
9400 * Set the current scale for the view.
9403 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9406 Data_Get_Struct(self, Molecule, mol);
9407 if (mol->mview == NULL)
9409 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9410 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9416 * set_view_center(vec) -> self
9418 * Set the current center point of the view.
9421 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9426 Data_Get_Struct(self, Molecule, mol);
9427 if (mol->mview == NULL)
9429 VectorFromValue(aval, &v);
9430 f[0] = -v.x / mol->mview->dimension;
9431 f[1] = -v.y / mol->mview->dimension;
9432 f[2] = -v.z / mol->mview->dimension;
9433 TrackballSetTranslate(mol->mview->track, f);
9434 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9440 * set_background_color(red, green, blue)
9442 * Set the background color of the model window.
9445 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9448 Data_Get_Struct(self, Molecule, mol);
9449 if (mol->mview != NULL) {
9450 VALUE rval, gval, bval;
9451 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9452 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9459 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9461 * Export the current graphic to a PNG or TIF file (determined by the extension).
9462 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9463 * If either width or height is not specified, then the screen width/height is used instead.
9466 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9469 VALUE fval, sval, bval, wval, hval;
9472 int bg_color, width, height;
9473 Data_Get_Struct(self, Molecule, mol);
9474 if (mol->mview == NULL)
9475 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9476 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9477 fname = FileStringValuePtr(fval);
9480 else scale = NUM2DBL(rb_Float(sval));
9483 else bg_color = NUM2INT(rb_Integer(bval));
9486 else width = NUM2INT(rb_Integer(wval));
9489 else height = NUM2INT(rb_Integer(hval));
9490 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9495 #pragma mark ------ Graphics ------
9498 s_CalculateGraphicNormals(MainViewGraphic *gp)
9502 if (gp == NULL || gp->npoints < 3)
9504 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9505 v1.x = gp->points[3] - gp->points[0];
9506 v1.y = gp->points[4] - gp->points[1];
9507 v1.z = gp->points[5] - gp->points[2];
9508 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9509 for (i = 2; i < gp->npoints; i++) {
9510 v2.x = gp->points[i * 3] - gp->points[0];
9511 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9512 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9513 VecCross(v3, v1, v2);
9514 NormalizeVec(&v3, &v3);
9515 gp->normals[i * 3] = v3.x;
9516 gp->normals[i * 3 + 1] = v3.y;
9517 gp->normals[i * 3 + 2] = v3.z;
9520 /* normals[0] = average of all nv[i] (i=2..n-1) */
9522 for (i = 2; i < gp->npoints; i++) {
9523 v1.x += gp->normals[i * 3];
9524 v1.y += gp->normals[i * 3 + 1];
9525 v1.z += gp->normals[i * 3 + 2];
9527 NormalizeVec(&v1, &v1);
9528 gp->normals[0] = v1.x;
9529 gp->normals[1] = v1.y;
9530 gp->normals[2] = v1.z;
9531 /* normals[1] = nv[2].normalize */
9532 v2.x = gp->normals[6];
9533 v2.y = gp->normals[7];
9534 v2.z = gp->normals[8];
9535 NormalizeVec(&v1, &v2);
9536 gp->normals[3] = v1.x;
9537 gp->normals[4] = v1.y;
9538 gp->normals[5] = v1.z;
9539 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9540 for (i = 2; i < gp->npoints; i++) {
9541 if (i == gp->npoints - 1)
9544 v3.x = gp->normals[i * 3 + 3];
9545 v3.y = gp->normals[i * 3 + 4];
9546 v3.z = gp->normals[i * 3 + 5];
9549 NormalizeVec(&v1, &v2);
9550 gp->normals[i * 3] = v1.x;
9551 gp->normals[i * 3 + 1] = v1.y;
9552 gp->normals[i * 3 + 2] = v1.z;
9559 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9561 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9562 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9563 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9564 * points: an array of Vectors
9568 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9574 VALUE kval, cval, pval, fval, ival;
9575 Data_Get_Struct(self, Molecule, mol);
9576 if (mol->mview == NULL)
9577 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9578 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9579 idx = NUM2INT(rb_Integer(ival));
9581 idx = mol->mview->ngraphics;
9582 else if (idx < 0 || idx > mol->mview->ngraphics)
9583 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9584 memset(&g, 0, sizeof(g));
9586 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9587 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9589 kval = rb_obj_as_string(kval);
9590 p = StringValuePtr(kval);
9591 if (strcmp(p, "line") == 0)
9592 g.kind = kMainViewGraphicLine;
9593 else if (strcmp(p, "poly") == 0)
9594 g.kind = kMainViewGraphicPoly;
9595 else if (strcmp(p, "cylinder") == 0)
9596 g.kind = kMainViewGraphicCylinder;
9597 else if (strcmp(p, "cone") == 0)
9598 g.kind = kMainViewGraphicCone;
9599 else if (strcmp(p, "ellipsoid") == 0)
9600 g.kind = kMainViewGraphicEllipsoid;
9601 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9603 g.closed = (RTEST(fval) ? 1 : 0);
9604 cval = rb_ary_to_ary(cval);
9605 n = RARRAY_LEN(cval);
9606 if (n < 3 || n >= 5)
9607 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9610 for (i = 0; i < n; i++)
9611 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9612 pval = rb_ary_to_ary(pval);
9613 n = RARRAY_LEN(pval);
9614 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9616 rb_raise(rb_eArgError, "no control points are given");
9618 case kMainViewGraphicLine:
9620 rb_raise(rb_eArgError, "the line object must have at least two control points");
9622 case kMainViewGraphicPoly:
9624 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9626 case kMainViewGraphicCylinder:
9627 case kMainViewGraphicCone:
9629 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9632 case kMainViewGraphicEllipsoid:
9636 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9639 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9640 for (i = 0; i < n; i++) {
9642 VALUE rval = RARRAY_PTR(pval)[i];
9644 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9645 /* The float argument can also be given as a vector (for simplify undo registration) */
9646 VectorFromValue(rval, &v);
9648 v.x = NUM2DBL(rb_Float(rval));
9652 VectorFromValue(rval, &v);
9654 g.points[i * 3] = v.x;
9655 g.points[i * 3 + 1] = v.y;
9656 g.points[i * 3 + 2] = v.z;
9658 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9660 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9661 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9662 g.points[7] = g.points[11] = g.points[3];
9664 if (g.kind == kMainViewGraphicPoly) {
9665 /* Calculate normals */
9666 s_CalculateGraphicNormals(&g);
9668 MainView_insertGraphic(mol->mview, idx, &g);
9673 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9674 MolActionCallback_registerUndo(mol, act);
9675 MolActionRelease(act);
9678 return INT2NUM(idx);
9683 * create_graphic(kind, color, points, fill = nil) -> integer
9685 * Create a new graphic object. The arguments are similar as insert_graphic.
9688 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9691 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9692 args[0] = INT2NUM(-1);
9693 return s_Molecule_InsertGraphic(argc + 1, args, self);
9698 * remove_graphic(index) -> integer
9700 * Remove a graphic object.
9703 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9707 Data_Get_Struct(self, Molecule, mol);
9708 if (mol->mview == NULL)
9709 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9710 i = NUM2INT(rb_Integer(ival));
9711 if (i < 0 || i >= mol->mview->ngraphics)
9712 rb_raise(rb_eArgError, "graphic index is out of range");
9714 /* Prepare data for undo */
9715 MainViewGraphic *gp;
9720 gp = mol->mview->graphics + i;
9721 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9722 for (n = 0; n < gp->npoints; n++) {
9723 vp[n].x = gp->points[n * 3];
9724 vp[n].y = gp->points[n * 3 + 1];
9725 vp[n].z = gp->points[n * 3 + 2];
9727 col[0] = gp->rgba[0];
9728 col[1] = gp->rgba[1];
9729 col[2] = gp->rgba[2];
9730 col[3] = gp->rgba[3];
9731 if (gp->visible == 0) {
9732 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9733 MolActionCallback_registerUndo(mol, act);
9734 MolActionRelease(act);
9736 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9737 MolActionCallback_registerUndo(mol, act);
9739 MolActionRelease(act);
9741 MainView_removeGraphic(mol->mview, i);
9747 * ngraphics -> integer
9749 * Get the number of graphic objects.
9752 s_Molecule_NGraphics(VALUE self)
9755 Data_Get_Struct(self, Molecule, mol);
9756 if (mol->mview == NULL)
9757 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9758 return INT2NUM(mol->mview->ngraphics);
9763 * get_graphic_point(graphic_index, point_index) -> value
9764 * get_graphic_points(graphic_index) -> values
9766 * Get the point_index-th control point of graphic_index-th graphic object.
9767 * Get an array of all control points with the given values.
9771 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9773 MainViewGraphic *gp;
9778 Data_Get_Struct(self, Molecule, mol);
9779 if (mol->mview == NULL)
9780 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9781 rb_scan_args(argc, argv, "11", &gval, &pval);
9782 index = NUM2INT(rb_Integer(gval));
9783 if (index < 0 || index >= mol->mview->ngraphics)
9784 rb_raise(rb_eArgError, "the graphic index is out of range");
9785 gp = mol->mview->graphics + index;
9787 pindex = NUM2INT(rb_Integer(pval));
9788 if (pindex < 0 || pindex >= gp->npoints)
9789 rb_raise(rb_eArgError, "the point index is out of range");
9790 v.x = gp->points[pindex * 3];
9791 v.y = gp->points[pindex * 3 + 1];
9792 v.z = gp->points[pindex * 3 + 2];
9793 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9794 return rb_float_new(v.x);
9796 return ValueFromVector(&v);
9799 pval = rb_ary_new();
9800 for (pindex = 0; pindex < gp->npoints; pindex++) {
9801 v.x = gp->points[pindex * 3];
9802 v.y = gp->points[pindex * 3 + 1];
9803 v.z = gp->points[pindex * 3 + 2];
9804 rb_ary_push(pval, ValueFromVector(&v));
9812 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9813 * set_graphic_points(graphic_index, new_values) -> new_values
9815 * Change the point_index-th control point of graphic_index-th graphic object.
9816 * Replace the control points with the given values.
9820 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9822 MainViewGraphic *gp;
9826 VALUE gval, pval, nval;
9828 Data_Get_Struct(self, Molecule, mol);
9829 if (mol->mview == NULL)
9830 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9831 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9832 index = NUM2INT(rb_Integer(gval));
9833 if (index < 0 || index >= mol->mview->ngraphics)
9834 rb_raise(rb_eArgError, "the graphic index is out of range");
9835 gp = mol->mview->graphics + index;
9837 pindex = NUM2INT(rb_Integer(pval));
9838 if (pindex < 0 || pindex >= gp->npoints)
9839 rb_raise(rb_eArgError, "the point index is out of range");
9840 v0.x = gp->points[pindex * 3];
9841 v0.y = gp->points[pindex * 3 + 1];
9842 v0.z = gp->points[pindex * 3 + 2];
9843 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9844 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9845 v.x = NUM2DBL(rb_Float(nval));
9847 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9848 v.x = NUM2DBL(rb_Float(nval));
9850 gp->points[7] = gp->points[11] = v.x;
9851 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9852 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9855 v.x = kInvalidFloat;
9857 } else VectorFromValue(nval, &v);
9859 gp->points[pindex * 3] = v.x;
9860 gp->points[pindex * 3 + 1] = v.y;
9861 gp->points[pindex * 3 + 2] = v.z;
9862 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9866 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9867 for (pindex = 0; pindex < gp->npoints; pindex++) {
9868 vp[pindex].x = gp->points[pindex * 3];
9869 vp[pindex].y = gp->points[pindex * 3 + 1];
9870 vp[pindex].z = gp->points[pindex * 3 + 2];
9872 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9874 pval = rb_ary_to_ary(pval);
9875 len = RARRAY_LEN(pval);
9876 if (gp->npoints < len) {
9877 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9879 } else if (gp->npoints > len) {
9882 case kMainViewGraphicLine: len2 = 2; break;
9883 case kMainViewGraphicPoly: len2 = 3; break;
9884 case kMainViewGraphicCylinder: len2 = 3; break;
9885 case kMainViewGraphicCone: len2 = 3; break;
9886 case kMainViewGraphicEllipsoid: len2 = 4; break;
9892 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9893 aval = RARRAY_PTR(pval)[pindex];
9894 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9895 v.x = NUM2DBL(rb_Float(aval));
9897 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9898 v.x = NUM2DBL(rb_Float(aval));
9900 gp->points[7] = gp->points[11] = v.x;
9901 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9903 } else VectorFromValue(aval, &v);
9904 gp->points[pindex * 3] = v.x;
9905 gp->points[pindex * 3 + 1] = v.y;
9906 gp->points[pindex * 3 + 2] = v.z;
9909 if (gp->kind == kMainViewGraphicPoly) {
9910 /* Calculate normals */
9911 s_CalculateGraphicNormals(gp);
9913 MolActionCallback_registerUndo(mol, act);
9914 MolActionRelease(act);
9915 MoleculeCallback_notifyModification(mol, 0);
9921 * get_graphic_color(graphic_index) -> value
9923 * Get the color of graphic_index-th graphic object
9926 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9928 MainViewGraphic *gp;
9931 Data_Get_Struct(self, Molecule, mol);
9932 if (mol->mview == NULL)
9933 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9934 index = NUM2INT(rb_Integer(gval));
9935 if (index < 0 || index >= mol->mview->ngraphics)
9936 rb_raise(rb_eArgError, "the graphic index is out of range");
9937 gp = mol->mview->graphics + index;
9938 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]));
9943 * set_graphic_color(graphic_index, new_value) -> new_value
9945 * Change the color of graphic_index-th graphic object
9949 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9951 MainViewGraphic *gp;
9956 Data_Get_Struct(self, Molecule, mol);
9957 if (mol->mview == NULL)
9958 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9959 index = NUM2INT(rb_Integer(gval));
9960 if (index < 0 || index >= mol->mview->ngraphics)
9961 rb_raise(rb_eArgError, "the graphic index is out of range");
9962 gp = mol->mview->graphics + index;
9963 for (i = 0; i < 4; i++)
9965 cval = rb_ary_to_ary(cval);
9966 n = RARRAY_LEN(cval);
9967 if (n != 3 && n != 4)
9968 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9970 for (i = 0; i < n; i++) {
9971 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9975 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9976 MolActionCallback_registerUndo(mol, act);
9977 MolActionRelease(act);
9978 MoleculeCallback_notifyModification(mol, 0);
9984 * show_graphic(graphic_index) -> self
9986 * Enable the visible flag of the graphic_index-th graphic object
9990 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9992 MainViewGraphic *gp;
9995 Data_Get_Struct(self, Molecule, mol);
9996 if (mol->mview == NULL)
9997 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9998 index = NUM2INT(rb_Integer(gval));
9999 if (index < 0 || index >= mol->mview->ngraphics)
10000 rb_raise(rb_eArgError, "the graphic index is out of range");
10001 gp = mol->mview->graphics + index;
10003 MoleculeCallback_notifyModification(mol, 0);
10009 * hide_graphic(graphic_index) -> self
10011 * Disable the visible flag of the graphic_index-th graphic object
10015 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10017 MainViewGraphic *gp;
10020 Data_Get_Struct(self, Molecule, mol);
10021 if (mol->mview == NULL)
10022 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10023 index = NUM2INT(rb_Integer(gval));
10024 if (index < 0 || index >= mol->mview->ngraphics)
10025 rb_raise(rb_eArgError, "the graphic index is out of range");
10026 gp = mol->mview->graphics + index;
10028 MoleculeCallback_notifyModification(mol, 0);
10034 * show_text(string)
10036 * Show the string in the info text box.
10039 s_Molecule_ShowText(VALUE self, VALUE arg)
10042 Data_Get_Struct(self, Molecule, mol);
10043 if (mol->mview != NULL)
10044 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10048 #pragma mark ------ MD Support ------
10052 * md_arena -> MDArena
10054 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10055 * this molecule, a new arena is created.
10058 s_Molecule_MDArena(VALUE self)
10062 Data_Get_Struct(self, Molecule, mol);
10063 if (mol->arena == NULL)
10065 retval = ValueFromMDArena(mol->arena);
10071 * set_parameter_attr(type, index, key, value, src) -> value
10073 * This method is used only internally.
10076 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10078 /* This method is called from MolAction to change a MM parameter attribute. */
10081 ParameterRef *pref;
10083 Data_Get_Struct(self, Molecule, mol);
10084 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10085 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10087 /* This is the special part of this method; it allows modification of the src field. */
10088 /* (ParameterRef#set_attr sets 0 to the src field) */
10089 Data_Get_Struct(pval, ParameterRef, pref);
10090 up = ParameterRefGetPar(pref);
10091 up->bond.src = FIX2INT(sval);
10098 * parameter -> Parameter
10100 * Get the local parameter of this molecule. If not defined, returns nil.
10103 s_Molecule_Parameter(VALUE self)
10106 Data_Get_Struct(self, Molecule, mol);
10107 /* if (mol->par == NULL)
10109 return s_NewParameterValueFromValue(self);
10114 * start_step -> Integer
10116 * Returns the start step (defined by dcd format).
10119 s_Molecule_StartStep(VALUE self)
10122 Data_Get_Struct(self, Molecule, mol);
10123 return INT2NUM(mol->startStep);
10128 * start_step = Integer
10130 * Set the start step (defined by dcd format).
10133 s_Molecule_SetStartStep(VALUE self, VALUE val)
10136 Data_Get_Struct(self, Molecule, mol);
10137 mol->startStep = NUM2INT(rb_Integer(val));
10143 * steps_per_frame -> Integer
10145 * Returns the number of steps between frames (defined by dcd format).
10148 s_Molecule_StepsPerFrame(VALUE self)
10151 Data_Get_Struct(self, Molecule, mol);
10152 return INT2NUM(mol->stepsPerFrame);
10157 * steps_per_frame = Integer
10159 * Set the number of steps between frames (defined by dcd format).
10162 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10165 Data_Get_Struct(self, Molecule, mol);
10166 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10172 * ps_per_step -> Float
10174 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10177 s_Molecule_PsPerStep(VALUE self)
10180 Data_Get_Struct(self, Molecule, mol);
10181 return rb_float_new(mol->psPerStep);
10186 * ps_per_step = Float
10188 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10191 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10194 Data_Get_Struct(self, Molecule, mol);
10195 mol->psPerStep = NUM2DBL(rb_Float(val));
10200 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10202 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.");
10205 #pragma mark ------ MO Handling ------
10209 * selectedMO -> IntGroup
10211 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10212 * is not selected, returns nil. If the MO info table is selected but no MOs
10213 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10216 s_Molecule_SelectedMO(VALUE self)
10221 Data_Get_Struct(self, Molecule, mol);
10222 if (mol->mview == NULL)
10224 ig = MainView_selectedMO(mol->mview);
10227 IntGroupOffset(ig, 1);
10228 val = ValueFromIntGroup(ig);
10229 IntGroupRelease(ig);
10235 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10237 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10238 * If the molecule does not contain a basis set information, then returns nil.
10241 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10244 Vector o, dx, dy, dz;
10247 Int npoints = 80 * 80 * 80;
10248 Data_Get_Struct(self, Molecule, mol);
10249 if (mol->bset == NULL)
10251 rb_scan_args(argc, argv, "01", &nval);
10253 npoints = NUM2INT(rb_Integer(nval));
10254 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10256 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));
10260 s_Cubegen_callback(double progress, void *ref)
10262 MyAppCallback_setProgressValue(progress);
10263 if (MyAppCallback_checkInterrupt())
10270 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10271 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10273 * Calculate the molecular orbital with number mo and create a 'cube' file.
10274 * In the first form, the cube size is estimated from the atomic coordinates. In the
10275 * second form, the cube dimension is explicitly given.
10276 * Returns fname when successful, nil otherwise.
10277 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10278 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10279 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10282 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10284 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10286 Int mono, nx, ny, nz, npoints;
10287 Vector o, dx, dy, dz;
10290 Data_Get_Struct(self, Molecule, mol);
10291 if (mol->bset == NULL)
10292 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10293 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10295 /* Set up parameters */
10296 mono = NUM2INT(rb_Integer(mval));
10297 if (mono < 0 || mono > mol->bset->ncomps)
10298 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);
10300 if (mol->bset->rflag != 0)
10301 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10302 mono += mol->bset->ncomps;
10305 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10306 /* Automatic grid formation */
10308 npoints = NUM2INT(rb_Integer(oval));
10312 else if (npoints < 8)
10313 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10314 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10315 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10319 VectorFromValue(oval, &o);
10320 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10321 VectorFromValue(dxval, &dx);
10323 dx.x = NUM2DBL(rb_Float(dxval));
10326 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10327 VectorFromValue(dyval, &dy);
10329 dy.y = NUM2DBL(rb_Float(dyval));
10332 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10333 VectorFromValue(dzval, &dz);
10335 dz.z = NUM2DBL(rb_Float(dzval));
10338 nx = NUM2INT(rb_Integer(nxval));
10339 ny = NUM2INT(rb_Integer(nyval));
10340 nz = NUM2INT(rb_Integer(nzval));
10341 if (nx <= 0 || ny <= 0 || nz <= 0)
10342 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10343 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10344 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);
10348 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10351 else if (index < 0)
10352 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10354 /* Output to file */
10355 MoleculeCallback_displayName(mol, buf, sizeof buf);
10356 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10358 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10360 /* Discard the cube */
10361 MoleculeClearCubeAtIndex(mol, index);
10369 * Clear the MO surface if present.
10372 s_Molecule_ClearSurface(VALUE self)
10375 Data_Get_Struct(self, Molecule, mol);
10376 if (mol->mcube != NULL)
10377 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10385 * Hide the MO surface if present.
10388 s_Molecule_HideSurface(VALUE self)
10391 Data_Get_Struct(self, Molecule, mol);
10392 if (mol->mcube != NULL) {
10393 mol->mcube->hidden = 1;
10394 MoleculeCallback_notifyModification(mol, 0);
10403 * Show the MO surface if present.
10406 s_Molecule_ShowSurface(VALUE self)
10409 Data_Get_Struct(self, Molecule, mol);
10410 if (mol->mcube != NULL) {
10411 mol->mcube->hidden = 0;
10412 MoleculeCallback_notifyModification(mol, 0);
10419 * create_surface(mo, attr = nil)
10421 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10422 * then it denotes the beta orbital.
10423 * If mo is nil, then the attributes of the current surface are modified.
10425 * :npoints : the approximate number of grid points
10426 * :expand : the scale factor to expand/shrink the display box size for each atom,
10427 * :thres : the threshold for the isovalue surface
10428 * If the molecule does not contain MO information, raises exception.
10431 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10434 Vector o, dx, dy, dz;
10435 Int nmo, nx, ny, nz, i;
10436 Int need_recalc = 0;
10437 VALUE nval, hval, aval;
10442 Data_Get_Struct(self, Molecule, mol);
10443 rb_scan_args(argc, argv, "11", &nval, &hval);
10444 if (mol->bset == NULL)
10445 rb_raise(rb_eMolbyError, "No MO information is given");
10446 if (nval == Qnil) {
10448 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10449 nmo = mol->bset->nmos + 1;
10451 nmo = NUM2INT(rb_Integer(nval));
10452 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10453 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);
10455 nmo = -nmo + mol->bset->ncomps;
10457 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10458 npoints = NUM2INT(rb_Integer(aval));
10460 } else if (mol->mcube != NULL) {
10461 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10462 } else npoints = 80 * 80 * 80;
10463 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10464 expand = NUM2DBL(rb_Float(aval));
10465 } else if (mol->mcube != NULL) {
10466 expand = mol->mcube->expand;
10467 } else expand = 1.0;
10468 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10469 thres = NUM2DBL(rb_Float(aval));
10470 } else if (mol->mcube != NULL) {
10471 thres = mol->mcube->thres;
10472 } else thres = 0.05;
10473 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10474 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10475 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10476 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10477 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10479 for (nx = 0; nx < 2; nx++) {
10480 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10481 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10482 aval = rb_ary_to_ary(aval);
10483 if (RARRAY_LEN(aval) < 3) {
10485 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10487 for (i = 0; i < 4; i++)
10488 d[i] = mol->mcube->c[nx].rgba[i];
10489 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10490 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10491 if (d[i] < 0.0 && d[i] > 1.0)
10494 for (i = 0; i < 4; i++)
10495 mol->mcube->c[nx].rgba[i] = d[i];
10498 if (mol->mcube->expand != expand)
10500 mol->mcube->thres = thres;
10501 mol->mcube->expand = expand;
10503 if (mol->mcube->idn < 0)
10504 return self; /* Only set attributes for now */
10506 nmo = mol->mcube->idn; /* Force recalculation */
10508 if (MoleculeUpdateMCube(mol, nmo) != 0)
10509 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10515 * set_surface_attr(attr = nil)
10517 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10520 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10525 return s_Molecule_CreateSurface(2, args, self);
10532 * Get the number of electrostatic potential info.
10535 s_Molecule_NElpots(VALUE self)
10538 Data_Get_Struct(self, Molecule, mol);
10539 return INT2NUM(mol->nelpots);
10546 * Get the electrostatic potential info at the given index. If present, then the
10547 * return value is [Vector, Float] (position and potential). If not present, then
10551 s_Molecule_Elpot(VALUE self, VALUE ival)
10555 Data_Get_Struct(self, Molecule, mol);
10556 idx = NUM2INT(rb_Integer(ival));
10557 if (idx < 0 || idx >= mol->nelpots)
10559 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10566 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10567 * cube and marching cube information are discarded. This operation is _not_ undoable!
10570 s_Molecule_ClearBasisSet(VALUE self)
10573 Data_Get_Struct(self, Molecule, mol);
10575 if (mol->bset != NULL) {
10576 BasisSetRelease(mol->bset);
10579 if (mol->mcube != NULL) {
10580 MoleculeDeallocateMCube(mol->mcube);
10589 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10591 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10592 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10596 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10599 int sym, nprims, a_idx, n;
10600 Data_Get_Struct(self, Molecule, mol);
10601 a_idx = NUM2INT(rb_Integer(aval));
10602 sym = NUM2INT(rb_Integer(symval));
10603 nprims = NUM2INT(rb_Integer(npval));
10604 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10606 rb_raise(rb_eMolbyError, "Molecule is emptry");
10608 rb_raise(rb_eMolbyError, "Low memory");
10610 rb_raise(rb_eMolbyError, "Unknown orbital type");
10612 rb_raise(rb_eMolbyError, "Unknown error");
10618 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10620 * To be used internally. Add a gaussian primitive coefficients.
10623 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10627 Double exponent, contraction, contraction_sp;
10628 Data_Get_Struct(self, Molecule, mol);
10629 exponent = NUM2DBL(rb_Float(expval));
10630 contraction = NUM2DBL(rb_Float(cval));
10631 contraction_sp = NUM2DBL(rb_Float(cspval));
10632 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10634 rb_raise(rb_eMolbyError, "Molecule is emptry");
10636 rb_raise(rb_eMolbyError, "Low memory");
10638 rb_raise(rb_eMolbyError, "Unknown error");
10644 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10646 * Get the Gaussian shell information for the given MO coefficient index.
10647 * The symmetry code is the same as in add_gaussian_orbital_shell.
10648 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10649 * is the number of MO component belonging to this shell.
10652 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10657 Data_Get_Struct(self, Molecule, mol);
10658 if (mol->bset == NULL)
10659 rb_raise(rb_eMolbyError, "No basis set information is defined");
10660 s_idx = NUM2INT(rb_Integer(sval));
10661 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10663 sp = mol->bset->shells + s_idx;
10666 case kGTOType_S: sym = 0; break;
10667 case kGTOType_SP: sym = -1; break;
10668 case kGTOType_P: sym = 1; break;
10669 case kGTOType_D: sym = 2; break;
10670 case kGTOType_D5: sym = -2; break;
10671 case kGTOType_F: sym = 3; break;
10672 case kGTOType_F7: sym = -3; break;
10673 case kGTOType_G: sym = 4; break;
10674 case kGTOType_G9: sym = -4; break;
10676 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10678 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10683 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10685 * Get the Gaussian primitive coefficients for the given MO component.
10688 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10694 VALUE retval, aval;
10695 Data_Get_Struct(self, Molecule, mol);
10696 if (mol->bset == NULL)
10697 rb_raise(rb_eMolbyError, "No basis set information is defined");
10698 s_idx = NUM2INT(rb_Integer(sval));
10699 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10701 sp = mol->bset->shells + s_idx;
10702 pp = mol->bset->priminfos + sp->p_idx;
10703 retval = rb_ary_new2(sp->nprim);
10704 for (i = 0; i < sp->nprim; i++) {
10705 if (sp->sym == kGTOType_SP) {
10706 /* With P contraction coefficient */
10707 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10709 /* Without P contraction coefficient */
10710 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10712 rb_ary_store(retval, i, aval);
10719 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10721 * Get the Gaussian shell information for the given MO coefficient index.
10724 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10727 Int n, c, atom_idx, shell_idx;
10729 Data_Get_Struct(self, Molecule, mol);
10730 if (mol->bset == NULL)
10731 rb_raise(rb_eMolbyError, "No basis set information is defined");
10732 c = NUM2INT(rb_Integer(cval));
10733 if (c < 0 || c >= mol->bset->ncomps)
10735 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10737 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10738 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10743 * clear_mo_coefficients
10745 * Clear the existing MO coefficients.
10748 s_Molecule_ClearMOCoefficients(VALUE self)
10751 Data_Get_Struct(self, Molecule, mol);
10752 if (mol->bset != NULL) {
10753 if (mol->bset->moenergies != NULL) {
10754 free(mol->bset->moenergies);
10755 mol->bset->moenergies = NULL;
10757 if (mol->bset->mo != NULL) {
10758 free(mol->bset->mo);
10759 mol->bset->mo = NULL;
10761 mol->bset->nmos = 0;
10768 * set_mo_coefficients(idx, energy, coefficients)
10770 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10771 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10772 * Energy is the MO energy, and coefficients is an array
10773 * of MO coefficients.
10776 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10779 Int idx, ncomps, i;
10782 Data_Get_Struct(self, Molecule, mol);
10783 idx = NUM2INT(rb_Integer(ival));
10784 energy = NUM2DBL(rb_Float(eval));
10785 aval = rb_ary_to_ary(aval);
10786 ncomps = RARRAY_LEN(aval);
10787 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10788 if (coeffs == NULL) {
10792 for (i = 0; i < ncomps; i++)
10793 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10794 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10797 rb_raise(rb_eMolbyError, "Molecule is emptry");
10799 rb_raise(rb_eMolbyError, "Low memory");
10801 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10803 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10805 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10807 rb_raise(rb_eMolbyError, "Unknown error");
10813 * get_mo_coefficients(idx)
10815 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10818 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10821 Int idx, ncomps, n;
10825 Data_Get_Struct(self, Molecule, mol);
10826 idx = NUM2INT(rb_Integer(ival));
10829 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10831 rb_raise(rb_eMolbyError, "Molecule is emptry");
10833 rb_raise(rb_eMolbyError, "No basis set information is present");
10835 return Qnil; /* Silently returns nil */
10836 retval = rb_ary_new2(ncomps);
10837 for (n = 0; n < ncomps; n++)
10838 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10845 * get_mo_energy(idx)
10847 * To be used internally. Get the MO energy for the given MO index (1-based).
10850 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10855 Data_Get_Struct(self, Molecule, mol);
10856 idx = NUM2INT(rb_Integer(ival));
10857 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10859 rb_raise(rb_eMolbyError, "Molecule is emptry");
10861 rb_raise(rb_eMolbyError, "No basis set information is present");
10864 return rb_float_new(energy);
10867 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10870 s_InitMOInfoKeys(void)
10872 if (sTypeSym == 0) {
10873 sTypeSym = ID2SYM(rb_intern("type"));
10874 sAlphaSym = ID2SYM(rb_intern("alpha"));
10875 sBetaSym = ID2SYM(rb_intern("beta"));
10876 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10877 sNshellsSym = ID2SYM(rb_intern("nshells"));
10883 * set_mo_info(hash)
10885 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10886 * :alpha=>integer, :beta=>integer
10889 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10893 Int rflag, na, nb, n;
10895 Data_Get_Struct(self, Molecule, mol);
10896 if (mol->bset != NULL) {
10897 rflag = mol->bset->rflag;
10898 na = mol->bset->ne_alpha;
10899 nb = mol->bset->ne_beta;
10905 if (hval != Qnil) {
10906 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10907 s = StringValuePtr(aval);
10908 if (strcasecmp(s, "RHF") == 0)
10910 else if (strcasecmp(s, "UHF") == 0)
10912 else if (strcasecmp(s, "ROHF") == 0)
10915 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10916 n = NUM2INT(rb_Integer(aval));
10920 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10921 n = NUM2INT(rb_Integer(aval));
10925 MoleculeSetMOInfo(mol, rflag, na, nb);
10934 * Get the MO info. The key is as described in set_mo_info.
10935 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10938 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10941 Data_Get_Struct(self, Molecule, mol);
10942 if (mol->bset == NULL)
10944 if (kval == sTypeSym) {
10945 switch (mol->bset->rflag) {
10946 case 0: return Ruby_NewEncodedStringValue2("UHF");
10947 case 1: return Ruby_NewEncodedStringValue2("RHF");
10948 case 2: return Ruby_NewEncodedStringValue2("ROHF");
10949 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10951 } else if (kval == sAlphaSym) {
10952 return INT2NUM(mol->bset->ne_alpha);
10953 } else if (kval == sBetaSym) {
10954 return INT2NUM(mol->bset->ne_beta);
10955 } else if (kval == sNcompsSym) {
10956 return INT2NUM(mol->bset->ncomps);
10957 } else if (kval == sNshellsSym) {
10958 return INT2NUM(mol->bset->nshells);
10960 kval = rb_inspect(kval);
10961 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10962 return Qnil; /* Does not reach here */
10970 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10973 s_Molecule_MOType(VALUE self)
10975 return s_Molecule_GetMOInfo(self, sTypeSym);
10978 #pragma mark ------ Molecular Topology ------
10982 * search_equivalent_atoms(ig = nil)
10984 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10987 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10993 Data_Get_Struct(self, Molecule, mol);
10994 if (mol->natoms == 0)
10996 rb_scan_args(argc, argv, "01", &val);
10998 ig = s_Molecule_AtomGroupFromValue(self, val);
11000 result = MoleculeSearchEquivalentAtoms(mol, ig);
11001 if (result == NULL)
11002 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11004 IntGroupRelease(ig);
11005 val = rb_ary_new2(mol->natoms);
11006 for (i = 0; i < mol->natoms; i++)
11007 rb_ary_push(val, INT2NUM(result[i]));
11014 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11016 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11017 * Name is the name of the new pi anchor, and group is the atoms that define
11018 * the pi system. Type (a String) is an atom type for MM implementation.
11019 * Weights represent the relative significance of the component atoms; if omitted, then
11020 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
11021 * The weight values will be normalized so that the sum of the weights is 1.0.
11022 * The weight values must be positive.
11023 * Index is the atom index where the created pi-anchor is inserted in the
11024 * atoms array; if omitted, the pi-anchor is inserted after the component atom
11025 * having the largest index.
11026 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
11027 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11030 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11035 Int i, n, idx, last_component;
11039 if (argc < 2 || argc >= 6)
11040 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11044 Data_Get_Struct(self, Molecule, mol);
11048 ig = s_Molecule_AtomGroupFromValue(self, gval);
11049 if (ig == NULL || IntGroupGetCount(ig) == 0)
11050 rb_raise(rb_eMolbyError, "atom group is not given correctly");
11051 memset(&a, 0, sizeof(a));
11052 memset(&an, 0, sizeof(an));
11053 strncpy(a.aname, StringValuePtr(nval), 4);
11054 if (a.aname[0] == '_')
11055 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11056 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11057 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11058 if (n >= mol->natoms) {
11059 AtomConnectResize(&an.connect, 0);
11060 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11062 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11063 last_component = n;
11065 if (an.connect.count == 0)
11066 rb_raise(rb_eMolbyError, "no atoms are specified");
11067 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11068 for (i = 0; i < an.connect.count; i++) {
11069 an.coeffs[i] = 1.0 / an.connect.count;
11071 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11073 if (argv[0] != Qnil)
11074 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11078 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11079 if (argv[0] != Qnil) {
11080 VALUE aval = rb_ary_to_ary(argv[0]);
11082 if (RARRAY_LEN(aval) != an.connect.count)
11083 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11084 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11085 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11087 rb_raise(rb_eMolbyError, "the weight value must be positive");
11091 for (i = 0; i < an.connect.count; i++)
11092 an.coeffs[i] /= sum;
11097 if (argc > 0 && argv[0] != Qnil) {
11099 idx = NUM2INT(rb_Integer(argv[0]));
11101 if (idx < 0 || idx > mol->natoms) {
11102 /* Immediately after the last specified atom */
11103 idx = last_component + 1;
11105 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11106 memmove(a.anchor, &an, sizeof(PiAnchor));
11107 /* Use residue information of the last specified atom */
11108 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11109 a.resSeq = ap->resSeq;
11110 strncpy(a.resName, ap->resName, 4);
11111 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11113 MoleculeCalculatePiAnchorPosition(mol, idx);
11114 aref = AtomRefNew(mol, idx);
11115 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11118 #pragma mark ------ Molecular Properties ------
11122 * set_property(name, value[, index]) -> value
11123 * set_property(name, values, group) -> values
11125 * Set molecular property. A property is a floating-point number with a specified name,
11126 * and can be set for each frame separately. The name of the property is given as a String.
11127 * The value can be a single floating point number, which is set to the current frame.
11131 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11134 VALUE nval, vval, ival;
11137 Int i, n, idx, fidx;
11139 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11140 Data_Get_Struct(self, Molecule, mol);
11141 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11142 idx = NUM2INT(rb_Integer(nval));
11143 if (idx < 0 || idx >= mol->nmolprops)
11144 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11146 name = StringValuePtr(nval);
11147 idx = MoleculeLookUpProperty(mol, name);
11149 idx = MoleculeCreateProperty(mol, name);
11151 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11154 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11156 fidx = mol->cframe;
11158 fidx = NUM2INT(rb_Integer(ival));
11159 n = MoleculeGetNumberOfFrames(mol);
11160 if (fidx < 0 || fidx >= n)
11161 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11163 ig = IntGroupNewWithPoints(fidx, 1, -1);
11164 dp = (Double *)malloc(sizeof(Double));
11165 *dp = NUM2DBL(rb_Float(vval));
11168 vval = rb_ary_to_ary(vval);
11169 ig = IntGroupFromValue(ival);
11170 n = IntGroupGetCount(ig);
11172 rb_raise(rb_eMolbyError, "No frames are specified");
11173 if (RARRAY_LEN(vval) < n)
11174 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11175 dp = (Double *)calloc(sizeof(Double), n);
11176 for (i = 0; i < n; i++)
11177 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11180 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11182 IntGroupRelease(ig);
11188 * get_property(name[, index]) -> value
11189 * get_property(name, group) -> values
11191 * Get molecular property. In the first form, a property value for a single frame is returned.
11192 * (If index is omitted, then the value for the current frame is given)
11193 * In the second form, an array of property values for the given frames is returned.
11194 * If name is not one of known properties or a valid index integer, exception is raised.
11197 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11203 Int i, n, idx, fidx;
11205 rb_scan_args(argc, argv, "11", &nval, &ival);
11206 Data_Get_Struct(self, Molecule, mol);
11207 if (mol->nmolprops == 0)
11208 rb_raise(rb_eMolbyError, "The molecule has no properties");
11209 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11210 idx = NUM2INT(rb_Integer(nval));
11211 if (idx < 0 || idx >= mol->nmolprops)
11212 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11214 name = StringValuePtr(nval);
11215 idx = MoleculeLookUpProperty(mol, name);
11217 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11219 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11221 fidx = mol->cframe;
11223 fidx = NUM2INT(rb_Integer(ival));
11224 n = MoleculeGetNumberOfFrames(mol);
11225 if (fidx < 0 || fidx >= n)
11226 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11228 ig = IntGroupNewWithPoints(fidx, 1, -1);
11229 ival = INT2FIX(fidx);
11232 ig = IntGroupFromValue(ival);
11233 n = IntGroupGetCount(ig);
11235 return rb_ary_new();
11237 dp = (Double *)calloc(sizeof(Double), n);
11238 MoleculeGetProperty(mol, idx, ig, dp);
11239 if (FIXNUM_P(ival))
11240 ival = rb_float_new(dp[0]);
11242 ival = rb_ary_new();
11243 for (i = n - 1; i >= 0; i--) {
11244 nval = rb_float_new(dp[i]);
11245 rb_ary_store(ival, i, nval);
11249 IntGroupRelease(ig);
11255 * property_names -> Array
11257 * Get an array of property names.
11260 s_Molecule_PropertyNames(VALUE self)
11265 Data_Get_Struct(self, Molecule, mol);
11266 rval = rb_ary_new();
11267 for (i = mol->nmolprops - 1; i >= 0; i--) {
11268 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11269 rb_ary_store(rval, i, nval);
11274 #pragma mark ------ Class methods ------
11278 * current -> Molecule
11280 * Get the currently "active" molecule.
11283 s_Molecule_Current(VALUE klass)
11285 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11290 * Molecule[] -> Molecule
11291 * Molecule[n] -> Molecule
11292 * Molecule[name] -> Molecule
11293 * Molecule[name, k] -> Molecule
11294 * Molecule[regex] -> Molecule
11295 * Molecule[regex, k] -> Molecule
11297 * Molecule[] is equivalent to Molecule.current.
11298 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11299 * Molecule[name] gives the first document (in the order of creation time) that has
11300 * the given name. If a second argument (k) is given, the k-th document that has the
11301 * given name is returned.
11302 * Molecule[regex] gives the first document (in the order of creation time) that
11303 * has a name matching the regular expression. If a second argument (k) is given,
11304 * the k-th document that has a name matching the re is returned.
11307 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11313 rb_scan_args(argc, argv, "02", &val, &kval);
11315 return s_Molecule_Current(klass);
11316 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11317 idx = NUM2INT(val);
11318 mol = MoleculeCallback_moleculeAtIndex(idx);
11319 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11320 char *p = StringValuePtr(val);
11321 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11322 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11323 MoleculeCallback_displayName(mol, buf, sizeof buf);
11324 if (strcmp(buf, p) == 0 && --k == 0)
11327 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11328 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11329 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11331 MoleculeCallback_displayName(mol, buf, sizeof buf);
11332 name = Ruby_NewEncodedStringValue2(buf);
11333 if (rb_reg_match(val, name) != Qnil && --k == 0)
11336 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11340 else return ValueFromMolecule(mol);
11345 * list -> array of Molecules
11347 * Get the list of molecules associated to the documents, in the order of creation
11348 * time of the document. If no document is open, returns an empry array.
11351 s_Molecule_List(VALUE klass)
11357 ary = rb_ary_new();
11358 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11359 rb_ary_push(ary, ValueFromMolecule(mol));
11367 * ordered_list -> array of Molecules
11369 * Get the list of molecules associated to the documents, in the order of front-to-back
11370 * ordering of the associated window. If no document is open, returns an empry array.
11373 s_Molecule_OrderedList(VALUE klass)
11379 ary = rb_ary_new();
11380 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11381 rb_ary_push(ary, ValueFromMolecule(mol));
11387 #pragma mark ------ Call Subprocess ------
11389 /* The callback functions for call_subprocess_async */
11391 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11394 VALUE procval, retval, args[2];
11395 args[0] = ValueFromMolecule(mol);
11396 args[1] = INT2NUM(status);
11397 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11398 if (procval != Qnil) {
11399 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11400 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11407 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11410 VALUE procval, retval, args[2];
11411 args[0] = ValueFromMolecule(mol);
11412 args[1] = INT2NUM(tcount);
11413 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11414 if (procval != Qnil) {
11415 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11416 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11424 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11426 * Call subprocess asynchronically.
11427 * If end_callback is given, it will be called (with two arguments self and termination status)
11428 * when the subprocess terminated.
11429 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11430 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11431 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11432 * filename begins with ">>", then the message will be appended to the file.
11433 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11434 * If the argument is nil, then the message will be sent to the Ruby console.
11435 * Returns the process ID as an integer.
11438 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11440 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11444 FILE *fpout, *fperr;
11445 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11446 Data_Get_Struct(self, Molecule, mol);
11448 if (stdout_val == Qnil) {
11451 sout = StringValuePtr(stdout_val);
11452 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11455 if (strncmp(sout, ">>", 2) == 0) {
11457 fpout = fopen(sout, "a");
11461 fpout = fopen(sout, "w");
11464 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11467 if (stderr_val == Qnil) {
11470 serr = StringValuePtr(stderr_val);
11471 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11474 if (strncmp(serr, ">>", 2) == 0) {
11476 fpout = fopen(serr, "a");
11480 fperr = fopen(serr, "w");
11483 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11487 /* Register procs as instance variables */
11488 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11489 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11490 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11491 if (fpout != NULL && fpout != (FILE *)1)
11493 if (fperr != NULL && fperr != (FILE *)1)
11498 #pragma mark ====== Define Molby Classes ======
11505 /* Define module Molby */
11506 rb_mMolby = rb_define_module("Molby");
11508 /* Define Vector3D, Transform, IntGroup */
11511 /* Define MDArena */
11512 Init_MolbyMDTypes();
11514 /* class Molecule */
11515 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11517 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11518 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11519 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11520 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11521 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11523 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11524 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11525 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11526 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11527 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11528 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11529 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11530 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11531 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11532 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11533 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11534 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11535 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11536 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11537 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11538 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11539 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11540 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11541 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11542 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11543 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11544 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11546 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11547 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11548 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11549 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11550 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11552 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11553 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11554 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11555 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11556 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11557 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11558 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11559 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11560 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11561 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11562 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11563 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11564 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11565 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11566 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11568 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11569 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11570 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11571 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11572 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11574 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11575 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11576 rb_define_alias(rb_cMolecule, "+", "add");
11577 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11578 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11579 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11580 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11581 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11582 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11583 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11584 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11585 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11586 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11587 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11588 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11589 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11590 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11591 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11592 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11593 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11594 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11595 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11596 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11597 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11599 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11600 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11601 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11602 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11603 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11605 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11606 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11607 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11608 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11609 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11610 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11611 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11612 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11613 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11615 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11616 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11617 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11618 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11619 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11620 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11621 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11622 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11623 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11624 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11625 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11626 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11627 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11628 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11629 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11630 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11631 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11632 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11633 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11634 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11635 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11636 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11638 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11639 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11640 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11641 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11642 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11643 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11644 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11646 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11647 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11648 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11649 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11650 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11651 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11652 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11653 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11654 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11655 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11656 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11657 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11658 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11660 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11661 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11662 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11663 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11664 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11665 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11667 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11668 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11669 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11670 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11671 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11672 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11673 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11674 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11675 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11676 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11677 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11678 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11679 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11680 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11681 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11682 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11683 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11684 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11685 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11686 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11687 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11688 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11689 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11690 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11691 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11692 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11693 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11694 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11695 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11696 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11697 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11698 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11699 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11700 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11701 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11702 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11703 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11704 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11705 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11706 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11707 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11708 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11709 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11710 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11711 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11712 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11713 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11714 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11715 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11716 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11717 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11718 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11719 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11720 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11722 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11723 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11724 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11725 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11726 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11727 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11728 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11729 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11730 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11731 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11732 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11733 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11734 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11736 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11737 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11738 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11739 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11740 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11741 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11742 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11743 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11744 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11745 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11746 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11747 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11748 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11749 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11751 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11752 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11753 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11754 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11755 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11756 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11757 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11758 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11759 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11760 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11761 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11762 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11763 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11764 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11765 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11766 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11767 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11768 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11769 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11770 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11771 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11772 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11773 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11775 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11776 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11778 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11779 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11780 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11782 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11783 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11784 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11785 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11787 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11789 /* class MolEnumerable */
11790 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11791 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11792 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11793 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11794 rb_define_alias(rb_cMolEnumerable, "size", "length");
11795 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11796 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11798 /* class AtomRef */
11799 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11800 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11802 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11803 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11804 s_AtomAttrDefTable[i].id = rb_intern(buf);
11805 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11807 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11809 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11810 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11811 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11812 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11813 s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11814 rb_global_variable(&s_SetAtomAttrString);
11815 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11816 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11818 /* class Parameter */
11819 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11820 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11821 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11822 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11823 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11824 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11825 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11826 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11827 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11828 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11829 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11830 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11831 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11832 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11833 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11834 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11835 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11836 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11837 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11838 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11839 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11840 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11841 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11842 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11843 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11844 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11845 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11846 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11847 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11848 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11849 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11850 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11851 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11852 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11853 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11854 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11855 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11856 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11857 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11858 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11859 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11860 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11861 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11862 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11863 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11864 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11865 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11866 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11867 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11868 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11869 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11870 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11871 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11872 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11874 /* class ParEnumerable */
11875 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11876 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11877 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11878 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11879 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11880 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11881 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11882 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11883 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11884 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11885 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11887 /* class ParameterRef */
11888 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11889 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11891 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11892 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11893 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11894 if (s_ParameterAttrDefTable[i].symref != NULL)
11895 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11896 if (s_ParameterAttrDefTable[i].setter != NULL) {
11898 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11901 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11902 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11903 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11904 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11905 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11906 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11907 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11908 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11910 /* class MolbyError */
11911 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11913 /* module Kernel */
11914 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11915 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11916 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11917 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11918 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11919 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11920 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11921 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11922 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11923 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11924 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11925 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11926 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11927 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11928 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11929 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11930 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11931 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11932 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11933 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11934 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11935 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11936 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11937 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11940 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11942 s_ID_equal = rb_intern("==");
11943 g_RubyID_call = rb_intern("call");
11945 s_InitMOInfoKeys();
11947 /* Symbols for graphics */
11948 s_LineSym = ID2SYM(rb_intern("line"));
11949 s_PolySym = ID2SYM(rb_intern("poly"));
11950 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11951 s_ConeSym = ID2SYM(rb_intern("cone"));
11952 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11955 #pragma mark ====== Interface with RubyDialog class ======
11958 RubyDialogCallback_parentModule(void)
11960 return (RubyValue)rb_mMolby;
11963 #pragma mark ====== External functions ======
11965 static VALUE s_ruby_top_self = Qfalse;
11966 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11967 static VALUE s_ruby_export_local_variables = Qfalse;
11970 s_evalRubyScriptOnMoleculeSub(VALUE val)
11972 void **ptr = (void **)val;
11973 Molecule *mol = (Molecule *)ptr[1];
11974 VALUE sval, fnval, lnval, retval;
11977 /* Clear the error information (store in the history array if necessary) */
11978 sval = rb_errinfo();
11979 if (sval != Qnil) {
11980 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11981 rb_set_errinfo(Qnil);
11984 if (s_ruby_top_self == Qfalse) {
11985 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11987 if (s_ruby_get_binding_for_molecule == Qfalse) {
11989 "lambda { |_mol_, _bind_| \n"
11990 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11991 " _proc_.call(_mol_) } ";
11992 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11993 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11995 if (s_ruby_export_local_variables == Qfalse) {
11997 "lambda { |_bind_| \n"
11998 " # find local variables newly defined in _bind_ \n"
11999 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12000 " _a_.each { |_vsym_| \n"
12001 " _vname_ = _vsym_.to_s \n"
12002 " _vval_ = _bind_.eval(_vname_) \n"
12003 " # Define local variable \n"
12004 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12005 " # Then set value \n"
12006 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12009 s_ruby_export_local_variables = rb_eval_string(s2);
12010 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12012 if (ptr[2] == NULL) {
12014 /* String literal: we need to specify string encoding */
12015 #if defined(__WXMSW__)
12016 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12018 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12020 sval = Ruby_NewEncodedStringValue2(scr);
12022 fnval = Ruby_NewEncodedStringValue2("(eval)");
12023 lnval = INT2FIX(0);
12025 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12026 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12027 lnval = INT2FIX(1);
12029 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12031 VALUE mval = ValueFromMolecule(mol);
12032 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12034 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12036 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12042 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12046 VALUE save_interrupt_flag;
12047 /* char *save_ruby_sourcefile;
12048 int save_ruby_sourceline; */
12049 if (gMolbyIsCheckingInterrupt) {
12050 MolActionAlertRubyIsRunning();
12052 return (RubyValue)Qnil;
12055 args[0] = (void *)script;
12056 args[1] = (void *)mol;
12057 args[2] = (void *)fname;
12058 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12059 /* save_ruby_sourcefile = ruby_sourcefile;
12060 save_ruby_sourceline = ruby_sourceline; */
12061 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12062 if (*status != 0) {
12063 /* Is this 'exit' exception? */
12064 VALUE last_exception = rb_gv_get("$!");
12065 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12066 /* Capture exit and return the status value */
12067 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12069 rb_set_errinfo(Qnil);
12072 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12073 /* ruby_sourcefile = save_ruby_sourcefile;
12074 ruby_sourceline = save_ruby_sourceline; */
12081 Ruby_inspectValue(RubyValue value)
12084 static char buf[256];
12085 VALUE val = (VALUE)value;
12087 val = rb_protect(rb_inspect, val, &status);
12090 char *str = StringValuePtr(val);
12091 strncpy(buf, str, sizeof(buf) - 1);
12092 buf[sizeof(buf) - 1] = 0;
12094 snprintf(buf, sizeof(buf), "Error status = %d", status);
12100 Ruby_showValue(RubyValue value, char **outValueString)
12102 VALUE val = (VALUE)value;
12103 if (gMolbyIsCheckingInterrupt) {
12104 MolActionAlertRubyIsRunning();
12111 val = rb_protect(rb_inspect, val, &status);
12115 str = StringValuePtr(val);
12116 if (outValueString != NULL)
12117 *outValueString = strdup(str);
12118 MyAppCallback_showScriptMessage("%s", str);
12120 if (outValueString != NULL)
12121 *outValueString = NULL;
12127 Ruby_showError(int status)
12129 static const int tag_raise = 6;
12130 char *main_message = "Molby script error";
12131 char *msg = NULL, *msg2;
12132 VALUE val, backtrace;
12133 int interrupted = 0;
12134 int exit_status = -1;
12135 if (status == tag_raise) {
12136 VALUE errinfo = rb_errinfo();
12137 VALUE eclass = CLASS_OF(errinfo);
12138 if (eclass == rb_eInterrupt) {
12139 main_message = "Molby script interrupted";
12142 } else if (eclass == rb_eSystemExit) {
12143 main_message = "Molby script exit";
12145 val = rb_eval_string_protect("$!.status", &status);
12147 exit_status = NUM2INT(rb_Integer(val));
12148 asprintf(&msg, "Molby script exit with status %d", exit_status);
12150 asprintf(&msg, "Molby script exit with unknown status");
12155 if (exit_status != 0) {
12156 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12158 val = rb_eval_string_protect("$!.to_s", &status);
12160 msg = RSTRING_PTR(val);
12162 msg = "(message not available)";
12164 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12166 msg2 = strdup(msg);
12168 MyAppCallback_messageBox(msg2, main_message, 0, 3);
12170 if (interrupted == 2) {
12172 if (!gUseGUI && exit_status == 0)
12173 exit(0); // Capture exit(0) here and force exit
12178 /* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
12180 Molby_loadScript(const char *script, int from_file)
12185 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12187 rb_eval_string_protect(script, &status);
12193 Molby_getDescription(char **versionString, char **auxString)
12195 extern const char *gVersionString, *gCopyrightString;
12196 extern int gRevisionNumber;
12197 extern char *gLastBuildString;
12199 char *revisionString;
12200 if (gRevisionNumber > 0) {
12201 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12202 } else revisionString = "";
12204 asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12205 #if defined(__WXMSW__)
12206 #if TARGET_ARCH == 64
12214 gVersionString, revisionString, gCopyrightString, gLastBuildString);
12219 "ruby %s, http://www.ruby-lang.org/\n"
12221 "FFTW 3.3.2, http://www.fftw.org/\n"
12222 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12223 " and Massachusetts Institute of Technology",
12224 MyAppCallback_getGUIDescriptionString(),
12225 gRubyVersion, gRubyCopyright);
12229 "ruby %s, http://www.ruby-lang.org/\n"
12231 "FFTW 3.3.2, http://www.fftw.org/\n"
12232 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12233 " and Massachusetts Institute of Technology",
12234 gRubyVersion, gRubyCopyright);
12237 if (revisionString[0] != 0)
12238 free(revisionString);
12239 if (versionString != NULL)
12240 *versionString = s1;
12241 if (auxString != NULL)
12246 Molby_startup(const char *script, const char *dir)
12251 char *respath, *p, *wbuf;
12253 /* Get version/copyright string from Ruby interpreter */
12255 gRubyVersion = strdup(ruby_version);
12256 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12257 " ", /* Indent for displaying in About dialog */
12258 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12261 /* Read build and revision information for Molby */
12264 extern int gRevisionNumber;
12265 extern char *gLastBuildString;
12266 FILE *fp = fopen("../buildInfo.txt", "r");
12267 gLastBuildString = "";
12269 if (fgets(buf, sizeof(buf), fp) != NULL) {
12270 char *p1 = strchr(buf, '\"');
12271 char *p2 = strrchr(buf, '\"');
12272 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12273 memmove(buf, p1 + 1, p2 - p1 - 1);
12274 buf[p2 - p1 - 1] = 0;
12275 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12280 fp = fopen("../revisionInfo.txt", "r");
12281 gRevisionNumber = 0;
12283 if (fgets(buf, sizeof(buf), fp) != NULL) {
12284 gRevisionNumber = strtol(buf, NULL, 0);
12292 Molby_getDescription(&wbuf, &wbuf2);
12293 MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12298 /* Read atom display parameters */
12299 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12300 MyAppCallback_setConsoleColor(1);
12301 MyAppCallback_showScriptMessage("%s", wbuf);
12302 MyAppCallback_setConsoleColor(0);
12306 /* Read default parameters */
12307 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12308 if (wbuf != NULL) {
12309 MyAppCallback_setConsoleColor(1);
12310 MyAppCallback_showScriptMessage("%s", wbuf);
12311 MyAppCallback_setConsoleColor(0);
12315 /* Initialize Ruby interpreter */
12318 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12319 it causes rb_bug() (= fatal error) during ruby_init().
12320 As a workaround, these standard streams are reopend as
12322 freopen("NUL", "r", stdin);
12323 freopen("NUL", "w", stdout);
12324 freopen("NUL", "w", stderr);
12330 /* Initialize CP932/Windows-31J encodings */
12331 extern void Init_shift_jis(void), Init_windows_31j(void), Init_trans_japanese_sjis(void);
12332 extern int rb_enc_alias(const char *, const char *);
12334 Init_windows_31j();
12335 Init_trans_japanese_sjis();
12336 rb_enc_alias("CP932", "Windows-31J");
12339 #if defined(__WXMSW__)
12341 /* Set default external encoding */
12342 /* The following snippet is taken from encoding.c */
12343 extern void rb_enc_set_default_external(VALUE encoding);
12344 char cp[sizeof(int) * 8 / 3 + 22];
12347 snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12348 enc = rb_eval_string_protect(cp, &status);
12349 if (status == 0 && !NIL_P(enc)) {
12350 rb_enc_set_default_external(enc);
12355 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12357 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12358 ruby_incpush(libpath);
12362 ruby_script("Molby");
12364 /* Find the resource path (the parent directory of the given directory) */
12365 respath = strdup(dir);
12366 p = strrchr(respath, '/');
12367 if (p == NULL && PATH_SEPARATOR != '/')
12368 p = strrchr(respath, PATH_SEPARATOR);
12371 val = Ruby_NewFileStringValue(respath);
12372 rb_define_global_const("MolbyResourcePath", val);
12375 /* Define Molby classes */
12378 RubyDialogInitClass();
12380 rb_define_const(rb_mMolby, "ResourcePath", val);
12381 val = Ruby_NewFileStringValue(dir);
12382 rb_define_const(rb_mMolby, "ScriptPath", val);
12383 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12384 val = Ruby_NewFileStringValue(p);
12385 rb_define_const(rb_mMolby, "MbsfPath", val);
12388 p = MyAppCallback_getHomeDir();
12389 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12390 rb_define_const(rb_mMolby, "HomeDirectory", val);
12392 p = MyAppCallback_getDocumentHomeDir();
12393 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12394 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12398 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12400 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12403 /* Create objects for stdout and stderr */
12404 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12405 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12406 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12407 rb_gv_set("$stdout", val);
12408 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12409 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12410 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12411 rb_gv_set("$stderr", val);
12413 /* Create objects for stdin */
12414 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12415 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12416 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12417 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12418 rb_gv_set("$stdin", val);
12421 /* Global variable to hold error information */
12422 rb_define_variable("$backtrace", &gMolbyBacktrace);
12423 rb_define_variable("$error_history", &gMolbyErrorHistory);
12424 gMolbyErrorHistory = rb_ary_new();
12426 /* Global variables for script menus */
12427 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12428 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12429 gScriptMenuCommands = rb_ary_new();
12430 gScriptMenuEnablers = rb_ary_new();
12433 /* Register interrupt check code */
12434 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12435 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12436 s_SetIntervalTimer(0, 50);
12439 /* Read the startup script */
12440 if (script != NULL && script[0] != 0) {
12441 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12443 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12446 Ruby_showError(status);
12448 MyAppCallback_showScriptMessage("Done.\n");
12453 Molby_buildARGV(int argc, const char **argv)
12456 rb_ary_clear(rb_argv);
12457 for (i = 0; i < argc; i++) {
12458 VALUE arg = rb_tainted_str_new2(argv[i]);
12460 rb_ary_push(rb_argv, arg);