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");
391 * hartree_to_kcal(val)
393 * Convert hartree to kcal/mol
396 s_Kernel_HartreeToKcal(VALUE self, VALUE fval)
398 double d = NUM2DBL(rb_Float(fval));
399 return rb_float_new(d * 627.5094740630557);
404 * kcal_to_hartree(val)
406 * Convert kcal/mol to hartree
409 s_Kernel_KcalToHartree(VALUE self, VALUE fval)
411 double d = NUM2DBL(rb_Float(fval));
412 return rb_float_new(d / 627.5094740630557);
419 * Convert hartree to kJ/mol
422 s_Kernel_HartreeToKJ(VALUE self, VALUE fval)
424 double d = NUM2DBL(rb_Float(fval));
425 return rb_float_new(d * 2625.4996394798253);
432 * Convert kJ/mol to hartree
435 s_Kernel_KJToHartree(VALUE self, VALUE fval)
437 double d = NUM2DBL(rb_Float(fval));
438 return rb_float_new(d / 2625.4996394798253);
443 * bohr_to_angstrom(val)
445 * Convert bohr to angstrom
448 s_Kernel_BohrToAngstrom(VALUE self, VALUE fval)
450 double d = NUM2DBL(rb_Float(fval));
451 return rb_float_new(d * 0.529177210903);
456 * angstrom_to_bohr(val)
458 * Convert angstrom to bohr
461 s_Kernel_AngstromToBohr(VALUE self, VALUE fval)
463 double d = NUM2DBL(rb_Float(fval));
464 return rb_float_new(d / 0.529177210903);
471 * Put the message in the main text view in black color.
474 s_StandardOutput(VALUE self, VALUE str)
477 MyAppCallback_setConsoleColor(0);
478 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
486 * Put the message in the main text view in red color.
489 s_StandardErrorOutput(VALUE self, VALUE str)
492 MyAppCallback_setConsoleColor(1);
493 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
494 MyAppCallback_setConsoleColor(0);
503 * Flush the standard (error) output. Actually do nothing.
506 s_FlushConsoleOutput(VALUE self)
513 * stdin.gets(rs = $/)
515 * Read one line message via dialog box.
518 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
521 pval = Ruby_NewEncodedStringValue2("Enter a line:");
522 rval = s_Kernel_Ask(1, &pval, self);
525 rb_str_cat2(rval, "\n");
531 * stdin.method_missing(name, args, ...)
533 * Throw an exception, noting only gets and readline are defined.
536 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
539 rb_scan_args(argc, argv, "10", &nval);
540 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
541 return Qnil; /* Not reached */
544 #pragma mark ====== Track key events ======
546 /* User interrupt handling
547 * User interrupt (command-period on Mac OS) is handled by periodic polling of
548 * key events. This polling should only be enabled during "normal" execution
549 * of scripts and must be disabled when the rest of the application (or Ruby
550 * script itself) is handling GUI. This is ensured by appropriate calls to
551 * enable_interrupt and disable_interrupt. */
553 static VALUE s_interrupt_flag = Qfalse;
556 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
558 volatile VALUE message;
560 if (Ruby_GetInterruptFlag() == Qtrue) {
561 rb_scan_args(argc, argv, "01", &message);
563 p = StringValuePtr(message);
566 MyAppCallback_showProgressPanel(p);
572 s_HideProgressPanel(VALUE self)
574 MyAppCallback_hideProgressPanel();
579 s_SetProgressValue(VALUE self, VALUE val)
581 double dval = NUM2DBL(rb_Float(val));
582 MyAppCallback_setProgressValue(dval);
587 s_SetProgressMessage(VALUE self, VALUE msg)
592 else p = StringValuePtr(msg);
593 MyAppCallback_setProgressMessage(p);
598 s_SetInterruptFlag(VALUE self, VALUE val)
602 if (val == Qfalse || val == Qnil)
606 oldval = s_interrupt_flag;
608 s_interrupt_flag = val;
614 s_GetInterruptFlag(VALUE self)
616 return s_SetInterruptFlag(self, Qundef);
620 Ruby_SetInterruptFlag(VALUE val)
622 return s_SetInterruptFlag(Qnil, val);
626 Ruby_GetInterruptFlag(void)
628 return s_SetInterruptFlag(Qnil, Qundef);
633 * check_interrupt -> integer
635 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
638 s_Kernel_CheckInterrupt(VALUE self)
640 if (Ruby_GetInterruptFlag() == Qfalse)
642 else if (MyAppCallback_checkInterrupt())
644 else return INT2NUM(0);
647 static volatile unsigned long sITimerCount = 0;
650 static HANDLE sITimerEvent;
651 static HANDLE sITimerThread;
652 static int sITimerInterval;
654 static __stdcall unsigned
655 s_ITimerThreadFunc(void *p)
657 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
663 #elif USE_PTHREAD_FOR_TIMER
666 static pthread_t sTimerThread;
668 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
669 static volatile signed char sTimerFlag = -1;
670 static volatile int sTimerIntervalMicrosec = 0;
673 s_TimerThreadEntry(void *param)
676 usleep(sTimerIntervalMicrosec);
679 else if (sTimerFlag == -2)
688 s_SignalAction(int n)
694 s_SetIntervalTimer(int n, int msec)
698 /* Start interval timer */
699 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
700 sITimerInterval = msec;
702 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
705 /* Stop interval timer */
707 SetEvent(sITimerEvent); /* Tell thread to terminate */
709 WaitForSingleObject(sITimerThread, 1000);
710 CloseHandle(sITimerThread);
713 CloseHandle(sITimerEvent);
715 sITimerThread = NULL;
717 #elif USE_PTHREAD_FOR_TIMER
719 if (sTimerFlag == -1) {
720 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
722 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
725 sTimerFlag = 0; /* Active */
726 sTimerIntervalMicrosec = msec * 1000;
727 } else if (sTimerFlag != -1)
728 sTimerFlag = 1; /* Inactive */
730 static struct itimerval sOldValue;
731 static struct sigaction sOldAction;
732 struct itimerval val;
733 struct sigaction act;
736 act.sa_handler = s_SignalAction;
739 sigaction(SIGALRM, &act, &sOldAction);
740 val.it_value.tv_sec = 0;
741 val.it_value.tv_usec = msec * 1000;
742 val.it_interval.tv_sec = 0;
743 val.it_interval.tv_usec = msec * 1000;
744 setitimer(ITIMER_REAL, &val, &sOldValue);
746 setitimer(ITIMER_REAL, &sOldValue, &val);
747 sigaction(SIGALRM, &sOldAction, &act);
753 s_GetTimerCount(void)
759 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
760 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
762 if (s_interrupt_flag != Qfalse) {
763 static unsigned long sLastTime = 0;
764 unsigned long currentTime;
766 currentTime = s_GetTimerCount();
767 if (currentTime != sLastTime) {
768 sLastTime = currentTime;
769 gMolbyIsCheckingInterrupt = 1;
770 flag = MyAppCallback_checkInterrupt();
771 gMolbyIsCheckingInterrupt = 0;
773 s_SetInterruptFlag(Qnil, Qfalse);
780 #pragma mark ====== Menu handling ======
784 * register_menu(title, method, enable_proc = nil)
786 * Register the method (specified as a symbol) in the script menu.
787 * The method must be either an instance method of Molecule with no argument,
788 * or a class method of Molecule with one argument (the current molecule),
789 * or a proc object with one argument (the current molecule).
790 * The menu associated with the class method can be invoked even when no document
791 * is open (the argument is set to Qnil in this case). On the other hand, the
792 * menu associated with the instance method can only be invoked when at least one
793 * document is active.
794 * If enable_proc is non-nil, then it is called whenever the availability of
795 * the menu command is tested. It is usually a proc object with one argument
796 * (the current molecule or nil). As a special case, the following symbols can
797 * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
798 * the top-level molecule has at least one atom), :selection (enabled when
799 * the top-level molecule has one or more selected atoms).
802 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
805 VALUE tval, mval, pval;
806 static VALUE sMolSym, sNonEmptySym, sSelectionSym;
807 static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
808 rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
809 tval = rb_str_to_str(tval);
810 n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
813 if (TYPE(mval) == T_SYMBOL) {
814 /* Create an appropriate proc object */
815 const char *name = rb_id2name(SYM2ID(mval));
817 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
818 /* Defined as a Molecule method */
819 asprintf(&s, "lambda { |m| m.%s }", name);
821 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
822 /* Defined as a Molecule class method */
823 asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
825 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
826 mval = rb_eval_string(s);
829 if (sMolSym == Qfalse) {
830 sMolSym = ID2SYM(rb_intern("mol"));
831 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
832 sSelectionSym = ID2SYM(rb_intern("selection"));
833 sMolProc = rb_eval_string("lambda { |m| m != nil }");
834 rb_define_variable("$is_a_molecule_proc", &sMolProc);
835 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
836 rb_define_variable("$is_molecule_not_empty_proc", &sNonEmptyProc);
837 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
838 rb_define_variable("$has_molecule_selection_proc", &sSelectionProc);
839 sTrueProc = rb_eval_string("lambda { |m| true }");
840 rb_define_variable("$always_true_proc", &sTrueProc);
848 } else if (pval == sMolSym)
850 else if (pval == sNonEmptySym)
851 pval = sNonEmptyProc;
852 else if (pval == sSelectionSym)
853 pval = sSelectionProc;
854 rb_ary_store(gScriptMenuCommands, n, mval);
855 rb_ary_store(gScriptMenuEnablers, n, pval);
860 s_Kernel_LookupMenu(VALUE self, VALUE title)
862 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
867 s_Ruby_UpdateUI_handler(VALUE data)
869 void **p = (void **)data;
870 int index = (intptr_t)p[0];
871 Molecule *mol = (Molecule *)p[1];
872 int *outChecked = (int *)p[2];
873 char **outTitle = (char **)p[3];
874 VALUE mval = ValueFromMolecule(mol);
875 VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
876 static ID call_id = 0;
878 call_id = rb_intern("call");
881 pval = rb_funcall(pval, call_id, 1, mval);
882 if (rb_obj_is_kind_of(pval, rb_cArray)) {
884 if (outChecked != NULL) {
885 val = rb_ary_entry(pval, 1); /* Checked or not */
886 *outChecked = (RTEST(val) ? 1 : 0);
888 if (outTitle != NULL) {
889 val = rb_ary_entry(pval, 2); /* Text */
890 if (TYPE(val) == T_STRING) {
891 *outTitle = strdup(StringValuePtr(val));
894 pval = rb_ary_entry(pval, 0);
900 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
905 p[0] = (void *)(intptr_t)index;
909 retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
910 return (RTEST(retval) ? 1 : 0);
915 s_Ruby_methodType_sub(VALUE data)
917 const char **p = (const char **)data;
918 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
919 ID mid = rb_intern(p[1]);
921 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
923 else if (rb_respond_to(klass, mid))
926 return INT2FIX(ival);
929 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
930 has the singleton method (class method) with the given name, 0 otherwise. */
932 Ruby_methodType(const char *className, const char *methodName)
939 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
941 return FIX2INT(retval);
948 * execute_script_file(fname)
950 * Execute the script in the given file. If a molecule is active, then
951 * the script is evaluated as Molecule.current.instance_eval(script).
952 * Before entering the script, the current directory is set to the parent
953 * directory of the script.
956 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
959 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
960 if (retval == (VALUE)6 && status == -1)
961 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
971 * Get the directory suitable for storing user documents. On Windows
972 * it is the home directory + "My Documents". On other platforms
973 * it is the home directory.
976 s_Kernel_DocumentHome(VALUE self)
978 char *s = MyAppCallback_getDocumentHomeDir();
979 VALUE retval = Ruby_NewFileStringValue(s);
984 /* The callback function for call_subprocess */
986 s_Kernel_CallSubProcess_Callback(void *data)
989 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
990 if (status != 0 || retval == Qnil || retval == Qfalse)
997 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
999 * Call subprocess. A progress dialog window is displayed, with a message
1000 * "Running #{process_name}...".
1001 * A callback proc can be given, which is called periodically during execution. If the proc returns
1002 * nil or false, then the execution will be interrupted.
1003 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
1004 * filename begins with ">>", then the message will be appended to the file.
1005 * If the filename is "/dev/null" or "NUL", then the message will be lost.
1006 * If the argument is nil, then the message will be sent to the Ruby console.
1009 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
1011 VALUE cmd, procname, cproc, stdout_val, stderr_val;
1012 VALUE save_interruptFlag;
1013 int n, exitstatus, pid;
1015 const char *pnamestr;
1016 FILE *fpout, *fperr;
1018 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1020 if (stdout_val == Qnil) {
1023 sout = StringValuePtr(stdout_val);
1024 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1027 if (strncmp(sout, ">>", 2) == 0) {
1029 fpout = fopen(sout, "a");
1033 fpout = fopen(sout, "w");
1036 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1039 if (stderr_val == Qnil) {
1042 serr = StringValuePtr(stderr_val);
1043 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1046 if (strncmp(serr, ">>", 2) == 0) {
1048 fpout = fopen(serr, "a");
1052 fperr = fopen(serr, "w");
1055 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1059 save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1060 if (procname != Qnil)
1061 pnamestr = StringValuePtr(procname);
1062 else pnamestr = NULL;
1063 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1064 s_SetInterruptFlag(self, save_interruptFlag);
1066 if (fpout != NULL && fpout != (FILE *)1)
1068 if (fperr != NULL && fperr != (FILE *)1)
1080 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
1083 s_Kernel_Backquote(VALUE self, VALUE cmd)
1086 int n, exitstatus, pid;
1088 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1089 /* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1090 if (n >= 0 && buf != NULL) {
1091 val = Ruby_NewEncodedStringValue(buf, 0);
1094 val = Ruby_NewEncodedStringValue("", 0);
1096 rb_last_status_set(exitstatus, pid);
1100 #pragma mark ====== User defaults ======
1104 * get_global_settings(key)
1106 * Get a setting data for key from the application preferences.
1109 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1111 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1113 VALUE retval = rb_eval_string(p);
1121 * set_global_settings(key, value)
1123 * Set a setting data for key to the application preferences.
1126 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1128 VALUE sval = rb_inspect(value);
1129 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1133 #pragma mark ====== IO extension ======
1136 s_Ruby_str_encode_protected(VALUE val)
1138 return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1139 ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1146 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1149 s_IO_gets_any_eol(VALUE self)
1151 VALUE val, val2, cval;
1154 static ID id_getbyte = 0, id_ungetbyte;
1155 if (id_getbyte == 0) {
1156 id_getbyte = rb_intern("getbyte");
1157 id_ungetbyte = rb_intern("ungetbyte");
1161 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1162 c = NUM2INT(rb_Integer(cval));
1164 cval = rb_funcall(self, id_getbyte, 0);
1166 c = NUM2INT(rb_Integer(cval));
1168 rb_funcall(self, id_ungetbyte, 1, cval);
1171 } else if (c != 0x0a) {
1176 val = rb_str_new(buf, i);
1178 rb_str_append(val, rb_str_new(buf, i));
1183 if (cval == Qnil && i == 0 && val == Qnil)
1184 return Qnil; /* End of file */
1187 val = rb_str_new(buf, i);
1189 rb_str_append(val, rb_str_new(buf, i));
1190 val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
1194 /* Needs a end-of-line mark */
1195 cval = rb_gv_get("$/");
1196 rb_str_append(val, cval);
1198 rb_gv_set("$_", val);
1202 #pragma mark ====== Utility functions (protected funcall) ======
1204 struct Ruby_funcall2_record {
1212 s_Ruby_funcall2_sub(VALUE data)
1214 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1215 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1219 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1221 struct Ruby_funcall2_record rec;
1226 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1230 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1232 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1235 #pragma mark ====== ParameterRef Class ======
1238 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1242 Data_Get_Struct(self, ParameterRef, pref);
1244 *typep = pref->parType;
1245 if (pref->parType == kElementParType) {
1246 up = (UnionPar *)&gElementParameters[pref->idx];
1248 up = ParameterRefGetPar(pref);
1249 if (checkEditable) {
1251 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1252 if (up->bond.src != 0 && up->bond.src != -1)
1253 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1260 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1264 Data_Get_Struct(self, ParameterRef, pref);
1265 if (pref->mol == NULL)
1267 up = ParameterRefGetPar(pref);
1268 if (key != s_SourceSym)
1269 up->bond.src = 0; /* Becomes automatically molecule-local */
1270 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1273 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1274 MolActionCallback_registerUndo(pref->mol, act);
1275 MoleculeCallback_notifyModification(pref->mol, 0);
1276 pref->mol->needsMDRebuild = 1;
1281 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1283 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1285 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1287 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1291 s_AtomTypeIndexFromValue(VALUE val)
1293 if (rb_obj_is_kind_of(val, rb_cNumeric))
1294 return NUM2INT(val);
1296 return AtomTypeEncodeToUInt(StringValuePtr(val));
1299 static const char *s_ParameterTypeNames[] = {
1300 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1302 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1305 s_ParTypeFromValue(VALUE val)
1309 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1310 if (s_ParameterTypeIDs[0] == 0) {
1311 for (i = 0; i < n; i++)
1312 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1314 valid = rb_to_id(val);
1315 for (i = 0; i < n; i++) {
1316 if (valid == s_ParameterTypeIDs[i]) {
1318 return kElementParType;
1319 else return kFirstParType + i;
1322 return kInvalidParType;
1329 * Get the index in the parameter list.
1331 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1333 Data_Get_Struct(self, ParameterRef, pref);
1334 return INT2NUM(pref->idx);
1339 * par_type -> String
1341 * Get the parameter type, like "bond", "angle", etc.
1343 static VALUE s_ParameterRef_GetParType(VALUE self) {
1345 s_UnionParFromValue(self, &tp, 0);
1346 if (tp == kElementParType)
1347 return Ruby_NewEncodedStringValue2("element");
1348 tp -= kFirstParType;
1349 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1350 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1351 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1356 * atom_type -> String or Array of String
1357 * atom_types -> String or Array of String
1359 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1360 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1361 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1362 * The atom type may be "X", which is a wildcard that matches any atom type.
1364 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1369 up = s_UnionParFromValue(self, &tp, 0);
1370 n = ParameterGetAtomTypes(tp, up, types);
1372 rb_raise(rb_eMolbyError, "invalid member atom_types");
1373 for (i = 0; i < n; i++) {
1374 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1375 vals[i] = INT2NUM(types[i]);
1377 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1382 return rb_ary_new4(n, vals);
1389 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1391 static VALUE s_ParameterRef_GetK(VALUE self) {
1395 up = s_UnionParFromValue(self, &tp, 0);
1398 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1400 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1401 case kDihedralParType:
1402 case kImproperParType:
1403 if (up->torsion.mult == 1)
1404 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1405 n = up->torsion.mult;
1408 for (i = 0; i < n; i++)
1409 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1410 return rb_ary_new4(n, vals);
1412 rb_raise(rb_eMolbyError, "invalid member k");
1420 * Get the equilibrium bond length. Only available for bond parameters.
1422 static VALUE s_ParameterRef_GetR0(VALUE self) {
1425 up = s_UnionParFromValue(self, &tp, 0);
1426 if (tp == kBondParType)
1427 return rb_float_new(up->bond.r0);
1428 else rb_raise(rb_eMolbyError, "invalid member r0");
1435 * Get the equilibrium angle (in degree). Only available for angle parameters.
1437 static VALUE s_ParameterRef_GetA0(VALUE self) {
1440 up = s_UnionParFromValue(self, &tp, 0);
1441 if (tp == kAngleParType)
1442 return rb_float_new(up->angle.a0 * kRad2Deg);
1443 else rb_raise(rb_eMolbyError, "invalid member a0");
1450 * Get the multiplicity. Only available for dihedral and improper parameters.
1451 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1453 static VALUE s_ParameterRef_GetMult(VALUE self) {
1456 up = s_UnionParFromValue(self, &tp, 0);
1457 if (tp == kDihedralParType || tp == kImproperParType)
1458 return rb_float_new(up->torsion.mult);
1459 else rb_raise(rb_eMolbyError, "invalid member mult");
1464 * period -> Integer or Array of Integers
1466 * Get the periodicity. Only available for dihedral and improper parameters.
1467 * If the multiplicity is larger than 1, then an array of integers is returned.
1468 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1470 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1474 up = s_UnionParFromValue(self, &tp, 0);
1475 if (tp == kDihedralParType || tp == kImproperParType) {
1476 if (up->torsion.mult == 1)
1477 return INT2NUM(up->torsion.period[0]);
1478 n = up->torsion.mult;
1481 for (i = 0; i < n; i++)
1482 vals[i] = INT2NUM(up->torsion.period[i]);
1483 return rb_ary_new4(n, vals);
1484 } else rb_raise(rb_eMolbyError, "invalid member period");
1489 * phi0 -> Float or Array of Floats
1491 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1492 * If the multiplicity is larger than 1, then an array of floats is returned.
1493 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1495 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1499 up = s_UnionParFromValue(self, &tp, 0);
1500 if (tp == kDihedralParType || tp == kImproperParType) {
1501 if (up->torsion.mult == 1)
1502 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1503 n = up->torsion.mult;
1506 for (i = 0; i < n; i++)
1507 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1508 return rb_ary_new4(n, vals);
1509 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1516 * Get the "A" value for the van der Waals parameter.
1519 static VALUE s_ParameterRef_GetA(VALUE self) {
1522 up = s_UnionParFromValue(self, &tp, 0);
1523 if (tp == kVdwParType)
1524 return rb_float_new(up->vdw.A);
1525 else if (tp == kVdwPairParType)
1526 return rb_float_new(up->vdwp.A);
1527 else rb_raise(rb_eMolbyError, "invalid member A");
1535 * Get the "B" value for the van der Waals parameter.
1538 static VALUE s_ParameterRef_GetB(VALUE self) {
1541 up = s_UnionParFromValue(self, &tp, 0);
1542 if (tp == kVdwParType)
1543 return rb_float_new(up->vdw.B);
1544 else if (tp == kVdwPairParType)
1545 return rb_float_new(up->vdwp.B);
1546 else rb_raise(rb_eMolbyError, "invalid member B");
1554 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1556 static VALUE s_ParameterRef_GetReq(VALUE self) {
1559 /* Double a, b, r; */
1561 up = s_UnionParFromValue(self, &tp, 0);
1562 if (tp == kVdwParType) {
1566 } else if (tp == kVdwPairParType) {
1570 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1571 /* if (a == 0.0 || b == 0.0) */
1572 return rb_float_new(r);
1573 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1580 * Get the minimum energy for the van der Waals parameter.
1582 static VALUE s_ParameterRef_GetEps(VALUE self) {
1587 up = s_UnionParFromValue(self, &tp, 0);
1588 if (tp == kVdwParType) {
1592 } else if (tp == kVdwPairParType) {
1596 } else rb_raise(rb_eMolbyError, "invalid member eps");
1597 /* if (a == 0.0 || b == 0.0) */
1598 return rb_float_new(eps * INTERNAL2KCAL);
1599 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1606 * Get the "A" value for the 1-4 van der Waals parameter.
1609 static VALUE s_ParameterRef_GetA14(VALUE self) {
1612 up = s_UnionParFromValue(self, &tp, 0);
1613 if (tp == kVdwParType)
1614 return rb_float_new(up->vdw.A14);
1615 else if (tp == kVdwPairParType)
1616 return rb_float_new(up->vdwp.A14);
1617 else rb_raise(rb_eMolbyError, "invalid member A14");
1625 * Get the "B" value for the 1-4 van der Waals parameter.
1628 static VALUE s_ParameterRef_GetB14(VALUE self) {
1631 up = s_UnionParFromValue(self, &tp, 0);
1632 if (tp == kVdwParType)
1633 return rb_float_new(up->vdw.B14);
1634 else if (tp == kVdwPairParType)
1635 return rb_float_new(up->vdwp.B14);
1636 else rb_raise(rb_eMolbyError, "invalid member B14");
1644 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1646 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1649 /* Double a, b, r; */
1651 up = s_UnionParFromValue(self, &tp, 0);
1652 if (tp == kVdwParType) {
1656 } else if (tp == kVdwPairParType) {
1657 /* a = up->vdwp.A14;
1658 b = up->vdwp.B14; */
1659 r = up->vdwp.r_eq14;
1660 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1661 /* if (a == 0.0 || b == 0.0) */
1662 return rb_float_new(r);
1663 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1670 * Get the minimum energy for the 1-4 van der Waals parameter.
1672 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1677 up = s_UnionParFromValue(self, &tp, 0);
1678 if (tp == kVdwParType) {
1681 eps = up->vdw.eps14;
1682 } else if (tp == kVdwPairParType) {
1683 /* a = up->vdwp.A14;
1684 b = up->vdwp.B14; */
1685 eps = up->vdwp.eps14;
1686 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1687 /* if (a == 0.0 || b == 0.0) */
1688 return rb_float_new(eps * INTERNAL2KCAL);
1689 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1696 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1698 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1701 up = s_UnionParFromValue(self, &tp, 0);
1702 if (tp == kVdwCutoffParType)
1703 return rb_float_new(up->vdwcutoff.cutoff);
1704 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1711 * Get the atomic (covalent) radius for the element parameter.
1713 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1716 up = s_UnionParFromValue(self, &tp, 0);
1717 if (tp == kElementParType)
1718 return rb_float_new(up->atom.radius);
1719 else rb_raise(rb_eMolbyError, "invalid member radius");
1724 * vdw_radius -> Float
1726 * Get the van der Waals radius for the element parameter. (0 if not given)
1728 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1731 up = s_UnionParFromValue(self, &tp, 0);
1732 if (tp == kElementParType)
1733 return rb_float_new(up->atom.vdw_radius);
1734 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1739 * color -> [Float, Float, Float]
1741 * Get the rgb color for the element parameter.
1743 static VALUE s_ParameterRef_GetColor(VALUE self) {
1746 up = s_UnionParFromValue(self, &tp, 0);
1747 if (tp == kElementParType)
1748 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));
1749 else rb_raise(rb_eMolbyError, "invalid member color");
1754 * atomic_number -> Integer
1756 * Get the atomic number for the vdw or element parameter.
1758 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1761 up = s_UnionParFromValue(self, &tp, 0);
1762 if (tp == kElementParType)
1763 return INT2NUM(up->atom.number);
1764 else if (tp == kVdwParType)
1765 return INT2NUM(up->vdw.atomicNumber);
1766 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1773 * Get the name for the element parameter.
1775 static VALUE s_ParameterRef_GetName(VALUE self) {
1778 up = s_UnionParFromValue(self, &tp, 0);
1779 if (tp == kElementParType) {
1781 strncpy(name, up->atom.name, 4);
1783 return Ruby_NewEncodedStringValue2(name);
1784 } else rb_raise(rb_eMolbyError, "invalid member name");
1791 * Get the atomic weight for the element parameter.
1793 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1796 up = s_UnionParFromValue(self, &tp, 0);
1797 if (tp == kElementParType)
1798 return rb_float_new(up->atom.weight);
1799 else if (tp == kVdwParType)
1800 return rb_float_new(up->vdw.weight);
1801 else rb_raise(rb_eMolbyError, "invalid member weight");
1806 * fullname -> String
1808 * Get the full name for the element parameter.
1810 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1813 up = s_UnionParFromValue(self, &tp, 0);
1814 if (tp == kElementParType) {
1816 strncpy(fullname, up->atom.fullname, 15);
1818 return Ruby_NewEncodedStringValue2(fullname);
1819 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1826 * Get the comment for the parameter.
1828 static VALUE s_ParameterRef_GetComment(VALUE self) {
1831 up = s_UnionParFromValue(self, &tp, 0);
1835 else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1842 * Get the source string for the parameter. Returns false for undefined parameter,
1843 * and nil for "local" parameter that is specific for the molecule.
1845 static VALUE s_ParameterRef_GetSource(VALUE self) {
1848 up = s_UnionParFromValue(self, &tp, 0);
1851 return Qfalse; /* undefined */
1853 return Qnil; /* local */
1854 else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1858 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1865 if (rb_obj_is_kind_of(val, rb_cString)) {
1866 char *s = StringValuePtr(val);
1868 for (i = 0; i < n; i++) {
1871 /* Skip leading separaters */
1872 while (*s == '-' || *s == ' ' || *s == '\t')
1874 for (p = s; *p != 0; p++) {
1875 if (*p == '-' || *p == ' ' || *p == '\t')
1879 if (len >= sizeof(buf))
1880 len = sizeof(buf) - 1;
1881 strncpy(buf, s, len);
1883 /* Skip trailing blanks */
1884 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1887 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1888 if (buf[0] >= '0' && buf[0] <= '9')
1889 types[i] = atoi(buf);
1891 types[i] = AtomTypeEncodeToUInt(buf);
1892 if (p == NULL || *p == 0) {
1898 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1901 val = rb_ary_to_ary(val);
1902 if (RARRAY_LEN(val) != n)
1903 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1904 valp = RARRAY_PTR(val);
1906 for (i = 0; i < n; i++) {
1907 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1908 types[i] = NUM2INT(rb_Integer(valp[i]));
1910 VALUE sval = valp[i];
1911 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1916 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1921 up = s_UnionParFromValue(self, &tp, 1);
1922 oldval = s_ParameterRef_GetAtomTypes(self);
1923 oldsrc = up->bond.src;
1926 s_ScanAtomTypes(val, 2, types);
1927 up->bond.type1 = types[0];
1928 up->bond.type2 = types[1];
1931 s_ScanAtomTypes(val, 3, types);
1932 up->angle.type1 = types[0];
1933 up->angle.type2 = types[1];
1934 up->angle.type3 = types[2];
1936 case kDihedralParType:
1937 case kImproperParType:
1938 s_ScanAtomTypes(val, 4, types);
1939 up->torsion.type1 = types[0];
1940 up->torsion.type2 = types[1];
1941 up->torsion.type3 = types[2];
1942 up->torsion.type4 = types[3];
1945 s_ScanAtomTypes(val, 1, types);
1946 up->vdw.type1 = types[0];
1948 case kVdwPairParType:
1949 s_ScanAtomTypes(val, 2, types);
1950 up->vdwp.type1 = types[0];
1951 up->vdwp.type2 = types[1];
1953 case kVdwCutoffParType:
1954 s_ScanAtomTypes(val, 2, types);
1955 up->vdwcutoff.type1 = types[0];
1956 up->vdwcutoff.type2 = types[1];
1961 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1965 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1967 Int tp, i, n, oldsrc;
1968 VALUE *valp, oldval;
1969 up = s_UnionParFromValue(self, &tp, 1);
1970 oldval = s_ParameterRef_GetK(self);
1971 oldsrc = up->bond.src;
1974 val = rb_Float(val);
1975 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1978 val = rb_Float(val);
1979 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1981 case kDihedralParType:
1982 case kImproperParType:
1983 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1984 up->torsion.mult = 1;
1985 val = rb_Float(val);
1986 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1989 n = up->torsion.mult;
1992 val = rb_ary_to_ary(val);
1993 if (RARRAY_LEN(val) != n)
1994 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1995 valp = RARRAY_PTR(val);
1996 for (i = 0; i < n; i++) {
1997 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
2001 rb_raise(rb_eMolbyError, "invalid member k");
2003 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2007 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2011 up = s_UnionParFromValue(self, &tp, 1);
2012 oldval = s_ParameterRef_GetR0(self);
2013 oldsrc = up->bond.src;
2014 if (tp == kBondParType) {
2015 val = rb_Float(val);
2016 up->bond.r0 = NUM2DBL(val);
2017 } else rb_raise(rb_eMolbyError, "invalid member r0");
2018 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2022 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2026 up = s_UnionParFromValue(self, &tp, 1);
2027 oldval = s_ParameterRef_GetA0(self);
2028 oldsrc = up->bond.src;
2029 if (tp == kAngleParType) {
2030 val = rb_Float(val);
2031 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2032 } else rb_raise(rb_eMolbyError, "invalid member a0");
2033 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2037 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2041 up = s_UnionParFromValue(self, &tp, 1);
2042 oldval = s_ParameterRef_GetMult(self);
2043 oldsrc = up->bond.src;
2044 if (tp == kDihedralParType || tp == kImproperParType) {
2046 val = rb_Integer(val);
2049 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2050 up->torsion.mult = i;
2051 } else rb_raise(rb_eMolbyError, "invalid member mult");
2052 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2056 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2058 Int tp, i, n, oldsrc;
2059 VALUE *valp, oldval;
2060 up = s_UnionParFromValue(self, &tp, 1);
2061 oldval = s_ParameterRef_GetPeriod(self);
2062 oldsrc = up->bond.src;
2063 if (tp == kDihedralParType || tp == kImproperParType) {
2064 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2065 up->torsion.mult = 1;
2066 val = rb_Integer(val);
2067 up->torsion.period[0] = NUM2INT(val);
2069 n = up->torsion.mult;
2072 val = rb_ary_to_ary(val);
2073 if (RARRAY_LEN(val) != n)
2074 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2075 valp = RARRAY_PTR(val);
2076 for (i = 0; i < n; i++) {
2077 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2080 } else rb_raise(rb_eMolbyError, "invalid member period");
2081 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2085 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2087 Int tp, i, n, oldsrc;
2088 VALUE *valp, oldval;
2089 up = s_UnionParFromValue(self, &tp, 1);
2090 oldval = s_ParameterRef_GetPhi0(self);
2091 oldsrc = up->bond.src;
2092 if (tp == kDihedralParType || tp == kImproperParType) {
2093 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2094 up->torsion.mult = 1;
2095 val = rb_Float(val);
2096 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2098 n = up->torsion.mult;
2101 val = rb_ary_to_ary(val);
2102 if (RARRAY_LEN(val) != n)
2103 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2104 valp = RARRAY_PTR(val);
2105 for (i = 0; i < n; i++)
2106 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2108 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2109 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2114 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2119 up = s_UnionParFromValue(self, &tp, 1);
2120 oldval = s_ParameterRef_GetA(self);
2121 oldsrc = up->bond.src;
2122 val = rb_Float(val);
2124 if (tp == kVdwParType)
2126 else if (tp == kVdwPairParType)
2128 else rb_raise(rb_eMolbyError, "invalid member A");
2129 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2133 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2138 up = s_UnionParFromValue(self, &tp, 1);
2139 oldval = s_ParameterRef_GetB(self);
2140 oldsrc = up->bond.src;
2141 val = rb_Float(val);
2143 if (tp == kVdwParType)
2145 else if (tp == kVdwPairParType)
2147 else rb_raise(rb_eMolbyError, "invalid member B");
2148 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2153 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2158 up = s_UnionParFromValue(self, &tp, 1);
2159 oldval = s_ParameterRef_GetReq(self);
2160 oldsrc = up->bond.src;
2161 val = rb_Float(val);
2163 if (tp == kVdwParType) {
2165 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2166 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2167 } else if (tp == kVdwPairParType) {
2169 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2170 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2171 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2172 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2176 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2181 up = s_UnionParFromValue(self, &tp, 1);
2182 oldval = s_ParameterRef_GetEps(self);
2183 oldsrc = up->bond.src;
2184 val = rb_Float(val);
2185 e = NUM2DBL(val) * KCAL2INTERNAL;
2186 if (tp == kVdwParType) {
2188 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2189 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2190 } else if (tp == kVdwPairParType) {
2192 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2193 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2194 } else rb_raise(rb_eMolbyError, "invalid member eps");
2195 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2200 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2205 up = s_UnionParFromValue(self, &tp, 1);
2206 oldval = s_ParameterRef_GetA14(self);
2207 oldsrc = up->bond.src;
2208 val = rb_Float(val);
2210 if (tp == kVdwParType)
2212 else if (tp == kVdwPairParType)
2214 else rb_raise(rb_eMolbyError, "invalid member A14");
2215 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2219 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2224 up = s_UnionParFromValue(self, &tp, 1);
2225 oldval = s_ParameterRef_GetB14(self);
2226 oldsrc = up->bond.src;
2227 val = rb_Float(val);
2229 if (tp == kVdwParType)
2231 else if (tp == kVdwPairParType)
2233 else rb_raise(rb_eMolbyError, "invalid member B14");
2234 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2239 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2244 up = s_UnionParFromValue(self, &tp, 1);
2245 oldval = s_ParameterRef_GetReq14(self);
2246 oldsrc = up->bond.src;
2247 val = rb_Float(val);
2249 if (tp == kVdwParType) {
2251 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2252 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2253 } else if (tp == kVdwPairParType) {
2254 up->vdwp.r_eq14 = r;
2255 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2256 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2257 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2258 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2262 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2267 up = s_UnionParFromValue(self, &tp, 1);
2268 oldval = s_ParameterRef_GetEps14(self);
2269 oldsrc = up->bond.src;
2270 val = rb_Float(val);
2271 e = NUM2DBL(val) * KCAL2INTERNAL;
2272 if (tp == kVdwParType) {
2274 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2275 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2276 } else if (tp == kVdwPairParType) {
2278 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2279 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2280 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2281 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2285 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2289 oldval = s_ParameterRef_GetCutoff(self);
2290 oldsrc = up->bond.src;
2291 up = s_UnionParFromValue(self, &tp, 1);
2292 val = rb_Float(val);
2293 if (tp == kVdwCutoffParType) {
2294 up->vdwcutoff.cutoff = NUM2DBL(val);
2295 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2296 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2300 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2304 up = s_UnionParFromValue(self, &tp, 1);
2305 oldval = s_ParameterRef_GetRadius(self);
2306 oldsrc = up->bond.src;
2307 val = rb_Float(val);
2308 if (tp == kElementParType) {
2309 up->atom.radius = NUM2DBL(val);
2310 } else rb_raise(rb_eMolbyError, "invalid member radius");
2311 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2315 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2319 up = s_UnionParFromValue(self, &tp, 1);
2320 oldval = s_ParameterRef_GetVdwRadius(self);
2321 oldsrc = up->bond.src;
2322 val = rb_Float(val);
2323 if (tp == kElementParType) {
2324 up->atom.vdw_radius = NUM2DBL(val);
2325 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2326 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2330 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2333 VALUE *valp, oldval;
2334 up = s_UnionParFromValue(self, &tp, 1);
2335 oldval = s_ParameterRef_GetColor(self);
2336 oldsrc = up->bond.src;
2337 val = rb_ary_to_ary(val);
2338 if (RARRAY_LEN(val) != 3)
2339 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2340 valp = RARRAY_PTR(val);
2341 if (tp == kElementParType) {
2342 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2343 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2344 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2345 } else rb_raise(rb_eMolbyError, "invalid member color");
2346 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2350 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2354 up = s_UnionParFromValue(self, &tp, 1);
2355 oldval = s_ParameterRef_GetAtomicNumber(self);
2356 oldsrc = up->bond.src;
2357 val = rb_Integer(val);
2358 if (tp == kElementParType)
2359 up->atom.number = NUM2INT(val);
2360 else if (tp == kVdwParType) {
2361 up->vdw.atomicNumber = NUM2INT(val);
2362 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2363 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2364 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2368 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2372 up = s_UnionParFromValue(self, &tp, 1);
2373 oldval = s_ParameterRef_GetName(self);
2374 oldsrc = up->bond.src;
2375 if (tp == kElementParType) {
2376 strncpy(up->atom.name, StringValuePtr(val), 4);
2377 } else rb_raise(rb_eMolbyError, "invalid member name");
2378 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2382 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2386 val = rb_Float(val);
2387 oldval = s_ParameterRef_GetWeight(self);
2388 up = s_UnionParFromValue(self, &tp, 1);
2389 oldsrc = up->bond.src;
2390 if (tp == kElementParType)
2391 up->atom.weight = NUM2DBL(val);
2392 else if (tp == kVdwParType)
2393 up->vdw.weight = NUM2DBL(val);
2394 else rb_raise(rb_eMolbyError, "invalid member weight");
2395 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2399 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2403 up = s_UnionParFromValue(self, &tp, 1);
2404 oldval = s_ParameterRef_GetFullName(self);
2405 oldsrc = up->bond.src;
2406 if (tp == kElementParType) {
2407 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2408 up->atom.fullname[15] = 0;
2409 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2410 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2414 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2416 Int tp, com, oldsrc;
2418 up = s_UnionParFromValue(self, &tp, 1);
2419 oldval = s_ParameterRef_GetComment(self);
2420 oldsrc = up->bond.src;
2424 com = ParameterCommentIndex(StringValuePtr(val));
2427 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2431 /* Only false (undefined) and nil (local) can be set */
2432 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2436 up = s_UnionParFromValue(self, &tp, 1);
2437 if (val != Qfalse && val != Qnil)
2438 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2439 oldval = s_ParameterRef_GetSource(self);
2440 oldsrc = up->bond.src;
2441 if (oldsrc != 0 && oldsrc != -1)
2442 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2443 up->bond.src = (val == Qfalse ? -1 : 0);
2444 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2448 static struct s_ParameterAttrDef {
2450 VALUE *symref; /* Address of s_IndexSymbol etc. */
2451 ID id; /* Will be set within InitMolby() */
2452 VALUE (*getter)(VALUE);
2453 VALUE (*setter)(VALUE, VALUE);
2454 } s_ParameterAttrDefTable[] = {
2455 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2456 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2457 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2458 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2459 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2460 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2461 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2462 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2463 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2464 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2465 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2466 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2467 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2468 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2469 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2470 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2471 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2472 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2473 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2474 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2475 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2476 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2477 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2478 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2479 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2480 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2481 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2482 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2483 {NULL} /* Sentinel */
2487 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2491 if (TYPE(key) != T_SYMBOL) {
2492 kid = rb_intern(StringValuePtr(key));
2494 } else kid = SYM2ID(key);
2495 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2496 if (s_ParameterAttrDefTable[i].id == kid) {
2497 if (value == Qundef)
2498 return (*(s_ParameterAttrDefTable[i].getter))(self);
2499 else if (s_ParameterAttrDefTable[i].setter == NULL)
2500 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2502 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2505 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2506 return Qnil; /* not reached */
2510 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2512 return s_ParameterRef_SetAttr(self, key, Qundef);
2517 * keys(idx) -> array of valid parameter attributes
2519 * Returns an array of valid parameter attributes (as Symbols).
2522 s_ParameterRef_Keys(VALUE self)
2525 Data_Get_Struct(self, ParameterRef, pref);
2526 switch (pref->parType) {
2528 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2530 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2531 case kDihedralParType:
2532 case kImproperParType:
2533 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2535 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);
2536 case kVdwPairParType:
2537 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2538 case kVdwCutoffParType:
2539 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2540 case kElementParType:
2541 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);
2543 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2545 return Qnil; /* Not reached */
2550 * to_hash(idx) -> Hash
2552 * Returns a hash containing valid parameter names and values
2555 s_ParameterRef_ToHash(VALUE self)
2557 VALUE keys = s_ParameterRef_Keys(self);
2562 retval = rb_hash_new();
2563 for (i = 0; i < RARRAY_LEN(keys); i++) {
2564 VALUE key = RARRAY_PTR(keys)[i];
2565 VALUE val = s_ParameterRef_GetAttr(self, key);
2566 rb_hash_aset(retval, key, val);
2573 * parameter.to_s(idx) -> String
2575 * Returns a string representation of the given parameter
2578 s_ParameterRef_ToString(VALUE self)
2581 char buf[1024], types[4][8];
2582 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2585 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);
2588 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);
2590 case kDihedralParType:
2591 case kImproperParType:
2592 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]));
2594 for (i = 0; i < up->torsion.mult; i++) {
2595 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);
2600 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);
2602 case kVdwPairParType:
2603 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);
2605 case kVdwCutoffParType:
2606 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);
2608 case kElementParType:
2609 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);
2612 return Ruby_NewEncodedStringValue2(buf);
2617 * self == parameterRef -> boolean
2619 * True if the parameters point to the same parameter record.
2622 s_ParameterRef_Equal(VALUE self, VALUE val)
2625 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2626 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2627 } else return Qfalse;
2630 #pragma mark ====== Parameter Class ======
2632 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2633 * is NULL, then the global parameters are looked for. */
2635 /* Rebuild the MD parameter record if necessary: may throw an exception */
2636 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2638 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2641 Data_Get_Struct(val, Molecule, mol);
2643 rb_raise(rb_eMolbyError, "the molecule is empty");
2644 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2645 /* Do self.md_arena.prepare */
2646 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2648 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2653 s_NewParameterValueFromValue(VALUE val)
2656 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2657 Data_Get_Struct(val, Molecule, mol);
2658 s_RebuildMDParameterIfNecessary(val, Qtrue);
2659 MoleculeRetain(mol);
2660 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2663 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2668 s_MoleculeFromParameterValue(VALUE val)
2671 Data_Get_Struct(val, Molecule, mol);
2676 s_ParameterFromParameterValue(VALUE val)
2679 Data_Get_Struct(val, Molecule, mol);
2682 return gBuiltinParameters;
2685 /* Forward declarations */
2686 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2687 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2690 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2692 if (val == rb_cParameter) {
2693 return NULL; /* Parameter class method: builtin parameters */
2694 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2695 return s_MoleculeFromParameterValue(val);
2696 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2697 return s_MoleculeFromParEnumerableValue(val);
2703 * builtin -> Parameter
2705 * Returns a parameter value that points to the global (builtin) parameters.
2706 * Equivalent to Parameter::Builtin (constant).
2709 s_Parameter_Builtin(VALUE self)
2711 static ID s_builtin_id = 0;
2712 if (s_builtin_id == 0)
2713 s_builtin_id = rb_intern("Builtin");
2714 return rb_const_get(rb_cParameter, s_builtin_id);
2719 * bond(idx) -> ParameterRef
2721 * The index-th bond parameter record is returned.
2724 s_Parameter_Bond(VALUE self, VALUE ival)
2728 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2729 idx = NUM2INT(rb_Integer(ival));
2731 n = gBuiltinParameters->nbondPars;
2732 else if (mol->par != NULL)
2733 n = mol->par->nbondPars;
2735 if (idx < -n || idx >= n)
2736 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2739 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2744 * angle(idx) -> ParameterRef
2746 * The index-th angle parameter record is returned.
2749 s_Parameter_Angle(VALUE self, VALUE ival)
2753 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2754 idx = NUM2INT(rb_Integer(ival));
2756 n = gBuiltinParameters->nanglePars;
2757 else if (mol->par != NULL)
2758 n = mol->par->nanglePars;
2760 if (idx < -n || idx >= n)
2761 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2764 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2769 * dihedral(idx) -> ParameterRef
2771 * The index-th dihedral parameter record is returned.
2774 s_Parameter_Dihedral(VALUE self, VALUE ival)
2778 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2779 idx = NUM2INT(rb_Integer(ival));
2781 n = gBuiltinParameters->ndihedralPars;
2782 else if (mol->par != NULL)
2783 n = mol->par->ndihedralPars;
2785 if (idx < -n || idx >= n)
2786 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2789 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2794 * improper(idx) -> ParameterRef
2796 * The index-th improper parameter record is returned.
2799 s_Parameter_Improper(VALUE self, VALUE ival)
2803 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2804 idx = NUM2INT(rb_Integer(ival));
2806 n = gBuiltinParameters->nimproperPars;
2807 else if (mol->par != NULL)
2808 n = mol->par->nimproperPars;
2810 if (idx < -n || idx >= n)
2811 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2814 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2819 * vdw(idx) -> ParameterRef
2821 * The index-th vdw parameter record is returned.
2824 s_Parameter_Vdw(VALUE self, VALUE ival)
2828 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2829 idx = NUM2INT(rb_Integer(ival));
2831 n = gBuiltinParameters->nvdwPars;
2832 else if (mol->par != NULL)
2833 n = mol->par->nvdwPars;
2835 if (idx < -n || idx >= n)
2836 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2839 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2844 * vdw_pair(idx) -> ParameterRef
2846 * The index-th vdw pair parameter record is returned.
2849 s_Parameter_VdwPair(VALUE self, VALUE ival)
2853 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2854 idx = NUM2INT(rb_Integer(ival));
2856 n = gBuiltinParameters->nvdwpPars;
2857 else if (mol->par != NULL)
2858 n = mol->par->nvdwpPars;
2860 if (idx < -n || idx >= n)
2861 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2864 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2869 * vdw_cutoff(idx) -> ParameterRef
2871 * The index-th vdw cutoff parameter record is returned.
2874 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2878 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2879 idx = NUM2INT(rb_Integer(ival));
2881 n = gBuiltinParameters->nvdwCutoffPars;
2882 else if (mol->par != NULL)
2883 n = mol->par->nvdwCutoffPars;
2885 if (idx < -n || idx >= n)
2886 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2889 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2894 * element(idx) -> ParameterRef
2895 * element(t1) -> ParameterRef
2897 * In the first form, the index-th element parameter record is returned. In the second
2898 * form, the element parameter for t1 is looked up (the last index first). t1
2899 * is the element name string (up to 4 characters).
2900 * Unlike other Parameter methods, this is used only for the global parameter.
2903 s_Parameter_Element(VALUE self, VALUE ival)
2906 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2907 int n = gCountElementParameters;
2908 idx1 = NUM2INT(rb_Integer(ival));
2909 if (idx1 < -n || idx1 >= n)
2910 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2913 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2918 strncpy(name, StringValuePtr(ival), 4);
2920 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2921 if (strncmp(ep->name, name, 4) == 0)
2922 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2932 * Returns the number of bond parameters.
2935 s_Parameter_Nbonds(VALUE self)
2938 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2940 n = gBuiltinParameters->nbondPars;
2941 else if (mol->par != NULL)
2942 n = mol->par->nbondPars;
2949 * nangles -> Integer
2951 * Returns the number of angle parameters.
2954 s_Parameter_Nangles(VALUE self)
2957 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2959 n = gBuiltinParameters->nanglePars;
2960 else if (mol->par != NULL)
2961 n = mol->par->nanglePars;
2968 * ndihedrals -> Integer
2970 * Returns the number of dihedral parameters.
2973 s_Parameter_Ndihedrals(VALUE self)
2976 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2978 n = gBuiltinParameters->ndihedralPars;
2979 else if (mol->par != NULL)
2980 n = mol->par->ndihedralPars;
2987 * nimpropers -> Integer
2989 * Returns the number of improper parameters.
2992 s_Parameter_Nimpropers(VALUE self)
2995 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2997 n = gBuiltinParameters->nimproperPars;
2998 else if (mol->par != NULL)
2999 n = mol->par->nimproperPars;
3008 * Returns the number of vdw parameters.
3011 s_Parameter_Nvdws(VALUE self)
3014 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3016 n = gBuiltinParameters->nvdwPars;
3017 else if (mol->par != NULL)
3018 n = mol->par->nvdwPars;
3025 * nvdw_pairs -> Integer
3027 * Returns the number of vdw pair parameters.
3030 s_Parameter_NvdwPairs(VALUE self)
3033 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3035 n = gBuiltinParameters->nvdwpPars;
3036 else if (mol->par != NULL)
3037 n = mol->par->nvdwpPars;
3044 * nvdw_cutoffs -> Integer
3046 * Returns the number of vdw cutoff parameters.
3049 s_Parameter_NvdwCutoffs(VALUE self)
3052 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3054 n = gBuiltinParameters->nvdwCutoffPars;
3055 else if (mol->par != NULL)
3056 n = mol->par->nvdwCutoffPars;
3063 * nelements -> Integer
3065 * Returns the number of element parameters.
3068 s_Parameter_Nelements(VALUE self)
3070 return INT2NUM(gCountElementParameters);
3075 * bonds -> ParEnumerable
3077 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3078 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3079 * useful when all accessible parameters should be examined by use of 'each' method.
3082 s_Parameter_Bonds(VALUE self)
3084 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3085 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3090 * angles -> ParEnumerable
3092 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3093 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3094 * useful when all accessible parameters should be examined by use of 'each' method.
3097 s_Parameter_Angles(VALUE self)
3099 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3100 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3105 * dihedrals -> ParEnumerable
3107 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3108 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3109 * useful when all accessible parameters should be examined by use of 'each' method.
3112 s_Parameter_Dihedrals(VALUE self)
3114 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3115 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3120 * impropers -> ParEnumerable
3122 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3123 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3124 * useful when all accessible parameters should be examined by use of 'each' method.
3127 s_Parameter_Impropers(VALUE self)
3129 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3130 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3135 * vdws -> ParEnumerable
3137 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3138 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3139 * useful when all accessible parameters should be examined by use of 'each' method.
3142 s_Parameter_Vdws(VALUE self)
3144 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3145 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3150 * vdw_pairs -> ParEnumerable
3152 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3153 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3154 * useful when all accessible parameters should be examined by use of 'each' method.
3157 s_Parameter_VdwPairs(VALUE self)
3159 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3160 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3165 * vdw_cutoffs -> ParEnumerable
3167 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3168 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3169 * useful when all accessible parameters should be examined by use of 'each' method.
3172 s_Parameter_VdwCutoffs(VALUE self)
3174 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3175 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3180 * elements -> ParEnumerable
3182 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3183 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3184 * useful when all accessible parameters should be examined by use of 'each' method.
3187 s_Parameter_Elements(VALUE self)
3189 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3190 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3194 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3196 VALUE atval, optval;
3199 int i, n, idx, flags, is_global;
3201 rb_scan_args(argc, argv, "1*", &atval, &optval);
3203 /* Get the atom types */
3205 case kBondParType: n = 2; break;
3206 case kAngleParType: n = 3; break;
3207 case kDihedralParType: n = 4; break;
3208 case kImproperParType: n = 4; break;
3209 case kVdwParType: n = 1; break;
3210 case kVdwPairParType: n = 2; break;
3211 default: return Qnil;
3213 s_ScanAtomTypes(atval, n, t);
3214 for (i = 0; i < n; i++) {
3215 if (t[i] < kAtomTypeMinimum) {
3216 /* Explicit atom index */
3218 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3219 if (t[i] >= mol->natoms)
3220 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3222 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3226 /* Analyze options */
3228 n = RARRAY_LEN(optval);
3229 for (i = 0; i < n; i++) {
3230 VALUE oval = RARRAY_PTR(optval)[i];
3231 if (oval == ID2SYM(rb_intern("global")))
3232 flags |= kParameterLookupGlobal;
3233 else if (oval == ID2SYM(rb_intern("local")))
3234 flags |= kParameterLookupLocal;
3235 else if (oval == ID2SYM(rb_intern("missing")))
3236 flags |= kParameterLookupMissing;
3237 else if (oval == ID2SYM(rb_intern("nowildcard")))
3238 flags |= kParameterLookupNoWildcard;
3239 else if (oval == ID2SYM(rb_intern("nobasetype")))
3240 flags |= kParameterLookupNoBaseAtomType;
3241 else if (oval == ID2SYM(rb_intern("create")))
3245 flags = kParameterLookupGlobal | kParameterLookupLocal;
3250 case kBondParType: {
3253 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3255 idx = bp - mol->par->bondPars;
3259 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3261 idx = bp - gBuiltinParameters->bondPars;
3266 case kAngleParType: {
3269 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3271 idx = ap - mol->par->anglePars;
3275 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3277 idx = ap - gBuiltinParameters->anglePars;
3282 case kDihedralParType: {
3285 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3287 idx = tp - mol->par->dihedralPars;
3291 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3293 idx = tp - gBuiltinParameters->dihedralPars;
3298 case kImproperParType: {
3301 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3303 idx = tp - mol->par->improperPars;
3307 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3309 idx = tp - gBuiltinParameters->improperPars;
3317 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3319 idx = vp - mol->par->vdwPars;
3323 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3325 idx = vp - gBuiltinParameters->vdwPars;
3330 case kVdwPairParType: {
3333 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3335 idx = vp - mol->par->vdwpPars;
3339 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3341 idx = vp - gBuiltinParameters->vdwpPars;
3350 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3353 /* Insert a new parameter record */
3355 Int count = ParameterGetCountForType(mol->par, parType);
3356 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3357 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3358 IntGroupRelease(ig);
3361 /* Set atom types */
3362 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3367 up->bond.type1 = t[0];
3368 up->bond.type2 = t[1];
3371 up->angle.type1 = t[0];
3372 up->angle.type2 = t[1];
3373 up->angle.type3 = t[2];
3375 case kDihedralParType:
3376 case kImproperParType:
3377 up->torsion.type1 = t[0];
3378 up->torsion.type2 = t[1];
3379 up->torsion.type3 = t[2];
3380 up->torsion.type4 = t[3];
3383 up->vdw.type1 = t[0];
3385 case kVdwPairParType:
3386 up->vdwp.type1 = t[0];
3387 up->vdwp.type2 = t[1];
3394 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3399 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3400 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3402 * Find the parameter record that matches the given atom types. The atom types are given
3403 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3404 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3405 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3406 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3409 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3412 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3414 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3415 parType = s_ParTypeFromValue(argv[0]);
3416 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3421 * self == parameter -> boolean
3423 * True if the parameters point to the same parameter table.
3426 s_Parameter_Equal(VALUE self, VALUE val)
3428 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3429 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3430 } else return Qfalse;
3433 #pragma mark ====== ParEnumerable Class ======
3435 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3436 and the parameter type. If the Molecule is NULL, then it refers to the
3437 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3438 the global parameters are always accessible. */
3440 typedef struct ParEnumerable {
3442 Int parType; /* Same as parType in ParameterRef */
3445 static ParEnumerable *
3446 s_ParEnumerableNew(Molecule *mol, Int parType)
3448 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3452 MoleculeRetain(mol);
3453 pen->parType = parType;
3459 s_ParEnumerableRelease(ParEnumerable *pen)
3462 if (pen->mol != NULL)
3463 MoleculeRelease(pen->mol);
3469 s_MoleculeFromParEnumerableValue(VALUE val)
3472 Data_Get_Struct(val, ParEnumerable, pen);
3477 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3479 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3481 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3482 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3487 * par_type -> String
3489 * Get the parameter type, like "bond", "angle", etc.
3492 s_ParEnumerable_ParType(VALUE self) {
3495 Data_Get_Struct(self, ParEnumerable, pen);
3497 if (tp == kElementParType)
3498 return Ruby_NewEncodedStringValue2("element");
3499 tp -= kFirstParType;
3500 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3501 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3502 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3507 * self[idx] -> ParameterRef
3509 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3510 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3511 * parent Parameter object of self.
3513 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3514 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3517 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3520 Data_Get_Struct(self, ParEnumerable, pen);
3521 switch (pen->parType) {
3522 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3523 case kBondParType: return s_Parameter_Bond(self, ival);
3524 case kAngleParType: return s_Parameter_Angle(self, ival);
3525 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3526 case kImproperParType: return s_Parameter_Improper(self, ival);
3527 case kVdwParType: return s_Parameter_Vdw(self, ival);
3528 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3529 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3530 case kElementParType: return s_Parameter_Element(self, ival);
3532 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3534 return Qnil; /* Not reached */
3541 * Returns the number of parameters included in this enumerable.
3544 s_ParEnumerable_Length(VALUE self)
3547 Data_Get_Struct(self, ParEnumerable, pen);
3548 switch (pen->parType) {
3549 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3550 case kBondParType: return s_Parameter_Nbonds(self);
3551 case kAngleParType: return s_Parameter_Nangles(self);
3552 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3553 case kImproperParType: return s_Parameter_Nimpropers(self);
3554 case kVdwParType: return s_Parameter_Nvdws(self);
3555 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3556 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3557 case kElementParType: return s_Parameter_Nelements(self);
3559 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3561 return Qnil; /* Not reached */
3568 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3571 s_ParEnumerable_Each(VALUE self)
3577 Data_Get_Struct(self, ParEnumerable, pen);
3578 if (pen->parType == kElementParType)
3579 n = gCountElementParameters;
3581 switch (pen->parType) {
3582 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3583 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3584 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3585 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3586 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3587 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3588 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3590 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3592 if (pen->mol == NULL)
3593 n = *((Int *)((char *)gBuiltinParameters + ofs));
3594 else if (pen->mol->par != NULL)
3595 n = *((Int *)((char *)(pen->mol->par) + ofs));
3598 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3599 Data_Get_Struct(aval, ParameterRef, pref);
3600 for (i = 0; i < n; i++) {
3609 * reverse_each {|pref| ...}
3611 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3614 s_ParEnumerable_ReverseEach(VALUE self)
3620 Data_Get_Struct(self, ParEnumerable, pen);
3621 if (pen->parType == kElementParType)
3622 n = gCountElementParameters;
3624 switch (pen->parType) {
3625 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3626 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3627 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3628 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3629 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3630 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3631 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3633 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3635 if (pen->mol == NULL)
3636 n = *((Int *)((char *)gBuiltinParameters + ofs));
3637 else if (pen->mol->par != NULL)
3638 n = *((Int *)((char *)(pen->mol->par) + ofs));
3641 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3642 Data_Get_Struct(aval, ParameterRef, pref);
3643 for (i = n - 1; i >= 0; i--) {
3652 * insert(idx = nil, pref = nil) -> ParameterRef
3654 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3655 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3656 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3657 * parameter is left undefined.
3658 * Throws an exception if ParEnumerable points to the global parameter.
3661 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3669 Data_Get_Struct(self, ParEnumerable, pen);
3670 if (pen->mol == NULL)
3671 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3672 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3673 rb_scan_args(argc, argv, "02", &ival, &pval);
3675 i = NUM2INT(rb_Integer(ival));
3677 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3682 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3683 if (up == NULL || type != pen->parType)
3684 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3685 ParameterCopyOneWithType(&u, up, pen->parType);
3688 memset(&u, 0, sizeof(u));
3691 ig = IntGroupNewWithPoints(n, 1, -1);
3692 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3694 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3695 MolActionCallback_registerUndo(pen->mol, act);
3696 MolActionRelease(act);
3698 IntGroupRelease(ig);
3699 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3707 * Delete the parameter(s) specified by the argument.
3710 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3715 Data_Get_Struct(self, ParEnumerable, pen);
3716 if (pen->mol == NULL)
3717 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3718 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3719 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3720 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3723 ig = IntGroupFromValue(ival);
3724 if ((i = IntGroupGetCount(ig)) == 0) {
3725 IntGroupRelease(ig);
3729 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3730 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3733 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3734 IntGroupRelease(ig);
3740 * lookup(atom_types, options, ...) -> ParameterRef
3741 * lookup(atom_type_string, options, ...) -> ParameterRef
3743 * Find the parameter record that matches the given atom types. The arguments are
3744 * the same as Parameter#lookup, except for the parameter type which is implicitly
3748 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3751 Data_Get_Struct(self, ParEnumerable, pen);
3752 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3757 * self == parEnumerable -> boolean
3759 * True if the arguments point to the same parameter table and type.
3762 s_ParEnumerable_Equal(VALUE self, VALUE val)
3764 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3765 ParEnumerable *pen1, *pen2;
3766 Data_Get_Struct(self, ParEnumerable, pen1);
3767 Data_Get_Struct(val, ParEnumerable, pen2);
3768 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3769 } else return Qfalse;
3772 #pragma mark ====== AtomRef Class ======
3774 /* Forward declaration for register undo */
3775 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3777 /* Ruby string "set_atom_attr" */
3778 static VALUE s_SetAtomAttrString;
3781 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3785 Data_Get_Struct(self, AtomRef, aref);
3786 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3787 if (idx < 0 || idx >= aref->mol->natoms)
3788 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3790 *app = aref->mol->atoms + idx;
3797 s_AtomFromValue(VALUE self)
3800 s_AtomIndexFromValue(self, &ap, NULL);
3805 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3808 s_AtomIndexFromValue(self, &ap, mpp);
3813 s_NotifyModificationForAtomRef(VALUE self)
3816 Data_Get_Struct(self, AtomRef, aref);
3817 MoleculeIncrementModifyCount(aref->mol);
3821 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3824 Data_Get_Struct(self, AtomRef, aref);
3825 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3828 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3829 MolActionCallback_registerUndo(aref->mol, act);
3830 MoleculeCallback_notifyModification(aref->mol, 0);
3831 /* Request MD rebuilt if necessary */
3832 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3833 aref->mol->needsMDRebuild = 1;
3838 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3841 aref = AtomRefNew(mol, idx);
3842 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3846 s_AtomRef_GetMolecule(VALUE self)
3849 s_AtomIndexFromValue(self, NULL, &mpp);
3850 return ValueFromMolecule(mpp);
3853 static VALUE s_AtomRef_GetIndex(VALUE self) {
3854 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3857 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3858 return INT2NUM(s_AtomFromValue(self)->segSeq);
3861 static VALUE s_AtomRef_GetSegName(VALUE self) {
3862 char *p = s_AtomFromValue(self)->segName;
3863 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3866 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3867 return INT2NUM(s_AtomFromValue(self)->resSeq);
3870 static VALUE s_AtomRef_GetResName(VALUE self) {
3871 char *p = s_AtomFromValue(self)->resName;
3872 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3875 static VALUE s_AtomRef_GetName(VALUE self) {
3876 char *p = s_AtomFromValue(self)->aname;
3877 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3880 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3881 int type = s_AtomFromValue(self)->type;
3882 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3883 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3886 static VALUE s_AtomRef_GetCharge(VALUE self) {
3887 return rb_float_new(s_AtomFromValue(self)->charge);
3890 static VALUE s_AtomRef_GetWeight(VALUE self) {
3891 return rb_float_new(s_AtomFromValue(self)->weight);
3894 static VALUE s_AtomRef_GetElement(VALUE self) {
3895 char *p = s_AtomFromValue(self)->element;
3896 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3899 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3900 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3903 static VALUE s_AtomRef_GetConnects(VALUE self) {
3906 Atom *ap = s_AtomFromValue(self);
3907 retval = rb_ary_new();
3908 cp = AtomConnectData(&ap->connect);
3909 for (i = 0; i < ap->connect.count; i++)
3910 rb_ary_push(retval, INT2NUM(cp[i]));
3914 static VALUE s_AtomRef_GetR(VALUE self) {
3915 return ValueFromVector(&(s_AtomFromValue(self)->r));
3918 static VALUE s_AtomRef_GetX(VALUE self) {
3919 return rb_float_new(s_AtomFromValue(self)->r.x);
3922 static VALUE s_AtomRef_GetY(VALUE self) {
3923 return rb_float_new(s_AtomFromValue(self)->r.y);
3926 static VALUE s_AtomRef_GetZ(VALUE self) {
3927 return rb_float_new(s_AtomFromValue(self)->r.z);
3930 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3934 s_AtomIndexFromValue(self, &ap, &mp);
3936 if (mp->cell != NULL)
3937 TransformVec(&r1, mp->cell->rtr, &r1);
3941 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3942 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3943 return ValueFromVector(&r1);
3946 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3947 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3950 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3951 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3954 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3955 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3958 static VALUE s_AtomRef_GetSigma(VALUE self) {
3959 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3962 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3963 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3966 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3967 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3970 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3971 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3974 static VALUE s_AtomRef_GetV(VALUE self) {
3975 return ValueFromVector(&(s_AtomFromValue(self)->v));
3978 static VALUE s_AtomRef_GetF(VALUE self) {
3979 Vector v = s_AtomFromValue(self)->f;
3980 VecScaleSelf(v, INTERNAL2KCAL);
3981 return ValueFromVector(&v);
3984 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3985 return rb_float_new(s_AtomFromValue(self)->occupancy);
3988 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3989 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3992 static VALUE s_AtomRef_GetAniso(VALUE self) {
3995 Atom *ap = s_AtomFromValue(self);
3996 if (ap->aniso == NULL)
3998 retval = rb_ary_new();
3999 for (i = 0; i < 6; i++)
4000 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
4001 if (ap->aniso->has_bsig) {
4002 rb_ary_push(retval, INT2NUM(0));
4003 for (i = 0; i < 6; i++)
4004 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4009 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4012 Atom *ap = s_AtomFromValue(self);
4013 if (ap->aniso == NULL)
4015 retval = rb_ary_new();
4016 for (i = 0; i < 3; i++)
4017 rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4021 static VALUE s_AtomRef_GetSymop(VALUE self) {
4023 Atom *ap = s_AtomFromValue(self);
4024 if (!ap->symop.alive)
4026 retval = rb_ary_new();
4027 rb_ary_push(retval, INT2NUM(ap->symop.sym));
4028 rb_ary_push(retval, INT2NUM(ap->symop.dx));
4029 rb_ary_push(retval, INT2NUM(ap->symop.dy));
4030 rb_ary_push(retval, INT2NUM(ap->symop.dz));
4031 rb_ary_push(retval, INT2NUM(ap->symbase));
4035 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4036 return INT2NUM(s_AtomFromValue(self)->intCharge);
4039 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4040 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4043 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4044 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4047 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4051 MDExclusion *exinfo;
4054 idx = s_AtomIndexFromValue(self, &ap, &mol);
4055 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4056 VALUE mval = ValueFromMolecule(mol);
4057 s_RebuildMDParameterIfNecessary(mval, Qnil);
4059 if (mol->arena->exinfo == NULL)
4061 exinfo = mol->arena->exinfo + idx;
4062 exlist = mol->arena->exlist;
4063 retval = rb_ary_new();
4064 aval = rb_ary_new();
4065 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
4066 rb_ary_push(aval, INT2NUM(exlist[i]));
4067 rb_ary_push(retval, aval);
4068 aval = rb_ary_new();
4069 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
4070 rb_ary_push(aval, INT2NUM(exlist[i]));
4071 rb_ary_push(retval, aval);
4072 aval = rb_ary_new();
4073 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
4074 rb_ary_push(aval, INT2NUM(exlist[i]));
4075 rb_ary_push(retval, aval);
4079 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4080 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4083 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4084 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4087 static VALUE s_AtomRef_GetHidden(VALUE self) {
4088 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4091 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4094 Atom *ap = s_AtomFromValue(self);
4095 if (ap->anchor == NULL)
4097 count = ap->anchor->connect.count;
4098 retval = rb_ary_new2(count * 2);
4099 cp = AtomConnectData(&ap->anchor->connect);
4100 for (i = 0; i < count; i++) {
4101 rb_ary_store(retval, i, INT2NUM(cp[i]));
4102 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4107 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4108 char *p = s_AtomFromValue(self)->uff_type;
4109 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4112 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4113 rb_raise(rb_eMolbyError, "index cannot be directly set");
4117 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4118 VALUE oval = s_AtomRef_GetSegSeq(self);
4119 val = rb_Integer(val);
4120 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4121 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4125 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4126 char *p = StringValuePtr(val);
4127 VALUE oval = s_AtomRef_GetSegName(self);
4128 strncpy(s_AtomFromValue(self)->segName, p, 4);
4129 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4133 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4134 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4135 return val; /* Not reached */
4138 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4139 Atom *ap = s_AtomFromValue(self);
4140 char *p = StringValuePtr(val);
4141 VALUE oval = s_AtomRef_GetName(self);
4142 if (ap->anchor != NULL && p[0] == '_')
4143 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4144 strncpy(ap->aname, p, 4);
4145 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4149 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4151 char *p = StringValuePtr(val);
4152 VALUE oval = s_AtomRef_GetAtomType(self);
4153 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4154 if (type != 0 && type < kAtomTypeMinimum)
4155 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4156 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4157 mp->needsMDRebuild = 1;
4158 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4162 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4164 VALUE oval = s_AtomRef_GetCharge(self);
4165 val = rb_Float(val);
4166 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4167 mp->needsMDRebuild = 1;
4168 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4172 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4174 VALUE oval = s_AtomRef_GetWeight(self);
4175 val = rb_Float(val);
4176 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4177 mp->needsMDRebuild = 1;
4178 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4182 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4185 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4186 char *p = StringValuePtr(val);
4187 VALUE oval = s_AtomRef_GetElement(self);
4188 ap->atomicNumber = ElementToInt(p);
4189 ElementToString(ap->atomicNumber, ap->element);
4190 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4192 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4193 mp->needsMDRebuild = 1;
4197 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4200 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4201 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4202 val = rb_Integer(val);
4203 ap->atomicNumber = NUM2INT(val);
4204 ElementToString(ap->atomicNumber, ap->element);
4205 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4207 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4208 mp->needsMDRebuild = 1;
4212 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4213 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4214 return val; /* Not reached */
4217 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4220 VALUE oval = s_AtomRef_GetR(self);
4221 VectorFromValue(val, &v);
4222 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4223 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4224 mp->needsMDCopyCoordinates = 1;
4228 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4231 VALUE oval = s_AtomRef_GetX(self);
4232 val = rb_Float(val);
4234 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4235 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4236 mp->needsMDCopyCoordinates = 1;
4240 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4243 VALUE oval = s_AtomRef_GetY(self);
4244 val = rb_Float(val);
4246 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4247 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4248 mp->needsMDCopyCoordinates = 1;
4252 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4255 VALUE oval = s_AtomRef_GetZ(self);
4256 val = rb_Float(val);
4258 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4259 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4260 mp->needsMDCopyCoordinates = 1;
4264 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4268 s_AtomIndexFromValue(self, &ap, &mp);
4270 VectorFromValue(val, &v);
4271 if (mp->cell != NULL)
4272 TransformVec(&v, mp->cell->tr, &v);
4274 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4275 mp->needsMDCopyCoordinates = 1;
4279 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4284 s_AtomIndexFromValue(self, &ap, &mp);
4286 val = rb_Float(val);
4288 if (mp->cell != NULL) {
4289 TransformVec(&v, mp->cell->rtr, &v);
4291 TransformVec(&v, mp->cell->tr, &v);
4294 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4295 mp->needsMDCopyCoordinates = 1;
4299 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4304 s_AtomIndexFromValue(self, &ap, &mp);
4306 val = rb_Float(val);
4308 if (mp->cell != NULL) {
4309 TransformVec(&v, mp->cell->rtr, &v);
4311 TransformVec(&v, mp->cell->tr, &v);
4314 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4315 mp->needsMDCopyCoordinates = 1;
4319 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4324 s_AtomIndexFromValue(self, &ap, &mp);
4326 val = rb_Float(val);
4328 if (mp->cell != NULL) {
4329 TransformVec(&v, mp->cell->rtr, &v);
4331 TransformVec(&v, mp->cell->tr, &v);
4334 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4335 mp->needsMDCopyCoordinates = 1;
4339 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4342 VALUE oval = s_AtomRef_GetSigma(self);
4343 VectorFromValue(val, &v);
4344 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4345 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4346 mp->needsMDCopyCoordinates = 1;
4350 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4353 VALUE oval = s_AtomRef_GetSigmaX(self);
4354 val = rb_Float(val);
4356 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4357 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4358 mp->needsMDCopyCoordinates = 1;
4362 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4365 VALUE oval = s_AtomRef_GetSigmaY(self);
4366 val = rb_Float(val);
4368 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4369 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4370 mp->needsMDCopyCoordinates = 1;
4374 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4377 VALUE oval = s_AtomRef_GetSigmaZ(self);
4378 val = rb_Float(val);
4380 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4381 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4382 mp->needsMDCopyCoordinates = 1;
4386 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4390 VALUE oval = s_AtomRef_GetV(self);
4391 VectorFromValue(val, &v);
4392 s_AtomIndexFromValue(self, &ap, &mp);
4394 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4395 mp->needsMDCopyCoordinates = 1;
4399 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4402 VALUE oval = s_AtomRef_GetF(self);
4403 VectorFromValue(val, &v);
4404 VecScaleSelf(v, KCAL2INTERNAL);
4405 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4406 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4407 mp->needsMDCopyCoordinates = 1;
4411 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4412 VALUE oval = s_AtomRef_GetOccupancy(self);
4414 val = rb_Float(val);
4415 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4416 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4417 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4421 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4422 VALUE oval = s_AtomRef_GetTempFactor(self);
4423 val = rb_Float(val);
4424 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4425 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4429 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4434 VALUE oval = s_AtomRef_GetAniso(self);
4435 Data_Get_Struct(self, AtomRef, aref);
4436 val = rb_funcall(val, rb_intern("to_a"), 0);
4437 n = RARRAY_LEN(val);
4438 valp = RARRAY_PTR(val);
4439 for (i = 0; i < 6; i++) {
4441 f[i] = NUM2DBL(rb_Float(valp[i]));
4445 type = NUM2INT(rb_Integer(valp[6]));
4448 for (i = 0; i < 6; i++)
4449 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4451 for (i = 0; i < 6; i++)
4454 i = s_AtomIndexFromValue(self, NULL, NULL);
4455 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4456 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4460 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4461 rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4462 return val; /* Not reached */
4465 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4471 VALUE oval = s_AtomRef_GetSymop(self);
4472 i = s_AtomIndexFromValue(self, &ap, &mol);
4474 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4476 val = rb_funcall(val, rb_intern("to_a"), 0);
4477 n = RARRAY_LEN(val);
4478 valp = RARRAY_PTR(val);
4479 for (i = 0; i < 5; i++) {
4481 if (valp[i] == Qnil)
4484 ival[i] = NUM2INT(rb_Integer(valp[i]));
4485 } else ival[i] = -100000;
4488 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4489 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));
4490 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4491 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4492 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4493 return val; /* No need to change */
4494 if (ival[0] != -100000)
4495 ap->symop.sym = ival[0];
4496 if (ival[1] != -100000)
4497 ap->symop.dx = ival[1];
4498 if (ival[2] != -100000)
4499 ap->symop.dy = ival[2];
4500 if (ival[3] != -100000)
4501 ap->symop.dz = ival[3];
4502 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4503 if (ival[4] != -100000)
4504 ap->symbase = ival[4];
4505 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4506 /* The anisotropic parameters should be recalculated */
4507 VALUE oaval = s_AtomRef_GetAniso(self);
4508 MoleculeSetAnisoBySymop(mol, i);
4509 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4511 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4515 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4516 VALUE oval = s_AtomRef_GetIntCharge(self);
4517 val = rb_Integer(val);
4518 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4519 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4523 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4525 VALUE oval = s_AtomRef_GetFixForce(self);
4526 val = rb_Float(val);
4527 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4528 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4529 mp->needsMDRebuild = 1;
4533 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4536 VALUE oval = s_AtomRef_GetFixPos(self);
4537 VectorFromValue(val, &v);
4538 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4539 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4540 mp->needsMDRebuild = 1;
4544 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4545 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4546 return val; /* Not reached */
4549 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4550 VALUE oval = s_AtomRef_GetIntCharge(self);
4551 val = rb_Integer(val);
4552 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4553 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4557 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4558 VALUE oval = s_AtomRef_GetIntCharge(self);
4559 val = rb_Integer(val);
4560 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4561 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4565 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4566 Atom *ap = s_AtomFromValue(self);
4567 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4569 ap->exflags |= kAtomHiddenFlag;
4571 ap->exflags &= ~kAtomHiddenFlag;
4573 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4577 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4578 Int idx, i, j, k, n, *ip;
4585 MolAction **undoActions;
4586 memset(&ac, 0, sizeof(ac));
4587 idx = s_AtomIndexFromValue(self, &ap, &mol);
4588 oval = s_AtomRef_GetAnchorList(self);
4590 val = rb_ary_to_ary(val);
4591 n = RARRAY_LEN(val);
4594 if (ap->anchor != NULL) {
4595 AtomConnectResize(&ap->anchor->connect, 0);
4596 free(ap->anchor->coeffs);
4599 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4604 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4605 if (ap->aname[0] == '_')
4606 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4607 ip = (Int *)malloc(sizeof(Int) * n);
4609 for (i = 0; i < n; i++) {
4610 v = RARRAY_PTR(val)[i];
4611 if (rb_obj_is_kind_of(v, rb_cFloat))
4613 j = NUM2INT(rb_Integer(v));
4614 if (j < 0 || j >= mol->natoms)
4615 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4616 for (k = 0; k < i; k++) {
4618 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4624 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4625 else if (i * 2 != n)
4626 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4627 dp = (Double *)malloc(sizeof(Double) * n / 2);
4628 for (i = 0; i < n / 2; i++) {
4629 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4631 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4637 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4641 rb_raise(rb_eMolbyError, "invalid argument");
4642 if (nUndoActions > 0) {
4643 for (i = 0; i < nUndoActions; i++) {
4644 MolActionCallback_registerUndo(mol, undoActions[i]);
4645 MolActionRelease(undoActions[i]);
4649 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4653 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4654 Atom *ap = s_AtomFromValue(self);
4655 char *p = StringValuePtr(val);
4656 VALUE oval = s_AtomRef_GetUFFType(self);
4657 strncpy(ap->uff_type, p, 5);
4658 ap->uff_type[5] = 0;
4659 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4663 static struct s_AtomAttrDef {
4665 VALUE *symref; /* Address of s_IndexSymbol etc. */
4666 ID id; /* Will be set within InitMolby() */
4667 VALUE (*getter)(VALUE);
4668 VALUE (*setter)(VALUE, VALUE);
4669 } s_AtomAttrDefTable[] = {
4670 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4671 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4672 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4673 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4674 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4675 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4676 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4677 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4678 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4679 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4680 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4681 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4682 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4683 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4684 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4685 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4686 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4687 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4688 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4689 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4690 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4691 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4692 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4693 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4694 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4695 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4696 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4697 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4698 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4699 {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues, s_AtomRef_SetAnisoEigenValues},
4700 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4701 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4702 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4703 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4704 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4705 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4706 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4707 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4708 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4709 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4710 {NULL} /* Sentinel */
4714 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4718 if (TYPE(key) != T_SYMBOL) {
4719 kid = rb_intern(StringValuePtr(key));
4721 } else kid = SYM2ID(key);
4722 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4723 if (s_AtomAttrDefTable[i].id == kid) {
4724 if (value == Qundef)
4725 return (*(s_AtomAttrDefTable[i].getter))(self);
4727 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4730 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4731 return Qnil; /* not reached */
4735 s_AtomRef_GetAttr(VALUE self, VALUE key)
4737 return s_AtomRef_SetAttr(self, key, Qundef);
4742 * self == atomRef -> boolean
4744 * True if the two references point to the same atom.
4747 s_AtomRef_Equal(VALUE self, VALUE val)
4749 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4750 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4751 } else return Qfalse;
4754 #pragma mark ====== MolEnumerable Class ======
4756 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4760 * self[idx] -> AtomRef or Array of Integers
4762 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4763 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4764 * value is a String. Otherwise, the return value is an Array of Integers.
4767 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4769 MolEnumerable *mseq;
4772 Data_Get_Struct(self, MolEnumerable, mseq);
4774 if (mseq->kind == kAtomKind) {
4775 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4777 idx1 = NUM2INT(arg1);
4778 switch (mseq->kind) {
4780 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4781 if (idx2 < 0 || idx2 >= mol->nbonds)
4782 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4783 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4786 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4787 if (idx2 < 0 || idx2 >= mol->nangles)
4788 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4789 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4791 case kDihedralKind: {
4792 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4793 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4794 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4795 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]));
4797 case kImproperKind: {
4798 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4799 if (idx2 < 0 || idx2 >= mol->nimpropers)
4800 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4801 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]));
4803 case kResidueKind: {
4805 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4806 if (idx2 < 0 || idx2 >= mol->nresidues)
4807 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4808 p = mol->residues[idx2];
4809 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4819 * Returns the number of objects included in this enumerable.
4822 s_MolEnumerable_Length(VALUE self)
4824 MolEnumerable *mseq;
4825 Data_Get_Struct(self, MolEnumerable, mseq);
4826 switch (mseq->kind) {
4828 return INT2NUM(mseq->mol->natoms);
4830 return INT2NUM(mseq->mol->nbonds);
4832 return INT2NUM(mseq->mol->nangles);
4834 return INT2NUM(mseq->mol->ndihedrals);
4836 return INT2NUM(mseq->mol->nimpropers);
4838 return INT2NUM(mseq->mol->nresidues);
4847 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4848 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4849 * For the atoms, a same AtomRef object is passed (with different internal information)
4850 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4851 * for each iteration.
4854 s_MolEnumerable_Each(VALUE self)
4856 MolEnumerable *mseq;
4858 int len = NUM2INT(s_MolEnumerable_Length(self));
4859 Data_Get_Struct(self, MolEnumerable, mseq);
4860 if (mseq->kind == kAtomKind) {
4861 /* The same AtomRef object will be used during the loop */
4862 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4863 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4864 for (i = 0; i < len; i++) {
4869 /* A new ruby object will be created at each iteration (not very efficient) */
4870 for (i = 0; i < len; i++) {
4871 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4879 * self == molEnumerable -> boolean
4881 * True if the two arguments point to the same molecule and enumerable type.
4884 s_MolEnumerable_Equal(VALUE self, VALUE val)
4886 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4887 MolEnumerable *mseq1, *mseq2;
4888 Data_Get_Struct(self, MolEnumerable, mseq1);
4889 Data_Get_Struct(val, MolEnumerable, mseq2);
4890 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4891 } else return Qfalse;
4895 #pragma mark ====== Molecule Class ======
4897 #pragma mark ------ Allocate/Release/Accessor ------
4899 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method. */
4900 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4901 char *gLoadSaveErrorMessage = NULL;
4903 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4906 MoleculeFromValue(VALUE val)
4909 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4910 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4911 Data_Get_Struct(val, Molecule, mol);
4915 static VALUE sMoleculeRetainArray = Qnil;
4917 /* The function is called from MoleculeRelease() */
4918 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4919 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4920 /* object is always returned for the same Molecule. */
4921 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4922 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4923 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4924 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4925 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4927 /* Register/unregister the exmolobj Ruby object */
4929 MoleculeReleaseExternalObj(Molecule *mol)
4931 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4932 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4933 mol->exmolobjProtected = 0;
4938 MoleculeRetainExternalObj(Molecule *mol)
4940 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4941 if (sMoleculeRetainArray == Qnil) {
4942 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4943 sMoleculeRetainArray = rb_ary_new();
4946 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4947 mol->exmolobjProtected = 1;
4951 /* Release hook function for Ruby */
4953 MoleculeReleaseHook(Molecule *mol)
4955 if (mol->exmolobj != NULL) {
4956 /* No need to remove from sMoleculeRetainArray */
4957 mol->exmolobj = NULL;
4958 mol->exmolobjProtected = 0;
4960 MoleculeRelease(mol);
4964 ValueFromMolecule(Molecule *mol)
4968 if (mol->exmolobj != NULL)
4969 return (VALUE)mol->exmolobj;
4970 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4971 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4972 return (VALUE)mol->exmolobj;
4977 s_Molecule_Alloc(VALUE klass)
4980 Molecule *mol = MoleculeNew();
4981 val = ValueFromMolecule(mol);
4982 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4987 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4991 if (FIXNUM_P(val)) {
4993 if (n >= 0 && n < mol->natoms)
4995 n = -1; /* No such atom */
4996 val = rb_inspect(val);
4998 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
5000 if (n >= 0 && n < mol->natoms)
5002 p = StringValuePtr(val);
5004 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5006 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5008 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5009 return 0; /* Not reached */
5013 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5017 Data_Get_Struct(self, Molecule, mp1);
5018 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5019 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5020 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5021 Data_Get_Struct(val, IntGroup, ig);
5022 IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5031 * Duplicate a molecule. All entries are deep copied, so modifying the newly
5032 * created object does not affect the old object in any sense.
5035 s_Molecule_InitCopy(VALUE self, VALUE arg)
5037 Molecule *mp1, *mp2;
5038 Data_Get_Struct(self, Molecule, mp1);
5039 mp2 = MoleculeFromValue(arg);
5040 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5041 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5047 * atom_index(val) -> Integer
5049 * Returns the atom index represented by val. val can be either a non-negative integer
5050 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5051 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
5052 * where resname, resid, name are the residue name, residue id, and atom name respectively.
5053 * If val is a string and multiple atoms match the description, the atom with the lowest index
5057 s_Molecule_AtomIndex(VALUE self, VALUE val)
5060 Data_Get_Struct(self, Molecule, mol);
5061 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5066 * self == Molecule -> boolean
5068 * True if the two arguments point to the same molecule.
5071 s_Molecule_Equal(VALUE self, VALUE val)
5073 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5074 Molecule *mol1, *mol2;
5075 Data_Get_Struct(self, Molecule, mol1);
5076 Data_Get_Struct(val, Molecule, mol2);
5077 return (mol1 == mol2 ? Qtrue : Qfalse);
5078 } else return Qfalse;
5081 #pragma mark ------ Load/Save ------
5084 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5086 if (gLoadSaveErrorMessage != NULL) {
5087 MyAppCallback_setConsoleColor(1);
5088 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5089 MyAppCallback_setConsoleColor(0);
5092 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5097 * loadmbsf(file) -> bool
5099 * Read a structure from a mbsf file.
5100 * Return true if successful.
5103 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5109 MoleculeClearLoadSaveErrorMessage();
5110 Data_Get_Struct(self, Molecule, mol);
5111 rb_scan_args(argc, argv, "1", &fname);
5112 fstr = FileStringValuePtr(fname);
5113 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5114 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5120 * loadpsf(file, pdbfile = nil) -> bool
5122 * Read a structure from a psf file. molecule must be empty. The psf may be
5123 * an "extended" version, which also contains coordinates. If pdbfile
5124 * is given, then atomic coordinates are read from that file.
5125 * Return true if successful.
5128 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5130 VALUE fname, pdbname;
5131 char *fstr, *pdbstr;
5134 Data_Get_Struct(self, Molecule, mol);
5135 if (mol->natoms > 0)
5136 return Qnil; /* Must be a new molecule */
5137 MoleculeClearLoadSaveErrorMessage();
5138 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5139 fstr = FileStringValuePtr(fname);
5140 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5141 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5143 if (!NIL_P(pdbname)) {
5144 pdbstr = strdup(FileStringValuePtr(pdbname));
5145 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5147 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5154 * loadpdb(file) -> bool
5156 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5157 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5158 * Return true if successful.
5161 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5167 Data_Get_Struct(self, Molecule, mol);
5168 rb_scan_args(argc, argv, "1", &fname);
5169 MoleculeClearLoadSaveErrorMessage();
5170 fstr = FileStringValuePtr(fname);
5171 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5172 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5178 * loaddcd(file) -> bool
5180 * Read coordinates from a dcd file. The molecule should not empty.
5181 * Return true if successful.
5184 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5190 Data_Get_Struct(self, Molecule, mol);
5191 rb_scan_args(argc, argv, "1", &fname);
5192 MoleculeClearLoadSaveErrorMessage();
5193 fstr = FileStringValuePtr(fname);
5194 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5195 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5201 * loadtep(file) -> bool
5203 * Read coordinates from an ortep .tep file.
5204 * Return true if successful.
5207 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5213 Data_Get_Struct(self, Molecule, mol);
5214 rb_scan_args(argc, argv, "1", &fname);
5215 MoleculeClearLoadSaveErrorMessage();
5216 fstr = FileStringValuePtr(fname);
5217 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5218 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5224 * loadres(file) -> bool
5226 * Read coordinates from a shelx .res file.
5227 * Return true if successful.
5230 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5236 Data_Get_Struct(self, Molecule, mol);
5237 rb_scan_args(argc, argv, "1", &fname);
5238 MoleculeClearLoadSaveErrorMessage();
5239 fstr = FileStringValuePtr(fname);
5240 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5241 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5247 * loadfchk(file) -> bool
5249 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5250 * Return true if successful.
5253 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5259 Data_Get_Struct(self, Molecule, mol);
5260 rb_scan_args(argc, argv, "1", &fname);
5261 MoleculeClearLoadSaveErrorMessage();
5262 fstr = FileStringValuePtr(fname);
5263 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5264 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5270 * loaddat(file) -> bool
5272 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5273 * Return true if successful.
5276 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5282 Data_Get_Struct(self, Molecule, mol);
5283 rb_scan_args(argc, argv, "1", &fname);
5284 MoleculeClearLoadSaveErrorMessage();
5285 fstr = FileStringValuePtr(fname);
5286 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5287 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5288 MyAppCallback_hideProgressPanel();
5289 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5295 * savembsf(file) -> bool
5297 * Write structure as a mbsf file. Returns true if successful.
5300 s_Molecule_Savembsf(VALUE self, VALUE fname)
5305 Data_Get_Struct(self, Molecule, mol);
5306 MoleculeClearLoadSaveErrorMessage();
5307 fstr = FileStringValuePtr(fname);
5308 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5309 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5315 * savepsf(file) -> bool
5317 * Write structure as a psf file. Returns true if successful.
5320 s_Molecule_Savepsf(VALUE self, VALUE fname)
5325 Data_Get_Struct(self, Molecule, mol);
5326 MoleculeClearLoadSaveErrorMessage();
5327 fstr = FileStringValuePtr(fname);
5328 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5329 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5335 * savepdb(file) -> bool
5337 * Write coordinates as a pdb file. Returns true if successful.
5340 s_Molecule_Savepdb(VALUE self, VALUE fname)
5345 Data_Get_Struct(self, Molecule, mol);
5346 MoleculeClearLoadSaveErrorMessage();
5347 fstr = FileStringValuePtr(fname);
5348 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5349 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5355 * savedcd(file) -> bool
5357 * Write coordinates as a dcd file. Returns true if successful.
5360 s_Molecule_Savedcd(VALUE self, VALUE fname)
5365 Data_Get_Struct(self, Molecule, mol);
5366 MoleculeClearLoadSaveErrorMessage();
5367 fstr = FileStringValuePtr(fname);
5368 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5369 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5373 /* load([ftype, ] fname, ...) */
5375 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5378 char *argstr, *methname, *p, *type = "";
5381 const char *ls = (loadFlag ? "load" : "save");
5382 int lslen = strlen(ls);
5387 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5388 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5389 if (argstr[0] == ':') {
5390 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5391 methname = ALLOC_N(char, lslen + strlen(argstr));
5392 strcpy(methname, ls);
5393 strcat(methname, argstr + 1);
5395 for (i = lslen; methname[i] != 0; i++)
5396 methname[i] = tolower(methname[i]);
5397 mid = rb_intern(methname);
5401 rval = rb_funcall2(self, mid, argc, argv);
5407 /* Guess file type from extension */
5408 p = strrchr(argstr, '.');
5412 for (methname = p; *methname != 0; methname++) {
5413 if (!isalpha(*methname))
5416 if (*methname == 0) {
5417 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5418 if (methname == NULL)
5419 rb_raise(rb_eMolbyError, "Low memory");
5420 strcpy(methname, ls);
5421 strcat(methname, p);
5422 for (i = lslen; methname[i] != 0; i++)
5423 methname[i] = tolower(methname[i]);
5424 mid = rb_intern(methname);
5427 if (rb_respond_to(self, mid)) {
5428 /* Load: try to call the load procedure only if it is available */
5429 rval = rb_funcall2(self, mid, argc, argv);
5434 /* Save: call the save procedure, and if not found then call 'method_missing' */
5435 rval = rb_funcall2(self, mid, argc, argv);
5442 rval = rb_str_to_str(argv[0]);
5443 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5444 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5445 return Qnil; /* Does not reach here */
5449 /* Register the path */
5452 Data_Get_Struct(self, Molecule, mol);
5453 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5455 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5456 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5457 if (ap->occupancy != 0.0)
5460 if (i == mol->natoms) {
5461 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5462 ap->occupancy = 1.0;
5471 * molload(file, *args) -> bool
5473 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5474 * file type given by the extension). If this method fails, then all defined (public)
5475 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5478 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5480 return s_Molecule_LoadSave(argc, argv, self, 1);
5485 * molsave(file, *args) -> bool
5487 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5488 * (XXX is the file type given by the extension).
5491 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5493 return s_Molecule_LoadSave(argc, argv, self, 0);
5499 * open(file) -> Molecule
5501 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5504 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5512 rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5515 rb_scan_args(argc, argv, "01", &fname);
5519 p = FileStringValuePtr(fname);
5520 iflag = Ruby_SetInterruptFlag(Qfalse);
5521 mp = MoleculeCallback_openNewMolecule(p);
5522 Ruby_SetInterruptFlag(iflag);
5525 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5527 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5529 return ValueFromMolecule(mp);
5535 * new(file, *args) -> Molecule
5537 * Create a new molecule and call "load" method with the same arguments.
5540 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5543 return s_Molecule_Load(argc, argv, self);
5544 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5549 * error_message -> String
5551 * Get the error_message from the last load/save method. If no error, returns nil.
5554 s_Molecule_ErrorMessage(VALUE klass)
5556 if (gLoadSaveErrorMessage == NULL)
5558 else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5563 * set_error_message(String)
5564 * Molecule.error_message = String
5566 * Set the error_message for the present load/save method.
5569 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5571 if (gLoadSaveErrorMessage != NULL) {
5572 free(gLoadSaveErrorMessage);
5573 gLoadSaveErrorMessage = NULL;
5576 sval = rb_str_to_str(sval);
5577 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5584 * set_molecule(Molecule)
5586 * Duplicate the given molecule and set to self. The present molecule must be empty.
5587 * This method is exclusively used for associating a new document with an existing molecule.
5590 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5592 Molecule *mp1, *mp2;
5593 Data_Get_Struct(self, Molecule, mp1);
5594 mp2 = MoleculeFromValue(mval);
5595 MoleculeInitWithMolecule(mp1, mp2);
5596 MoleculeCallback_notifyModification(mp1, 1);
5600 #pragma mark ------ Name attributes ------
5606 * Returns the display name of the molecule. If the molecule has no associated
5607 * document, then returns nil.
5610 s_Molecule_Name(VALUE self)
5614 Data_Get_Struct(self, Molecule, mol);
5615 MoleculeCallback_displayName(mol, buf, sizeof buf);
5619 return Ruby_NewEncodedStringValue2(buf);
5624 * set_name(string) -> self
5626 * Set the name of an untitled molecule. If the molecule is not associated with window
5627 * or it already has an associated file, then exception is thrown.
5630 s_Molecule_SetName(VALUE self, VALUE nval)
5633 Data_Get_Struct(self, Molecule, mol);
5634 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5635 rb_raise(rb_eMolbyError, "Cannot change the window title");
5644 * Returns the full path name of the molecule, if it is associated with a file.
5645 * If the molecule has no associated file, then returns nil.
5648 s_Molecule_Path(VALUE self)
5652 Data_Get_Struct(self, Molecule, mol);
5653 MoleculeCallback_pathName(mol, buf, sizeof buf);
5657 return Ruby_NewFileStringValue(buf);
5664 * Returns the full path name of the directory in which the file associated with the
5665 * molecule is located. If the molecule has no associated file, then returns nil.
5668 s_Molecule_Dir(VALUE self)
5672 Data_Get_Struct(self, Molecule, mol);
5673 MoleculeCallback_pathName(mol, buf, sizeof buf);
5675 translate_char(buf, '\\', '/');
5680 p = strrchr(buf, '/');
5683 return Ruby_NewEncodedStringValue2(buf);
5691 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5692 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5693 * the Molecule structure) is returned.
5696 s_Molecule_Inspect(VALUE self)
5700 Data_Get_Struct(self, Molecule, mol);
5701 MoleculeCallback_displayName(mol, buf, sizeof buf);
5703 /* No associated document */
5704 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5705 return Ruby_NewEncodedStringValue2(buf);
5707 /* Check whether the document name is duplicate */
5711 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5712 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5713 if (strcmp(buf, buf2) == 0) {
5720 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5722 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5724 return Ruby_NewEncodedStringValue2(buf2);
5728 #pragma mark ------ MolEnumerables ------
5731 s_Molecule_MolEnumerable(VALUE self, int kind)
5734 MolEnumerable *mseq;
5735 Data_Get_Struct(self, Molecule, mol);
5736 mseq = MolEnumerableNew(mol, kind);
5737 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5742 * atoms -> MolEnumerable
5744 * Returns a MolEnumerable object representing the array of atoms.
5747 s_Molecule_Atoms(VALUE self)
5749 return s_Molecule_MolEnumerable(self, kAtomKind);
5754 * bonds -> MolEnumerable
5756 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5757 * by an array of two atom indices.
5760 s_Molecule_Bonds(VALUE self)
5762 return s_Molecule_MolEnumerable(self, kBondKind);
5767 * angles -> MolEnumerable
5769 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5770 * by an array of three atom indices.
5773 s_Molecule_Angles(VALUE self)
5775 return s_Molecule_MolEnumerable(self, kAngleKind);
5780 * dihedrals -> MolEnumerable
5782 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5783 * by an array of four atom indices.
5786 s_Molecule_Dihedrals(VALUE self)
5788 return s_Molecule_MolEnumerable(self, kDihedralKind);
5793 * impropers -> MolEnumerable
5795 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5796 * by an array of four atom indices.
5799 s_Molecule_Impropers(VALUE self)
5801 return s_Molecule_MolEnumerable(self, kImproperKind);
5806 * residues -> MolEnumerable
5808 * Returns a MolEnumerable object representing the array of residue names.
5811 s_Molecule_Residues(VALUE self)
5813 return s_Molecule_MolEnumerable(self, kResidueKind);
5820 * Returns the number of atoms.
5823 s_Molecule_Natoms(VALUE self)
5826 Data_Get_Struct(self, Molecule, mol);
5827 return INT2NUM(mol->natoms);
5834 * Returns the number of bonds.
5837 s_Molecule_Nbonds(VALUE self)
5840 Data_Get_Struct(self, Molecule, mol);
5841 return INT2NUM(mol->nbonds);
5846 * nangles -> Integer
5848 * Returns the number of angles.
5851 s_Molecule_Nangles(VALUE self)
5854 Data_Get_Struct(self, Molecule, mol);
5855 return INT2NUM(mol->nangles);
5860 * ndihedrals -> Integer
5862 * Returns the number of dihedrals.
5865 s_Molecule_Ndihedrals(VALUE self)
5868 Data_Get_Struct(self, Molecule, mol);
5869 return INT2NUM(mol->ndihedrals);
5874 * nimpropers -> Integer
5876 * Returns the number of impropers.
5879 s_Molecule_Nimpropers(VALUE self)
5882 Data_Get_Struct(self, Molecule, mol);
5883 return INT2NUM(mol->nimpropers);
5888 * nresidues -> Integer
5890 * Returns the number of residues.
5893 s_Molecule_Nresidues(VALUE self)
5896 Data_Get_Struct(self, Molecule, mol);
5897 return INT2NUM(mol->nresidues);
5902 * nresidues = Integer
5904 * Change the number of residues.
5907 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5910 int ival = NUM2INT(val);
5911 Data_Get_Struct(self, Molecule, mol);
5912 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5913 if (ival != mol->nresidues)
5914 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5920 * max_residue_number(atom_group = nil) -> Integer
5922 * Returns the maximum residue number actually used. If an atom group is given, only
5923 * these atoms are examined. If no atom is present, nil is returned.
5926 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5932 Data_Get_Struct(self, Molecule, mol);
5933 rb_scan_args(argc, argv, "01", &gval);
5934 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5935 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5936 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5941 * min_residue_number(atom_group = nil) -> Integer
5943 * Returns the minimum residue number actually used. If an atom group is given, only
5944 * these atoms are examined. If no atom is present, nil is returned.
5947 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5953 Data_Get_Struct(self, Molecule, mol);
5954 rb_scan_args(argc, argv, "01", &gval);
5955 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5956 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5957 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5962 * each_atom(atom_group = nil) {|aref| ...}
5964 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5965 * group is given, only these atoms are processed.
5966 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5967 * is self (a Molecule object).
5970 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5978 Data_Get_Struct(self, Molecule, mol);
5979 rb_scan_args(argc, argv, "01", &gval);
5980 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5981 arval = ValueFromMoleculeAndIndex(mol, 0);
5982 Data_Get_Struct(arval, AtomRef, aref);
5983 for (i = 0; i < mol->natoms; i++) {
5985 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5989 IntGroupRelease(ig);
5993 #pragma mark ------ Atom Group ------
5996 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5998 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5999 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6000 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6001 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6008 * atom_group {|aref| ...}
6009 * atom_group(arg1, arg2, ...)
6010 * atom_group(arg1, arg2, ...) {|aref| ...}
6012 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6013 * If arguments are given, then the atoms reprensented by the arguments are added to the
6014 * group. For a conversion of a string to an atom index, see the description
6015 * of Molecule#atom_index.
6016 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6017 * representing each atom, and the atoms are removed from the result if the block returns false.
6021 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6023 IntGroup *ig1, *ig2;
6025 Int i, startPt, interval;
6026 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6027 Data_Get_Struct(retval, IntGroup, ig1);
6028 Data_Get_Struct(self, Molecule, mol);
6030 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6033 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6034 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6035 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6036 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6037 ig2 = IntGroupFromValue(*argv);
6038 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6039 interval = IntGroupGetInterval(ig2, i);
6040 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6042 IntGroupRelease(ig2);
6043 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6045 values[0] = (VALUE)mol;
6046 values[1] = (VALUE)ig1;
6047 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6049 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6054 if (rb_block_given_p()) {
6055 /* Evaluate the given block with an AtomRef as the argument, and delete
6056 the index if the block returns false */
6057 AtomRef *aref = AtomRefNew(mol, 0);
6058 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6059 ig2 = IntGroupNew();
6060 IntGroupCopy(ig2, ig1);
6061 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6063 if (startPt >= mol->natoms)
6065 aref->idx = startPt;
6066 resval = rb_yield(arval);
6068 IntGroupRemove(ig1, startPt, 1);
6070 IntGroupRelease(ig2);
6073 /* Remove points that are out of bounds */
6074 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6081 * selection -> IntGroup
6083 * Returns the current selection.
6086 s_Molecule_Selection(VALUE self)
6091 Data_Get_Struct(self, Molecule, mol);
6092 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6093 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6094 val = ValueFromIntGroup(ig);
6095 IntGroupRelease(ig);
6097 val = IntGroup_Alloc(rb_cIntGroup);
6103 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6107 Data_Get_Struct(self, Molecule, mol);
6111 ig = s_Molecule_AtomGroupFromValue(self, val);
6113 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6115 MoleculeSetSelection(mol, ig);
6117 IntGroupRelease(ig);
6123 * selection = IntGroup
6125 * Set the current selection. The right-hand operand may be nil.
6126 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6129 s_Molecule_SetSelection(VALUE self, VALUE val)
6131 return s_Molecule_SetSelectionSub(self, val, 0);
6136 * set_undoable_selection(IntGroup)
6138 * Set the current selection with undo registration. The right-hand operand may be nil.
6139 * This operation is undoable.
6142 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6144 return s_Molecule_SetSelectionSub(self, val, 1);
6147 #pragma mark ------ Editing ------
6151 * extract(group, dummy_flag = nil) -> Molecule
6153 * Extract the atoms given by group and return as a new molecule object.
6154 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6155 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6156 * names beginning with an underscore) and included in the new molecule object.
6159 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6161 Molecule *mol1, *mol2;
6163 VALUE group, dummy_flag, retval;
6164 Data_Get_Struct(self, Molecule, mol1);
6165 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6166 ig = s_Molecule_AtomGroupFromValue(self, group);
6167 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6170 retval = ValueFromMolecule(mol2);
6172 IntGroupRelease(ig);
6178 * add(molecule2) -> self
6180 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6182 This operation is undoable.
6185 s_Molecule_Add(VALUE self, VALUE val)
6187 Molecule *mol1, *mol2;
6188 Data_Get_Struct(self, Molecule, mol1);
6189 mol2 = MoleculeFromValue(val);
6190 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6196 * remove(group) -> Molecule
6198 * The atoms designated by the given group are removed from the molecule.
6199 * This operation is undoable.
6202 s_Molecule_Remove(VALUE self, VALUE group)
6207 IntGroupIterator iter;
6209 ig = s_Molecule_AtomGroupFromValue(self, group);
6210 /* Data_Get_Struct(self, Molecule, mol1);
6211 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6212 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6213 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6214 Data_Get_Struct(group, IntGroup, ig); */
6215 Data_Get_Struct(self, Molecule, mol1);
6217 /* Remove the bonds between the two fragments */
6218 /* (This is necessary for undo to work correctly) */
6219 IntGroupIteratorInit(ig, &iter);
6221 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6222 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6224 cp = AtomConnectData(&ap->connect);
6225 for (j = 0; j < ap->connect.count; j++) {
6227 if (!IntGroupLookup(ig, n, NULL)) {
6228 /* bond i-n, i is in ig and n is not */
6229 int k = MoleculeLookupBond(mol1, i, n);
6233 IntGroupAdd(bg, k, 1);
6238 IntGroupIteratorRelease(&iter);
6241 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6242 IntGroupRelease(bg);
6245 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6252 * create_atom(name, pos = -1) -> AtomRef
6254 * Create a new atom with the specified name (may contain residue
6255 * information) and position (if position is out of range, the atom is appended at
6256 * the end). Returns the reference to the new atom.
6257 * This operation is undoable.
6260 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6267 char *p, resName[6], atomName[6];
6269 Data_Get_Struct(self, Molecule, mol);
6270 rb_scan_args(argc, argv, "02", &name, &ival);
6272 pos = NUM2INT(rb_Integer(ival));
6275 p = StringValuePtr(name);
6277 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6278 if (atomName[0] == 0)
6279 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6282 if (p == NULL || p[0] == 0) {
6283 memset(atomName, 0, 4);
6286 memset(&arec, 0, sizeof(arec));
6287 strncpy(arec.aname, atomName, 4);
6289 strncpy(arec.resName, resName, 4);
6290 arec.resSeq = resSeq;
6292 arec.occupancy = 1.0;
6293 // i = MoleculeCreateAnAtom(mol, &arec);
6294 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6296 aref = AtomRefNew(mol, pos);
6297 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6302 * duplicate_atom(atomref, pos = -1) -> AtomRef
6304 * Create a new atom with the same attributes (but no bonding information)
6305 * with the specified atom. Returns the reference to the new atom.
6306 * This operation is undoable.
6309 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6315 VALUE retval, aval, ival;
6317 Data_Get_Struct(self, Molecule, mol);
6318 rb_scan_args(argc, argv, "11", &aval, &ival);
6319 if (FIXNUM_P(aval)) {
6320 int idx = NUM2INT(aval);
6321 if (idx < 0 || idx >= mol->natoms)
6322 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6323 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6325 apsrc = s_AtomFromValue(aval);
6328 rb_raise(rb_eMolbyError, "bad atom specification");
6330 pos = NUM2INT(rb_Integer(ival));
6332 AtomDuplicate(&arec, apsrc);
6333 arec.connect.count = 0;
6334 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6337 aref = AtomRefNew(mol, pos);
6338 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6346 * create_bond(n1, n2, ...) -> Integer
6348 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6349 * do nothing for that pair. Returns the number of bonds actually created.
6350 * This operation is undoable.
6353 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6356 Int i, j, k, *ip, old_nbonds;
6358 rb_raise(rb_eMolbyError, "missing arguments");
6360 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6361 Data_Get_Struct(self, Molecule, mol);
6362 ip = ALLOC_N(Int, argc + 1);
6363 for (i = j = 0; i < argc; i++, j++) {
6364 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6366 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6367 j -= 2; /* This bond is already present: skip it */
6369 for (k = 0; k < j - 1; k += 2) {
6370 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6371 j -= 2; /* The same entry is already in the argument */
6378 old_nbonds = mol->nbonds;
6380 ip[j] = kInvalidIndex;
6381 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6385 rb_raise(rb_eMolbyError, "atom index out of range");
6387 rb_raise(rb_eMolbyError, "too many bonds");
6389 rb_raise(rb_eMolbyError, "duplicate bonds");
6391 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6393 rb_raise(rb_eMolbyError, "error in creating bonds");
6394 return INT2NUM(mol->nbonds - old_nbonds);
6399 * molecule.remove_bonds(n1, n2, ...) -> Integer
6401 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6402 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6403 * This operation is undoable.
6406 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6412 rb_raise(rb_eMolbyError, "missing arguments");
6414 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6415 Data_Get_Struct(self, Molecule, mol);
6417 for (i = j = 0; i < argc; i++, j = 1 - j) {
6418 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6420 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6424 IntGroupAdd(bg, k, 1);
6429 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6430 i = IntGroupGetCount(bg);
6431 IntGroupRelease(bg);
6438 * assign_bond_order(idx, d1)
6439 * assign_bond_orders(group, [d1, d2, ...])
6441 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6442 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6443 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6444 * (This may change in the future)
6445 * This operation is undoable.
6448 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6452 Data_Get_Struct(self, Molecule, mol);
6453 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6454 /* The first form */
6455 Int idx = NUM2INT(rb_Integer(idxval));
6456 Double d1 = NUM2DBL(rb_Float(dval));
6457 if (idx < 0 || idx >= mol->nbonds)
6458 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6459 ig = IntGroupNewWithPoints(idx, 1, -1);
6460 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6461 IntGroupRelease(ig);
6465 ig = IntGroupFromValue(idxval);
6466 n = IntGroupGetCount(ig);
6468 rb_raise(rb_eMolbyError, "the bond index is empty");
6469 dval = rb_ary_to_ary(dval);
6470 dp = (Double *)calloc(sizeof(Double), n);
6471 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6472 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6474 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6476 IntGroupRelease(ig);
6483 * get_bond_order(idx) -> Float
6484 * get_bond_orders(group) -> Array
6486 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6487 * In the second form, the bond orders at the indices in the group are returned as an array.
6488 * If no bond order information have been assigned, returns nil (the first form)
6489 * or an empty array (the second form).
6492 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6498 Int i, n, numericArg;
6499 Data_Get_Struct(self, Molecule, mol);
6500 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6501 /* The first form */
6502 Int idx = NUM2INT(rb_Integer(idxval));
6503 if (idx < 0 || idx >= mol->nbonds)
6504 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6505 if (mol->bondOrders == NULL)
6507 ig = IntGroupNewWithPoints(idx, 1, -1);
6511 if (mol->bondOrders == NULL)
6512 return rb_ary_new();
6513 ig = IntGroupFromValue(idxval);
6514 n = IntGroupGetCount(ig);
6516 rb_raise(rb_eMolbyError, "the bond index is empty");
6519 dp = (Double *)calloc(sizeof(Double), n);
6520 MoleculeGetBondOrders(mol, dp, ig);
6522 retval = rb_float_new(dp[0]);
6524 retval = rb_ary_new();
6525 for (i = 0; i < n; i++)
6526 rb_ary_push(retval, rb_float_new(dp[i]));
6529 IntGroupRelease(ig);
6535 * bond_exist?(idx1, idx2) -> bool
6537 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6538 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6541 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6547 Data_Get_Struct(self, Molecule, mol);
6548 idx1 = NUM2INT(rb_Integer(ival1));
6549 idx2 = NUM2INT(rb_Integer(ival2));
6550 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6551 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6552 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6553 cp = AtomConnectData(&ap->connect);
6554 for (i = 0; i < ap->connect.count; i++) {
6563 * add_angle(n1, n2, n3) -> Molecule
6565 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6566 * when a bond is created, so it is rarely necessary to use this method explicitly.
6567 * This operation is undoable.
6570 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6574 Data_Get_Struct(self, Molecule, mol);
6575 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6576 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6577 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6578 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6579 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6580 n[3] = kInvalidIndex;
6581 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6587 * remove_angle(n1, n2, n3) -> Molecule
6589 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6590 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6591 * This operation is undoable.
6594 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6599 Data_Get_Struct(self, Molecule, mol);
6600 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6601 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6602 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6603 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6604 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6605 ig = IntGroupNewWithPoints(n[3], 1, -1);
6606 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6607 IntGroupRelease(ig);
6613 * add_dihedral(n1, n2, n3, n4) -> Molecule
6615 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6616 * when a bond is created, so it is rarely necessary to use this method explicitly.
6617 * This operation is undoable.
6620 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6624 Data_Get_Struct(self, Molecule, mol);
6625 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6626 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6627 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6628 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6629 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6630 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6631 n[4] = kInvalidIndex;
6632 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6638 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6640 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6641 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6642 * This operation is undoable.
6645 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6650 Data_Get_Struct(self, Molecule, mol);
6651 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6652 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6653 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6654 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6655 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6656 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6657 ig = IntGroupNewWithPoints(n[4], 1, -1);
6658 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6659 IntGroupRelease(ig);
6665 * add_improper(n1, n2, n3, n4) -> Molecule
6667 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6668 * not automatically added when a new bond is created, so this method is more useful than
6669 * the angle/dihedral counterpart.
6670 * This operation is undoable.
6673 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6677 Data_Get_Struct(self, Molecule, mol);
6678 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6679 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6680 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6681 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6682 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6683 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6684 n[4] = kInvalidIndex;
6685 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6691 * remove_improper(n1, n2, n3, n4) -> Molecule
6692 * remove_improper(intgroup) -> Molecule
6694 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6695 * Returns self. Unlike angles and dihedrals, impropers are
6696 * not automatically added when a new bond is created, so this method is more useful than
6697 * the angle/dihedral counterpart.
6698 * This operation is undoable.
6701 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6704 VALUE v1, v2, v3, v4;
6707 Data_Get_Struct(self, Molecule, mol);
6709 ig = IntGroupFromValue(argv[0]);
6711 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6712 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6713 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6714 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6715 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6716 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6717 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6718 ig = IntGroupNewWithPoints(n[4], 1, -1);
6720 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6721 IntGroupRelease(ig);
6727 * assign_residue(group, res) -> Molecule
6729 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6730 * or "resname.resno". When the residue number is not specified, the residue number of
6731 * the first atom in the group is used.
6732 * This operation is undoable.
6735 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6739 char *p, *pp, buf[16];
6742 Data_Get_Struct(self, Molecule, mol);
6744 /* Parse the argument res */
6745 if (FIXNUM_P(res)) {
6746 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6747 resid = NUM2INT(res);
6750 p = StringValuePtr(res);
6751 pp = strchr(p, '.');
6753 resid = atoi(pp + 1);
6759 if (n > sizeof buf - 1)
6764 ig = s_Molecule_AtomGroupFromValue(self, range);
6765 if (ig == NULL || IntGroupGetCount(ig) == 0)
6769 /* Use the residue number of the first specified atom */
6770 n = IntGroupGetNthPoint(ig, 0);
6771 if (n >= mol->natoms)
6772 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6773 ap = ATOM_AT_INDEX(mol->atoms, n);
6776 /* Change the residue number */
6777 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6778 /* Change the residue name if necessary */
6782 seqs[1] = kInvalidIndex; */
6783 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6785 IntGroupRelease(ig);
6791 * offset_residue(group, offset) -> Molecule
6793 * Offset the residue number of the specified atoms. If any of the residue number gets
6794 * negative, then exception is thrown.
6795 * This operation is undoable.
6798 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6803 Data_Get_Struct(self, Molecule, mol);
6804 ig = s_Molecule_AtomGroupFromValue(self, range);
6805 ofs = NUM2INT(offset);
6806 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6808 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6809 IntGroupRelease(ig);
6815 * renumber_atoms(array) -> IntGroup
6817 * Change the order of atoms so that the atoms specified in the array argument appear
6818 * in this order from the top of the molecule. The atoms that are not included in array
6819 * are placed after these atoms, and these atoms are returned as an intGroup.
6820 * This operation is undoable.
6823 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6829 VALUE *valp, retval;
6830 Data_Get_Struct(self, Molecule, mol);
6831 if (TYPE(array) != T_ARRAY)
6832 array = rb_funcall(array, rb_intern("to_a"), 0);
6833 n = RARRAY_LEN(array);
6834 valp = RARRAY_PTR(array);
6835 new2old = ALLOC_N(Int, n + 1);
6836 for (i = 0; i < n; i++)
6837 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6838 new2old[i] = kInvalidIndex;
6839 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6841 rb_raise(rb_eMolbyError, "Atom index out of range");
6843 rb_raise(rb_eMolbyError, "Duplicate entry");
6845 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6846 retval = IntGroup_Alloc(rb_cIntGroup);
6847 Data_Get_Struct(retval, IntGroup, ig);
6848 if (mol->natoms > n)
6849 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6856 * set_atom_attr(index, key, value)
6858 * Set the atom attribute for the specified atom.
6859 * This operation is undoable.
6862 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6866 Data_Get_Struct(self, Molecule, mol);
6867 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6868 oldval = s_AtomRef_GetAttr(aref, key);
6871 s_AtomRef_SetAttr(aref, key, val);
6877 * get_atom_attr(index, key)
6879 * Get the atom attribute for the specified atom.
6882 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6884 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6887 #pragma mark ------ Undo Support ------
6891 * register_undo(script, *args)
6893 * Register an undo operation with the current molecule.
6896 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6901 Data_Get_Struct(self, Molecule, mol);
6902 rb_scan_args(argc, argv, "1*", &script, &args);
6903 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6904 MolActionCallback_registerUndo(mol, act);
6910 * undo_enabled? -> bool
6912 * Returns true if undo is enabled for this molecule; otherwise no.
6915 s_Molecule_UndoEnabled(VALUE self)
6918 Data_Get_Struct(self, Molecule, mol);
6919 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6926 * undo_enabled = bool
6928 * Enable or disable undo.
6931 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6934 Data_Get_Struct(self, Molecule, mol);
6935 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6939 #pragma mark ------ Measure ------
6942 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6944 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6945 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6946 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6948 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6954 * center_of_mass(group = nil) -> Vector3D
6956 * Calculate the center of mass for the given set of atoms. The argument
6957 * group is null, then all atoms are considered.
6960 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6966 Data_Get_Struct(self, Molecule, mol);
6967 rb_scan_args(argc, argv, "01", &group);
6968 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6969 s_Molecule_DoCenterOfMass(mol, &v, ig);
6971 IntGroupRelease(ig);
6972 return ValueFromVector(&v);
6977 * centralize(group = nil) -> self
6979 * Translate the molecule so that the center of mass of the given group is located
6980 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6983 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6989 Data_Get_Struct(self, Molecule, mol);
6990 rb_scan_args(argc, argv, "01", &group);
6991 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6992 s_Molecule_DoCenterOfMass(mol, &v, ig);
6994 IntGroupRelease(ig);
6998 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7004 * bounds(group = nil) -> [min, max]
7006 * Calculate the boundary. The return value is an array of two Vector3D objects.
7009 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7017 Data_Get_Struct(self, Molecule, mol);
7018 rb_scan_args(argc, argv, "01", &group);
7019 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7020 if (ig != NULL && IntGroupGetCount(ig) == 0)
7021 rb_raise(rb_eMolbyError, "atom group is empty");
7022 vmin.x = vmin.y = vmin.z = 1e30;
7023 vmax.x = vmax.y = vmax.z = -1e30;
7024 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7026 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7042 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7045 /* Get atom position or a vector */
7047 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7049 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7050 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7051 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7053 VectorFromValue(val, vp);
7059 * measure_bond(n1, n2) -> Float
7061 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7062 * or Vector3D values.
7063 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7066 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7070 Data_Get_Struct(self, Molecule, mol);
7071 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7072 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7073 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7078 * measure_angle(n1, n2, n3) -> Float
7080 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7081 * or Vector3D values. The return value is in degree.
7082 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7085 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7090 Data_Get_Struct(self, Molecule, mol);
7091 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7092 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7093 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7094 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7096 return Qnil; /* Cannot define */
7097 else return rb_float_new(d);
7102 * measure_dihedral(n1, n2, n3, n4) -> Float
7104 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7105 * or Vector3D values. The return value is in degree.
7106 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7109 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7112 Vector v1, v2, v3, v4;
7114 Data_Get_Struct(self, Molecule, mol);
7115 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7116 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7117 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7118 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7119 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7121 return Qnil; /* Cannot define */
7122 else return rb_float_new(d);
7127 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7129 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7130 * first and second atom in the pair should belong to group1 and group2, respectively.
7131 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7134 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7137 VALUE limval, gval1, gval2, rval, igval;
7138 IntGroup *ig1, *ig2;
7139 IntGroupIterator iter1, iter2;
7145 MDExclusion *exinfo;
7148 Data_Get_Struct(self, Molecule, mol);
7149 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7150 lim = NUM2DBL(rb_Float(limval));
7152 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7154 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7156 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7158 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7160 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7162 if (!RTEST(igval)) {
7163 /* Use the exclusion table in MDArena */
7164 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7165 VALUE mval = ValueFromMolecule(mol);
7166 s_RebuildMDParameterIfNecessary(mval, Qnil);
7168 exinfo = mol->arena->exinfo; /* May be NULL */
7169 exlist = mol->arena->exlist;
7174 IntGroupIteratorInit(ig1, &iter1);
7175 IntGroupIteratorInit(ig2, &iter2);
7178 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7180 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7182 if (exinfo != NULL) {
7183 exn1 = exinfo[n[0]].index1;
7184 exn2 = exinfo[n[0] + 1].index1;
7185 } else exn1 = exn2 = -1;
7186 IntGroupIteratorReset(&iter2);
7187 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7188 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7190 continue; /* Same atom */
7191 if (exinfo != NULL) {
7192 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7193 for (i = exn1; i < exn2; i++) {
7194 if (exlist[i] == n[1])
7198 continue; /* Should be excluded */
7200 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7201 /* Is this pair already registered? */
7203 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7204 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7208 /* Not registered yet */
7209 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7214 IntGroupIteratorRelease(&iter2);
7215 IntGroupIteratorRelease(&iter1);
7216 IntGroupRelease(ig2);
7217 IntGroupRelease(ig1);
7218 rval = rb_ary_new2(npairs);
7219 if (pairs != NULL) {
7220 for (i = 0; i < npairs; i++) {
7221 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7230 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7232 * Find atoms that are within the threshold distance from the given atom.
7233 * (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.)
7234 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7235 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7236 * If limit is not given, a default value of 1.2 is used.
7237 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7240 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7243 VALUE aval, limval, radval;
7244 double limit, radius;
7245 Int n1, nbonds, *bonds, an;
7247 Data_Get_Struct(self, Molecule, mol);
7248 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7249 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)) {
7250 VectorFromValue(aval, &v);
7252 radius = gElementParameters[6].radius;
7254 radius = NUM2DBL(rb_Float(radval));
7257 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7258 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7259 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7260 if (an >= 0 && an < gCountElementParameters)
7261 radius = gElementParameters[an].radius;
7262 else radius = gElementParameters[6].radius;
7267 limit = NUM2DBL(rb_Float(limval));
7268 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7270 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7271 aval = rb_ary_new();
7273 for (n1 = 0; n1 < nbonds; n1++)
7274 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7282 * guess_bonds(limit = 1.2) -> Integer
7284 * Create bonds between atoms that are within the threshold distance.
7285 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7286 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7287 * If limit is not given, a default value of 1.2 is used.
7288 * The number of the newly created bonds is returned.
7289 * This operation is undoable.
7292 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7298 Data_Get_Struct(self, Molecule, mol);
7299 rb_scan_args(argc, argv, "01", &limval);
7303 limit = NUM2DBL(rb_Float(limval));
7304 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7306 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7309 return INT2NUM(nbonds);
7312 #pragma mark ------ Cell and Symmetry ------
7316 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7318 * Returns the unit cell parameters. If cell is not set, returns nil.
7321 s_Molecule_Cell(VALUE self)
7326 Data_Get_Struct(self, Molecule, mol);
7327 if (mol->cell == NULL)
7329 val = rb_ary_new2(6);
7330 for (i = 0; i < 6; i++)
7331 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7332 if (mol->cell->has_sigma) {
7333 for (i = 0; i < 6; i++) {
7334 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7342 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7343 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7345 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7346 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7347 This operation is undoable.
7348 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7351 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7355 int i, convert_coord, n;
7357 Data_Get_Struct(self, Molecule, mol);
7358 rb_scan_args(argc, argv, "11", &val, &cval);
7363 val = rb_ary_to_ary(val);
7364 len = RARRAY_LEN(val);
7367 } else if (len >= 6) {
7369 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7370 for (i = 0; i < n; i++)
7371 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7373 convert_coord = (RTEST(cval) ? 1 : 0);
7374 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7380 * box -> [avec, bvec, cvec, origin, flags]
7382 * Get the unit cell information in the form of a periodic bounding box.
7383 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7384 * Integers which define whether the system is periodic along the axis.
7385 * If no unit cell is defined, nil is returned.
7388 s_Molecule_Box(VALUE self)
7392 Data_Get_Struct(self, Molecule, mol);
7393 if (mol == NULL || mol->cell == NULL)
7395 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7396 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7397 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7398 v[3] = ValueFromVector(&(mol->cell->origin));
7399 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7400 val = rb_ary_new4(5, v);
7406 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7407 * set_box(d, origin = [0, 0, 0])
7410 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7411 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7413 Flags, if present, is a 3-member array of Integers defining whether the system is
7414 periodic along the axis.
7415 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7416 In the second form, an isotropic box with cell-length d is set.
7417 In the third form, the existing box is cleared.
7418 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7421 s_Molecule_SetBox(VALUE self, VALUE aval)
7425 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7427 Vector origin = {0, 0, 0};
7430 int i, convertCoordinates = 0;
7431 Data_Get_Struct(self, Molecule, mol);
7433 MolActionCreateAndPerform(mol, gMolActionClearBox);
7436 aval = rb_ary_to_ary(aval);
7437 for (i = 0; i < 6; i++) {
7438 if (i < RARRAY_LEN(aval))
7439 v[i] = (RARRAY_PTR(aval))[i];
7443 MolActionCreateAndPerform(mol, gMolActionClearBox);
7446 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7447 d = NUM2DBL(rb_Float(v[0]));
7448 for (i = 0; i < 3; i++)
7449 VecScale(vv[i], ax[i], d);
7451 VectorFromValue(v[1], &origin);
7452 flags[0] = flags[1] = flags[2] = 1;
7454 for (i = 0; i < 3; i++) {
7457 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7458 d = NUM2DBL(rb_Float(v[i]));
7459 VecScale(vv[i], ax[i], d);
7461 VectorFromValue(v[i], &vv[i]);
7463 flags[i] = (VecLength2(vv[i]) > 0.0);
7466 VectorFromValue(v[3], &origin);
7468 for (i = 0; i < 3; i++) {
7469 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7470 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7474 convertCoordinates = 1;
7476 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7482 * cell_periodicity -> [n1, n2, n3]
7484 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7488 s_Molecule_CellPeriodicity(VALUE self)
7491 Data_Get_Struct(self, Molecule, mol);
7492 if (mol->cell == NULL)
7494 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7499 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7500 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7502 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7503 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7504 * If cell is not defined, exception is raised.
7505 * This operation is undoable.
7508 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7512 Data_Get_Struct(self, Molecule, mol);
7513 if (mol->cell == NULL)
7514 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7517 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7518 flag = NUM2INT(rb_Integer(arg));
7522 arg = rb_ary_to_ary(arg);
7524 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7525 arg0 = RARRAY_PTR(arg)[i];
7526 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7527 flag |= (1 << (2 - i));
7530 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7536 * cell_flexibility -> bool
7538 * Returns the unit cell is flexible or not
7541 s_Molecule_CellFlexibility(VALUE self)
7543 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7546 Data_Get_Struct(self, Molecule, mol);
7547 if (mol->cell == NULL)
7549 if (mol->useFlexibleCell)
7551 else return Qfalse; */
7556 * self.cell_flexibility = bool
7557 * set_cell_flexibility(bool)
7559 * Change the unit cell is flexible or not
7562 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7564 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7567 Data_Get_Struct(self, Molecule, mol);
7568 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7574 * cell_transform -> Transform
7576 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7577 * If cell is not defined, nil is returned.
7580 s_Molecule_CellTransform(VALUE self)
7583 Data_Get_Struct(self, Molecule, mol);
7584 if (mol == NULL || mol->cell == NULL)
7586 return ValueFromTransform(&(mol->cell->tr));
7591 * symmetry -> Array of Transforms
7592 * symmetries -> Array of Transforms
7594 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7595 * returns an empty array.
7598 s_Molecule_Symmetry(VALUE self)
7603 Data_Get_Struct(self, Molecule, mol);
7604 if (mol->nsyms <= 0)
7605 return rb_ary_new();
7606 val = rb_ary_new2(mol->nsyms);
7607 for (i = 0; i < mol->nsyms; i++) {
7608 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7615 * nsymmetries -> Integer
7617 * Get the number of currently defined symmetry operations.
7620 s_Molecule_Nsymmetries(VALUE self)
7623 Data_Get_Struct(self, Molecule, mol);
7624 return INT2NUM(mol->nsyms);
7629 * add_symmetry(Transform) -> Integer
7631 * Add a new symmetry operation. If no symmetry operation is defined and the
7632 * given argument is not an identity transform, then also add an identity
7633 * transform at the index 0.
7634 * Returns the total number of symmetries after operation.
7637 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7641 Data_Get_Struct(self, Molecule, mol);
7642 TransformFromValue(trans, &tr);
7643 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7644 return INT2NUM(mol->nsyms);
7649 * remove_symmetry(count = nil) -> Integer
7650 * remove_symmetries(count = nil) -> Integer
7652 * Remove the specified number of symmetry operations. The last added ones are removed
7653 * first. If count is nil, then all symmetry operations are removed. Returns the
7654 * number of leftover symmetries.
7657 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7662 Data_Get_Struct(self, Molecule, mol);
7663 rb_scan_args(argc, argv, "01", &cval);
7667 n = NUM2INT(rb_Integer(cval));
7668 if (n < 0 || n > mol->nsyms)
7669 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7670 if (n == mol->nsyms)
7673 for (i = 0; i < n; i++)
7674 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7675 return INT2NUM(mol->nsyms);
7680 * wrap_unit_cell(group) -> Vector3D
7682 * Move the specified group so that the center of mass of the group is within the
7683 * unit cell. The offset vector is returned. If no periodic box is defined,
7684 * exception is raised.
7687 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7692 Data_Get_Struct(self, Molecule, mol);
7693 if (mol->cell == NULL)
7694 rb_raise(rb_eMolbyError, "no unit cell is defined");
7695 ig = s_Molecule_AtomGroupFromValue(self, gval);
7696 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7697 TransformVec(&v, mol->cell->rtr, &cv);
7698 if (mol->cell->flags[0])
7700 if (mol->cell->flags[1])
7702 if (mol->cell->flags[2])
7704 TransformVec(&dv, mol->cell->tr, &v);
7706 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7707 IntGroupRelease(ig);
7708 return ValueFromVector(&dv);
7713 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7715 * Expand the specified part of the molecule by the given symmetry operation.
7716 * Returns the array of atom indices corresponding to the expanded atoms.
7717 * If allow_overlap is true, then new atoms are created even when the
7718 * coordinates coincide with the some other atom (special position) of the
7719 * same element; otherwise, such atom will not be created and the index of the
7720 * existing atom is given in the returned array.
7723 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7726 VALUE gval, sval, xval, yval, zval, rval, oval;
7728 Int n[4], allow_overlap;
7732 Data_Get_Struct(self, Molecule, mol);
7733 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7734 n[0] = NUM2INT(rb_Integer(sval));
7735 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7736 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7737 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7738 allow_overlap = (RTEST(oval) ? 1 : 0);
7739 ig = s_Molecule_AtomGroupFromValue(self, gval);
7740 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7741 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7742 natoms = mol->natoms;
7744 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7746 rval = rb_ary_new2(nidx);
7747 while (--nidx >= 0) {
7748 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7750 /* if (natoms == mol->natoms)
7753 rval = IntGroup_Alloc(rb_cIntGroup);
7754 Data_Get_Struct(rval, IntGroup, ig);
7755 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7762 * amend_by_symmetry(group = nil) -> IntGroup
7764 * Expand the specified part of the molecule by the given symmetry operation.
7765 * Returns an IntGroup containing the added atoms.
7768 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7773 Data_Get_Struct(self, Molecule, mol);
7774 rb_scan_args(argc, argv, "01", &gval);
7776 ig = s_Molecule_AtomGroupFromValue(self, gval);
7778 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7779 rval = ValueFromIntGroup(ig2);
7780 IntGroupRelease(ig2);
7784 #pragma mark ------ Transforms ------
7788 * translate(vec, group = nil) -> Molecule
7790 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7791 * This operation is undoable.
7794 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7800 Data_Get_Struct(self, Molecule, mol);
7801 rb_scan_args(argc, argv, "11", &vec, &group);
7802 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7803 VectorFromValue(vec, &v);
7804 // MoleculeTranslate(mol, &v, ig);
7805 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7807 IntGroupRelease(ig);
7813 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7815 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7816 * If group is given, only atoms in the group are moved.
7817 * This operation is undoable.
7820 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7823 volatile VALUE aval, anval, cval, gval;
7828 Data_Get_Struct(self, Molecule, mol);
7829 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7830 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7831 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7832 VectorFromValue(aval, &av);
7834 cv.x = cv.y = cv.z = 0.0;
7836 VectorFromValue(cval, &cv);
7837 if (TransformForRotation(tr, &av, angle, &cv))
7838 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7839 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7841 IntGroupRelease(ig);
7847 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7849 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7850 * axis must not be a zero vector.
7851 * If group is given, only atoms in the group are moved.
7852 * This operation is undoable.
7855 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7858 volatile VALUE aval, cval, gval;
7862 Data_Get_Struct(self, Molecule, mol);
7863 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7864 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7865 VectorFromValue(aval, &av);
7867 cv.x = cv.y = cv.z = 0.0;
7869 VectorFromValue(cval, &cv);
7870 if (TransformForReflection(tr, &av, &cv))
7871 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7872 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7874 IntGroupRelease(ig);
7880 * invert(center = [0,0,0], group = nil) -> Molecule
7882 * Invert the molecule with the given center.
7883 * If group is given, only atoms in the group are moved.
7884 * This operation is undoable.
7887 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7890 volatile VALUE cval, gval;
7894 Data_Get_Struct(self, Molecule, mol);
7895 rb_scan_args(argc, argv, "02", &cval, &gval);
7896 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7898 cv.x = cv.y = cv.z = 0.0;
7900 VectorFromValue(cval, &cv);
7901 TransformForInversion(tr, &cv);
7902 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7904 IntGroupRelease(ig);
7910 * transform(transform, group = nil) -> Molecule
7912 * Transform the molecule by the given Transform object.
7913 * If group is given, only atoms in the group are moved.
7914 * This operation is undoable.
7917 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7923 Data_Get_Struct(self, Molecule, mol);
7924 rb_scan_args(argc, argv, "11", &trans, &group);
7925 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7926 TransformFromValue(trans, &tr);
7927 /* MoleculeTransform(mol, tr, ig); */
7928 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7930 IntGroupRelease(ig);
7936 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7938 * Get the transform corresponding to the symmetry operation. The symop can either be
7939 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7940 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7941 * Otherwise, the returned transform is for fractional coordinates.
7942 * Raises exception when no cell or no transform are defined.
7945 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7951 Data_Get_Struct(self, Molecule, mol);
7952 if (mol->cell == NULL)
7953 rb_raise(rb_eMolbyError, "no unit cell is defined");
7954 if (mol->nsyms == 0)
7955 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7956 rb_scan_args(argc, argv, "11", &sval, &fval);
7957 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7958 symop.sym = NUM2INT(rb_Integer(sval));
7959 symop.dx = symop.dy = symop.dz = 0;
7961 sval = rb_ary_to_ary(sval);
7962 if (RARRAY_LEN(sval) < 4)
7963 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7964 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7965 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7966 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7967 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7969 if (symop.sym >= mol->nsyms)
7970 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7971 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7972 return ValueFromTransform(&tr);
7977 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7979 * Get the symmetry operation corresponding to the given transform.
7980 * If is_cartesian is true, the given transform is for cartesian coordinates.
7981 * Otherwise, the given transform is for fractional coordinates.
7982 * Raises exception when no cell or no transform are defined.
7985 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7992 Data_Get_Struct(self, Molecule, mol);
7993 if (mol->cell == NULL)
7994 rb_raise(rb_eMolbyError, "no unit cell is defined");
7995 if (mol->nsyms == 0)
7996 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7997 rb_scan_args(argc, argv, "11", &tval, &fval);
7998 TransformFromValue(tval, &tr);
7999 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8001 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8003 return Qnil; /* Not found */
8007 #pragma mark ------ Frames ------
8011 * select_frame(index)
8014 * Select the specified frame. If successful, returns true, otherwise returns false.
8017 s_Molecule_SelectFrame(VALUE self, VALUE val)
8020 int ival = NUM2INT(val);
8021 Data_Get_Struct(self, Molecule, mol);
8022 ival = MoleculeSelectFrame(mol, ival, 1);
8032 * Get the current frame.
8035 s_Molecule_Frame(VALUE self)
8038 Data_Get_Struct(self, Molecule, mol);
8039 return INT2NUM(mol->cframe);
8044 * nframes -> Integer
8046 * Get the number of frames.
8049 s_Molecule_Nframes(VALUE self)
8052 Data_Get_Struct(self, Molecule, mol);
8053 return INT2NUM(MoleculeGetNumberOfFrames(mol));
8058 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8059 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8061 * Insert new frames at the indices specified by the intGroup. If the first argument is
8062 * an integer, a single new frame is inserted at that index. If the first argument is
8063 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8064 * should be an array of arrays of Vector3Ds, then those coordinates are set
8065 * to the new frame. Otherwise, the coordinates of current molecule are copied
8067 * Returns an intGroup representing the inserted frames if successful, nil if not.
8070 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8072 VALUE val, coords, cells;
8075 int count, ival, i, j, len, len_c, len2, nframes;
8078 Data_Get_Struct(self, Molecule, mol);
8079 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8080 if (coords != Qnil) {
8081 if (TYPE(coords) != T_ARRAY)
8082 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8083 len = RARRAY_LEN(coords);
8085 if (cells != Qnil) {
8086 if (mol->cell == NULL)
8087 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8088 if (TYPE(cells) != T_ARRAY)
8089 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8090 len_c = RARRAY_LEN(cells);
8092 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
8093 nframes = MoleculeGetNumberOfFrames(mol);
8095 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8096 val = ValueFromIntGroup(ig);
8098 ig = IntGroupFromValue(val);
8100 count = IntGroupGetCount(ig); /* Count is updated here */
8101 vp = ALLOC_N(Vector, mol->natoms * count);
8103 vp2 = ALLOC_N(Vector, 4 * count);
8107 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8108 ptr = RARRAY_PTR(coords);
8109 for (i = 0; i < count; i++) {
8110 if (TYPE(ptr[i]) != T_ARRAY)
8111 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8112 len2 = RARRAY_LEN(ptr[i]);
8113 if (len2 < mol->natoms)
8114 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8115 ptr2 = RARRAY_PTR(ptr[i]);
8116 for (j = 0; j < mol->natoms; j++)
8117 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8121 for (i = 0; i < count; i++) {
8122 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8123 vp[i * mol->natoms + j] = ap->r;
8129 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8130 ptr = RARRAY_PTR(cells);
8131 for (i = 0; i < count; i++) {
8132 if (TYPE(ptr[i]) != T_ARRAY)
8133 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8134 len2 = RARRAY_LEN(ptr[i]);
8136 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8137 ptr2 = RARRAY_PTR(ptr[i]);
8138 for (j = 0; j < 4; j++)
8139 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8142 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8143 IntGroupRelease(ig);
8147 return (ival >= 0 ? val : Qnil);
8152 * create_frame(coordinates = nil) -> Integer
8153 * create_frames(coordinates = nil) -> Integer
8155 * Same as molecule.insert_frames(nil, coordinates).
8158 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8161 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8163 return s_Molecule_InsertFrames(3, vals, self);
8168 * remove_frames(IntGroup, wantCoordinates = false)
8170 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8171 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8172 * removed frames is returned if operation is successful.
8175 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8182 Data_Get_Struct(self, Molecule, mol);
8183 rb_scan_args(argc, argv, "11", &val, &flag);
8184 ig = IntGroupFromValue(val);
8185 count = IntGroupGetCount(ig);
8187 /* Create return value before removing frames */
8192 retval = rb_ary_new2(count);
8193 for (i = 0; i < count; i++) {
8194 n = IntGroupGetNthPoint(ig, i);
8195 coords = rb_ary_new2(mol->natoms);
8196 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8197 if (n < ap->nframes && n != mol->cframe)
8200 rb_ary_push(coords, ValueFromVector(&v));
8202 rb_ary_push(retval, coords);
8204 } else retval = Qtrue;
8205 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8212 * each_frame {|n| ...}
8214 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8215 * the frame number. After completion, the original frame number is restored.
8218 s_Molecule_EachFrame(VALUE self)
8220 int i, cframe, nframes;
8222 Data_Get_Struct(self, Molecule, mol);
8223 cframe = mol->cframe;
8224 nframes = MoleculeGetNumberOfFrames(mol);
8226 for (i = 0; i < nframes; i++) {
8227 MoleculeSelectFrame(mol, i, 1);
8228 rb_yield(INT2NUM(i));
8230 MoleculeSelectFrame(mol, cframe, 1);
8237 * get_coord_from_frame(index, group = nil)
8239 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8240 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8241 * copied; now they are always copied)
8244 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8247 VALUE ival, gval, cval;
8248 Int index, i, j, n, nn;
8250 IntGroupIterator iter;
8253 Data_Get_Struct(self, Molecule, mol);
8254 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8256 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8257 index = NUM2INT(rb_Integer(ival));
8258 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8260 rb_raise(rb_eMolbyError, "No frame is present");
8262 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8265 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8267 ig = s_Molecule_AtomGroupFromValue(self, gval);
8269 n = IntGroupGetCount(ig);
8271 vp = (Vector *)calloc(sizeof(Vector), n);
8272 IntGroupIteratorInit(ig, &iter);
8275 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8276 ap = ATOM_AT_INDEX(mol->atoms, i);
8277 if (index < ap->nframes) {
8278 vp[j] = ap->frames[index];
8286 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8288 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8289 vp = mol->frame_cells + index * 4;
8290 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8292 IntGroupIteratorRelease(&iter);
8294 /* Copy the extra properties */
8295 IntGroupRelease(ig);
8296 for (i = 0; i < mol->nmolprops; i++) {
8297 Double *dp = (Double *)malloc(sizeof(Double));
8299 IntGroupAdd(ig, mol->cframe, 1);
8300 *dp = mol->molprops[i].propvals[index];
8301 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8303 IntGroupRelease(ig);
8311 * reorder_frames(old_indices)
8313 * Reorder the frames. The argument is an array of integers that specify the 'old'
8314 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8315 * same as the old frames 2/0/1, respectively.
8316 * The argument must have the same number of integers as the number of frames.
8319 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8322 Int *ip, *ip2, i, n, nframes;
8323 Data_Get_Struct(self, Molecule, mol);
8324 aval = rb_ary_to_ary(aval);
8325 nframes = MoleculeGetNumberOfFrames(mol);
8326 if (RARRAY_LEN(aval) != nframes)
8327 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8328 ip2 = (Int *)calloc(sizeof(Int), nframes);
8329 ip = (Int *)calloc(sizeof(Int), nframes);
8330 for (i = 0; i < nframes; i++) {
8331 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8332 if (n < 0 || n >= nframes || ip2[n] != 0) {
8335 if (n < 0 || n >= nframes)
8336 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8338 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8344 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8349 #pragma mark ------ Fragments ------
8353 * fragment(n1, *exatoms) -> IntGroup
8354 * fragment(group, *exatoms) -> IntGroup
8356 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8357 * those atoms will not be counted during the search.
8360 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8363 IntGroup *baseg, *ig, *exatoms;
8365 volatile VALUE nval, exval;
8366 Data_Get_Struct(self, Molecule, mol);
8367 rb_scan_args(argc, argv, "1*", &nval, &exval);
8368 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8370 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8372 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8374 if (RARRAY_LEN(exval) == 0) {
8377 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8378 Data_Get_Struct(exval, IntGroup, exatoms);
8380 if (baseg == NULL) {
8381 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8383 IntGroupIterator iter;
8384 IntGroupIteratorInit(baseg, &iter);
8385 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8388 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8390 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8392 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8394 IntGroupAddIntGroup(ig, subg);
8395 IntGroupRelease(subg);
8400 IntGroupIteratorRelease(&iter);
8401 IntGroupRelease(baseg);
8404 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8405 nval = ValueFromIntGroup(ig);
8406 IntGroupRelease(ig);
8412 * fragments(exclude = nil)
8414 * Returns the fragments as an array of IntGroups.
8415 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8416 * in defining the fragment.
8419 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8422 IntGroup *ag, *fg, *eg;
8423 VALUE gval, exval, retval;
8424 Data_Get_Struct(self, Molecule, mol);
8427 if (mol->natoms == 0)
8428 return rb_ary_new();
8429 rb_scan_args(argc, argv, "01", &exval);
8433 eg = IntGroupFromValue(exval);
8434 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8436 IntGroupRemoveIntGroup(ag, eg);
8437 retval = rb_ary_new();
8438 while (IntGroupGetCount(ag) > 0) {
8439 int n = IntGroupGetNthPoint(ag, 0);
8440 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8442 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8443 gval = ValueFromIntGroup(fg);
8444 rb_ary_push(retval, gval);
8445 IntGroupRemoveIntGroup(ag, fg);
8446 IntGroupRelease(fg);
8448 IntGroupRelease(ag);
8450 IntGroupRelease(eg);
8456 * each_fragment(exclude = nil) {|group| ...}
8458 * Execute the block, with the IntGroup object for each fragment as the argument.
8459 * Atoms or bonds should not be added or removed during the execution of the block.
8460 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8461 * in defining the fragment.
8464 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8467 IntGroup *ag, *fg, *eg;
8469 Data_Get_Struct(self, Molecule, mol);
8470 if (mol == NULL || mol->natoms == 0)
8472 rb_scan_args(argc, argv, "01", &exval);
8476 eg = IntGroupFromValue(exval);
8477 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8479 IntGroupRemoveIntGroup(ag, eg);
8480 while (IntGroupGetCount(ag) > 0) {
8481 int n = IntGroupGetNthPoint(ag, 0);
8482 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8484 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8485 gval = ValueFromIntGroup(fg);
8487 IntGroupRemoveIntGroup(ag, fg);
8488 IntGroupRelease(fg);
8490 IntGroupRelease(ag);
8492 IntGroupRelease(eg);
8498 * detachable?(group) -> [n1, n2]
8500 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8501 * of the molecule via only one bond. If it is, then the indices of the atoms
8502 * belonging to the bond is returned, the first element being the atom included
8503 * in the fragment. Otherwise, Qnil is returned.
8506 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8512 Data_Get_Struct(self, Molecule, mol);
8513 ig = s_Molecule_AtomGroupFromValue(self, gval);
8514 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8515 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8516 } else retval = Qnil;
8517 IntGroupRelease(ig);
8523 * bonds_on_border(group = selection) -> Array of Array of two Integers
8525 * Returns an array of bonds that connect an atom in the group and an atom out
8526 * of the group. The first atom in the bond always belongs to the group. If no
8527 * such bonds are present, an empty array is returned.
8530 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8535 Data_Get_Struct(self, Molecule, mol);
8536 rb_scan_args(argc, argv, "01", &gval);
8538 ig = MoleculeGetSelection(mol);
8542 ig = s_Molecule_AtomGroupFromValue(self, gval);
8544 retval = rb_ary_new();
8547 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8549 IntGroupIterator iter;
8551 IntGroupIteratorInit(bg, &iter);
8552 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8553 /* The atoms at the border */
8555 n1 = mol->bonds[i * 2];
8556 n2 = mol->bonds[i * 2 + 1];
8557 if (IntGroupLookupPoint(ig, n1) < 0) {
8561 if (IntGroupLookupPoint(ig, n1) < 0)
8562 continue; /* Actually this is an internal error */
8564 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8566 IntGroupIteratorRelease(&iter);
8568 IntGroupRelease(bg);
8569 IntGroupRelease(ig);
8573 /* Calculate the transform that moves the current coordinates to the reference
8574 coordinates with least displacements. */
8576 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8584 Double eigen_val[3];
8585 Vector eigen_vec[3];
8587 IntGroupIterator iter;
8589 natoms = mol->natoms;
8591 IntGroupIteratorInit(ig, &iter);
8593 /* Calculate the weighted center */
8597 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8598 ap1 = ATOM_AT_INDEX(ap, in);
8599 w1 = (weights != NULL ? weights[i] : ap1->weight);
8600 VecScaleInc(org1, ap1->r, w1);
8601 VecScaleInc(org2, ref[i], w1);
8605 VecScaleSelf(org1, w);
8606 VecScaleSelf(org2, w);
8608 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8609 /* Matrix to diagonalize = R * tR */
8610 memset(r, 0, sizeof(Mat33));
8611 memset(q, 0, sizeof(Mat33));
8612 memset(u, 0, sizeof(Mat33));
8614 IntGroupIteratorReset(&iter);
8615 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8617 ap1 = ATOM_AT_INDEX(ap, in);
8618 w1 = (weights != NULL ? weights[i] : ap1->weight);
8620 VecSub(v1, ap1->r, org1);
8621 VecSub(v2, ref[i], org2);
8622 r[0] += w1 * v1.x * v2.x;
8623 r[1] += w1 * v1.y * v2.x;
8624 r[2] += w1 * v1.z * v2.x;
8625 r[3] += w1 * v1.x * v2.y;
8626 r[4] += w1 * v1.y * v2.y;
8627 r[5] += w1 * v1.z * v2.y;
8628 r[6] += w1 * v1.x * v2.z;
8629 r[7] += w1 * v1.y * v2.z;
8630 r[8] += w1 * v1.z * v2.z;
8633 for (i = 0; i < 9; i++)
8635 for (i = 0; i < 3; i++) {
8636 for (j = 0; j < 3; j++) {
8637 for (k = 0; k < 3; k++) {
8638 q[i+j*3] += r[i+k*3] * r[j+k*3];
8643 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8644 IntGroupIteratorRelease(&iter);
8645 return -1.0; /* Cannot determine the eigenvector */
8648 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8649 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8650 MatrixTranspose(r, r);
8651 for (i = 0; i < 3; i++) {
8652 MatrixVec(&s[i], r, &eigen_vec[i]);
8653 w1 = 1.0 / sqrt(eigen_val[i]);
8654 VecScaleSelf(s[i], w1);
8656 for (k = 0; k < 3; k++) {
8657 u[0] += s[k].x * eigen_vec[k].x;
8658 u[1] += s[k].y * eigen_vec[k].x;
8659 u[2] += s[k].z * eigen_vec[k].x;
8660 u[3] += s[k].x * eigen_vec[k].y;
8661 u[4] += s[k].y * eigen_vec[k].y;
8662 u[5] += s[k].z * eigen_vec[k].y;
8663 u[6] += s[k].x * eigen_vec[k].z;
8664 u[7] += s[k].y * eigen_vec[k].z;
8665 u[8] += s[k].z * eigen_vec[k].z;
8668 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8669 MatrixVec(&org1, u, &org1);
8671 for (i = 0; i < 9; i++)
8677 /* Calculate rmsd */
8678 IntGroupIteratorReset(&iter);
8680 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8682 ap1 = ATOM_AT_INDEX(ap, in);
8683 TransformVec(&tv, trans, &ap1->r);
8685 w += VecLength2(tv);
8688 IntGroupIteratorRelease(&iter);
8694 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8696 * Calculate the transform to fit the given group to the set of reference coordinates.
8697 * The reference coordinates ref is given as either a frame number, an array of
8698 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8699 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8700 * Return values are the transform (that converts the present coordinates to the
8701 * target coordinates) and root mean square deviation (without weight).
8704 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8708 VALUE gval, rval, wval;
8710 IntGroupIterator iter;
8711 int nn, errnum, i, j, in, status;
8713 Double *weights, dval[3];
8716 Data_Get_Struct(self, Molecule, mol);
8717 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8719 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8721 ig = s_Molecule_AtomGroupFromValue(self, gval);
8722 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8723 IntGroupRelease(ig);
8724 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8726 ref = (Vector *)calloc(sizeof(Vector), nn);
8727 weights = (Double *)calloc(sizeof(Double), nn);
8728 IntGroupIteratorInit(ig, &iter);
8729 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8730 int fn = NUM2INT(rb_Integer(rval));
8731 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8736 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8737 ap = ATOM_AT_INDEX(mol->atoms, in);
8738 if (fn < ap->nframes)
8739 ref[i] = ap->frames[fn];
8740 else ref[i] = ap->r;
8742 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8743 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8744 if (m->row * m->column < nn * 3) {
8748 for (i = 0; i < nn; i++) {
8749 ref[i].x = m->data[i * 3];
8750 ref[i].y = m->data[i * 3 + 1];
8751 ref[i].z = m->data[i * 3 + 2];
8755 rval = rb_protect(rb_ary_to_ary, rval, &status);
8760 if (RARRAY_LEN(rval) < nn) {
8764 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8765 /* Array of 3*nn numbers */
8766 if (RARRAY_LEN(rval) < nn * 3) {
8770 for (i = 0; i < nn; i++) {
8771 for (j = 0; j < 3; j++) {
8772 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8777 dval[j] = NUM2DBL(aval);
8784 /* Array of nn Vector3Ds or Arrays */
8785 for (i = 0; i < nn; i++) {
8786 aval = (RARRAY_PTR(rval))[i];
8787 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8788 VectorFromValue(aval, &ref[i]);
8790 aval = rb_protect(rb_ary_to_ary, aval, &status);
8795 if (RARRAY_LEN(aval) < 3) {
8800 for (j = 0; j < 3; j++) {
8801 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8806 dval[j] = NUM2DBL(aaval);
8816 /* Use atomic weights */
8817 IntGroupIteratorReset(&iter);
8818 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8819 ap = ATOM_AT_INDEX(mol->atoms, in);
8820 weights[i] = ap->weight;
8823 wval = rb_protect(rb_ary_to_ary, wval, &status);
8828 if (RARRAY_LEN(wval) < nn) {
8832 for (i = 0; i < nn; i++) {
8833 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8838 weights[i] = NUM2DBL(wwval);
8841 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8848 IntGroupIteratorRelease(&iter);
8852 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8853 } else if (errnum == 1) {
8854 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8855 } else if (errnum == 2) {
8856 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8857 } else if (errnum == 3) {
8858 rb_jump_tag(status);
8859 } else if (errnum == 4) {
8860 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8861 } else if (errnum == 5) {
8862 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8863 } else if (errnum == 6) {
8864 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8866 return Qnil; /* Not reached */
8869 #pragma mark ------ Screen Display ------
8875 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8878 s_Molecule_Display(VALUE self)
8881 Data_Get_Struct(self, Molecule, mol);
8882 if (mol->mview != NULL)
8883 MainViewCallback_display(mol->mview);
8891 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8894 s_Molecule_MakeFront(VALUE self)
8897 Data_Get_Struct(self, Molecule, mol);
8898 if (mol->mview != NULL)
8899 MainViewCallback_makeFront(mol->mview);
8905 * update_enabled? -> bool
8907 * Returns true if screen update is enabled; otherwise no.
8910 s_Molecule_UpdateEnabled(VALUE self)
8913 Data_Get_Struct(self, Molecule, mol);
8914 if (mol->mview != NULL && !mol->mview->freezeScreen)
8921 * update_enabled = bool
8923 * Enable or disable screen update. This is effective for automatic update on modification.
8924 * Explicit call to molecule.display() always updates the screen.
8927 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8930 Data_Get_Struct(self, Molecule, mol);
8931 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8932 if (mol->mview != NULL)
8933 mol->mview->freezeScreen = (val == Qfalse);
8941 * show_unitcell(bool)
8942 * show_unitcell = bool
8944 * Set the flag whether to show the unit cell. If no argument is given, the
8945 * current flag is returned.
8948 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8951 Data_Get_Struct(self, Molecule, mol);
8952 if (mol->mview == NULL)
8955 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8956 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8958 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8964 * show_hydrogens(bool)
8965 * show_hydrogens = bool
8967 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8968 * current flag is returned.
8971 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8974 Data_Get_Struct(self, Molecule, mol);
8975 if (mol->mview == NULL)
8978 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8979 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8981 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8987 * show_dummy_atoms(bool)
8988 * show_dummy_atoms = bool
8990 * Set the flag whether to show the dummy atoms. If no argument is given, the
8991 * current flag is returned.
8994 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8997 Data_Get_Struct(self, Molecule, mol);
8998 if (mol->mview == NULL)
9001 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9002 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9004 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9010 * show_expanded(bool)
9011 * show_expanded = bool
9013 * Set the flag whether to show the expanded atoms. If no argument is given, the
9014 * current flag is returned.
9017 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9020 Data_Get_Struct(self, Molecule, mol);
9021 if (mol->mview == NULL)
9024 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9025 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9027 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9033 * show_ellipsoids(bool)
9034 * show_ellipsoids = bool
9036 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9037 * current flag is returned.
9040 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9043 Data_Get_Struct(self, Molecule, mol);
9044 if (mol->mview == NULL)
9047 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9048 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9050 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9055 * is_atom_visible(index) -> Boolean
9057 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9058 * as well as the molecule attributes (showHydrogens, etc.)
9061 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9066 Data_Get_Struct(self, Molecule, mol);
9067 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9068 if (idx < 0 || idx >= mol->natoms)
9070 ap = ATOM_AT_INDEX(mol->atoms, idx);
9071 if (mol->mview != NULL) {
9072 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9074 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9076 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9079 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9084 * hidden_atoms -> IntGroup
9086 * Returns the currently hidden atoms.
9089 s_Molecule_HiddenAtoms(VALUE self)
9091 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9092 return Qnil; /* Not reached */
9097 * set_hidden_atoms(IntGroup)
9098 * self.hidden_atoms = IntGroup
9100 * Hide the specified atoms. This operation is _not_ undoable.
9103 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9105 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9106 return Qnil; /* Not reached */
9111 * show_graphite -> Integer
9112 * show_graphite = Integer
9113 * show_graphite = boolean
9115 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9116 * number of rings to display for each direction.
9117 * If the argument is boolean, only the show/hide flag is set.
9120 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9123 Data_Get_Struct(self, Molecule, mol);
9124 if (mol->mview == NULL)
9127 if (argv[0] == Qnil || argv[0] == Qfalse)
9128 mol->mview->showGraphiteFlag = 0;
9129 else if (argv[0] == Qtrue)
9130 mol->mview->showGraphiteFlag = 1;
9132 int n = NUM2INT(rb_Integer(argv[0]));
9134 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9135 mol->mview->showGraphite = n;
9137 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9139 return INT2NUM(mol->mview->showGraphite);
9144 * show_graphite? -> boolean
9146 * Return whether the graphite is set visible or not.
9149 s_Molecule_ShowGraphiteFlag(VALUE self)
9152 Data_Get_Struct(self, Molecule, mol);
9153 if (mol->mview == NULL)
9155 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9160 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9161 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9162 * show_periodic_image = boolean
9164 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9165 * set but no visual effects are observed.
9166 * If the argument is boolean, only the show/hide flag is modified.
9169 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9175 Data_Get_Struct(self, Molecule, mol);
9176 if (mol->mview == NULL)
9178 rb_scan_args(argc, argv, "01", &val);
9180 /* Change current settings */
9181 if (val == Qnil || val == Qfalse)
9182 mol->mview->showPeriodicImageFlag = 0;
9183 else if (val == Qtrue)
9184 mol->mview->showPeriodicImageFlag = 1;
9186 val = rb_ary_to_ary(val);
9187 for (i = 0; i < 6; i++) {
9188 if (i < RARRAY_LEN(val))
9189 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9191 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9192 rb_raise(rb_eMolbyError, "bad arguments");
9193 for (i = 0; i < 6; i++)
9194 mol->mview->showPeriodicImage[i] = ival[i];
9196 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9199 for (i = 0; i < 6; i++)
9200 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9206 * show_periodic_image? -> boolean
9208 * Return whether the periodic images are set to visible or not. This flag is
9209 * independent from the show_periodic_image settings.
9212 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9215 Data_Get_Struct(self, Molecule, mol);
9216 if (mol->mview == NULL)
9218 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9223 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9224 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9225 * show_rotation_center = boolean
9227 * Set to show the rotation center of the screen.
9228 * If the argument is boolean, only the show/hide flag is modified.
9231 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9234 Data_Get_Struct(self, Molecule, mol);
9235 if (mol->mview == NULL)
9238 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9239 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9241 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9250 * Set the flag whether to draw the model in line mode. If no argument is given, the
9251 * current flag is returned.
9254 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9257 Data_Get_Struct(self, Molecule, mol);
9258 if (mol->mview == NULL)
9261 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9262 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9264 return (mol->mview->lineMode ? Qtrue : Qfalse);
9269 * atom_radius = float
9272 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9274 * If no argument is given, the current value is returned.
9277 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9280 Data_Get_Struct(self, Molecule, mol);
9281 if (mol->mview == NULL)
9284 double rad = NUM2DBL(rb_Float(argv[0]));
9286 mol->mview->atomRadius = rad;
9287 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9291 return rb_float_new(mol->mview->atomRadius);
9296 * bond_radius = float
9299 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9300 * If no argument is given, the current value is returned.
9303 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9306 Data_Get_Struct(self, Molecule, mol);
9307 if (mol->mview == NULL)
9310 double rad = NUM2DBL(rb_Float(argv[0]));
9312 mol->mview->bondRadius = rad;
9313 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9317 return rb_float_new(mol->mview->bondRadius);
9322 * atom_resolution = integer
9325 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9326 * (Default = 12; minimum = 6)
9327 * If no argument is given, the current value is returned.
9330 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9333 Data_Get_Struct(self, Molecule, mol);
9334 if (mol->mview == NULL)
9337 int res = NUM2INT(rb_Integer(argv[0]));
9339 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9340 mol->mview->atomResolution = res;
9341 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9342 return INT2NUM(res);
9344 return INT2NUM(mol->mview->atomResolution);
9349 * bond_resolution = integer
9352 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9353 * (Default = 8; minimum = 4)
9354 * If no argument is given, the current value is returned.
9357 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9360 Data_Get_Struct(self, Molecule, mol);
9361 if (mol->mview == NULL)
9364 int res = NUM2INT(rb_Integer(argv[0]));
9366 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9367 mol->mview->bondResolution = res;
9368 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9369 return INT2NUM(res);
9371 return INT2NUM(mol->mview->bondResolution);
9378 * Resize the model drawing to fit in the window.
9381 s_Molecule_ResizeToFit(VALUE self)
9384 Data_Get_Struct(self, Molecule, mol);
9385 if (mol->mview != NULL)
9386 MainView_resizeToFit(mol->mview);
9392 * get_view_rotation -> [[ax, ay, az], angle]
9394 * Get the current rotation for the view. Angle is in degree, not radian.
9397 s_Molecule_GetViewRotation(VALUE self)
9402 Data_Get_Struct(self, Molecule, mol);
9403 if (mol->mview == NULL)
9405 TrackballGetRotate(mol->mview->track, f);
9406 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9410 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9415 * get_view_scale -> float
9417 * Get the current scale for the view.
9420 s_Molecule_GetViewScale(VALUE self)
9423 Data_Get_Struct(self, Molecule, mol);
9424 if (mol->mview == NULL)
9426 return rb_float_new(TrackballGetScale(mol->mview->track));
9431 * get_view_center -> Vector
9433 * Get the current center point of the view.
9436 s_Molecule_GetViewCenter(VALUE self)
9441 Data_Get_Struct(self, Molecule, mol);
9442 if (mol->mview == NULL)
9444 TrackballGetTranslate(mol->mview->track, f);
9445 v.x = -f[0] * mol->mview->dimension;
9446 v.y = -f[1] * mol->mview->dimension;
9447 v.z = -f[2] * mol->mview->dimension;
9448 return ValueFromVector(&v);
9453 * set_view_rotation([ax, ay, az], angle) -> self
9455 * Set the current rotation for the view. Angle is in degree, not radian.
9458 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9463 Data_Get_Struct(self, Molecule, mol);
9464 if (mol->mview == NULL)
9466 VectorFromValue(aval, &v);
9467 if (NormalizeVec(&v, &v))
9468 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9472 f[0] = -NUM2DBL(rb_Float(angval));
9473 TrackballSetRotate(mol->mview->track, f);
9474 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9480 * set_view_scale(scale) -> self
9482 * Set the current scale for the view.
9485 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9488 Data_Get_Struct(self, Molecule, mol);
9489 if (mol->mview == NULL)
9491 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9492 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9498 * set_view_center(vec) -> self
9500 * Set the current center point of the view.
9503 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9508 Data_Get_Struct(self, Molecule, mol);
9509 if (mol->mview == NULL)
9511 VectorFromValue(aval, &v);
9512 f[0] = -v.x / mol->mview->dimension;
9513 f[1] = -v.y / mol->mview->dimension;
9514 f[2] = -v.z / mol->mview->dimension;
9515 TrackballSetTranslate(mol->mview->track, f);
9516 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9522 * set_background_color(red, green, blue)
9524 * Set the background color of the model window.
9527 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9530 Data_Get_Struct(self, Molecule, mol);
9531 if (mol->mview != NULL) {
9532 VALUE rval, gval, bval;
9533 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9534 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9541 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9543 * Export the current graphic to a PNG or TIF file (determined by the extension).
9544 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9545 * If either width or height is not specified, then the screen width/height is used instead.
9548 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9551 VALUE fval, sval, bval, wval, hval;
9554 int bg_color, width, height;
9555 Data_Get_Struct(self, Molecule, mol);
9556 if (mol->mview == NULL)
9557 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9558 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9559 fname = FileStringValuePtr(fval);
9562 else scale = NUM2DBL(rb_Float(sval));
9565 else bg_color = NUM2INT(rb_Integer(bval));
9568 else width = NUM2INT(rb_Integer(wval));
9571 else height = NUM2INT(rb_Integer(hval));
9572 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9577 #pragma mark ------ Graphics ------
9580 s_CalculateGraphicNormals(MainViewGraphic *gp)
9584 if (gp == NULL || gp->npoints < 3)
9586 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9587 v1.x = gp->points[3] - gp->points[0];
9588 v1.y = gp->points[4] - gp->points[1];
9589 v1.z = gp->points[5] - gp->points[2];
9590 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9591 for (i = 2; i < gp->npoints; i++) {
9592 v2.x = gp->points[i * 3] - gp->points[0];
9593 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9594 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9595 VecCross(v3, v1, v2);
9596 NormalizeVec(&v3, &v3);
9597 gp->normals[i * 3] = v3.x;
9598 gp->normals[i * 3 + 1] = v3.y;
9599 gp->normals[i * 3 + 2] = v3.z;
9602 /* normals[0] = average of all nv[i] (i=2..n-1) */
9604 for (i = 2; i < gp->npoints; i++) {
9605 v1.x += gp->normals[i * 3];
9606 v1.y += gp->normals[i * 3 + 1];
9607 v1.z += gp->normals[i * 3 + 2];
9609 NormalizeVec(&v1, &v1);
9610 gp->normals[0] = v1.x;
9611 gp->normals[1] = v1.y;
9612 gp->normals[2] = v1.z;
9613 /* normals[1] = nv[2].normalize */
9614 v2.x = gp->normals[6];
9615 v2.y = gp->normals[7];
9616 v2.z = gp->normals[8];
9617 NormalizeVec(&v1, &v2);
9618 gp->normals[3] = v1.x;
9619 gp->normals[4] = v1.y;
9620 gp->normals[5] = v1.z;
9621 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9622 for (i = 2; i < gp->npoints; i++) {
9623 if (i == gp->npoints - 1)
9626 v3.x = gp->normals[i * 3 + 3];
9627 v3.y = gp->normals[i * 3 + 4];
9628 v3.z = gp->normals[i * 3 + 5];
9631 NormalizeVec(&v1, &v2);
9632 gp->normals[i * 3] = v1.x;
9633 gp->normals[i * 3 + 1] = v1.y;
9634 gp->normals[i * 3 + 2] = v1.z;
9641 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9643 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9644 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9645 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9646 * points: an array of Vectors
9650 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9656 VALUE kval, cval, pval, fval, ival;
9657 Data_Get_Struct(self, Molecule, mol);
9658 if (mol->mview == NULL)
9659 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9660 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9661 idx = NUM2INT(rb_Integer(ival));
9663 idx = mol->mview->ngraphics;
9664 else if (idx < 0 || idx > mol->mview->ngraphics)
9665 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9666 memset(&g, 0, sizeof(g));
9668 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9669 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9671 kval = rb_obj_as_string(kval);
9672 p = StringValuePtr(kval);
9673 if (strcmp(p, "line") == 0)
9674 g.kind = kMainViewGraphicLine;
9675 else if (strcmp(p, "poly") == 0)
9676 g.kind = kMainViewGraphicPoly;
9677 else if (strcmp(p, "cylinder") == 0)
9678 g.kind = kMainViewGraphicCylinder;
9679 else if (strcmp(p, "cone") == 0)
9680 g.kind = kMainViewGraphicCone;
9681 else if (strcmp(p, "ellipsoid") == 0)
9682 g.kind = kMainViewGraphicEllipsoid;
9683 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9685 g.closed = (RTEST(fval) ? 1 : 0);
9686 cval = rb_ary_to_ary(cval);
9687 n = RARRAY_LEN(cval);
9688 if (n < 3 || n >= 5)
9689 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9692 for (i = 0; i < n; i++)
9693 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9694 pval = rb_ary_to_ary(pval);
9695 n = RARRAY_LEN(pval);
9696 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9698 rb_raise(rb_eArgError, "no control points are given");
9700 case kMainViewGraphicLine:
9702 rb_raise(rb_eArgError, "the line object must have at least two control points");
9704 case kMainViewGraphicPoly:
9706 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9708 case kMainViewGraphicCylinder:
9709 case kMainViewGraphicCone:
9711 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9714 case kMainViewGraphicEllipsoid:
9718 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9721 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9722 for (i = 0; i < n; i++) {
9724 VALUE rval = RARRAY_PTR(pval)[i];
9726 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9727 /* The float argument can also be given as a vector (for simplify undo registration) */
9728 VectorFromValue(rval, &v);
9730 v.x = NUM2DBL(rb_Float(rval));
9734 VectorFromValue(rval, &v);
9736 g.points[i * 3] = v.x;
9737 g.points[i * 3 + 1] = v.y;
9738 g.points[i * 3 + 2] = v.z;
9740 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9742 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9743 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9744 g.points[7] = g.points[11] = g.points[3];
9746 if (g.kind == kMainViewGraphicPoly) {
9747 /* Calculate normals */
9748 s_CalculateGraphicNormals(&g);
9750 MainView_insertGraphic(mol->mview, idx, &g);
9755 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9756 MolActionCallback_registerUndo(mol, act);
9757 MolActionRelease(act);
9760 return INT2NUM(idx);
9765 * create_graphic(kind, color, points, fill = nil) -> integer
9767 * Create a new graphic object. The arguments are similar as insert_graphic.
9770 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9773 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9774 args[0] = INT2NUM(-1);
9775 return s_Molecule_InsertGraphic(argc + 1, args, self);
9780 * remove_graphic(index) -> integer
9782 * Remove a graphic object.
9785 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9789 Data_Get_Struct(self, Molecule, mol);
9790 if (mol->mview == NULL)
9791 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9792 i = NUM2INT(rb_Integer(ival));
9793 if (i < 0 || i >= mol->mview->ngraphics)
9794 rb_raise(rb_eArgError, "graphic index is out of range");
9796 /* Prepare data for undo */
9797 MainViewGraphic *gp;
9802 gp = mol->mview->graphics + i;
9803 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9804 for (n = 0; n < gp->npoints; n++) {
9805 vp[n].x = gp->points[n * 3];
9806 vp[n].y = gp->points[n * 3 + 1];
9807 vp[n].z = gp->points[n * 3 + 2];
9809 col[0] = gp->rgba[0];
9810 col[1] = gp->rgba[1];
9811 col[2] = gp->rgba[2];
9812 col[3] = gp->rgba[3];
9813 if (gp->visible == 0) {
9814 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9815 MolActionCallback_registerUndo(mol, act);
9816 MolActionRelease(act);
9818 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9819 MolActionCallback_registerUndo(mol, act);
9821 MolActionRelease(act);
9823 MainView_removeGraphic(mol->mview, i);
9829 * ngraphics -> integer
9831 * Get the number of graphic objects.
9834 s_Molecule_NGraphics(VALUE self)
9837 Data_Get_Struct(self, Molecule, mol);
9838 if (mol->mview == NULL)
9839 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9840 return INT2NUM(mol->mview->ngraphics);
9845 * get_graphic_point(graphic_index, point_index) -> value
9846 * get_graphic_points(graphic_index) -> values
9848 * Get the point_index-th control point of graphic_index-th graphic object.
9849 * Get an array of all control points with the given values.
9853 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9855 MainViewGraphic *gp;
9860 Data_Get_Struct(self, Molecule, mol);
9861 if (mol->mview == NULL)
9862 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9863 rb_scan_args(argc, argv, "11", &gval, &pval);
9864 index = NUM2INT(rb_Integer(gval));
9865 if (index < 0 || index >= mol->mview->ngraphics)
9866 rb_raise(rb_eArgError, "the graphic index is out of range");
9867 gp = mol->mview->graphics + index;
9869 pindex = NUM2INT(rb_Integer(pval));
9870 if (pindex < 0 || pindex >= gp->npoints)
9871 rb_raise(rb_eArgError, "the point index is out of range");
9872 v.x = gp->points[pindex * 3];
9873 v.y = gp->points[pindex * 3 + 1];
9874 v.z = gp->points[pindex * 3 + 2];
9875 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9876 return rb_float_new(v.x);
9878 return ValueFromVector(&v);
9881 pval = rb_ary_new();
9882 for (pindex = 0; pindex < gp->npoints; pindex++) {
9883 v.x = gp->points[pindex * 3];
9884 v.y = gp->points[pindex * 3 + 1];
9885 v.z = gp->points[pindex * 3 + 2];
9886 rb_ary_push(pval, ValueFromVector(&v));
9894 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9895 * set_graphic_points(graphic_index, new_values) -> new_values
9897 * Change the point_index-th control point of graphic_index-th graphic object.
9898 * Replace the control points with the given values.
9902 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9904 MainViewGraphic *gp;
9908 VALUE gval, pval, nval;
9910 Data_Get_Struct(self, Molecule, mol);
9911 if (mol->mview == NULL)
9912 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9913 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9914 index = NUM2INT(rb_Integer(gval));
9915 if (index < 0 || index >= mol->mview->ngraphics)
9916 rb_raise(rb_eArgError, "the graphic index is out of range");
9917 gp = mol->mview->graphics + index;
9919 pindex = NUM2INT(rb_Integer(pval));
9920 if (pindex < 0 || pindex >= gp->npoints)
9921 rb_raise(rb_eArgError, "the point index is out of range");
9922 v0.x = gp->points[pindex * 3];
9923 v0.y = gp->points[pindex * 3 + 1];
9924 v0.z = gp->points[pindex * 3 + 2];
9925 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9926 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9927 v.x = NUM2DBL(rb_Float(nval));
9929 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9930 v.x = NUM2DBL(rb_Float(nval));
9932 gp->points[7] = gp->points[11] = v.x;
9933 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9934 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9937 v.x = kInvalidFloat;
9939 } else VectorFromValue(nval, &v);
9941 gp->points[pindex * 3] = v.x;
9942 gp->points[pindex * 3 + 1] = v.y;
9943 gp->points[pindex * 3 + 2] = v.z;
9944 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9948 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9949 for (pindex = 0; pindex < gp->npoints; pindex++) {
9950 vp[pindex].x = gp->points[pindex * 3];
9951 vp[pindex].y = gp->points[pindex * 3 + 1];
9952 vp[pindex].z = gp->points[pindex * 3 + 2];
9954 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9956 pval = rb_ary_to_ary(pval);
9957 len = RARRAY_LEN(pval);
9958 if (gp->npoints < len) {
9959 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9961 } else if (gp->npoints > len) {
9964 case kMainViewGraphicLine: len2 = 2; break;
9965 case kMainViewGraphicPoly: len2 = 3; break;
9966 case kMainViewGraphicCylinder: len2 = 3; break;
9967 case kMainViewGraphicCone: len2 = 3; break;
9968 case kMainViewGraphicEllipsoid: len2 = 4; break;
9974 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9975 aval = RARRAY_PTR(pval)[pindex];
9976 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9977 v.x = NUM2DBL(rb_Float(aval));
9979 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9980 v.x = NUM2DBL(rb_Float(aval));
9982 gp->points[7] = gp->points[11] = v.x;
9983 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9985 } else VectorFromValue(aval, &v);
9986 gp->points[pindex * 3] = v.x;
9987 gp->points[pindex * 3 + 1] = v.y;
9988 gp->points[pindex * 3 + 2] = v.z;
9991 if (gp->kind == kMainViewGraphicPoly) {
9992 /* Calculate normals */
9993 s_CalculateGraphicNormals(gp);
9995 MolActionCallback_registerUndo(mol, act);
9996 MolActionRelease(act);
9997 MoleculeCallback_notifyModification(mol, 0);
10003 * get_graphic_color(graphic_index) -> value
10005 * Get the color of graphic_index-th graphic object
10008 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10010 MainViewGraphic *gp;
10013 Data_Get_Struct(self, Molecule, mol);
10014 if (mol->mview == NULL)
10015 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10016 index = NUM2INT(rb_Integer(gval));
10017 if (index < 0 || index >= mol->mview->ngraphics)
10018 rb_raise(rb_eArgError, "the graphic index is out of range");
10019 gp = mol->mview->graphics + index;
10020 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]));
10025 * set_graphic_color(graphic_index, new_value) -> new_value
10027 * Change the color of graphic_index-th graphic object
10031 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10033 MainViewGraphic *gp;
10038 Data_Get_Struct(self, Molecule, mol);
10039 if (mol->mview == NULL)
10040 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10041 index = NUM2INT(rb_Integer(gval));
10042 if (index < 0 || index >= mol->mview->ngraphics)
10043 rb_raise(rb_eArgError, "the graphic index is out of range");
10044 gp = mol->mview->graphics + index;
10045 for (i = 0; i < 4; i++)
10046 c[i] = gp->rgba[i];
10047 cval = rb_ary_to_ary(cval);
10048 n = RARRAY_LEN(cval);
10049 if (n != 3 && n != 4)
10050 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10052 for (i = 0; i < n; i++) {
10053 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10057 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10058 MolActionCallback_registerUndo(mol, act);
10059 MolActionRelease(act);
10060 MoleculeCallback_notifyModification(mol, 0);
10066 * show_graphic(graphic_index) -> self
10068 * Enable the visible flag of the graphic_index-th graphic object
10072 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10074 MainViewGraphic *gp;
10077 Data_Get_Struct(self, Molecule, mol);
10078 if (mol->mview == NULL)
10079 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10080 index = NUM2INT(rb_Integer(gval));
10081 if (index < 0 || index >= mol->mview->ngraphics)
10082 rb_raise(rb_eArgError, "the graphic index is out of range");
10083 gp = mol->mview->graphics + index;
10085 MoleculeCallback_notifyModification(mol, 0);
10091 * hide_graphic(graphic_index) -> self
10093 * Disable the visible flag of the graphic_index-th graphic object
10097 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10099 MainViewGraphic *gp;
10102 Data_Get_Struct(self, Molecule, mol);
10103 if (mol->mview == NULL)
10104 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10105 index = NUM2INT(rb_Integer(gval));
10106 if (index < 0 || index >= mol->mview->ngraphics)
10107 rb_raise(rb_eArgError, "the graphic index is out of range");
10108 gp = mol->mview->graphics + index;
10110 MoleculeCallback_notifyModification(mol, 0);
10116 * show_text(string)
10118 * Show the string in the info text box.
10121 s_Molecule_ShowText(VALUE self, VALUE arg)
10124 Data_Get_Struct(self, Molecule, mol);
10125 if (mol->mview != NULL)
10126 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10130 #pragma mark ------ MD Support ------
10134 * md_arena -> MDArena
10136 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10137 * this molecule, a new arena is created.
10140 s_Molecule_MDArena(VALUE self)
10144 Data_Get_Struct(self, Molecule, mol);
10145 if (mol->arena == NULL)
10147 retval = ValueFromMDArena(mol->arena);
10153 * set_parameter_attr(type, index, key, value, src) -> value
10155 * This method is used only internally.
10158 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10160 /* This method is called from MolAction to change a MM parameter attribute. */
10163 ParameterRef *pref;
10165 Data_Get_Struct(self, Molecule, mol);
10166 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10167 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10169 /* This is the special part of this method; it allows modification of the src field. */
10170 /* (ParameterRef#set_attr sets 0 to the src field) */
10171 Data_Get_Struct(pval, ParameterRef, pref);
10172 up = ParameterRefGetPar(pref);
10173 up->bond.src = FIX2INT(sval);
10180 * parameter -> Parameter
10182 * Get the local parameter of this molecule. If not defined, returns nil.
10185 s_Molecule_Parameter(VALUE self)
10188 Data_Get_Struct(self, Molecule, mol);
10189 /* if (mol->par == NULL)
10191 return s_NewParameterValueFromValue(self);
10196 * start_step -> Integer
10198 * Returns the start step (defined by dcd format).
10201 s_Molecule_StartStep(VALUE self)
10204 Data_Get_Struct(self, Molecule, mol);
10205 return INT2NUM(mol->startStep);
10210 * start_step = Integer
10212 * Set the start step (defined by dcd format).
10215 s_Molecule_SetStartStep(VALUE self, VALUE val)
10218 Data_Get_Struct(self, Molecule, mol);
10219 mol->startStep = NUM2INT(rb_Integer(val));
10225 * steps_per_frame -> Integer
10227 * Returns the number of steps between frames (defined by dcd format).
10230 s_Molecule_StepsPerFrame(VALUE self)
10233 Data_Get_Struct(self, Molecule, mol);
10234 return INT2NUM(mol->stepsPerFrame);
10239 * steps_per_frame = Integer
10241 * Set the number of steps between frames (defined by dcd format).
10244 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10247 Data_Get_Struct(self, Molecule, mol);
10248 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10254 * ps_per_step -> Float
10256 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10259 s_Molecule_PsPerStep(VALUE self)
10262 Data_Get_Struct(self, Molecule, mol);
10263 return rb_float_new(mol->psPerStep);
10268 * ps_per_step = Float
10270 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10273 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10276 Data_Get_Struct(self, Molecule, mol);
10277 mol->psPerStep = NUM2DBL(rb_Float(val));
10282 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10284 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.");
10287 #pragma mark ------ MO Handling ------
10291 * selectedMO -> IntGroup
10293 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10294 * is not selected, returns nil. If the MO info table is selected but no MOs
10295 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10298 s_Molecule_SelectedMO(VALUE self)
10303 Data_Get_Struct(self, Molecule, mol);
10304 if (mol->mview == NULL)
10306 ig = MainView_selectedMO(mol->mview);
10309 IntGroupOffset(ig, 1);
10310 val = ValueFromIntGroup(ig);
10311 IntGroupRelease(ig);
10317 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10319 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10320 * If the molecule does not contain a basis set information, then returns nil.
10323 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10326 Vector o, dx, dy, dz;
10329 Int npoints = 80 * 80 * 80;
10330 Data_Get_Struct(self, Molecule, mol);
10331 if (mol->bset == NULL)
10333 rb_scan_args(argc, argv, "01", &nval);
10335 npoints = NUM2INT(rb_Integer(nval));
10336 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10338 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));
10342 s_Cubegen_callback(double progress, void *ref)
10344 MyAppCallback_setProgressValue(progress);
10345 if (MyAppCallback_checkInterrupt())
10352 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10353 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10355 * Calculate the molecular orbital with number mo and create a 'cube' file.
10356 * In the first form, the cube size is estimated from the atomic coordinates. In the
10357 * second form, the cube dimension is explicitly given.
10358 * Returns fname when successful, nil otherwise.
10359 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10360 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10361 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10364 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10366 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10368 Int mono, nx, ny, nz, npoints;
10369 Vector o, dx, dy, dz;
10372 Data_Get_Struct(self, Molecule, mol);
10373 if (mol->bset == NULL)
10374 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10375 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10377 /* Set up parameters */
10378 mono = NUM2INT(rb_Integer(mval));
10379 if (mono < 0 || mono > mol->bset->ncomps)
10380 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);
10382 if (mol->bset->rflag != 0)
10383 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10384 mono += mol->bset->ncomps;
10387 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10388 /* Automatic grid formation */
10390 npoints = NUM2INT(rb_Integer(oval));
10394 else if (npoints < 8)
10395 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10396 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10397 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10401 VectorFromValue(oval, &o);
10402 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10403 VectorFromValue(dxval, &dx);
10405 dx.x = NUM2DBL(rb_Float(dxval));
10408 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10409 VectorFromValue(dyval, &dy);
10411 dy.y = NUM2DBL(rb_Float(dyval));
10414 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10415 VectorFromValue(dzval, &dz);
10417 dz.z = NUM2DBL(rb_Float(dzval));
10420 nx = NUM2INT(rb_Integer(nxval));
10421 ny = NUM2INT(rb_Integer(nyval));
10422 nz = NUM2INT(rb_Integer(nzval));
10423 if (nx <= 0 || ny <= 0 || nz <= 0)
10424 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10425 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10426 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);
10430 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10433 else if (index < 0)
10434 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10436 /* Output to file */
10437 MoleculeCallback_displayName(mol, buf, sizeof buf);
10438 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10440 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10442 /* Discard the cube */
10443 MoleculeClearCubeAtIndex(mol, index);
10451 * Clear the MO surface if present.
10454 s_Molecule_ClearSurface(VALUE self)
10457 Data_Get_Struct(self, Molecule, mol);
10458 if (mol->mcube != NULL)
10459 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10467 * Hide the MO surface if present.
10470 s_Molecule_HideSurface(VALUE self)
10473 Data_Get_Struct(self, Molecule, mol);
10474 if (mol->mcube != NULL) {
10475 mol->mcube->hidden = 1;
10476 MoleculeCallback_notifyModification(mol, 0);
10485 * Show the MO surface if present.
10488 s_Molecule_ShowSurface(VALUE self)
10491 Data_Get_Struct(self, Molecule, mol);
10492 if (mol->mcube != NULL) {
10493 mol->mcube->hidden = 0;
10494 MoleculeCallback_notifyModification(mol, 0);
10501 * create_surface(mo, attr = nil)
10503 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10504 * then it denotes the beta orbital.
10505 * If mo is nil, then the attributes of the current surface are modified.
10507 * :npoints : the approximate number of grid points
10508 * :expand : the scale factor to expand/shrink the display box size for each atom,
10509 * :thres : the threshold for the isovalue surface
10510 * If the molecule does not contain MO information, raises exception.
10513 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10516 Vector o, dx, dy, dz;
10517 Int nmo, nx, ny, nz, i;
10518 Int need_recalc = 0;
10519 VALUE nval, hval, aval;
10524 Data_Get_Struct(self, Molecule, mol);
10525 rb_scan_args(argc, argv, "11", &nval, &hval);
10526 if (mol->bset == NULL)
10527 rb_raise(rb_eMolbyError, "No MO information is given");
10528 if (nval == Qnil) {
10530 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10531 nmo = mol->bset->nmos + 1;
10533 nmo = NUM2INT(rb_Integer(nval));
10534 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10535 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);
10537 nmo = -nmo + mol->bset->ncomps;
10539 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10540 npoints = NUM2INT(rb_Integer(aval));
10542 } else if (mol->mcube != NULL) {
10543 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10544 } else npoints = 80 * 80 * 80;
10545 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10546 expand = NUM2DBL(rb_Float(aval));
10547 } else if (mol->mcube != NULL) {
10548 expand = mol->mcube->expand;
10549 } else expand = 1.0;
10550 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10551 thres = NUM2DBL(rb_Float(aval));
10552 } else if (mol->mcube != NULL) {
10553 thres = mol->mcube->thres;
10554 } else thres = 0.05;
10555 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10556 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10557 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10558 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10559 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10561 for (nx = 0; nx < 2; nx++) {
10562 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10563 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10564 aval = rb_ary_to_ary(aval);
10565 if (RARRAY_LEN(aval) < 3) {
10567 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10569 for (i = 0; i < 4; i++)
10570 d[i] = mol->mcube->c[nx].rgba[i];
10571 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10572 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10573 if (d[i] < 0.0 && d[i] > 1.0)
10576 for (i = 0; i < 4; i++)
10577 mol->mcube->c[nx].rgba[i] = d[i];
10580 if (mol->mcube->expand != expand)
10582 mol->mcube->thres = thres;
10583 mol->mcube->expand = expand;
10585 if (mol->mcube->idn < 0)
10586 return self; /* Only set attributes for now */
10588 nmo = mol->mcube->idn; /* Force recalculation */
10590 if (MoleculeUpdateMCube(mol, nmo) != 0)
10591 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10597 * set_surface_attr(attr = nil)
10599 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10602 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10607 return s_Molecule_CreateSurface(2, args, self);
10614 * Get the number of electrostatic potential info.
10617 s_Molecule_NElpots(VALUE self)
10620 Data_Get_Struct(self, Molecule, mol);
10621 return INT2NUM(mol->nelpots);
10628 * Get the electrostatic potential info at the given index. If present, then the
10629 * return value is [Vector, Float] (position and potential). If not present, then
10633 s_Molecule_Elpot(VALUE self, VALUE ival)
10637 Data_Get_Struct(self, Molecule, mol);
10638 idx = NUM2INT(rb_Integer(ival));
10639 if (idx < 0 || idx >= mol->nelpots)
10641 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10648 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10649 * cube and marching cube information are discarded. This operation is _not_ undoable!
10652 s_Molecule_ClearBasisSet(VALUE self)
10655 Data_Get_Struct(self, Molecule, mol);
10657 if (mol->bset != NULL) {
10658 BasisSetRelease(mol->bset);
10661 if (mol->mcube != NULL) {
10662 MoleculeDeallocateMCube(mol->mcube);
10671 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10673 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10674 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10678 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10681 int sym, nprims, a_idx, n;
10682 Data_Get_Struct(self, Molecule, mol);
10683 a_idx = NUM2INT(rb_Integer(aval));
10684 sym = NUM2INT(rb_Integer(symval));
10685 nprims = NUM2INT(rb_Integer(npval));
10686 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10688 rb_raise(rb_eMolbyError, "Molecule is emptry");
10690 rb_raise(rb_eMolbyError, "Low memory");
10692 rb_raise(rb_eMolbyError, "Unknown orbital type");
10694 rb_raise(rb_eMolbyError, "Unknown error");
10700 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10702 * To be used internally. Add a gaussian primitive coefficients.
10705 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10709 Double exponent, contraction, contraction_sp;
10710 Data_Get_Struct(self, Molecule, mol);
10711 exponent = NUM2DBL(rb_Float(expval));
10712 contraction = NUM2DBL(rb_Float(cval));
10713 contraction_sp = NUM2DBL(rb_Float(cspval));
10714 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10716 rb_raise(rb_eMolbyError, "Molecule is emptry");
10718 rb_raise(rb_eMolbyError, "Low memory");
10720 rb_raise(rb_eMolbyError, "Unknown error");
10726 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10728 * Get the Gaussian shell information for the given MO coefficient index.
10729 * The symmetry code is the same as in add_gaussian_orbital_shell.
10730 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10731 * is the number of MO component belonging to this shell.
10734 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10739 Data_Get_Struct(self, Molecule, mol);
10740 if (mol->bset == NULL)
10741 rb_raise(rb_eMolbyError, "No basis set information is defined");
10742 s_idx = NUM2INT(rb_Integer(sval));
10743 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10745 sp = mol->bset->shells + s_idx;
10748 case kGTOType_S: sym = 0; break;
10749 case kGTOType_SP: sym = -1; break;
10750 case kGTOType_P: sym = 1; break;
10751 case kGTOType_D: sym = 2; break;
10752 case kGTOType_D5: sym = -2; break;
10753 case kGTOType_F: sym = 3; break;
10754 case kGTOType_F7: sym = -3; break;
10755 case kGTOType_G: sym = 4; break;
10756 case kGTOType_G9: sym = -4; break;
10758 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10760 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10765 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10767 * Get the Gaussian primitive coefficients for the given MO component.
10770 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10776 VALUE retval, aval;
10777 Data_Get_Struct(self, Molecule, mol);
10778 if (mol->bset == NULL)
10779 rb_raise(rb_eMolbyError, "No basis set information is defined");
10780 s_idx = NUM2INT(rb_Integer(sval));
10781 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10783 sp = mol->bset->shells + s_idx;
10784 pp = mol->bset->priminfos + sp->p_idx;
10785 retval = rb_ary_new2(sp->nprim);
10786 for (i = 0; i < sp->nprim; i++) {
10787 if (sp->sym == kGTOType_SP) {
10788 /* With P contraction coefficient */
10789 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10791 /* Without P contraction coefficient */
10792 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10794 rb_ary_store(retval, i, aval);
10801 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10803 * Get the Gaussian shell information for the given MO coefficient index.
10806 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10809 Int n, c, atom_idx, shell_idx;
10811 Data_Get_Struct(self, Molecule, mol);
10812 if (mol->bset == NULL)
10813 rb_raise(rb_eMolbyError, "No basis set information is defined");
10814 c = NUM2INT(rb_Integer(cval));
10815 if (c < 0 || c >= mol->bset->ncomps)
10817 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10819 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10820 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10825 * clear_mo_coefficients
10827 * Clear the existing MO coefficients.
10830 s_Molecule_ClearMOCoefficients(VALUE self)
10833 Data_Get_Struct(self, Molecule, mol);
10834 if (mol->bset != NULL) {
10835 if (mol->bset->moenergies != NULL) {
10836 free(mol->bset->moenergies);
10837 mol->bset->moenergies = NULL;
10839 if (mol->bset->mo != NULL) {
10840 free(mol->bset->mo);
10841 mol->bset->mo = NULL;
10843 mol->bset->nmos = 0;
10850 * set_mo_coefficients(idx, energy, coefficients)
10852 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10853 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10854 * Energy is the MO energy, and coefficients is an array
10855 * of MO coefficients.
10858 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10861 Int idx, ncomps, i;
10864 Data_Get_Struct(self, Molecule, mol);
10865 idx = NUM2INT(rb_Integer(ival));
10866 energy = NUM2DBL(rb_Float(eval));
10867 aval = rb_ary_to_ary(aval);
10868 ncomps = RARRAY_LEN(aval);
10869 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10870 if (coeffs == NULL) {
10874 for (i = 0; i < ncomps; i++)
10875 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10876 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10879 rb_raise(rb_eMolbyError, "Molecule is emptry");
10881 rb_raise(rb_eMolbyError, "Low memory");
10883 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10885 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10887 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10889 rb_raise(rb_eMolbyError, "Unknown error");
10895 * get_mo_coefficients(idx)
10897 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10900 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10903 Int idx, ncomps, n;
10907 Data_Get_Struct(self, Molecule, mol);
10908 idx = NUM2INT(rb_Integer(ival));
10911 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10913 rb_raise(rb_eMolbyError, "Molecule is emptry");
10915 rb_raise(rb_eMolbyError, "No basis set information is present");
10917 return Qnil; /* Silently returns nil */
10918 retval = rb_ary_new2(ncomps);
10919 for (n = 0; n < ncomps; n++)
10920 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10927 * get_mo_energy(idx)
10929 * To be used internally. Get the MO energy for the given MO index (1-based).
10932 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10937 Data_Get_Struct(self, Molecule, mol);
10938 idx = NUM2INT(rb_Integer(ival));
10939 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10941 rb_raise(rb_eMolbyError, "Molecule is emptry");
10943 rb_raise(rb_eMolbyError, "No basis set information is present");
10946 return rb_float_new(energy);
10949 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10952 s_InitMOInfoKeys(void)
10954 if (sTypeSym == 0) {
10955 sTypeSym = ID2SYM(rb_intern("type"));
10956 sAlphaSym = ID2SYM(rb_intern("alpha"));
10957 sBetaSym = ID2SYM(rb_intern("beta"));
10958 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10959 sNshellsSym = ID2SYM(rb_intern("nshells"));
10965 * set_mo_info(hash)
10967 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10968 * :alpha=>integer, :beta=>integer
10971 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10975 Int rflag, na, nb, n;
10977 Data_Get_Struct(self, Molecule, mol);
10978 if (mol->bset != NULL) {
10979 rflag = mol->bset->rflag;
10980 na = mol->bset->ne_alpha;
10981 nb = mol->bset->ne_beta;
10987 if (hval != Qnil) {
10988 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10989 s = StringValuePtr(aval);
10990 if (strcasecmp(s, "RHF") == 0)
10992 else if (strcasecmp(s, "UHF") == 0)
10994 else if (strcasecmp(s, "ROHF") == 0)
10997 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10998 n = NUM2INT(rb_Integer(aval));
11002 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
11003 n = NUM2INT(rb_Integer(aval));
11007 MoleculeSetMOInfo(mol, rflag, na, nb);
11016 * Get the MO info. The key is as described in set_mo_info.
11017 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11020 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11023 Data_Get_Struct(self, Molecule, mol);
11024 if (mol->bset == NULL)
11026 if (kval == sTypeSym) {
11027 switch (mol->bset->rflag) {
11028 case 0: return Ruby_NewEncodedStringValue2("UHF");
11029 case 1: return Ruby_NewEncodedStringValue2("RHF");
11030 case 2: return Ruby_NewEncodedStringValue2("ROHF");
11031 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11033 } else if (kval == sAlphaSym) {
11034 return INT2NUM(mol->bset->ne_alpha);
11035 } else if (kval == sBetaSym) {
11036 return INT2NUM(mol->bset->ne_beta);
11037 } else if (kval == sNcompsSym) {
11038 return INT2NUM(mol->bset->ncomps);
11039 } else if (kval == sNshellsSym) {
11040 return INT2NUM(mol->bset->nshells);
11042 kval = rb_inspect(kval);
11043 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11044 return Qnil; /* Does not reach here */
11052 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11055 s_Molecule_MOType(VALUE self)
11057 return s_Molecule_GetMOInfo(self, sTypeSym);
11060 #pragma mark ------ Molecular Topology ------
11064 * search_equivalent_atoms(ig = nil)
11066 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
11069 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11075 Data_Get_Struct(self, Molecule, mol);
11076 if (mol->natoms == 0)
11078 rb_scan_args(argc, argv, "01", &val);
11080 ig = s_Molecule_AtomGroupFromValue(self, val);
11082 result = MoleculeSearchEquivalentAtoms(mol, ig);
11083 if (result == NULL)
11084 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11086 IntGroupRelease(ig);
11087 val = rb_ary_new2(mol->natoms);
11088 for (i = 0; i < mol->natoms; i++)
11089 rb_ary_push(val, INT2NUM(result[i]));
11096 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11098 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11099 * Name is the name of the new pi anchor, and group is the atoms that define
11100 * the pi system. Type (a String) is an atom type for MM implementation.
11101 * Weights represent the relative significance of the component atoms; if omitted, then
11102 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
11103 * The weight values will be normalized so that the sum of the weights is 1.0.
11104 * The weight values must be positive.
11105 * Index is the atom index where the created pi-anchor is inserted in the
11106 * atoms array; if omitted, the pi-anchor is inserted after the component atom
11107 * having the largest index.
11108 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
11109 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11112 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11117 Int i, n, idx, last_component;
11121 if (argc < 2 || argc >= 6)
11122 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11126 Data_Get_Struct(self, Molecule, mol);
11130 ig = s_Molecule_AtomGroupFromValue(self, gval);
11131 if (ig == NULL || IntGroupGetCount(ig) == 0)
11132 rb_raise(rb_eMolbyError, "atom group is not given correctly");
11133 memset(&a, 0, sizeof(a));
11134 memset(&an, 0, sizeof(an));
11135 strncpy(a.aname, StringValuePtr(nval), 4);
11136 if (a.aname[0] == '_')
11137 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11138 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11139 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11140 if (n >= mol->natoms) {
11141 AtomConnectResize(&an.connect, 0);
11142 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11144 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11145 last_component = n;
11147 if (an.connect.count == 0)
11148 rb_raise(rb_eMolbyError, "no atoms are specified");
11149 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11150 for (i = 0; i < an.connect.count; i++) {
11151 an.coeffs[i] = 1.0 / an.connect.count;
11153 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11155 if (argv[0] != Qnil)
11156 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11160 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11161 if (argv[0] != Qnil) {
11162 VALUE aval = rb_ary_to_ary(argv[0]);
11164 if (RARRAY_LEN(aval) != an.connect.count)
11165 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11166 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11167 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11169 rb_raise(rb_eMolbyError, "the weight value must be positive");
11173 for (i = 0; i < an.connect.count; i++)
11174 an.coeffs[i] /= sum;
11179 if (argc > 0 && argv[0] != Qnil) {
11181 idx = NUM2INT(rb_Integer(argv[0]));
11183 if (idx < 0 || idx > mol->natoms) {
11184 /* Immediately after the last specified atom */
11185 idx = last_component + 1;
11187 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11188 memmove(a.anchor, &an, sizeof(PiAnchor));
11189 /* Use residue information of the last specified atom */
11190 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11191 a.resSeq = ap->resSeq;
11192 strncpy(a.resName, ap->resName, 4);
11193 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11195 MoleculeCalculatePiAnchorPosition(mol, idx);
11196 aref = AtomRefNew(mol, idx);
11197 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11200 #pragma mark ------ Molecular Properties ------
11204 * set_property(name, value[, index]) -> value
11205 * set_property(name, values, group) -> values
11207 * Set molecular property. A property is a floating-point number with a specified name,
11208 * and can be set for each frame separately. The name of the property is given as a String.
11209 * The value can be a single floating point number, which is set to the current frame.
11213 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11216 VALUE nval, vval, ival;
11219 Int i, n, idx, fidx;
11221 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11222 Data_Get_Struct(self, Molecule, mol);
11223 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11224 idx = NUM2INT(rb_Integer(nval));
11225 if (idx < 0 || idx >= mol->nmolprops)
11226 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11228 name = StringValuePtr(nval);
11229 idx = MoleculeLookUpProperty(mol, name);
11231 idx = MoleculeCreateProperty(mol, name);
11233 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11236 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11238 fidx = mol->cframe;
11240 fidx = NUM2INT(rb_Integer(ival));
11241 n = MoleculeGetNumberOfFrames(mol);
11242 if (fidx < 0 || fidx >= n)
11243 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11245 ig = IntGroupNewWithPoints(fidx, 1, -1);
11246 dp = (Double *)malloc(sizeof(Double));
11247 *dp = NUM2DBL(rb_Float(vval));
11250 vval = rb_ary_to_ary(vval);
11251 ig = IntGroupFromValue(ival);
11252 n = IntGroupGetCount(ig);
11254 rb_raise(rb_eMolbyError, "No frames are specified");
11255 if (RARRAY_LEN(vval) < n)
11256 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11257 dp = (Double *)calloc(sizeof(Double), n);
11258 for (i = 0; i < n; i++)
11259 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11262 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11264 IntGroupRelease(ig);
11270 * get_property(name[, index]) -> value
11271 * get_property(name, group) -> values
11273 * Get molecular property. In the first form, a property value for a single frame is returned.
11274 * (If index is omitted, then the value for the current frame is given)
11275 * In the second form, an array of property values for the given frames is returned.
11276 * If name is not one of known properties or a valid index integer, exception is raised.
11279 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11285 Int i, n, idx, fidx;
11287 rb_scan_args(argc, argv, "11", &nval, &ival);
11288 Data_Get_Struct(self, Molecule, mol);
11289 if (mol->nmolprops == 0)
11290 rb_raise(rb_eMolbyError, "The molecule has no properties");
11291 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11292 idx = NUM2INT(rb_Integer(nval));
11293 if (idx < 0 || idx >= mol->nmolprops)
11294 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11296 name = StringValuePtr(nval);
11297 idx = MoleculeLookUpProperty(mol, name);
11299 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11301 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11303 fidx = mol->cframe;
11305 fidx = NUM2INT(rb_Integer(ival));
11306 n = MoleculeGetNumberOfFrames(mol);
11307 if (fidx < 0 || fidx >= n)
11308 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11310 ig = IntGroupNewWithPoints(fidx, 1, -1);
11311 ival = INT2FIX(fidx);
11314 ig = IntGroupFromValue(ival);
11315 n = IntGroupGetCount(ig);
11317 return rb_ary_new();
11319 dp = (Double *)calloc(sizeof(Double), n);
11320 MoleculeGetProperty(mol, idx, ig, dp);
11321 if (FIXNUM_P(ival))
11322 ival = rb_float_new(dp[0]);
11324 ival = rb_ary_new();
11325 for (i = n - 1; i >= 0; i--) {
11326 nval = rb_float_new(dp[i]);
11327 rb_ary_store(ival, i, nval);
11331 IntGroupRelease(ig);
11337 * property_names -> Array
11339 * Get an array of property names.
11342 s_Molecule_PropertyNames(VALUE self)
11347 Data_Get_Struct(self, Molecule, mol);
11348 rval = rb_ary_new();
11349 for (i = mol->nmolprops - 1; i >= 0; i--) {
11350 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11351 rb_ary_store(rval, i, nval);
11356 #pragma mark ------ Class methods ------
11360 * current -> Molecule
11362 * Get the currently "active" molecule.
11365 s_Molecule_Current(VALUE klass)
11367 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11372 * Molecule[] -> Molecule
11373 * Molecule[n] -> Molecule
11374 * Molecule[name] -> Molecule
11375 * Molecule[name, k] -> Molecule
11376 * Molecule[regex] -> Molecule
11377 * Molecule[regex, k] -> Molecule
11379 * Molecule[] is equivalent to Molecule.current.
11380 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11381 * Molecule[name] gives the first document (in the order of creation time) that has
11382 * the given name. If a second argument (k) is given, the k-th document that has the
11383 * given name is returned.
11384 * Molecule[regex] gives the first document (in the order of creation time) that
11385 * has a name matching the regular expression. If a second argument (k) is given,
11386 * the k-th document that has a name matching the re is returned.
11389 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11395 rb_scan_args(argc, argv, "02", &val, &kval);
11397 return s_Molecule_Current(klass);
11398 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11399 idx = NUM2INT(val);
11400 mol = MoleculeCallback_moleculeAtIndex(idx);
11401 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11402 char *p = StringValuePtr(val);
11403 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11404 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11405 MoleculeCallback_displayName(mol, buf, sizeof buf);
11406 if (strcmp(buf, p) == 0 && --k == 0)
11409 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11410 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11411 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11413 MoleculeCallback_displayName(mol, buf, sizeof buf);
11414 name = Ruby_NewEncodedStringValue2(buf);
11415 if (rb_reg_match(val, name) != Qnil && --k == 0)
11418 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11422 else return ValueFromMolecule(mol);
11427 * list -> array of Molecules
11429 * Get the list of molecules associated to the documents, in the order of creation
11430 * time of the document. If no document is open, returns an empry array.
11433 s_Molecule_List(VALUE klass)
11439 ary = rb_ary_new();
11440 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11441 rb_ary_push(ary, ValueFromMolecule(mol));
11449 * ordered_list -> array of Molecules
11451 * Get the list of molecules associated to the documents, in the order of front-to-back
11452 * ordering of the associated window. If no document is open, returns an empry array.
11455 s_Molecule_OrderedList(VALUE klass)
11461 ary = rb_ary_new();
11462 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11463 rb_ary_push(ary, ValueFromMolecule(mol));
11469 #pragma mark ------ Call Subprocess ------
11471 /* The callback functions for call_subprocess_async */
11473 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11476 VALUE procval, retval, args[2];
11477 args[0] = ValueFromMolecule(mol);
11478 args[1] = INT2NUM(status);
11479 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11480 if (procval != Qnil) {
11481 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11482 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11489 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11492 VALUE procval, retval, args[2];
11493 args[0] = ValueFromMolecule(mol);
11494 args[1] = INT2NUM(tcount);
11495 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11496 if (procval != Qnil) {
11497 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11498 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11506 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11508 * Call subprocess asynchronically.
11509 * If end_callback is given, it will be called (with two arguments self and termination status)
11510 * when the subprocess terminated.
11511 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11512 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11513 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11514 * filename begins with ">>", then the message will be appended to the file.
11515 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11516 * If the argument is nil, then the message will be sent to the Ruby console.
11517 * Returns the process ID as an integer.
11520 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11522 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11526 FILE *fpout, *fperr;
11527 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11528 Data_Get_Struct(self, Molecule, mol);
11530 if (stdout_val == Qnil) {
11533 sout = StringValuePtr(stdout_val);
11534 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11537 if (strncmp(sout, ">>", 2) == 0) {
11539 fpout = fopen(sout, "a");
11543 fpout = fopen(sout, "w");
11546 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11549 if (stderr_val == Qnil) {
11552 serr = StringValuePtr(stderr_val);
11553 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11556 if (strncmp(serr, ">>", 2) == 0) {
11558 fpout = fopen(serr, "a");
11562 fperr = fopen(serr, "w");
11565 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11569 /* Register procs as instance variables */
11570 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11571 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11572 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11573 if (fpout != NULL && fpout != (FILE *)1)
11575 if (fperr != NULL && fperr != (FILE *)1)
11580 #pragma mark ====== Define Molby Classes ======
11587 /* Define module Molby */
11588 rb_mMolby = rb_define_module("Molby");
11590 /* Define Vector3D, Transform, IntGroup */
11593 /* Define MDArena */
11594 Init_MolbyMDTypes();
11596 /* class Molecule */
11597 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11599 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11600 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11601 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11602 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11603 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11605 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11606 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11607 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11608 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11609 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11610 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11611 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11612 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11613 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11614 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11615 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11616 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11617 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11618 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11619 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11620 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11621 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11622 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11623 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11624 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11625 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11626 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11628 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11629 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11630 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11631 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11632 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11634 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11635 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11636 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11637 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11638 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11639 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11640 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11641 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11642 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11643 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11644 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11645 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11646 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11647 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11648 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11650 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11651 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11652 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11653 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11654 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11656 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11657 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11658 rb_define_alias(rb_cMolecule, "+", "add");
11659 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11660 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11661 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11662 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11663 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11664 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11665 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11666 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11667 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11668 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11669 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11670 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11671 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11672 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11673 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11674 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11675 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11676 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11677 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11678 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11679 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11681 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11682 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11683 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11684 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11685 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11687 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11688 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11689 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11690 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11691 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11692 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11693 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11694 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11695 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11697 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11698 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11699 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11700 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11701 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11702 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11703 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11704 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11705 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11706 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11707 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11708 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11709 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11710 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11711 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11712 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11713 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11714 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11715 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11716 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11717 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11718 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11720 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11721 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11722 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11723 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11724 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11725 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11726 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11728 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11729 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11730 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11731 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11732 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11733 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11734 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11735 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11736 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11737 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11738 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11739 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11740 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11742 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11743 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11744 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11745 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11746 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11747 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11749 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11750 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11751 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11752 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11753 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11754 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11755 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11756 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11757 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11758 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11759 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11760 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11761 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11762 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11763 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11764 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11765 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11766 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11767 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11768 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11769 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11770 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11771 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11772 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11773 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11774 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11775 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11776 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11777 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11778 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11779 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11780 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11781 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11782 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11783 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11784 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11785 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11786 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11787 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11788 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11789 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11790 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11791 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11792 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11793 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11794 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11795 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11796 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11797 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11798 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11799 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11800 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11801 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11802 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11804 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11805 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11806 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11807 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11808 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11809 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11810 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11811 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11812 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11813 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11814 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11815 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11816 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11818 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11819 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11820 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11821 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11822 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11823 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11824 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11825 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11826 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11827 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11828 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11829 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11830 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11831 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11833 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11834 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11835 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11836 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11837 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11838 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11839 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11840 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11841 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11842 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11843 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11844 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11845 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11846 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11847 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11848 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11849 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11850 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11851 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11852 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11853 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11854 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11855 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11857 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11858 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11860 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11861 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11862 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11864 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11865 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11866 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11867 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11869 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11871 /* class MolEnumerable */
11872 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11873 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11874 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11875 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11876 rb_define_alias(rb_cMolEnumerable, "size", "length");
11877 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11878 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11880 /* class AtomRef */
11881 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11882 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11884 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11885 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11886 s_AtomAttrDefTable[i].id = rb_intern(buf);
11887 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11889 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11891 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11892 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11893 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11894 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11895 s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11896 rb_global_variable(&s_SetAtomAttrString);
11897 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11898 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11900 /* class Parameter */
11901 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11902 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11903 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11904 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11905 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11906 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11907 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11908 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11909 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11910 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11911 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11912 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11913 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11914 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11915 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11916 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11917 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11918 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11919 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11920 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11921 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11922 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11923 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11924 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11925 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11926 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11927 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11928 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11929 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11930 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11931 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11932 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11933 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11934 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11935 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11936 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11937 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11938 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11939 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11940 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11941 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11942 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11943 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11944 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11945 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11946 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11947 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11948 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11949 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11950 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11951 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11952 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11953 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11954 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11956 /* class ParEnumerable */
11957 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11958 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11959 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11960 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11961 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11962 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11963 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11964 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11965 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11966 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11967 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11969 /* class ParameterRef */
11970 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11971 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11973 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11974 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11975 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11976 if (s_ParameterAttrDefTable[i].symref != NULL)
11977 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11978 if (s_ParameterAttrDefTable[i].setter != NULL) {
11980 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11983 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11984 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11985 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11986 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11987 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11988 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11989 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11990 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11992 /* class MolbyError */
11993 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11995 /* module Kernel */
11996 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11997 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11998 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11999 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
12000 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
12001 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
12002 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
12003 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12004 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12005 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12006 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12007 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12008 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12009 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12010 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12011 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12012 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12013 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12014 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12015 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12016 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12017 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12018 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12019 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12020 rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12021 rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12022 rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12023 rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12024 rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12025 rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12028 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12030 s_ID_equal = rb_intern("==");
12031 g_RubyID_call = rb_intern("call");
12033 s_InitMOInfoKeys();
12035 /* Symbols for graphics */
12036 s_LineSym = ID2SYM(rb_intern("line"));
12037 s_PolySym = ID2SYM(rb_intern("poly"));
12038 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12039 s_ConeSym = ID2SYM(rb_intern("cone"));
12040 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12043 #pragma mark ====== Interface with RubyDialog class ======
12046 RubyDialogCallback_parentModule(void)
12048 return (RubyValue)rb_mMolby;
12051 #pragma mark ====== External functions ======
12053 static VALUE s_ruby_top_self = Qfalse;
12054 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12055 static VALUE s_ruby_export_local_variables = Qfalse;
12058 s_evalRubyScriptOnMoleculeSub(VALUE val)
12060 void **ptr = (void **)val;
12061 Molecule *mol = (Molecule *)ptr[1];
12062 VALUE sval, fnval, lnval, retval;
12065 /* Clear the error information (store in the history array if necessary) */
12066 sval = rb_errinfo();
12067 if (sval != Qnil) {
12068 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12069 rb_set_errinfo(Qnil);
12072 if (s_ruby_top_self == Qfalse) {
12073 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12075 if (s_ruby_get_binding_for_molecule == Qfalse) {
12077 "lambda { |_mol_, _bind_| \n"
12078 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12079 " _proc_.call(_mol_) } ";
12080 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12081 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12083 if (s_ruby_export_local_variables == Qfalse) {
12085 "lambda { |_bind_| \n"
12086 " # find local variables newly defined in _bind_ \n"
12087 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12088 " _a_.each { |_vsym_| \n"
12089 " _vname_ = _vsym_.to_s \n"
12090 " _vval_ = _bind_.eval(_vname_) \n"
12091 " # Define local variable \n"
12092 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12093 " # Then set value \n"
12094 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12097 s_ruby_export_local_variables = rb_eval_string(s2);
12098 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12100 if (ptr[2] == NULL) {
12102 /* String literal: we need to specify string encoding */
12103 #if defined(__WXMSW__)
12104 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12106 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12108 sval = Ruby_NewEncodedStringValue2(scr);
12110 fnval = Ruby_NewEncodedStringValue2("(eval)");
12111 lnval = INT2FIX(0);
12113 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12114 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12115 lnval = INT2FIX(1);
12117 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12119 VALUE mval = ValueFromMolecule(mol);
12120 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12122 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12124 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12130 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12134 VALUE save_interrupt_flag;
12135 /* char *save_ruby_sourcefile;
12136 int save_ruby_sourceline; */
12137 if (gMolbyIsCheckingInterrupt) {
12138 MolActionAlertRubyIsRunning();
12140 return (RubyValue)Qnil;
12143 args[0] = (void *)script;
12144 args[1] = (void *)mol;
12145 args[2] = (void *)fname;
12146 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12147 /* save_ruby_sourcefile = ruby_sourcefile;
12148 save_ruby_sourceline = ruby_sourceline; */
12149 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12150 if (*status != 0) {
12151 /* Is this 'exit' exception? */
12152 VALUE last_exception = rb_gv_get("$!");
12153 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12154 /* Capture exit and return the status value */
12155 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12157 rb_set_errinfo(Qnil);
12160 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12161 /* ruby_sourcefile = save_ruby_sourcefile;
12162 ruby_sourceline = save_ruby_sourceline; */
12169 Ruby_inspectValue(RubyValue value)
12172 static char buf[256];
12173 VALUE val = (VALUE)value;
12175 val = rb_protect(rb_inspect, val, &status);
12178 char *str = StringValuePtr(val);
12179 strncpy(buf, str, sizeof(buf) - 1);
12180 buf[sizeof(buf) - 1] = 0;
12182 snprintf(buf, sizeof(buf), "Error status = %d", status);
12188 Ruby_showValue(RubyValue value, char **outValueString)
12190 VALUE val = (VALUE)value;
12191 if (gMolbyIsCheckingInterrupt) {
12192 MolActionAlertRubyIsRunning();
12199 val = rb_protect(rb_inspect, val, &status);
12203 str = StringValuePtr(val);
12204 if (outValueString != NULL)
12205 *outValueString = strdup(str);
12206 MyAppCallback_showScriptMessage("%s", str);
12208 if (outValueString != NULL)
12209 *outValueString = NULL;
12215 Ruby_showError(int status)
12217 static const int tag_raise = 6;
12218 char *main_message = "Molby script error";
12219 char *msg = NULL, *msg2;
12220 VALUE val, backtrace;
12221 int interrupted = 0;
12222 int exit_status = -1;
12223 if (status == tag_raise) {
12224 VALUE errinfo = rb_errinfo();
12225 VALUE eclass = CLASS_OF(errinfo);
12226 if (eclass == rb_eInterrupt) {
12227 main_message = "Molby script interrupted";
12230 } else if (eclass == rb_eSystemExit) {
12231 main_message = "Molby script exit";
12233 val = rb_eval_string_protect("$!.status", &status);
12235 exit_status = NUM2INT(rb_Integer(val));
12236 asprintf(&msg, "Molby script exit with status %d", exit_status);
12238 asprintf(&msg, "Molby script exit with unknown status");
12243 if (exit_status != 0) {
12244 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12246 val = rb_eval_string_protect("$!.to_s", &status);
12248 msg = RSTRING_PTR(val);
12250 msg = "(message not available)";
12252 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12254 msg2 = strdup(msg);
12256 MyAppCallback_messageBox(msg2, main_message, 0, 3);
12258 if (interrupted == 2) {
12260 if (!gUseGUI && exit_status == 0)
12261 exit(0); // Capture exit(0) here and force exit
12266 /* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
12268 Molby_loadScript(const char *script, int from_file)
12273 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12275 rb_eval_string_protect(script, &status);
12281 Molby_getDescription(char **versionString, char **auxString)
12283 extern const char *gVersionString, *gCopyrightString;
12284 extern int gRevisionNumber;
12285 extern char *gLastBuildString;
12287 char *revisionString;
12288 if (gRevisionNumber > 0) {
12289 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12290 } else revisionString = "";
12292 asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12293 #if defined(__WXMSW__)
12294 #if TARGET_ARCH == 64
12302 gVersionString, revisionString, gCopyrightString, gLastBuildString);
12307 "ruby %s, http://www.ruby-lang.org/\n"
12309 "FFTW 3.3.2, http://www.fftw.org/\n"
12310 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12311 " and Massachusetts Institute of Technology",
12312 MyAppCallback_getGUIDescriptionString(),
12313 gRubyVersion, gRubyCopyright);
12317 "ruby %s, http://www.ruby-lang.org/\n"
12319 "FFTW 3.3.2, http://www.fftw.org/\n"
12320 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12321 " and Massachusetts Institute of Technology",
12322 gRubyVersion, gRubyCopyright);
12325 if (revisionString[0] != 0)
12326 free(revisionString);
12327 if (versionString != NULL)
12328 *versionString = s1;
12329 if (auxString != NULL)
12334 Molby_startup(const char *script, const char *dir)
12339 char *respath, *p, *wbuf;
12341 /* Get version/copyright string from Ruby interpreter */
12343 gRubyVersion = strdup(ruby_version);
12344 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12345 " ", /* Indent for displaying in About dialog */
12346 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12349 /* Read build and revision information for Molby */
12352 extern int gRevisionNumber;
12353 extern char *gLastBuildString;
12354 FILE *fp = fopen("../buildInfo.txt", "r");
12355 gLastBuildString = "";
12357 if (fgets(buf, sizeof(buf), fp) != NULL) {
12358 char *p1 = strchr(buf, '\"');
12359 char *p2 = strrchr(buf, '\"');
12360 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12361 memmove(buf, p1 + 1, p2 - p1 - 1);
12362 buf[p2 - p1 - 1] = 0;
12363 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12368 fp = fopen("../revisionInfo.txt", "r");
12369 gRevisionNumber = 0;
12371 if (fgets(buf, sizeof(buf), fp) != NULL) {
12372 gRevisionNumber = strtol(buf, NULL, 0);
12380 Molby_getDescription(&wbuf, &wbuf2);
12381 MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12386 /* Read atom display parameters */
12387 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12388 MyAppCallback_setConsoleColor(1);
12389 MyAppCallback_showScriptMessage("%s", wbuf);
12390 MyAppCallback_setConsoleColor(0);
12394 /* Read default parameters */
12395 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12396 if (wbuf != NULL) {
12397 MyAppCallback_setConsoleColor(1);
12398 MyAppCallback_showScriptMessage("%s", wbuf);
12399 MyAppCallback_setConsoleColor(0);
12403 /* Initialize Ruby interpreter */
12406 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12407 it causes rb_bug() (= fatal error) during ruby_init().
12408 As a workaround, these standard streams are reopend as
12410 freopen("NUL", "r", stdin);
12411 freopen("NUL", "w", stdout);
12412 freopen("NUL", "w", stderr);
12418 /* Initialize CP932/Windows-31J encodings */
12419 extern void Init_shift_jis(void), Init_windows_31j(void), Init_trans_japanese_sjis(void);
12420 extern int rb_enc_alias(const char *, const char *);
12422 Init_windows_31j();
12423 Init_trans_japanese_sjis();
12424 rb_enc_alias("CP932", "Windows-31J");
12427 #if defined(__WXMSW__)
12429 /* Set default external encoding */
12430 /* The following snippet is taken from encoding.c */
12431 extern void rb_enc_set_default_external(VALUE encoding);
12432 char cp[sizeof(int) * 8 / 3 + 22];
12435 snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12436 enc = rb_eval_string_protect(cp, &status);
12437 if (status == 0 && !NIL_P(enc)) {
12438 rb_enc_set_default_external(enc);
12443 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12445 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12446 ruby_incpush(libpath);
12450 ruby_script("Molby");
12452 /* Find the resource path (the parent directory of the given directory) */
12453 respath = strdup(dir);
12454 p = strrchr(respath, '/');
12455 if (p == NULL && PATH_SEPARATOR != '/')
12456 p = strrchr(respath, PATH_SEPARATOR);
12459 val = Ruby_NewFileStringValue(respath);
12460 rb_define_global_const("MolbyResourcePath", val);
12463 /* Define Molby classes */
12466 RubyDialogInitClass();
12468 rb_define_const(rb_mMolby, "ResourcePath", val);
12469 val = Ruby_NewFileStringValue(dir);
12470 rb_define_const(rb_mMolby, "ScriptPath", val);
12471 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12472 val = Ruby_NewFileStringValue(p);
12473 rb_define_const(rb_mMolby, "MbsfPath", val);
12476 p = MyAppCallback_getHomeDir();
12477 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12478 rb_define_const(rb_mMolby, "HomeDirectory", val);
12480 p = MyAppCallback_getDocumentHomeDir();
12481 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12482 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12486 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12488 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12491 /* Create objects for stdout and stderr */
12492 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12493 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12494 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12495 rb_gv_set("$stdout", val);
12496 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12497 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12498 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12499 rb_gv_set("$stderr", val);
12501 /* Create objects for stdin */
12502 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12503 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12504 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12505 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12506 rb_gv_set("$stdin", val);
12509 /* Global variable to hold error information */
12510 rb_define_variable("$backtrace", &gMolbyBacktrace);
12511 rb_define_variable("$error_history", &gMolbyErrorHistory);
12512 gMolbyErrorHistory = rb_ary_new();
12514 /* Global variables for script menus */
12515 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12516 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12517 gScriptMenuCommands = rb_ary_new();
12518 gScriptMenuEnablers = rb_ary_new();
12521 /* Register interrupt check code */
12522 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12523 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12524 s_SetIntervalTimer(0, 50);
12527 /* Read the startup script */
12528 if (script != NULL && script[0] != 0) {
12529 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12531 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12534 Ruby_showError(status);
12536 MyAppCallback_showScriptMessage("Done.\n");
12541 Molby_buildARGV(int argc, const char **argv)
12544 rb_ary_clear(rb_argv);
12545 for (i = 0; i < argc; i++) {
12546 VALUE arg = rb_tainted_str_new2(argv[i]);
12548 rb_ary_push(rb_argv, arg);