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 FILE *fpout, *fperr;
1017 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1019 if (stdout_val == Qnil) {
1022 sout = StringValuePtr(stdout_val);
1023 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1026 if (strncmp(sout, ">>", 2) == 0) {
1028 fpout = fopen(sout, "a");
1032 fpout = fopen(sout, "w");
1035 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1038 if (stderr_val == Qnil) {
1041 serr = StringValuePtr(stderr_val);
1042 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1045 if (strncmp(serr, ">>", 2) == 0) {
1047 fpout = fopen(serr, "a");
1051 fperr = fopen(serr, "w");
1054 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1058 save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1059 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1060 s_SetInterruptFlag(self, save_interruptFlag);
1062 if (fpout != NULL && fpout != (FILE *)1)
1064 if (fperr != NULL && fperr != (FILE *)1)
1076 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
1079 s_Kernel_Backquote(VALUE self, VALUE cmd)
1082 int n, exitstatus, pid;
1084 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1085 /* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1086 if (n >= 0 && buf != NULL) {
1087 val = Ruby_NewEncodedStringValue(buf, 0);
1090 val = Ruby_NewEncodedStringValue("", 0);
1092 rb_last_status_set(exitstatus, pid);
1096 #pragma mark ====== User defaults ======
1100 * get_global_settings(key)
1102 * Get a setting data for key from the application preferences.
1105 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1107 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1109 VALUE retval = rb_eval_string(p);
1117 * set_global_settings(key, value)
1119 * Set a setting data for key to the application preferences.
1122 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1124 VALUE sval = rb_inspect(value);
1125 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1129 #pragma mark ====== IO extension ======
1132 s_Ruby_str_encode_protected(VALUE val)
1134 return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1135 ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1142 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1145 s_IO_gets_any_eol(VALUE self)
1147 VALUE val, val2, cval;
1150 static ID id_getbyte = 0, id_ungetbyte;
1151 if (id_getbyte == 0) {
1152 id_getbyte = rb_intern("getbyte");
1153 id_ungetbyte = rb_intern("ungetbyte");
1157 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1158 c = NUM2INT(rb_Integer(cval));
1160 cval = rb_funcall(self, id_getbyte, 0);
1162 c = NUM2INT(rb_Integer(cval));
1164 rb_funcall(self, id_ungetbyte, 1, cval);
1167 } else if (c != 0x0a) {
1172 val = rb_str_new(buf, i);
1174 rb_str_append(val, rb_str_new(buf, i));
1179 if (cval == Qnil && i == 0 && val == Qnil)
1180 return Qnil; /* End of file */
1183 val = rb_str_new(buf, i);
1185 rb_str_append(val, rb_str_new(buf, i));
1186 val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
1190 /* Needs a end-of-line mark */
1191 cval = rb_gv_get("$/");
1192 rb_str_append(val, cval);
1194 rb_gv_set("$_", val);
1198 #pragma mark ====== Utility functions (protected funcall) ======
1200 struct Ruby_funcall2_record {
1208 s_Ruby_funcall2_sub(VALUE data)
1210 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1211 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1215 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1217 struct Ruby_funcall2_record rec;
1222 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1226 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1228 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1231 #pragma mark ====== ParameterRef Class ======
1234 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1238 Data_Get_Struct(self, ParameterRef, pref);
1240 *typep = pref->parType;
1241 if (pref->parType == kElementParType) {
1242 up = (UnionPar *)&gElementParameters[pref->idx];
1244 up = ParameterRefGetPar(pref);
1245 if (checkEditable) {
1247 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1248 if (up->bond.src != 0 && up->bond.src != -1)
1249 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1256 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1260 Data_Get_Struct(self, ParameterRef, pref);
1261 if (pref->mol == NULL)
1263 up = ParameterRefGetPar(pref);
1264 if (key != s_SourceSym)
1265 up->bond.src = 0; /* Becomes automatically molecule-local */
1266 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1269 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1270 MolActionCallback_registerUndo(pref->mol, act);
1271 MoleculeCallback_notifyModification(pref->mol, 0);
1272 pref->mol->needsMDRebuild = 1;
1277 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1279 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1281 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1283 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1287 s_AtomTypeIndexFromValue(VALUE val)
1289 if (rb_obj_is_kind_of(val, rb_cNumeric))
1290 return NUM2INT(val);
1292 return AtomTypeEncodeToUInt(StringValuePtr(val));
1295 static const char *s_ParameterTypeNames[] = {
1296 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1298 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1301 s_ParTypeFromValue(VALUE val)
1305 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1306 if (s_ParameterTypeIDs[0] == 0) {
1307 for (i = 0; i < n; i++)
1308 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1310 valid = rb_to_id(val);
1311 for (i = 0; i < n; i++) {
1312 if (valid == s_ParameterTypeIDs[i]) {
1314 return kElementParType;
1315 else return kFirstParType + i;
1318 return kInvalidParType;
1325 * Get the index in the parameter list.
1327 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1329 Data_Get_Struct(self, ParameterRef, pref);
1330 return INT2NUM(pref->idx);
1335 * par_type -> String
1337 * Get the parameter type, like "bond", "angle", etc.
1339 static VALUE s_ParameterRef_GetParType(VALUE self) {
1341 s_UnionParFromValue(self, &tp, 0);
1342 if (tp == kElementParType)
1343 return Ruby_NewEncodedStringValue2("element");
1344 tp -= kFirstParType;
1345 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1346 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1347 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1352 * atom_type -> String or Array of String
1353 * atom_types -> String or Array of String
1355 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1356 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1357 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1358 * The atom type may be "X", which is a wildcard that matches any atom type.
1360 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1365 up = s_UnionParFromValue(self, &tp, 0);
1366 n = ParameterGetAtomTypes(tp, up, types);
1368 rb_raise(rb_eMolbyError, "invalid member atom_types");
1369 for (i = 0; i < n; i++) {
1370 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1371 vals[i] = INT2NUM(types[i]);
1373 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1378 return rb_ary_new4(n, vals);
1385 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1387 static VALUE s_ParameterRef_GetK(VALUE self) {
1391 up = s_UnionParFromValue(self, &tp, 0);
1394 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1396 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1397 case kDihedralParType:
1398 case kImproperParType:
1399 if (up->torsion.mult == 1)
1400 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1401 n = up->torsion.mult;
1404 for (i = 0; i < n; i++)
1405 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1406 return rb_ary_new4(n, vals);
1408 rb_raise(rb_eMolbyError, "invalid member k");
1416 * Get the equilibrium bond length. Only available for bond parameters.
1418 static VALUE s_ParameterRef_GetR0(VALUE self) {
1421 up = s_UnionParFromValue(self, &tp, 0);
1422 if (tp == kBondParType)
1423 return rb_float_new(up->bond.r0);
1424 else rb_raise(rb_eMolbyError, "invalid member r0");
1431 * Get the equilibrium angle (in degree). Only available for angle parameters.
1433 static VALUE s_ParameterRef_GetA0(VALUE self) {
1436 up = s_UnionParFromValue(self, &tp, 0);
1437 if (tp == kAngleParType)
1438 return rb_float_new(up->angle.a0 * kRad2Deg);
1439 else rb_raise(rb_eMolbyError, "invalid member a0");
1446 * Get the multiplicity. Only available for dihedral and improper parameters.
1447 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1449 static VALUE s_ParameterRef_GetMult(VALUE self) {
1452 up = s_UnionParFromValue(self, &tp, 0);
1453 if (tp == kDihedralParType || tp == kImproperParType)
1454 return rb_float_new(up->torsion.mult);
1455 else rb_raise(rb_eMolbyError, "invalid member mult");
1460 * period -> Integer or Array of Integers
1462 * Get the periodicity. Only available for dihedral and improper parameters.
1463 * If the multiplicity is larger than 1, then an array of integers is returned.
1464 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1466 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1470 up = s_UnionParFromValue(self, &tp, 0);
1471 if (tp == kDihedralParType || tp == kImproperParType) {
1472 if (up->torsion.mult == 1)
1473 return INT2NUM(up->torsion.period[0]);
1474 n = up->torsion.mult;
1477 for (i = 0; i < n; i++)
1478 vals[i] = INT2NUM(up->torsion.period[i]);
1479 return rb_ary_new4(n, vals);
1480 } else rb_raise(rb_eMolbyError, "invalid member period");
1485 * phi0 -> Float or Array of Floats
1487 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1488 * If the multiplicity is larger than 1, then an array of floats is returned.
1489 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1491 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1495 up = s_UnionParFromValue(self, &tp, 0);
1496 if (tp == kDihedralParType || tp == kImproperParType) {
1497 if (up->torsion.mult == 1)
1498 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1499 n = up->torsion.mult;
1502 for (i = 0; i < n; i++)
1503 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1504 return rb_ary_new4(n, vals);
1505 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1512 * Get the "A" value for the van der Waals parameter.
1515 static VALUE s_ParameterRef_GetA(VALUE self) {
1518 up = s_UnionParFromValue(self, &tp, 0);
1519 if (tp == kVdwParType)
1520 return rb_float_new(up->vdw.A);
1521 else if (tp == kVdwPairParType)
1522 return rb_float_new(up->vdwp.A);
1523 else rb_raise(rb_eMolbyError, "invalid member A");
1531 * Get the "B" value for the van der Waals parameter.
1534 static VALUE s_ParameterRef_GetB(VALUE self) {
1537 up = s_UnionParFromValue(self, &tp, 0);
1538 if (tp == kVdwParType)
1539 return rb_float_new(up->vdw.B);
1540 else if (tp == kVdwPairParType)
1541 return rb_float_new(up->vdwp.B);
1542 else rb_raise(rb_eMolbyError, "invalid member B");
1550 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1552 static VALUE s_ParameterRef_GetReq(VALUE self) {
1555 /* Double a, b, r; */
1557 up = s_UnionParFromValue(self, &tp, 0);
1558 if (tp == kVdwParType) {
1562 } else if (tp == kVdwPairParType) {
1566 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1567 /* if (a == 0.0 || b == 0.0) */
1568 return rb_float_new(r);
1569 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1576 * Get the minimum energy for the van der Waals parameter.
1578 static VALUE s_ParameterRef_GetEps(VALUE self) {
1583 up = s_UnionParFromValue(self, &tp, 0);
1584 if (tp == kVdwParType) {
1588 } else if (tp == kVdwPairParType) {
1592 } else rb_raise(rb_eMolbyError, "invalid member eps");
1593 /* if (a == 0.0 || b == 0.0) */
1594 return rb_float_new(eps * INTERNAL2KCAL);
1595 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1602 * Get the "A" value for the 1-4 van der Waals parameter.
1605 static VALUE s_ParameterRef_GetA14(VALUE self) {
1608 up = s_UnionParFromValue(self, &tp, 0);
1609 if (tp == kVdwParType)
1610 return rb_float_new(up->vdw.A14);
1611 else if (tp == kVdwPairParType)
1612 return rb_float_new(up->vdwp.A14);
1613 else rb_raise(rb_eMolbyError, "invalid member A14");
1621 * Get the "B" value for the 1-4 van der Waals parameter.
1624 static VALUE s_ParameterRef_GetB14(VALUE self) {
1627 up = s_UnionParFromValue(self, &tp, 0);
1628 if (tp == kVdwParType)
1629 return rb_float_new(up->vdw.B14);
1630 else if (tp == kVdwPairParType)
1631 return rb_float_new(up->vdwp.B14);
1632 else rb_raise(rb_eMolbyError, "invalid member B14");
1640 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1642 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1645 /* Double a, b, r; */
1647 up = s_UnionParFromValue(self, &tp, 0);
1648 if (tp == kVdwParType) {
1652 } else if (tp == kVdwPairParType) {
1653 /* a = up->vdwp.A14;
1654 b = up->vdwp.B14; */
1655 r = up->vdwp.r_eq14;
1656 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1657 /* if (a == 0.0 || b == 0.0) */
1658 return rb_float_new(r);
1659 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1666 * Get the minimum energy for the 1-4 van der Waals parameter.
1668 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1673 up = s_UnionParFromValue(self, &tp, 0);
1674 if (tp == kVdwParType) {
1677 eps = up->vdw.eps14;
1678 } else if (tp == kVdwPairParType) {
1679 /* a = up->vdwp.A14;
1680 b = up->vdwp.B14; */
1681 eps = up->vdwp.eps14;
1682 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1683 /* if (a == 0.0 || b == 0.0) */
1684 return rb_float_new(eps * INTERNAL2KCAL);
1685 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1692 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1694 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1697 up = s_UnionParFromValue(self, &tp, 0);
1698 if (tp == kVdwCutoffParType)
1699 return rb_float_new(up->vdwcutoff.cutoff);
1700 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1707 * Get the atomic (covalent) radius for the element parameter.
1709 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1712 up = s_UnionParFromValue(self, &tp, 0);
1713 if (tp == kElementParType)
1714 return rb_float_new(up->atom.radius);
1715 else rb_raise(rb_eMolbyError, "invalid member radius");
1720 * vdw_radius -> Float
1722 * Get the van der Waals radius for the element parameter. (0 if not given)
1724 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1727 up = s_UnionParFromValue(self, &tp, 0);
1728 if (tp == kElementParType)
1729 return rb_float_new(up->atom.vdw_radius);
1730 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1735 * color -> [Float, Float, Float]
1737 * Get the rgb color for the element parameter.
1739 static VALUE s_ParameterRef_GetColor(VALUE self) {
1742 up = s_UnionParFromValue(self, &tp, 0);
1743 if (tp == kElementParType)
1744 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));
1745 else rb_raise(rb_eMolbyError, "invalid member color");
1750 * atomic_number -> Integer
1752 * Get the atomic number for the vdw or element parameter.
1754 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1757 up = s_UnionParFromValue(self, &tp, 0);
1758 if (tp == kElementParType)
1759 return INT2NUM(up->atom.number);
1760 else if (tp == kVdwParType)
1761 return INT2NUM(up->vdw.atomicNumber);
1762 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1769 * Get the name for the element parameter.
1771 static VALUE s_ParameterRef_GetName(VALUE self) {
1774 up = s_UnionParFromValue(self, &tp, 0);
1775 if (tp == kElementParType) {
1777 strncpy(name, up->atom.name, 4);
1779 return Ruby_NewEncodedStringValue2(name);
1780 } else rb_raise(rb_eMolbyError, "invalid member name");
1787 * Get the atomic weight for the element parameter.
1789 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1792 up = s_UnionParFromValue(self, &tp, 0);
1793 if (tp == kElementParType)
1794 return rb_float_new(up->atom.weight);
1795 else if (tp == kVdwParType)
1796 return rb_float_new(up->vdw.weight);
1797 else rb_raise(rb_eMolbyError, "invalid member weight");
1802 * fullname -> String
1804 * Get the full name for the element parameter.
1806 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1809 up = s_UnionParFromValue(self, &tp, 0);
1810 if (tp == kElementParType) {
1812 strncpy(fullname, up->atom.fullname, 15);
1814 return Ruby_NewEncodedStringValue2(fullname);
1815 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1822 * Get the comment for the parameter.
1824 static VALUE s_ParameterRef_GetComment(VALUE self) {
1827 up = s_UnionParFromValue(self, &tp, 0);
1831 else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1838 * Get the source string for the parameter. Returns false for undefined parameter,
1839 * and nil for "local" parameter that is specific for the molecule.
1841 static VALUE s_ParameterRef_GetSource(VALUE self) {
1844 up = s_UnionParFromValue(self, &tp, 0);
1847 return Qfalse; /* undefined */
1849 return Qnil; /* local */
1850 else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1854 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1861 if (rb_obj_is_kind_of(val, rb_cString)) {
1862 char *s = StringValuePtr(val);
1864 for (i = 0; i < n; i++) {
1867 /* Skip leading separaters */
1868 while (*s == '-' || *s == ' ' || *s == '\t')
1870 for (p = s; *p != 0; p++) {
1871 if (*p == '-' || *p == ' ' || *p == '\t')
1875 if (len >= sizeof(buf))
1876 len = sizeof(buf) - 1;
1877 strncpy(buf, s, len);
1879 /* Skip trailing blanks */
1880 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1883 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1884 if (buf[0] >= '0' && buf[0] <= '9')
1885 types[i] = atoi(buf);
1887 types[i] = AtomTypeEncodeToUInt(buf);
1888 if (p == NULL || *p == 0) {
1894 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1897 val = rb_ary_to_ary(val);
1898 if (RARRAY_LEN(val) != n)
1899 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1900 valp = RARRAY_PTR(val);
1902 for (i = 0; i < n; i++) {
1903 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1904 types[i] = NUM2INT(rb_Integer(valp[i]));
1906 VALUE sval = valp[i];
1907 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1912 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1917 up = s_UnionParFromValue(self, &tp, 1);
1918 oldval = s_ParameterRef_GetAtomTypes(self);
1919 oldsrc = up->bond.src;
1922 s_ScanAtomTypes(val, 2, types);
1923 up->bond.type1 = types[0];
1924 up->bond.type2 = types[1];
1927 s_ScanAtomTypes(val, 3, types);
1928 up->angle.type1 = types[0];
1929 up->angle.type2 = types[1];
1930 up->angle.type3 = types[2];
1932 case kDihedralParType:
1933 case kImproperParType:
1934 s_ScanAtomTypes(val, 4, types);
1935 up->torsion.type1 = types[0];
1936 up->torsion.type2 = types[1];
1937 up->torsion.type3 = types[2];
1938 up->torsion.type4 = types[3];
1941 s_ScanAtomTypes(val, 1, types);
1942 up->vdw.type1 = types[0];
1944 case kVdwPairParType:
1945 s_ScanAtomTypes(val, 2, types);
1946 up->vdwp.type1 = types[0];
1947 up->vdwp.type2 = types[1];
1949 case kVdwCutoffParType:
1950 s_ScanAtomTypes(val, 2, types);
1951 up->vdwcutoff.type1 = types[0];
1952 up->vdwcutoff.type2 = types[1];
1957 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1961 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1963 Int tp, i, n, oldsrc;
1964 VALUE *valp, oldval;
1965 up = s_UnionParFromValue(self, &tp, 1);
1966 oldval = s_ParameterRef_GetK(self);
1967 oldsrc = up->bond.src;
1970 val = rb_Float(val);
1971 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1974 val = rb_Float(val);
1975 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1977 case kDihedralParType:
1978 case kImproperParType:
1979 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1980 up->torsion.mult = 1;
1981 val = rb_Float(val);
1982 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1985 n = up->torsion.mult;
1988 val = rb_ary_to_ary(val);
1989 if (RARRAY_LEN(val) != n)
1990 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1991 valp = RARRAY_PTR(val);
1992 for (i = 0; i < n; i++) {
1993 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1997 rb_raise(rb_eMolbyError, "invalid member k");
1999 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2003 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2007 up = s_UnionParFromValue(self, &tp, 1);
2008 oldval = s_ParameterRef_GetR0(self);
2009 oldsrc = up->bond.src;
2010 if (tp == kBondParType) {
2011 val = rb_Float(val);
2012 up->bond.r0 = NUM2DBL(val);
2013 } else rb_raise(rb_eMolbyError, "invalid member r0");
2014 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2018 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2022 up = s_UnionParFromValue(self, &tp, 1);
2023 oldval = s_ParameterRef_GetA0(self);
2024 oldsrc = up->bond.src;
2025 if (tp == kAngleParType) {
2026 val = rb_Float(val);
2027 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2028 } else rb_raise(rb_eMolbyError, "invalid member a0");
2029 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2033 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2037 up = s_UnionParFromValue(self, &tp, 1);
2038 oldval = s_ParameterRef_GetMult(self);
2039 oldsrc = up->bond.src;
2040 if (tp == kDihedralParType || tp == kImproperParType) {
2042 val = rb_Integer(val);
2045 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2046 up->torsion.mult = i;
2047 } else rb_raise(rb_eMolbyError, "invalid member mult");
2048 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2052 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2054 Int tp, i, n, oldsrc;
2055 VALUE *valp, oldval;
2056 up = s_UnionParFromValue(self, &tp, 1);
2057 oldval = s_ParameterRef_GetPeriod(self);
2058 oldsrc = up->bond.src;
2059 if (tp == kDihedralParType || tp == kImproperParType) {
2060 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2061 up->torsion.mult = 1;
2062 val = rb_Integer(val);
2063 up->torsion.period[0] = NUM2INT(val);
2065 n = up->torsion.mult;
2068 val = rb_ary_to_ary(val);
2069 if (RARRAY_LEN(val) != n)
2070 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2071 valp = RARRAY_PTR(val);
2072 for (i = 0; i < n; i++) {
2073 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2076 } else rb_raise(rb_eMolbyError, "invalid member period");
2077 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2081 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2083 Int tp, i, n, oldsrc;
2084 VALUE *valp, oldval;
2085 up = s_UnionParFromValue(self, &tp, 1);
2086 oldval = s_ParameterRef_GetPhi0(self);
2087 oldsrc = up->bond.src;
2088 if (tp == kDihedralParType || tp == kImproperParType) {
2089 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2090 up->torsion.mult = 1;
2091 val = rb_Float(val);
2092 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2094 n = up->torsion.mult;
2097 val = rb_ary_to_ary(val);
2098 if (RARRAY_LEN(val) != n)
2099 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2100 valp = RARRAY_PTR(val);
2101 for (i = 0; i < n; i++)
2102 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2104 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2105 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2110 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2115 up = s_UnionParFromValue(self, &tp, 1);
2116 oldval = s_ParameterRef_GetA(self);
2117 oldsrc = up->bond.src;
2118 val = rb_Float(val);
2120 if (tp == kVdwParType)
2122 else if (tp == kVdwPairParType)
2124 else rb_raise(rb_eMolbyError, "invalid member A");
2125 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2129 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2134 up = s_UnionParFromValue(self, &tp, 1);
2135 oldval = s_ParameterRef_GetB(self);
2136 oldsrc = up->bond.src;
2137 val = rb_Float(val);
2139 if (tp == kVdwParType)
2141 else if (tp == kVdwPairParType)
2143 else rb_raise(rb_eMolbyError, "invalid member B");
2144 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2149 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2154 up = s_UnionParFromValue(self, &tp, 1);
2155 oldval = s_ParameterRef_GetReq(self);
2156 oldsrc = up->bond.src;
2157 val = rb_Float(val);
2159 if (tp == kVdwParType) {
2161 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2162 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2163 } else if (tp == kVdwPairParType) {
2165 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2166 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2167 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2168 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2172 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2177 up = s_UnionParFromValue(self, &tp, 1);
2178 oldval = s_ParameterRef_GetEps(self);
2179 oldsrc = up->bond.src;
2180 val = rb_Float(val);
2181 e = NUM2DBL(val) * KCAL2INTERNAL;
2182 if (tp == kVdwParType) {
2184 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2185 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2186 } else if (tp == kVdwPairParType) {
2188 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2189 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2190 } else rb_raise(rb_eMolbyError, "invalid member eps");
2191 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2196 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2201 up = s_UnionParFromValue(self, &tp, 1);
2202 oldval = s_ParameterRef_GetA14(self);
2203 oldsrc = up->bond.src;
2204 val = rb_Float(val);
2206 if (tp == kVdwParType)
2208 else if (tp == kVdwPairParType)
2210 else rb_raise(rb_eMolbyError, "invalid member A14");
2211 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2215 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2220 up = s_UnionParFromValue(self, &tp, 1);
2221 oldval = s_ParameterRef_GetB14(self);
2222 oldsrc = up->bond.src;
2223 val = rb_Float(val);
2225 if (tp == kVdwParType)
2227 else if (tp == kVdwPairParType)
2229 else rb_raise(rb_eMolbyError, "invalid member B14");
2230 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2235 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2240 up = s_UnionParFromValue(self, &tp, 1);
2241 oldval = s_ParameterRef_GetReq14(self);
2242 oldsrc = up->bond.src;
2243 val = rb_Float(val);
2245 if (tp == kVdwParType) {
2247 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2248 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2249 } else if (tp == kVdwPairParType) {
2250 up->vdwp.r_eq14 = r;
2251 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2252 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2253 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2254 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2258 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2263 up = s_UnionParFromValue(self, &tp, 1);
2264 oldval = s_ParameterRef_GetEps14(self);
2265 oldsrc = up->bond.src;
2266 val = rb_Float(val);
2267 e = NUM2DBL(val) * KCAL2INTERNAL;
2268 if (tp == kVdwParType) {
2270 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2271 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2272 } else if (tp == kVdwPairParType) {
2274 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2275 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2276 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2277 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2281 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2285 oldval = s_ParameterRef_GetCutoff(self);
2286 oldsrc = up->bond.src;
2287 up = s_UnionParFromValue(self, &tp, 1);
2288 val = rb_Float(val);
2289 if (tp == kVdwCutoffParType) {
2290 up->vdwcutoff.cutoff = NUM2DBL(val);
2291 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2292 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2296 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2300 up = s_UnionParFromValue(self, &tp, 1);
2301 oldval = s_ParameterRef_GetRadius(self);
2302 oldsrc = up->bond.src;
2303 val = rb_Float(val);
2304 if (tp == kElementParType) {
2305 up->atom.radius = NUM2DBL(val);
2306 } else rb_raise(rb_eMolbyError, "invalid member radius");
2307 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2311 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2315 up = s_UnionParFromValue(self, &tp, 1);
2316 oldval = s_ParameterRef_GetVdwRadius(self);
2317 oldsrc = up->bond.src;
2318 val = rb_Float(val);
2319 if (tp == kElementParType) {
2320 up->atom.vdw_radius = NUM2DBL(val);
2321 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2322 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2326 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2329 VALUE *valp, oldval;
2330 up = s_UnionParFromValue(self, &tp, 1);
2331 oldval = s_ParameterRef_GetColor(self);
2332 oldsrc = up->bond.src;
2333 val = rb_ary_to_ary(val);
2334 if (RARRAY_LEN(val) != 3)
2335 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2336 valp = RARRAY_PTR(val);
2337 if (tp == kElementParType) {
2338 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2339 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2340 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2341 } else rb_raise(rb_eMolbyError, "invalid member color");
2342 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2346 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2350 up = s_UnionParFromValue(self, &tp, 1);
2351 oldval = s_ParameterRef_GetAtomicNumber(self);
2352 oldsrc = up->bond.src;
2353 val = rb_Integer(val);
2354 if (tp == kElementParType)
2355 up->atom.number = NUM2INT(val);
2356 else if (tp == kVdwParType) {
2357 up->vdw.atomicNumber = NUM2INT(val);
2358 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2359 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2360 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2364 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2368 up = s_UnionParFromValue(self, &tp, 1);
2369 oldval = s_ParameterRef_GetName(self);
2370 oldsrc = up->bond.src;
2371 if (tp == kElementParType) {
2372 strncpy(up->atom.name, StringValuePtr(val), 4);
2373 } else rb_raise(rb_eMolbyError, "invalid member name");
2374 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2378 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2382 val = rb_Float(val);
2383 oldval = s_ParameterRef_GetWeight(self);
2384 up = s_UnionParFromValue(self, &tp, 1);
2385 oldsrc = up->bond.src;
2386 if (tp == kElementParType)
2387 up->atom.weight = NUM2DBL(val);
2388 else if (tp == kVdwParType)
2389 up->vdw.weight = NUM2DBL(val);
2390 else rb_raise(rb_eMolbyError, "invalid member weight");
2391 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2395 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2399 up = s_UnionParFromValue(self, &tp, 1);
2400 oldval = s_ParameterRef_GetFullName(self);
2401 oldsrc = up->bond.src;
2402 if (tp == kElementParType) {
2403 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2404 up->atom.fullname[15] = 0;
2405 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2406 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2410 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2412 Int tp, com, oldsrc;
2414 up = s_UnionParFromValue(self, &tp, 1);
2415 oldval = s_ParameterRef_GetComment(self);
2416 oldsrc = up->bond.src;
2420 com = ParameterCommentIndex(StringValuePtr(val));
2423 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2427 /* Only false (undefined) and nil (local) can be set */
2428 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2432 up = s_UnionParFromValue(self, &tp, 1);
2433 if (val != Qfalse && val != Qnil)
2434 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2435 oldval = s_ParameterRef_GetSource(self);
2436 oldsrc = up->bond.src;
2437 if (oldsrc != 0 && oldsrc != -1)
2438 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2439 up->bond.src = (val == Qfalse ? -1 : 0);
2440 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2444 static struct s_ParameterAttrDef {
2446 VALUE *symref; /* Address of s_IndexSymbol etc. */
2447 ID id; /* Will be set within InitMolby() */
2448 VALUE (*getter)(VALUE);
2449 VALUE (*setter)(VALUE, VALUE);
2450 } s_ParameterAttrDefTable[] = {
2451 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2452 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2453 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2454 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2455 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2456 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2457 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2458 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2459 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2460 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2461 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2462 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2463 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2464 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2465 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2466 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2467 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2468 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2469 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2470 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2471 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2472 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2473 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2474 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2475 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2476 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2477 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2478 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2479 {NULL} /* Sentinel */
2483 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2487 if (TYPE(key) != T_SYMBOL) {
2488 kid = rb_intern(StringValuePtr(key));
2490 } else kid = SYM2ID(key);
2491 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2492 if (s_ParameterAttrDefTable[i].id == kid) {
2493 if (value == Qundef)
2494 return (*(s_ParameterAttrDefTable[i].getter))(self);
2495 else if (s_ParameterAttrDefTable[i].setter == NULL)
2496 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2498 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2501 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2502 return Qnil; /* not reached */
2506 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2508 return s_ParameterRef_SetAttr(self, key, Qundef);
2513 * keys(idx) -> array of valid parameter attributes
2515 * Returns an array of valid parameter attributes (as Symbols).
2518 s_ParameterRef_Keys(VALUE self)
2521 Data_Get_Struct(self, ParameterRef, pref);
2522 switch (pref->parType) {
2524 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2526 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2527 case kDihedralParType:
2528 case kImproperParType:
2529 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2531 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);
2532 case kVdwPairParType:
2533 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2534 case kVdwCutoffParType:
2535 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2536 case kElementParType:
2537 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);
2539 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2541 return Qnil; /* Not reached */
2546 * to_hash(idx) -> Hash
2548 * Returns a hash containing valid parameter names and values
2551 s_ParameterRef_ToHash(VALUE self)
2553 VALUE keys = s_ParameterRef_Keys(self);
2558 retval = rb_hash_new();
2559 for (i = 0; i < RARRAY_LEN(keys); i++) {
2560 VALUE key = RARRAY_PTR(keys)[i];
2561 VALUE val = s_ParameterRef_GetAttr(self, key);
2562 rb_hash_aset(retval, key, val);
2569 * parameter.to_s(idx) -> String
2571 * Returns a string representation of the given parameter
2574 s_ParameterRef_ToString(VALUE self)
2577 char buf[1024], types[4][8];
2578 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2581 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);
2584 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);
2586 case kDihedralParType:
2587 case kImproperParType:
2588 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]));
2590 for (i = 0; i < up->torsion.mult; i++) {
2591 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);
2596 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);
2598 case kVdwPairParType:
2599 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);
2601 case kVdwCutoffParType:
2602 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);
2604 case kElementParType:
2605 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);
2608 return Ruby_NewEncodedStringValue2(buf);
2613 * self == parameterRef -> boolean
2615 * True if the parameters point to the same parameter record.
2618 s_ParameterRef_Equal(VALUE self, VALUE val)
2621 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2622 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2623 } else return Qfalse;
2626 #pragma mark ====== Parameter Class ======
2628 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2629 * is NULL, then the global parameters are looked for. */
2631 /* Rebuild the MD parameter record if necessary: may throw an exception */
2632 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2634 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2637 Data_Get_Struct(val, Molecule, mol);
2639 rb_raise(rb_eMolbyError, "the molecule is empty");
2640 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2641 /* Do self.md_arena.prepare */
2642 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2644 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2649 s_NewParameterValueFromValue(VALUE val)
2652 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2653 Data_Get_Struct(val, Molecule, mol);
2654 s_RebuildMDParameterIfNecessary(val, Qtrue);
2655 MoleculeRetain(mol);
2656 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2659 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2664 s_MoleculeFromParameterValue(VALUE val)
2667 Data_Get_Struct(val, Molecule, mol);
2672 s_ParameterFromParameterValue(VALUE val)
2675 Data_Get_Struct(val, Molecule, mol);
2678 return gBuiltinParameters;
2681 /* Forward declarations */
2682 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2683 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2686 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2688 if (val == rb_cParameter) {
2689 return NULL; /* Parameter class method: builtin parameters */
2690 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2691 return s_MoleculeFromParameterValue(val);
2692 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2693 return s_MoleculeFromParEnumerableValue(val);
2699 * builtin -> Parameter
2701 * Returns a parameter value that points to the global (builtin) parameters.
2702 * Equivalent to Parameter::Builtin (constant).
2705 s_Parameter_Builtin(VALUE self)
2707 static ID s_builtin_id = 0;
2708 if (s_builtin_id == 0)
2709 s_builtin_id = rb_intern("Builtin");
2710 return rb_const_get(rb_cParameter, s_builtin_id);
2715 * bond(idx) -> ParameterRef
2717 * The index-th bond parameter record is returned.
2720 s_Parameter_Bond(VALUE self, VALUE ival)
2724 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2725 idx = NUM2INT(rb_Integer(ival));
2727 n = gBuiltinParameters->nbondPars;
2728 else if (mol->par != NULL)
2729 n = mol->par->nbondPars;
2731 if (idx < -n || idx >= n)
2732 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2735 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2740 * angle(idx) -> ParameterRef
2742 * The index-th angle parameter record is returned.
2745 s_Parameter_Angle(VALUE self, VALUE ival)
2749 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2750 idx = NUM2INT(rb_Integer(ival));
2752 n = gBuiltinParameters->nanglePars;
2753 else if (mol->par != NULL)
2754 n = mol->par->nanglePars;
2756 if (idx < -n || idx >= n)
2757 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2760 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2765 * dihedral(idx) -> ParameterRef
2767 * The index-th dihedral parameter record is returned.
2770 s_Parameter_Dihedral(VALUE self, VALUE ival)
2774 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2775 idx = NUM2INT(rb_Integer(ival));
2777 n = gBuiltinParameters->ndihedralPars;
2778 else if (mol->par != NULL)
2779 n = mol->par->ndihedralPars;
2781 if (idx < -n || idx >= n)
2782 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2785 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2790 * improper(idx) -> ParameterRef
2792 * The index-th improper parameter record is returned.
2795 s_Parameter_Improper(VALUE self, VALUE ival)
2799 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2800 idx = NUM2INT(rb_Integer(ival));
2802 n = gBuiltinParameters->nimproperPars;
2803 else if (mol->par != NULL)
2804 n = mol->par->nimproperPars;
2806 if (idx < -n || idx >= n)
2807 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2810 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2815 * vdw(idx) -> ParameterRef
2817 * The index-th vdw parameter record is returned.
2820 s_Parameter_Vdw(VALUE self, VALUE ival)
2824 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2825 idx = NUM2INT(rb_Integer(ival));
2827 n = gBuiltinParameters->nvdwPars;
2828 else if (mol->par != NULL)
2829 n = mol->par->nvdwPars;
2831 if (idx < -n || idx >= n)
2832 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2835 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2840 * vdw_pair(idx) -> ParameterRef
2842 * The index-th vdw pair parameter record is returned.
2845 s_Parameter_VdwPair(VALUE self, VALUE ival)
2849 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2850 idx = NUM2INT(rb_Integer(ival));
2852 n = gBuiltinParameters->nvdwpPars;
2853 else if (mol->par != NULL)
2854 n = mol->par->nvdwpPars;
2856 if (idx < -n || idx >= n)
2857 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2860 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2865 * vdw_cutoff(idx) -> ParameterRef
2867 * The index-th vdw cutoff parameter record is returned.
2870 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2874 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2875 idx = NUM2INT(rb_Integer(ival));
2877 n = gBuiltinParameters->nvdwCutoffPars;
2878 else if (mol->par != NULL)
2879 n = mol->par->nvdwCutoffPars;
2881 if (idx < -n || idx >= n)
2882 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2885 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2890 * element(idx) -> ParameterRef
2891 * element(t1) -> ParameterRef
2893 * In the first form, the index-th element parameter record is returned. In the second
2894 * form, the element parameter for t1 is looked up (the last index first). t1
2895 * is the element name string (up to 4 characters).
2896 * Unlike other Parameter methods, this is used only for the global parameter.
2899 s_Parameter_Element(VALUE self, VALUE ival)
2902 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2903 int n = gCountElementParameters;
2904 idx1 = NUM2INT(rb_Integer(ival));
2905 if (idx1 < -n || idx1 >= n)
2906 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2909 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2914 strncpy(name, StringValuePtr(ival), 4);
2916 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2917 if (strncmp(ep->name, name, 4) == 0)
2918 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2928 * Returns the number of bond parameters.
2931 s_Parameter_Nbonds(VALUE self)
2934 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2936 n = gBuiltinParameters->nbondPars;
2937 else if (mol->par != NULL)
2938 n = mol->par->nbondPars;
2945 * nangles -> Integer
2947 * Returns the number of angle parameters.
2950 s_Parameter_Nangles(VALUE self)
2953 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2955 n = gBuiltinParameters->nanglePars;
2956 else if (mol->par != NULL)
2957 n = mol->par->nanglePars;
2964 * ndihedrals -> Integer
2966 * Returns the number of dihedral parameters.
2969 s_Parameter_Ndihedrals(VALUE self)
2972 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2974 n = gBuiltinParameters->ndihedralPars;
2975 else if (mol->par != NULL)
2976 n = mol->par->ndihedralPars;
2983 * nimpropers -> Integer
2985 * Returns the number of improper parameters.
2988 s_Parameter_Nimpropers(VALUE self)
2991 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2993 n = gBuiltinParameters->nimproperPars;
2994 else if (mol->par != NULL)
2995 n = mol->par->nimproperPars;
3004 * Returns the number of vdw parameters.
3007 s_Parameter_Nvdws(VALUE self)
3010 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3012 n = gBuiltinParameters->nvdwPars;
3013 else if (mol->par != NULL)
3014 n = mol->par->nvdwPars;
3021 * nvdw_pairs -> Integer
3023 * Returns the number of vdw pair parameters.
3026 s_Parameter_NvdwPairs(VALUE self)
3029 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3031 n = gBuiltinParameters->nvdwpPars;
3032 else if (mol->par != NULL)
3033 n = mol->par->nvdwpPars;
3040 * nvdw_cutoffs -> Integer
3042 * Returns the number of vdw cutoff parameters.
3045 s_Parameter_NvdwCutoffs(VALUE self)
3048 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3050 n = gBuiltinParameters->nvdwCutoffPars;
3051 else if (mol->par != NULL)
3052 n = mol->par->nvdwCutoffPars;
3059 * nelements -> Integer
3061 * Returns the number of element parameters.
3064 s_Parameter_Nelements(VALUE self)
3066 return INT2NUM(gCountElementParameters);
3071 * bonds -> ParEnumerable
3073 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3074 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3075 * useful when all accessible parameters should be examined by use of 'each' method.
3078 s_Parameter_Bonds(VALUE self)
3080 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3081 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3086 * angles -> ParEnumerable
3088 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3089 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3090 * useful when all accessible parameters should be examined by use of 'each' method.
3093 s_Parameter_Angles(VALUE self)
3095 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3096 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3101 * dihedrals -> ParEnumerable
3103 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3104 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3105 * useful when all accessible parameters should be examined by use of 'each' method.
3108 s_Parameter_Dihedrals(VALUE self)
3110 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3111 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3116 * impropers -> ParEnumerable
3118 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3119 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3120 * useful when all accessible parameters should be examined by use of 'each' method.
3123 s_Parameter_Impropers(VALUE self)
3125 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3126 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3131 * vdws -> ParEnumerable
3133 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3134 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3135 * useful when all accessible parameters should be examined by use of 'each' method.
3138 s_Parameter_Vdws(VALUE self)
3140 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3141 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3146 * vdw_pairs -> ParEnumerable
3148 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3149 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3150 * useful when all accessible parameters should be examined by use of 'each' method.
3153 s_Parameter_VdwPairs(VALUE self)
3155 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3156 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3161 * vdw_cutoffs -> ParEnumerable
3163 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3164 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3165 * useful when all accessible parameters should be examined by use of 'each' method.
3168 s_Parameter_VdwCutoffs(VALUE self)
3170 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3171 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3176 * elements -> ParEnumerable
3178 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3179 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3180 * useful when all accessible parameters should be examined by use of 'each' method.
3183 s_Parameter_Elements(VALUE self)
3185 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3186 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3190 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3192 VALUE atval, optval;
3195 int i, n, idx, flags, is_global;
3197 rb_scan_args(argc, argv, "1*", &atval, &optval);
3199 /* Get the atom types */
3201 case kBondParType: n = 2; break;
3202 case kAngleParType: n = 3; break;
3203 case kDihedralParType: n = 4; break;
3204 case kImproperParType: n = 4; break;
3205 case kVdwParType: n = 1; break;
3206 case kVdwPairParType: n = 2; break;
3207 default: return Qnil;
3209 s_ScanAtomTypes(atval, n, t);
3210 for (i = 0; i < n; i++) {
3211 if (t[i] < kAtomTypeMinimum) {
3212 /* Explicit atom index */
3214 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3215 if (t[i] >= mol->natoms)
3216 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3218 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3222 /* Analyze options */
3224 n = RARRAY_LEN(optval);
3225 for (i = 0; i < n; i++) {
3226 VALUE oval = RARRAY_PTR(optval)[i];
3227 if (oval == ID2SYM(rb_intern("global")))
3228 flags |= kParameterLookupGlobal;
3229 else if (oval == ID2SYM(rb_intern("local")))
3230 flags |= kParameterLookupLocal;
3231 else if (oval == ID2SYM(rb_intern("missing")))
3232 flags |= kParameterLookupMissing;
3233 else if (oval == ID2SYM(rb_intern("nowildcard")))
3234 flags |= kParameterLookupNoWildcard;
3235 else if (oval == ID2SYM(rb_intern("nobasetype")))
3236 flags |= kParameterLookupNoBaseAtomType;
3237 else if (oval == ID2SYM(rb_intern("create")))
3241 flags = kParameterLookupGlobal | kParameterLookupLocal;
3246 case kBondParType: {
3249 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3251 idx = bp - mol->par->bondPars;
3255 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3257 idx = bp - gBuiltinParameters->bondPars;
3262 case kAngleParType: {
3265 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3267 idx = ap - mol->par->anglePars;
3271 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3273 idx = ap - gBuiltinParameters->anglePars;
3278 case kDihedralParType: {
3281 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3283 idx = tp - mol->par->dihedralPars;
3287 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3289 idx = tp - gBuiltinParameters->dihedralPars;
3294 case kImproperParType: {
3297 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3299 idx = tp - mol->par->improperPars;
3303 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3305 idx = tp - gBuiltinParameters->improperPars;
3313 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3315 idx = vp - mol->par->vdwPars;
3319 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3321 idx = vp - gBuiltinParameters->vdwPars;
3326 case kVdwPairParType: {
3329 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3331 idx = vp - mol->par->vdwpPars;
3335 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3337 idx = vp - gBuiltinParameters->vdwpPars;
3346 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3349 /* Insert a new parameter record */
3351 Int count = ParameterGetCountForType(mol->par, parType);
3352 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3353 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3354 IntGroupRelease(ig);
3357 /* Set atom types */
3358 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3363 up->bond.type1 = t[0];
3364 up->bond.type2 = t[1];
3367 up->angle.type1 = t[0];
3368 up->angle.type2 = t[1];
3369 up->angle.type3 = t[2];
3371 case kDihedralParType:
3372 case kImproperParType:
3373 up->torsion.type1 = t[0];
3374 up->torsion.type2 = t[1];
3375 up->torsion.type3 = t[2];
3376 up->torsion.type4 = t[3];
3379 up->vdw.type1 = t[0];
3381 case kVdwPairParType:
3382 up->vdwp.type1 = t[0];
3383 up->vdwp.type2 = t[1];
3390 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3395 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3396 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3398 * Find the parameter record that matches the given atom types. The atom types are given
3399 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3400 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3401 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3402 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3405 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3408 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3410 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3411 parType = s_ParTypeFromValue(argv[0]);
3412 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3417 * self == parameter -> boolean
3419 * True if the parameters point to the same parameter table.
3422 s_Parameter_Equal(VALUE self, VALUE val)
3424 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3425 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3426 } else return Qfalse;
3429 #pragma mark ====== ParEnumerable Class ======
3431 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3432 and the parameter type. If the Molecule is NULL, then it refers to the
3433 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3434 the global parameters are always accessible. */
3436 typedef struct ParEnumerable {
3438 Int parType; /* Same as parType in ParameterRef */
3441 static ParEnumerable *
3442 s_ParEnumerableNew(Molecule *mol, Int parType)
3444 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3448 MoleculeRetain(mol);
3449 pen->parType = parType;
3455 s_ParEnumerableRelease(ParEnumerable *pen)
3458 if (pen->mol != NULL)
3459 MoleculeRelease(pen->mol);
3465 s_MoleculeFromParEnumerableValue(VALUE val)
3468 Data_Get_Struct(val, ParEnumerable, pen);
3473 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3475 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3477 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3478 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3483 * par_type -> String
3485 * Get the parameter type, like "bond", "angle", etc.
3488 s_ParEnumerable_ParType(VALUE self) {
3491 Data_Get_Struct(self, ParEnumerable, pen);
3493 if (tp == kElementParType)
3494 return Ruby_NewEncodedStringValue2("element");
3495 tp -= kFirstParType;
3496 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3497 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3498 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3503 * self[idx] -> ParameterRef
3505 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3506 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3507 * parent Parameter object of self.
3509 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3510 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3513 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3516 Data_Get_Struct(self, ParEnumerable, pen);
3517 switch (pen->parType) {
3518 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3519 case kBondParType: return s_Parameter_Bond(self, ival);
3520 case kAngleParType: return s_Parameter_Angle(self, ival);
3521 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3522 case kImproperParType: return s_Parameter_Improper(self, ival);
3523 case kVdwParType: return s_Parameter_Vdw(self, ival);
3524 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3525 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3526 case kElementParType: return s_Parameter_Element(self, ival);
3528 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3530 return Qnil; /* Not reached */
3537 * Returns the number of parameters included in this enumerable.
3540 s_ParEnumerable_Length(VALUE self)
3543 Data_Get_Struct(self, ParEnumerable, pen);
3544 switch (pen->parType) {
3545 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3546 case kBondParType: return s_Parameter_Nbonds(self);
3547 case kAngleParType: return s_Parameter_Nangles(self);
3548 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3549 case kImproperParType: return s_Parameter_Nimpropers(self);
3550 case kVdwParType: return s_Parameter_Nvdws(self);
3551 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3552 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3553 case kElementParType: return s_Parameter_Nelements(self);
3555 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3557 return Qnil; /* Not reached */
3564 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3567 s_ParEnumerable_Each(VALUE self)
3573 Data_Get_Struct(self, ParEnumerable, pen);
3574 if (pen->parType == kElementParType)
3575 n = gCountElementParameters;
3577 switch (pen->parType) {
3578 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3579 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3580 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3581 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3582 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3583 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3584 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3586 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3588 if (pen->mol == NULL)
3589 n = *((Int *)((char *)gBuiltinParameters + ofs));
3590 else if (pen->mol->par != NULL)
3591 n = *((Int *)((char *)(pen->mol->par) + ofs));
3594 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3595 Data_Get_Struct(aval, ParameterRef, pref);
3596 for (i = 0; i < n; i++) {
3605 * reverse_each {|pref| ...}
3607 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3610 s_ParEnumerable_ReverseEach(VALUE self)
3616 Data_Get_Struct(self, ParEnumerable, pen);
3617 if (pen->parType == kElementParType)
3618 n = gCountElementParameters;
3620 switch (pen->parType) {
3621 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3622 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3623 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3624 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3625 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3626 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3627 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3629 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3631 if (pen->mol == NULL)
3632 n = *((Int *)((char *)gBuiltinParameters + ofs));
3633 else if (pen->mol->par != NULL)
3634 n = *((Int *)((char *)(pen->mol->par) + ofs));
3637 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3638 Data_Get_Struct(aval, ParameterRef, pref);
3639 for (i = n - 1; i >= 0; i--) {
3648 * insert(idx = nil, pref = nil) -> ParameterRef
3650 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3651 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3652 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3653 * parameter is left undefined.
3654 * Throws an exception if ParEnumerable points to the global parameter.
3657 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3665 Data_Get_Struct(self, ParEnumerable, pen);
3666 if (pen->mol == NULL)
3667 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3668 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3669 rb_scan_args(argc, argv, "02", &ival, &pval);
3671 i = NUM2INT(rb_Integer(ival));
3673 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3678 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3679 if (up == NULL || type != pen->parType)
3680 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3681 ParameterCopyOneWithType(&u, up, pen->parType);
3684 memset(&u, 0, sizeof(u));
3687 ig = IntGroupNewWithPoints(n, 1, -1);
3688 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3690 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3691 MolActionCallback_registerUndo(pen->mol, act);
3692 MolActionRelease(act);
3694 IntGroupRelease(ig);
3695 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3703 * Delete the parameter(s) specified by the argument.
3706 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3711 Data_Get_Struct(self, ParEnumerable, pen);
3712 if (pen->mol == NULL)
3713 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3714 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3715 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3716 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3719 ig = IntGroupFromValue(ival);
3720 if ((i = IntGroupGetCount(ig)) == 0) {
3721 IntGroupRelease(ig);
3725 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3726 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3729 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3730 IntGroupRelease(ig);
3736 * lookup(atom_types, options, ...) -> ParameterRef
3737 * lookup(atom_type_string, options, ...) -> ParameterRef
3739 * Find the parameter record that matches the given atom types. The arguments are
3740 * the same as Parameter#lookup, except for the parameter type which is implicitly
3744 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3747 Data_Get_Struct(self, ParEnumerable, pen);
3748 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3753 * self == parEnumerable -> boolean
3755 * True if the arguments point to the same parameter table and type.
3758 s_ParEnumerable_Equal(VALUE self, VALUE val)
3760 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3761 ParEnumerable *pen1, *pen2;
3762 Data_Get_Struct(self, ParEnumerable, pen1);
3763 Data_Get_Struct(val, ParEnumerable, pen2);
3764 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3765 } else return Qfalse;
3768 #pragma mark ====== AtomRef Class ======
3770 /* Forward declaration for register undo */
3771 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3773 /* Ruby string "set_atom_attr" */
3774 static VALUE s_SetAtomAttrString;
3777 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3781 Data_Get_Struct(self, AtomRef, aref);
3782 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3783 if (idx < 0 || idx >= aref->mol->natoms)
3784 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3786 *app = aref->mol->atoms + idx;
3793 s_AtomFromValue(VALUE self)
3796 s_AtomIndexFromValue(self, &ap, NULL);
3801 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3804 s_AtomIndexFromValue(self, &ap, mpp);
3809 s_NotifyModificationForAtomRef(VALUE self)
3812 Data_Get_Struct(self, AtomRef, aref);
3813 MoleculeIncrementModifyCount(aref->mol);
3817 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3820 Data_Get_Struct(self, AtomRef, aref);
3821 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3824 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3825 MolActionCallback_registerUndo(aref->mol, act);
3826 MoleculeCallback_notifyModification(aref->mol, 0);
3827 /* Request MD rebuilt if necessary */
3828 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3829 aref->mol->needsMDRebuild = 1;
3834 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3837 aref = AtomRefNew(mol, idx);
3838 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3842 s_AtomRef_GetMolecule(VALUE self)
3845 s_AtomIndexFromValue(self, NULL, &mpp);
3846 return ValueFromMolecule(mpp);
3849 static VALUE s_AtomRef_GetIndex(VALUE self) {
3850 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3853 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3854 return INT2NUM(s_AtomFromValue(self)->segSeq);
3857 static VALUE s_AtomRef_GetSegName(VALUE self) {
3858 char *p = s_AtomFromValue(self)->segName;
3859 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3862 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3863 return INT2NUM(s_AtomFromValue(self)->resSeq);
3866 static VALUE s_AtomRef_GetResName(VALUE self) {
3867 char *p = s_AtomFromValue(self)->resName;
3868 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3871 static VALUE s_AtomRef_GetName(VALUE self) {
3872 char *p = s_AtomFromValue(self)->aname;
3873 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3876 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3877 int type = s_AtomFromValue(self)->type;
3878 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3879 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3882 static VALUE s_AtomRef_GetCharge(VALUE self) {
3883 return rb_float_new(s_AtomFromValue(self)->charge);
3886 static VALUE s_AtomRef_GetWeight(VALUE self) {
3887 return rb_float_new(s_AtomFromValue(self)->weight);
3890 static VALUE s_AtomRef_GetElement(VALUE self) {
3891 char *p = s_AtomFromValue(self)->element;
3892 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3895 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3896 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3899 static VALUE s_AtomRef_GetConnects(VALUE self) {
3902 Atom *ap = s_AtomFromValue(self);
3903 retval = rb_ary_new();
3904 cp = AtomConnectData(&ap->connect);
3905 for (i = 0; i < ap->connect.count; i++)
3906 rb_ary_push(retval, INT2NUM(cp[i]));
3910 static VALUE s_AtomRef_GetR(VALUE self) {
3911 return ValueFromVector(&(s_AtomFromValue(self)->r));
3914 static VALUE s_AtomRef_GetX(VALUE self) {
3915 return rb_float_new(s_AtomFromValue(self)->r.x);
3918 static VALUE s_AtomRef_GetY(VALUE self) {
3919 return rb_float_new(s_AtomFromValue(self)->r.y);
3922 static VALUE s_AtomRef_GetZ(VALUE self) {
3923 return rb_float_new(s_AtomFromValue(self)->r.z);
3926 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3930 s_AtomIndexFromValue(self, &ap, &mp);
3932 if (mp->cell != NULL)
3933 TransformVec(&r1, mp->cell->rtr, &r1);
3937 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3938 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3939 return ValueFromVector(&r1);
3942 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3943 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3946 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3947 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3950 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3951 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3954 static VALUE s_AtomRef_GetSigma(VALUE self) {
3955 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3958 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3959 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3962 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3963 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3966 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3967 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3970 static VALUE s_AtomRef_GetV(VALUE self) {
3971 return ValueFromVector(&(s_AtomFromValue(self)->v));
3974 static VALUE s_AtomRef_GetF(VALUE self) {
3975 Vector v = s_AtomFromValue(self)->f;
3976 VecScaleSelf(v, INTERNAL2KCAL);
3977 return ValueFromVector(&v);
3980 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3981 return rb_float_new(s_AtomFromValue(self)->occupancy);
3984 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3985 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3988 static VALUE s_AtomRef_GetAniso(VALUE self) {
3991 Atom *ap = s_AtomFromValue(self);
3992 if (ap->aniso == NULL)
3994 retval = rb_ary_new();
3995 for (i = 0; i < 6; i++)
3996 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3997 if (ap->aniso->has_bsig) {
3998 rb_ary_push(retval, INT2NUM(0));
3999 for (i = 0; i < 6; i++)
4000 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4005 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4008 Atom *ap = s_AtomFromValue(self);
4009 if (ap->aniso == NULL)
4011 retval = rb_ary_new();
4012 for (i = 0; i < 3; i++)
4013 rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4017 static VALUE s_AtomRef_GetSymop(VALUE self) {
4019 Atom *ap = s_AtomFromValue(self);
4020 if (!ap->symop.alive)
4022 retval = rb_ary_new();
4023 rb_ary_push(retval, INT2NUM(ap->symop.sym));
4024 rb_ary_push(retval, INT2NUM(ap->symop.dx));
4025 rb_ary_push(retval, INT2NUM(ap->symop.dy));
4026 rb_ary_push(retval, INT2NUM(ap->symop.dz));
4027 rb_ary_push(retval, INT2NUM(ap->symbase));
4031 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4032 return INT2NUM(s_AtomFromValue(self)->intCharge);
4035 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4036 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4039 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4040 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4043 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4047 MDExclusion *exinfo;
4050 idx = s_AtomIndexFromValue(self, &ap, &mol);
4051 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4052 VALUE mval = ValueFromMolecule(mol);
4053 s_RebuildMDParameterIfNecessary(mval, Qnil);
4055 if (mol->arena->exinfo == NULL)
4057 exinfo = mol->arena->exinfo + idx;
4058 exlist = mol->arena->exlist;
4059 retval = rb_ary_new();
4060 aval = rb_ary_new();
4061 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
4062 rb_ary_push(aval, INT2NUM(exlist[i]));
4063 rb_ary_push(retval, aval);
4064 aval = rb_ary_new();
4065 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
4066 rb_ary_push(aval, INT2NUM(exlist[i]));
4067 rb_ary_push(retval, aval);
4068 aval = rb_ary_new();
4069 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
4070 rb_ary_push(aval, INT2NUM(exlist[i]));
4071 rb_ary_push(retval, aval);
4075 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4076 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4079 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4080 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4083 static VALUE s_AtomRef_GetHidden(VALUE self) {
4084 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4087 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4090 Atom *ap = s_AtomFromValue(self);
4091 if (ap->anchor == NULL)
4093 count = ap->anchor->connect.count;
4094 retval = rb_ary_new2(count * 2);
4095 cp = AtomConnectData(&ap->anchor->connect);
4096 for (i = 0; i < count; i++) {
4097 rb_ary_store(retval, i, INT2NUM(cp[i]));
4098 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4103 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4104 char *p = s_AtomFromValue(self)->uff_type;
4105 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4108 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4109 rb_raise(rb_eMolbyError, "index cannot be directly set");
4113 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4114 VALUE oval = s_AtomRef_GetSegSeq(self);
4115 val = rb_Integer(val);
4116 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4117 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4121 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4122 char *p = StringValuePtr(val);
4123 VALUE oval = s_AtomRef_GetSegName(self);
4124 strncpy(s_AtomFromValue(self)->segName, p, 4);
4125 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4129 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4130 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4131 return val; /* Not reached */
4134 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4135 Atom *ap = s_AtomFromValue(self);
4136 char *p = StringValuePtr(val);
4137 VALUE oval = s_AtomRef_GetName(self);
4138 if (ap->anchor != NULL && p[0] == '_')
4139 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4140 strncpy(ap->aname, p, 4);
4141 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4145 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4147 char *p = StringValuePtr(val);
4148 VALUE oval = s_AtomRef_GetAtomType(self);
4149 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4150 if (type != 0 && type < kAtomTypeMinimum)
4151 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4152 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4153 mp->needsMDRebuild = 1;
4154 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4158 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4160 VALUE oval = s_AtomRef_GetCharge(self);
4161 val = rb_Float(val);
4162 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4163 mp->needsMDRebuild = 1;
4164 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4168 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4170 VALUE oval = s_AtomRef_GetWeight(self);
4171 val = rb_Float(val);
4172 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4173 mp->needsMDRebuild = 1;
4174 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4178 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4181 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4182 char *p = StringValuePtr(val);
4183 VALUE oval = s_AtomRef_GetElement(self);
4184 ap->atomicNumber = ElementToInt(p);
4185 ElementToString(ap->atomicNumber, ap->element);
4186 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4188 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4189 mp->needsMDRebuild = 1;
4193 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4196 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4197 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4198 val = rb_Integer(val);
4199 ap->atomicNumber = NUM2INT(val);
4200 ElementToString(ap->atomicNumber, ap->element);
4201 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4203 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4204 mp->needsMDRebuild = 1;
4208 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4209 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4210 return val; /* Not reached */
4213 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4216 VALUE oval = s_AtomRef_GetR(self);
4217 VectorFromValue(val, &v);
4218 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4219 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4220 mp->needsMDCopyCoordinates = 1;
4224 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4227 VALUE oval = s_AtomRef_GetX(self);
4228 val = rb_Float(val);
4230 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4231 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4232 mp->needsMDCopyCoordinates = 1;
4236 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4239 VALUE oval = s_AtomRef_GetY(self);
4240 val = rb_Float(val);
4242 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4243 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4244 mp->needsMDCopyCoordinates = 1;
4248 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4251 VALUE oval = s_AtomRef_GetZ(self);
4252 val = rb_Float(val);
4254 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4255 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4256 mp->needsMDCopyCoordinates = 1;
4260 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4264 s_AtomIndexFromValue(self, &ap, &mp);
4266 VectorFromValue(val, &v);
4267 if (mp->cell != NULL)
4268 TransformVec(&v, mp->cell->tr, &v);
4270 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4271 mp->needsMDCopyCoordinates = 1;
4275 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4280 s_AtomIndexFromValue(self, &ap, &mp);
4282 val = rb_Float(val);
4284 if (mp->cell != NULL) {
4285 TransformVec(&v, mp->cell->rtr, &v);
4287 TransformVec(&v, mp->cell->tr, &v);
4290 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4291 mp->needsMDCopyCoordinates = 1;
4295 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4300 s_AtomIndexFromValue(self, &ap, &mp);
4302 val = rb_Float(val);
4304 if (mp->cell != NULL) {
4305 TransformVec(&v, mp->cell->rtr, &v);
4307 TransformVec(&v, mp->cell->tr, &v);
4310 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4311 mp->needsMDCopyCoordinates = 1;
4315 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4320 s_AtomIndexFromValue(self, &ap, &mp);
4322 val = rb_Float(val);
4324 if (mp->cell != NULL) {
4325 TransformVec(&v, mp->cell->rtr, &v);
4327 TransformVec(&v, mp->cell->tr, &v);
4330 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4331 mp->needsMDCopyCoordinates = 1;
4335 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4338 VALUE oval = s_AtomRef_GetSigma(self);
4339 VectorFromValue(val, &v);
4340 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4341 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4342 mp->needsMDCopyCoordinates = 1;
4346 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4349 VALUE oval = s_AtomRef_GetSigmaX(self);
4350 val = rb_Float(val);
4352 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4353 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4354 mp->needsMDCopyCoordinates = 1;
4358 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4361 VALUE oval = s_AtomRef_GetSigmaY(self);
4362 val = rb_Float(val);
4364 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4365 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4366 mp->needsMDCopyCoordinates = 1;
4370 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4373 VALUE oval = s_AtomRef_GetSigmaZ(self);
4374 val = rb_Float(val);
4376 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4377 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4378 mp->needsMDCopyCoordinates = 1;
4382 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4386 VALUE oval = s_AtomRef_GetV(self);
4387 VectorFromValue(val, &v);
4388 s_AtomIndexFromValue(self, &ap, &mp);
4390 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4391 mp->needsMDCopyCoordinates = 1;
4395 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4398 VALUE oval = s_AtomRef_GetF(self);
4399 VectorFromValue(val, &v);
4400 VecScaleSelf(v, KCAL2INTERNAL);
4401 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4402 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4403 mp->needsMDCopyCoordinates = 1;
4407 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4408 VALUE oval = s_AtomRef_GetOccupancy(self);
4410 val = rb_Float(val);
4411 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4412 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4413 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4417 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4418 VALUE oval = s_AtomRef_GetTempFactor(self);
4419 val = rb_Float(val);
4420 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4421 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4425 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4430 VALUE oval = s_AtomRef_GetAniso(self);
4431 Data_Get_Struct(self, AtomRef, aref);
4432 val = rb_funcall(val, rb_intern("to_a"), 0);
4433 n = RARRAY_LEN(val);
4434 valp = RARRAY_PTR(val);
4435 for (i = 0; i < 6; i++) {
4437 f[i] = NUM2DBL(rb_Float(valp[i]));
4441 type = NUM2INT(rb_Integer(valp[6]));
4444 for (i = 0; i < 6; i++)
4445 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4447 for (i = 0; i < 6; i++)
4450 i = s_AtomIndexFromValue(self, NULL, NULL);
4451 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4452 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4456 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4457 rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4458 return val; /* Not reached */
4461 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4467 VALUE oval = s_AtomRef_GetSymop(self);
4468 i = s_AtomIndexFromValue(self, &ap, &mol);
4470 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4472 val = rb_funcall(val, rb_intern("to_a"), 0);
4473 n = RARRAY_LEN(val);
4474 valp = RARRAY_PTR(val);
4475 for (i = 0; i < 5; i++) {
4477 if (valp[i] == Qnil)
4480 ival[i] = NUM2INT(rb_Integer(valp[i]));
4481 } else ival[i] = -100000;
4484 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4485 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));
4486 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4487 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4488 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4489 return val; /* No need to change */
4490 if (ival[0] != -100000)
4491 ap->symop.sym = ival[0];
4492 if (ival[1] != -100000)
4493 ap->symop.dx = ival[1];
4494 if (ival[2] != -100000)
4495 ap->symop.dy = ival[2];
4496 if (ival[3] != -100000)
4497 ap->symop.dz = ival[3];
4498 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4499 if (ival[4] != -100000)
4500 ap->symbase = ival[4];
4501 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4502 /* The anisotropic parameters should be recalculated */
4503 VALUE oaval = s_AtomRef_GetAniso(self);
4504 MoleculeSetAnisoBySymop(mol, i);
4505 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4507 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4511 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4512 VALUE oval = s_AtomRef_GetIntCharge(self);
4513 val = rb_Integer(val);
4514 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4515 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4519 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4521 VALUE oval = s_AtomRef_GetFixForce(self);
4522 val = rb_Float(val);
4523 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4524 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4525 mp->needsMDRebuild = 1;
4529 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4532 VALUE oval = s_AtomRef_GetFixPos(self);
4533 VectorFromValue(val, &v);
4534 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4535 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4536 mp->needsMDRebuild = 1;
4540 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4541 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4542 return val; /* Not reached */
4545 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4546 VALUE oval = s_AtomRef_GetIntCharge(self);
4547 val = rb_Integer(val);
4548 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4549 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4553 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4554 VALUE oval = s_AtomRef_GetIntCharge(self);
4555 val = rb_Integer(val);
4556 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4557 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4561 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4562 Atom *ap = s_AtomFromValue(self);
4563 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4565 ap->exflags |= kAtomHiddenFlag;
4567 ap->exflags &= ~kAtomHiddenFlag;
4569 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4573 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4574 Int idx, i, j, k, n, *ip;
4581 MolAction **undoActions;
4582 memset(&ac, 0, sizeof(ac));
4583 idx = s_AtomIndexFromValue(self, &ap, &mol);
4584 oval = s_AtomRef_GetAnchorList(self);
4586 val = rb_ary_to_ary(val);
4587 n = RARRAY_LEN(val);
4590 if (ap->anchor != NULL) {
4591 AtomConnectResize(&ap->anchor->connect, 0);
4592 free(ap->anchor->coeffs);
4595 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4600 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4601 if (ap->aname[0] == '_')
4602 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4603 ip = (Int *)malloc(sizeof(Int) * n);
4605 for (i = 0; i < n; i++) {
4606 v = RARRAY_PTR(val)[i];
4607 if (rb_obj_is_kind_of(v, rb_cFloat))
4609 j = NUM2INT(rb_Integer(v));
4610 if (j < 0 || j >= mol->natoms)
4611 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4612 for (k = 0; k < i; k++) {
4614 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4620 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4621 else if (i * 2 != n)
4622 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4623 dp = (Double *)malloc(sizeof(Double) * n / 2);
4624 for (i = 0; i < n / 2; i++) {
4625 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4627 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4633 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4637 rb_raise(rb_eMolbyError, "invalid argument");
4638 if (nUndoActions > 0) {
4639 for (i = 0; i < nUndoActions; i++) {
4640 MolActionCallback_registerUndo(mol, undoActions[i]);
4641 MolActionRelease(undoActions[i]);
4645 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4649 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4650 Atom *ap = s_AtomFromValue(self);
4651 char *p = StringValuePtr(val);
4652 VALUE oval = s_AtomRef_GetUFFType(self);
4653 strncpy(ap->uff_type, p, 5);
4654 ap->uff_type[5] = 0;
4655 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4659 static struct s_AtomAttrDef {
4661 VALUE *symref; /* Address of s_IndexSymbol etc. */
4662 ID id; /* Will be set within InitMolby() */
4663 VALUE (*getter)(VALUE);
4664 VALUE (*setter)(VALUE, VALUE);
4665 } s_AtomAttrDefTable[] = {
4666 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4667 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4668 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4669 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4670 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4671 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4672 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4673 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4674 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4675 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4676 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4677 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4678 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4679 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4680 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4681 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4682 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4683 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4684 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4685 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4686 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4687 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4688 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4689 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4690 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4691 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4692 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4693 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4694 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4695 {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues, s_AtomRef_SetAnisoEigenValues},
4696 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4697 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4698 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4699 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4700 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4701 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4702 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4703 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4704 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4705 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4706 {NULL} /* Sentinel */
4710 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4714 if (TYPE(key) != T_SYMBOL) {
4715 kid = rb_intern(StringValuePtr(key));
4717 } else kid = SYM2ID(key);
4718 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4719 if (s_AtomAttrDefTable[i].id == kid) {
4720 if (value == Qundef)
4721 return (*(s_AtomAttrDefTable[i].getter))(self);
4723 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4726 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4727 return Qnil; /* not reached */
4731 s_AtomRef_GetAttr(VALUE self, VALUE key)
4733 return s_AtomRef_SetAttr(self, key, Qundef);
4738 * self == atomRef -> boolean
4740 * True if the two references point to the same atom.
4743 s_AtomRef_Equal(VALUE self, VALUE val)
4745 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4746 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4747 } else return Qfalse;
4750 #pragma mark ====== MolEnumerable Class ======
4752 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4756 * self[idx] -> AtomRef or Array of Integers
4758 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4759 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4760 * value is a String. Otherwise, the return value is an Array of Integers.
4763 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4765 MolEnumerable *mseq;
4768 Data_Get_Struct(self, MolEnumerable, mseq);
4770 if (mseq->kind == kAtomKind) {
4771 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4773 idx1 = NUM2INT(arg1);
4774 switch (mseq->kind) {
4776 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4777 if (idx2 < 0 || idx2 >= mol->nbonds)
4778 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4779 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4782 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4783 if (idx2 < 0 || idx2 >= mol->nangles)
4784 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4785 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4787 case kDihedralKind: {
4788 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4789 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4790 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4791 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]));
4793 case kImproperKind: {
4794 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4795 if (idx2 < 0 || idx2 >= mol->nimpropers)
4796 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4797 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]));
4799 case kResidueKind: {
4801 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4802 if (idx2 < 0 || idx2 >= mol->nresidues)
4803 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4804 p = mol->residues[idx2];
4805 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4815 * Returns the number of objects included in this enumerable.
4818 s_MolEnumerable_Length(VALUE self)
4820 MolEnumerable *mseq;
4821 Data_Get_Struct(self, MolEnumerable, mseq);
4822 switch (mseq->kind) {
4824 return INT2NUM(mseq->mol->natoms);
4826 return INT2NUM(mseq->mol->nbonds);
4828 return INT2NUM(mseq->mol->nangles);
4830 return INT2NUM(mseq->mol->ndihedrals);
4832 return INT2NUM(mseq->mol->nimpropers);
4834 return INT2NUM(mseq->mol->nresidues);
4843 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4844 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4845 * For the atoms, a same AtomRef object is passed (with different internal information)
4846 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4847 * for each iteration.
4850 s_MolEnumerable_Each(VALUE self)
4852 MolEnumerable *mseq;
4854 int len = NUM2INT(s_MolEnumerable_Length(self));
4855 Data_Get_Struct(self, MolEnumerable, mseq);
4856 if (mseq->kind == kAtomKind) {
4857 /* The same AtomRef object will be used during the loop */
4858 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4859 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4860 for (i = 0; i < len; i++) {
4865 /* A new ruby object will be created at each iteration (not very efficient) */
4866 for (i = 0; i < len; i++) {
4867 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4875 * self == molEnumerable -> boolean
4877 * True if the two arguments point to the same molecule and enumerable type.
4880 s_MolEnumerable_Equal(VALUE self, VALUE val)
4882 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4883 MolEnumerable *mseq1, *mseq2;
4884 Data_Get_Struct(self, MolEnumerable, mseq1);
4885 Data_Get_Struct(val, MolEnumerable, mseq2);
4886 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4887 } else return Qfalse;
4891 #pragma mark ====== Molecule Class ======
4893 #pragma mark ------ Allocate/Release/Accessor ------
4895 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method. */
4896 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4897 char *gLoadSaveErrorMessage = NULL;
4899 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4902 MoleculeFromValue(VALUE val)
4905 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4906 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4907 Data_Get_Struct(val, Molecule, mol);
4911 static VALUE sMoleculeRetainArray = Qnil;
4913 /* The function is called from MoleculeRelease() */
4914 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4915 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4916 /* object is always returned for the same Molecule. */
4917 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4918 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4919 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4920 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4921 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4923 /* Register/unregister the exmolobj Ruby object */
4925 MoleculeReleaseExternalObj(Molecule *mol)
4927 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4928 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4929 mol->exmolobjProtected = 0;
4934 MoleculeRetainExternalObj(Molecule *mol)
4936 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4937 if (sMoleculeRetainArray == Qnil) {
4938 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4939 sMoleculeRetainArray = rb_ary_new();
4942 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4943 mol->exmolobjProtected = 1;
4947 /* Release hook function for Ruby */
4949 MoleculeReleaseHook(Molecule *mol)
4951 if (mol->exmolobj != NULL) {
4952 /* No need to remove from sMoleculeRetainArray */
4953 mol->exmolobj = NULL;
4954 mol->exmolobjProtected = 0;
4956 MoleculeRelease(mol);
4960 ValueFromMolecule(Molecule *mol)
4964 if (mol->exmolobj != NULL)
4965 return (VALUE)mol->exmolobj;
4966 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4967 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4968 return (VALUE)mol->exmolobj;
4973 s_Molecule_Alloc(VALUE klass)
4976 Molecule *mol = MoleculeNew();
4977 val = ValueFromMolecule(mol);
4978 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4983 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4987 if (FIXNUM_P(val)) {
4989 if (n >= 0 && n < mol->natoms)
4991 n = -1; /* No such atom */
4992 val = rb_inspect(val);
4994 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4996 if (n >= 0 && n < mol->natoms)
4998 p = StringValuePtr(val);
5000 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5002 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5004 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5005 return 0; /* Not reached */
5009 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5013 Data_Get_Struct(self, Molecule, mp1);
5014 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5015 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5016 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5017 Data_Get_Struct(val, IntGroup, ig);
5018 IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5027 * Duplicate a molecule. All entries are deep copied, so modifying the newly
5028 * created object does not affect the old object in any sense.
5031 s_Molecule_InitCopy(VALUE self, VALUE arg)
5033 Molecule *mp1, *mp2;
5034 Data_Get_Struct(self, Molecule, mp1);
5035 mp2 = MoleculeFromValue(arg);
5036 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5037 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5043 * atom_index(val) -> Integer
5045 * Returns the atom index represented by val. val can be either a non-negative integer
5046 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5047 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
5048 * where resname, resid, name are the residue name, residue id, and atom name respectively.
5049 * If val is a string and multiple atoms match the description, the atom with the lowest index
5053 s_Molecule_AtomIndex(VALUE self, VALUE val)
5056 Data_Get_Struct(self, Molecule, mol);
5057 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5062 * self == Molecule -> boolean
5064 * True if the two arguments point to the same molecule.
5067 s_Molecule_Equal(VALUE self, VALUE val)
5069 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5070 Molecule *mol1, *mol2;
5071 Data_Get_Struct(self, Molecule, mol1);
5072 Data_Get_Struct(val, Molecule, mol2);
5073 return (mol1 == mol2 ? Qtrue : Qfalse);
5074 } else return Qfalse;
5077 #pragma mark ------ Load/Save ------
5080 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5082 if (gLoadSaveErrorMessage != NULL) {
5083 MyAppCallback_setConsoleColor(1);
5084 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5085 MyAppCallback_setConsoleColor(0);
5088 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5093 * loadmbsf(file) -> bool
5095 * Read a structure from a mbsf file.
5096 * Return true if successful.
5099 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5105 MoleculeClearLoadSaveErrorMessage();
5106 Data_Get_Struct(self, Molecule, mol);
5107 rb_scan_args(argc, argv, "1", &fname);
5108 fstr = FileStringValuePtr(fname);
5109 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5110 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5116 * loadpsf(file, pdbfile = nil) -> bool
5118 * Read a structure from a psf file. molecule must be empty. The psf may be
5119 * an "extended" version, which also contains coordinates. If pdbfile
5120 * is given, then atomic coordinates are read from that file.
5121 * Return true if successful.
5124 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5126 VALUE fname, pdbname;
5127 char *fstr, *pdbstr;
5130 Data_Get_Struct(self, Molecule, mol);
5131 if (mol->natoms > 0)
5132 return Qnil; /* Must be a new molecule */
5133 MoleculeClearLoadSaveErrorMessage();
5134 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5135 fstr = FileStringValuePtr(fname);
5136 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5137 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5139 if (!NIL_P(pdbname)) {
5140 pdbstr = strdup(FileStringValuePtr(pdbname));
5141 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5143 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5150 * loadpdb(file) -> bool
5152 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5153 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5154 * Return true if successful.
5157 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5163 Data_Get_Struct(self, Molecule, mol);
5164 rb_scan_args(argc, argv, "1", &fname);
5165 MoleculeClearLoadSaveErrorMessage();
5166 fstr = FileStringValuePtr(fname);
5167 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5168 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5174 * loaddcd(file) -> bool
5176 * Read coordinates from a dcd file. The molecule should not empty.
5177 * Return true if successful.
5180 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5186 Data_Get_Struct(self, Molecule, mol);
5187 rb_scan_args(argc, argv, "1", &fname);
5188 MoleculeClearLoadSaveErrorMessage();
5189 fstr = FileStringValuePtr(fname);
5190 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5191 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5197 * loadtep(file) -> bool
5199 * Read coordinates from an ortep .tep file.
5200 * Return true if successful.
5203 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5209 Data_Get_Struct(self, Molecule, mol);
5210 rb_scan_args(argc, argv, "1", &fname);
5211 MoleculeClearLoadSaveErrorMessage();
5212 fstr = FileStringValuePtr(fname);
5213 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5214 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5220 * loadres(file) -> bool
5222 * Read coordinates from a shelx .res file.
5223 * Return true if successful.
5226 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5232 Data_Get_Struct(self, Molecule, mol);
5233 rb_scan_args(argc, argv, "1", &fname);
5234 MoleculeClearLoadSaveErrorMessage();
5235 fstr = FileStringValuePtr(fname);
5236 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5237 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5243 * loadfchk(file) -> bool
5245 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5246 * Return true if successful.
5249 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5255 Data_Get_Struct(self, Molecule, mol);
5256 rb_scan_args(argc, argv, "1", &fname);
5257 MoleculeClearLoadSaveErrorMessage();
5258 fstr = FileStringValuePtr(fname);
5259 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5260 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5266 * loaddat(file) -> bool
5268 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5269 * Return true if successful.
5272 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5278 Data_Get_Struct(self, Molecule, mol);
5279 rb_scan_args(argc, argv, "1", &fname);
5280 MoleculeClearLoadSaveErrorMessage();
5281 fstr = FileStringValuePtr(fname);
5282 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5283 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5284 MyAppCallback_hideProgressPanel();
5285 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5291 * savembsf(file) -> bool
5293 * Write structure as a mbsf file. Returns true if successful.
5296 s_Molecule_Savembsf(VALUE self, VALUE fname)
5301 Data_Get_Struct(self, Molecule, mol);
5302 MoleculeClearLoadSaveErrorMessage();
5303 fstr = FileStringValuePtr(fname);
5304 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5305 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5311 * savepsf(file) -> bool
5313 * Write structure as a psf file. Returns true if successful.
5316 s_Molecule_Savepsf(VALUE self, VALUE fname)
5321 Data_Get_Struct(self, Molecule, mol);
5322 MoleculeClearLoadSaveErrorMessage();
5323 fstr = FileStringValuePtr(fname);
5324 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5325 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5331 * savepdb(file) -> bool
5333 * Write coordinates as a pdb file. Returns true if successful.
5336 s_Molecule_Savepdb(VALUE self, VALUE fname)
5341 Data_Get_Struct(self, Molecule, mol);
5342 MoleculeClearLoadSaveErrorMessage();
5343 fstr = FileStringValuePtr(fname);
5344 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5345 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5351 * savedcd(file) -> bool
5353 * Write coordinates as a dcd file. Returns true if successful.
5356 s_Molecule_Savedcd(VALUE self, VALUE fname)
5361 Data_Get_Struct(self, Molecule, mol);
5362 MoleculeClearLoadSaveErrorMessage();
5363 fstr = FileStringValuePtr(fname);
5364 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5365 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5369 /* load([ftype, ] fname, ...) */
5371 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5374 char *argstr, *methname, *p, *type = "";
5377 const char *ls = (loadFlag ? "load" : "save");
5378 int lslen = strlen(ls);
5383 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5384 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5385 if (argstr[0] == ':') {
5386 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5387 methname = ALLOC_N(char, lslen + strlen(argstr));
5388 strcpy(methname, ls);
5389 strcat(methname, argstr + 1);
5391 for (i = lslen; methname[i] != 0; i++)
5392 methname[i] = tolower(methname[i]);
5393 mid = rb_intern(methname);
5397 rval = rb_funcall2(self, mid, argc, argv);
5403 /* Guess file type from extension */
5404 p = strrchr(argstr, '.');
5408 for (methname = p; *methname != 0; methname++) {
5409 if (!isalpha(*methname))
5412 if (*methname == 0) {
5413 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5414 if (methname == NULL)
5415 rb_raise(rb_eMolbyError, "Low memory");
5416 strcpy(methname, ls);
5417 strcat(methname, p);
5418 for (i = lslen; methname[i] != 0; i++)
5419 methname[i] = tolower(methname[i]);
5420 mid = rb_intern(methname);
5423 if (rb_respond_to(self, mid)) {
5424 /* Load: try to call the load procedure only if it is available */
5425 rval = rb_funcall2(self, mid, argc, argv);
5430 /* Save: call the save procedure, and if not found then call 'method_missing' */
5431 rval = rb_funcall2(self, mid, argc, argv);
5438 rval = rb_str_to_str(argv[0]);
5439 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5440 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5441 return Qnil; /* Does not reach here */
5445 /* Register the path */
5448 Data_Get_Struct(self, Molecule, mol);
5449 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5451 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5452 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5453 if (ap->occupancy != 0.0)
5456 if (i == mol->natoms) {
5457 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5458 ap->occupancy = 1.0;
5467 * molload(file, *args) -> bool
5469 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5470 * file type given by the extension). If this method fails, then all defined (public)
5471 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5474 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5476 return s_Molecule_LoadSave(argc, argv, self, 1);
5481 * molsave(file, *args) -> bool
5483 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5484 * (XXX is the file type given by the extension).
5487 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5489 return s_Molecule_LoadSave(argc, argv, self, 0);
5495 * open(file) -> Molecule
5497 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5500 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5508 rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5511 rb_scan_args(argc, argv, "01", &fname);
5515 p = FileStringValuePtr(fname);
5516 iflag = Ruby_SetInterruptFlag(Qfalse);
5517 mp = MoleculeCallback_openNewMolecule(p);
5518 Ruby_SetInterruptFlag(iflag);
5521 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5523 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5525 return ValueFromMolecule(mp);
5531 * new(file, *args) -> Molecule
5533 * Create a new molecule and call "load" method with the same arguments.
5536 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5539 return s_Molecule_Load(argc, argv, self);
5540 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5545 * error_message -> String
5547 * Get the error_message from the last load/save method. If no error, returns nil.
5550 s_Molecule_ErrorMessage(VALUE klass)
5552 if (gLoadSaveErrorMessage == NULL)
5554 else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5559 * set_error_message(String)
5560 * Molecule.error_message = String
5562 * Set the error_message for the present load/save method.
5565 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5567 if (gLoadSaveErrorMessage != NULL) {
5568 free(gLoadSaveErrorMessage);
5569 gLoadSaveErrorMessage = NULL;
5572 sval = rb_str_to_str(sval);
5573 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5580 * set_molecule(Molecule)
5582 * Duplicate the given molecule and set to self. The present molecule must be empty.
5583 * This method is exclusively used for associating a new document with an existing molecule.
5586 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5588 Molecule *mp1, *mp2;
5589 Data_Get_Struct(self, Molecule, mp1);
5590 mp2 = MoleculeFromValue(mval);
5591 MoleculeInitWithMolecule(mp1, mp2);
5592 MoleculeCallback_notifyModification(mp1, 1);
5596 #pragma mark ------ Name attributes ------
5602 * Returns the display name of the molecule. If the molecule has no associated
5603 * document, then returns nil.
5606 s_Molecule_Name(VALUE self)
5610 Data_Get_Struct(self, Molecule, mol);
5611 MoleculeCallback_displayName(mol, buf, sizeof buf);
5615 return Ruby_NewEncodedStringValue2(buf);
5620 * set_name(string) -> self
5622 * Set the name of an untitled molecule. If the molecule is not associated with window
5623 * or it already has an associated file, then exception is thrown.
5626 s_Molecule_SetName(VALUE self, VALUE nval)
5629 Data_Get_Struct(self, Molecule, mol);
5630 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5631 rb_raise(rb_eMolbyError, "Cannot change the window title");
5640 * Returns the full path name of the molecule, if it is associated with a file.
5641 * If the molecule has no associated file, then returns nil.
5644 s_Molecule_Path(VALUE self)
5648 Data_Get_Struct(self, Molecule, mol);
5649 MoleculeCallback_pathName(mol, buf, sizeof buf);
5653 return Ruby_NewFileStringValue(buf);
5660 * Returns the full path name of the directory in which the file associated with the
5661 * molecule is located. If the molecule has no associated file, then returns nil.
5664 s_Molecule_Dir(VALUE self)
5668 Data_Get_Struct(self, Molecule, mol);
5669 MoleculeCallback_pathName(mol, buf, sizeof buf);
5671 translate_char(buf, '\\', '/');
5676 p = strrchr(buf, '/');
5679 return Ruby_NewEncodedStringValue2(buf);
5687 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5688 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5689 * the Molecule structure) is returned.
5692 s_Molecule_Inspect(VALUE self)
5696 Data_Get_Struct(self, Molecule, mol);
5697 MoleculeCallback_displayName(mol, buf, sizeof buf);
5699 /* No associated document */
5700 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5701 return Ruby_NewEncodedStringValue2(buf);
5703 /* Check whether the document name is duplicate */
5707 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5708 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5709 if (strcmp(buf, buf2) == 0) {
5716 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5718 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5720 return Ruby_NewEncodedStringValue2(buf2);
5724 #pragma mark ------ MolEnumerables ------
5727 s_Molecule_MolEnumerable(VALUE self, int kind)
5730 MolEnumerable *mseq;
5731 Data_Get_Struct(self, Molecule, mol);
5732 mseq = MolEnumerableNew(mol, kind);
5733 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5738 * atoms -> MolEnumerable
5740 * Returns a MolEnumerable object representing the array of atoms.
5743 s_Molecule_Atoms(VALUE self)
5745 return s_Molecule_MolEnumerable(self, kAtomKind);
5750 * bonds -> MolEnumerable
5752 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5753 * by an array of two atom indices.
5756 s_Molecule_Bonds(VALUE self)
5758 return s_Molecule_MolEnumerable(self, kBondKind);
5763 * angles -> MolEnumerable
5765 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5766 * by an array of three atom indices.
5769 s_Molecule_Angles(VALUE self)
5771 return s_Molecule_MolEnumerable(self, kAngleKind);
5776 * dihedrals -> MolEnumerable
5778 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5779 * by an array of four atom indices.
5782 s_Molecule_Dihedrals(VALUE self)
5784 return s_Molecule_MolEnumerable(self, kDihedralKind);
5789 * impropers -> MolEnumerable
5791 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5792 * by an array of four atom indices.
5795 s_Molecule_Impropers(VALUE self)
5797 return s_Molecule_MolEnumerable(self, kImproperKind);
5802 * residues -> MolEnumerable
5804 * Returns a MolEnumerable object representing the array of residue names.
5807 s_Molecule_Residues(VALUE self)
5809 return s_Molecule_MolEnumerable(self, kResidueKind);
5816 * Returns the number of atoms.
5819 s_Molecule_Natoms(VALUE self)
5822 Data_Get_Struct(self, Molecule, mol);
5823 return INT2NUM(mol->natoms);
5830 * Returns the number of bonds.
5833 s_Molecule_Nbonds(VALUE self)
5836 Data_Get_Struct(self, Molecule, mol);
5837 return INT2NUM(mol->nbonds);
5842 * nangles -> Integer
5844 * Returns the number of angles.
5847 s_Molecule_Nangles(VALUE self)
5850 Data_Get_Struct(self, Molecule, mol);
5851 return INT2NUM(mol->nangles);
5856 * ndihedrals -> Integer
5858 * Returns the number of dihedrals.
5861 s_Molecule_Ndihedrals(VALUE self)
5864 Data_Get_Struct(self, Molecule, mol);
5865 return INT2NUM(mol->ndihedrals);
5870 * nimpropers -> Integer
5872 * Returns the number of impropers.
5875 s_Molecule_Nimpropers(VALUE self)
5878 Data_Get_Struct(self, Molecule, mol);
5879 return INT2NUM(mol->nimpropers);
5884 * nresidues -> Integer
5886 * Returns the number of residues.
5889 s_Molecule_Nresidues(VALUE self)
5892 Data_Get_Struct(self, Molecule, mol);
5893 return INT2NUM(mol->nresidues);
5898 * nresidues = Integer
5900 * Change the number of residues.
5903 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5906 int ival = NUM2INT(val);
5907 Data_Get_Struct(self, Molecule, mol);
5908 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5909 if (ival != mol->nresidues)
5910 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5916 * max_residue_number(atom_group = nil) -> Integer
5918 * Returns the maximum residue number actually used. If an atom group is given, only
5919 * these atoms are examined. If no atom is present, nil is returned.
5922 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5928 Data_Get_Struct(self, Molecule, mol);
5929 rb_scan_args(argc, argv, "01", &gval);
5930 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5931 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5932 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5937 * min_residue_number(atom_group = nil) -> Integer
5939 * Returns the minimum residue number actually used. If an atom group is given, only
5940 * these atoms are examined. If no atom is present, nil is returned.
5943 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5949 Data_Get_Struct(self, Molecule, mol);
5950 rb_scan_args(argc, argv, "01", &gval);
5951 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5952 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5953 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5958 * each_atom(atom_group = nil) {|aref| ...}
5960 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5961 * group is given, only these atoms are processed.
5962 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5963 * is self (a Molecule object).
5966 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5974 Data_Get_Struct(self, Molecule, mol);
5975 rb_scan_args(argc, argv, "01", &gval);
5976 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5977 arval = ValueFromMoleculeAndIndex(mol, 0);
5978 Data_Get_Struct(arval, AtomRef, aref);
5979 for (i = 0; i < mol->natoms; i++) {
5981 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5985 IntGroupRelease(ig);
5989 #pragma mark ------ Atom Group ------
5992 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5994 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5995 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5996 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5997 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6004 * atom_group {|aref| ...}
6005 * atom_group(arg1, arg2, ...)
6006 * atom_group(arg1, arg2, ...) {|aref| ...}
6008 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6009 * If arguments are given, then the atoms reprensented by the arguments are added to the
6010 * group. For a conversion of a string to an atom index, see the description
6011 * of Molecule#atom_index.
6012 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6013 * representing each atom, and the atoms are removed from the result if the block returns false.
6017 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6019 IntGroup *ig1, *ig2;
6021 Int i, startPt, interval;
6022 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6023 Data_Get_Struct(retval, IntGroup, ig1);
6024 Data_Get_Struct(self, Molecule, mol);
6026 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6029 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6030 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6031 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6032 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6033 ig2 = IntGroupFromValue(*argv);
6034 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6035 interval = IntGroupGetInterval(ig2, i);
6036 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6038 IntGroupRelease(ig2);
6039 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6041 values[0] = (VALUE)mol;
6042 values[1] = (VALUE)ig1;
6043 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6045 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6050 if (rb_block_given_p()) {
6051 /* Evaluate the given block with an AtomRef as the argument, and delete
6052 the index if the block returns false */
6053 AtomRef *aref = AtomRefNew(mol, 0);
6054 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6055 ig2 = IntGroupNew();
6056 IntGroupCopy(ig2, ig1);
6057 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6059 if (startPt >= mol->natoms)
6061 aref->idx = startPt;
6062 resval = rb_yield(arval);
6064 IntGroupRemove(ig1, startPt, 1);
6066 IntGroupRelease(ig2);
6069 /* Remove points that are out of bounds */
6070 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6077 * selection -> IntGroup
6079 * Returns the current selection.
6082 s_Molecule_Selection(VALUE self)
6087 Data_Get_Struct(self, Molecule, mol);
6088 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6089 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6090 val = ValueFromIntGroup(ig);
6091 IntGroupRelease(ig);
6093 val = IntGroup_Alloc(rb_cIntGroup);
6099 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6103 Data_Get_Struct(self, Molecule, mol);
6107 ig = s_Molecule_AtomGroupFromValue(self, val);
6109 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6111 MoleculeSetSelection(mol, ig);
6113 IntGroupRelease(ig);
6119 * selection = IntGroup
6121 * Set the current selection. The right-hand operand may be nil.
6122 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6125 s_Molecule_SetSelection(VALUE self, VALUE val)
6127 return s_Molecule_SetSelectionSub(self, val, 0);
6132 * set_undoable_selection(IntGroup)
6134 * Set the current selection with undo registration. The right-hand operand may be nil.
6135 * This operation is undoable.
6138 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6140 return s_Molecule_SetSelectionSub(self, val, 1);
6143 #pragma mark ------ Editing ------
6147 * extract(group, dummy_flag = nil) -> Molecule
6149 * Extract the atoms given by group and return as a new molecule object.
6150 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6151 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6152 * names beginning with an underscore) and included in the new molecule object.
6155 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6157 Molecule *mol1, *mol2;
6159 VALUE group, dummy_flag, retval;
6160 Data_Get_Struct(self, Molecule, mol1);
6161 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6162 ig = s_Molecule_AtomGroupFromValue(self, group);
6163 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6166 retval = ValueFromMolecule(mol2);
6168 IntGroupRelease(ig);
6174 * add(molecule2) -> self
6176 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6178 This operation is undoable.
6181 s_Molecule_Add(VALUE self, VALUE val)
6183 Molecule *mol1, *mol2;
6184 Data_Get_Struct(self, Molecule, mol1);
6185 mol2 = MoleculeFromValue(val);
6186 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6192 * remove(group) -> Molecule
6194 * The atoms designated by the given group are removed from the molecule.
6195 * This operation is undoable.
6198 s_Molecule_Remove(VALUE self, VALUE group)
6203 IntGroupIterator iter;
6205 ig = s_Molecule_AtomGroupFromValue(self, group);
6206 /* Data_Get_Struct(self, Molecule, mol1);
6207 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6208 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6209 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6210 Data_Get_Struct(group, IntGroup, ig); */
6211 Data_Get_Struct(self, Molecule, mol1);
6213 /* Remove the bonds between the two fragments */
6214 /* (This is necessary for undo to work correctly) */
6215 IntGroupIteratorInit(ig, &iter);
6217 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6218 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6220 cp = AtomConnectData(&ap->connect);
6221 for (j = 0; j < ap->connect.count; j++) {
6223 if (!IntGroupLookup(ig, n, NULL)) {
6224 /* bond i-n, i is in ig and n is not */
6225 int k = MoleculeLookupBond(mol1, i, n);
6229 IntGroupAdd(bg, k, 1);
6234 IntGroupIteratorRelease(&iter);
6237 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6238 IntGroupRelease(bg);
6241 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6248 * create_atom(name, pos = -1) -> AtomRef
6250 * Create a new atom with the specified name (may contain residue
6251 * information) and position (if position is out of range, the atom is appended at
6252 * the end). Returns the reference to the new atom.
6253 * This operation is undoable.
6256 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6263 char *p, resName[6], atomName[6];
6265 Data_Get_Struct(self, Molecule, mol);
6266 rb_scan_args(argc, argv, "02", &name, &ival);
6268 pos = NUM2INT(rb_Integer(ival));
6271 p = StringValuePtr(name);
6273 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6274 if (atomName[0] == 0)
6275 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6278 if (p == NULL || p[0] == 0) {
6279 memset(atomName, 0, 4);
6282 memset(&arec, 0, sizeof(arec));
6283 strncpy(arec.aname, atomName, 4);
6285 strncpy(arec.resName, resName, 4);
6286 arec.resSeq = resSeq;
6288 arec.occupancy = 1.0;
6289 // i = MoleculeCreateAnAtom(mol, &arec);
6290 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6292 aref = AtomRefNew(mol, pos);
6293 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6298 * duplicate_atom(atomref, pos = -1) -> AtomRef
6300 * Create a new atom with the same attributes (but no bonding information)
6301 * with the specified atom. Returns the reference to the new atom.
6302 * This operation is undoable.
6305 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6311 VALUE retval, aval, ival;
6313 Data_Get_Struct(self, Molecule, mol);
6314 rb_scan_args(argc, argv, "11", &aval, &ival);
6315 if (FIXNUM_P(aval)) {
6316 int idx = NUM2INT(aval);
6317 if (idx < 0 || idx >= mol->natoms)
6318 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6319 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6321 apsrc = s_AtomFromValue(aval);
6324 rb_raise(rb_eMolbyError, "bad atom specification");
6326 pos = NUM2INT(rb_Integer(ival));
6328 AtomDuplicate(&arec, apsrc);
6329 arec.connect.count = 0;
6330 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6333 aref = AtomRefNew(mol, pos);
6334 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6342 * create_bond(n1, n2, ...) -> Integer
6344 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6345 * do nothing for that pair. Returns the number of bonds actually created.
6346 * This operation is undoable.
6349 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6352 Int i, j, k, *ip, old_nbonds;
6354 rb_raise(rb_eMolbyError, "missing arguments");
6356 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6357 Data_Get_Struct(self, Molecule, mol);
6358 ip = ALLOC_N(Int, argc + 1);
6359 for (i = j = 0; i < argc; i++, j++) {
6360 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6362 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6363 j -= 2; /* This bond is already present: skip it */
6365 for (k = 0; k < j - 1; k += 2) {
6366 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6367 j -= 2; /* The same entry is already in the argument */
6374 old_nbonds = mol->nbonds;
6376 ip[j] = kInvalidIndex;
6377 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6381 rb_raise(rb_eMolbyError, "atom index out of range");
6383 rb_raise(rb_eMolbyError, "too many bonds");
6385 rb_raise(rb_eMolbyError, "duplicate bonds");
6387 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6389 rb_raise(rb_eMolbyError, "error in creating bonds");
6390 return INT2NUM(mol->nbonds - old_nbonds);
6395 * molecule.remove_bonds(n1, n2, ...) -> Integer
6397 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6398 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6399 * This operation is undoable.
6402 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6408 rb_raise(rb_eMolbyError, "missing arguments");
6410 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6411 Data_Get_Struct(self, Molecule, mol);
6413 for (i = j = 0; i < argc; i++, j = 1 - j) {
6414 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6416 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6420 IntGroupAdd(bg, k, 1);
6425 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6426 i = IntGroupGetCount(bg);
6427 IntGroupRelease(bg);
6434 * assign_bond_order(idx, d1)
6435 * assign_bond_orders(group, [d1, d2, ...])
6437 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6438 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6439 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6440 * (This may change in the future)
6441 * This operation is undoable.
6444 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6448 Data_Get_Struct(self, Molecule, mol);
6449 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6450 /* The first form */
6451 Int idx = NUM2INT(rb_Integer(idxval));
6452 Double d1 = NUM2DBL(rb_Float(dval));
6453 if (idx < 0 || idx >= mol->nbonds)
6454 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6455 ig = IntGroupNewWithPoints(idx, 1, -1);
6456 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6457 IntGroupRelease(ig);
6461 ig = IntGroupFromValue(idxval);
6462 n = IntGroupGetCount(ig);
6464 rb_raise(rb_eMolbyError, "the bond index is empty");
6465 dval = rb_ary_to_ary(dval);
6466 dp = (Double *)calloc(sizeof(Double), n);
6467 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6468 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6470 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6472 IntGroupRelease(ig);
6479 * get_bond_order(idx) -> Float
6480 * get_bond_orders(group) -> Array
6482 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6483 * In the second form, the bond orders at the indices in the group are returned as an array.
6484 * If no bond order information have been assigned, returns nil (the first form)
6485 * or an empty array (the second form).
6488 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6494 Int i, n, numericArg;
6495 Data_Get_Struct(self, Molecule, mol);
6496 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6497 /* The first form */
6498 Int idx = NUM2INT(rb_Integer(idxval));
6499 if (idx < 0 || idx >= mol->nbonds)
6500 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6501 if (mol->bondOrders == NULL)
6503 ig = IntGroupNewWithPoints(idx, 1, -1);
6507 if (mol->bondOrders == NULL)
6508 return rb_ary_new();
6509 ig = IntGroupFromValue(idxval);
6510 n = IntGroupGetCount(ig);
6512 rb_raise(rb_eMolbyError, "the bond index is empty");
6515 dp = (Double *)calloc(sizeof(Double), n);
6516 MoleculeGetBondOrders(mol, dp, ig);
6518 retval = rb_float_new(dp[0]);
6520 retval = rb_ary_new();
6521 for (i = 0; i < n; i++)
6522 rb_ary_push(retval, rb_float_new(dp[i]));
6525 IntGroupRelease(ig);
6531 * bond_exist?(idx1, idx2) -> bool
6533 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6534 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6537 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6543 Data_Get_Struct(self, Molecule, mol);
6544 idx1 = NUM2INT(rb_Integer(ival1));
6545 idx2 = NUM2INT(rb_Integer(ival2));
6546 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6547 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6548 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6549 cp = AtomConnectData(&ap->connect);
6550 for (i = 0; i < ap->connect.count; i++) {
6559 * add_angle(n1, n2, n3) -> Molecule
6561 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6562 * when a bond is created, so it is rarely necessary to use this method explicitly.
6563 * This operation is undoable.
6566 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6570 Data_Get_Struct(self, Molecule, mol);
6571 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6572 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6573 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6574 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6575 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6576 n[3] = kInvalidIndex;
6577 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6583 * remove_angle(n1, n2, n3) -> Molecule
6585 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6586 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6587 * This operation is undoable.
6590 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6595 Data_Get_Struct(self, Molecule, mol);
6596 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6597 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6598 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6599 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6600 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6601 ig = IntGroupNewWithPoints(n[3], 1, -1);
6602 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6603 IntGroupRelease(ig);
6609 * add_dihedral(n1, n2, n3, n4) -> Molecule
6611 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6612 * when a bond is created, so it is rarely necessary to use this method explicitly.
6613 * This operation is undoable.
6616 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6620 Data_Get_Struct(self, Molecule, mol);
6621 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6622 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6623 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6624 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6625 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6626 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6627 n[4] = kInvalidIndex;
6628 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6634 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6636 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6637 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6638 * This operation is undoable.
6641 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6646 Data_Get_Struct(self, Molecule, mol);
6647 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6648 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6649 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6650 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6651 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6652 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6653 ig = IntGroupNewWithPoints(n[4], 1, -1);
6654 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6655 IntGroupRelease(ig);
6661 * add_improper(n1, n2, n3, n4) -> Molecule
6663 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6664 * not automatically added when a new bond is created, so this method is more useful than
6665 * the angle/dihedral counterpart.
6666 * This operation is undoable.
6669 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6673 Data_Get_Struct(self, Molecule, mol);
6674 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6675 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6676 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6677 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6678 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6679 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6680 n[4] = kInvalidIndex;
6681 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6687 * remove_improper(n1, n2, n3, n4) -> Molecule
6688 * remove_improper(intgroup) -> Molecule
6690 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6691 * Returns self. Unlike angles and dihedrals, impropers are
6692 * not automatically added when a new bond is created, so this method is more useful than
6693 * the angle/dihedral counterpart.
6694 * This operation is undoable.
6697 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6700 VALUE v1, v2, v3, v4;
6703 Data_Get_Struct(self, Molecule, mol);
6705 ig = IntGroupFromValue(argv[0]);
6707 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6708 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6709 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6710 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6711 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6712 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6713 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6714 ig = IntGroupNewWithPoints(n[4], 1, -1);
6716 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6717 IntGroupRelease(ig);
6723 * assign_residue(group, res) -> Molecule
6725 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6726 * or "resname.resno". When the residue number is not specified, the residue number of
6727 * the first atom in the group is used.
6728 * This operation is undoable.
6731 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6735 char *p, *pp, buf[16];
6738 Data_Get_Struct(self, Molecule, mol);
6740 /* Parse the argument res */
6741 if (FIXNUM_P(res)) {
6742 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6743 resid = NUM2INT(res);
6746 p = StringValuePtr(res);
6747 pp = strchr(p, '.');
6749 resid = atoi(pp + 1);
6755 if (n > sizeof buf - 1)
6760 ig = s_Molecule_AtomGroupFromValue(self, range);
6761 if (ig == NULL || IntGroupGetCount(ig) == 0)
6765 /* Use the residue number of the first specified atom */
6766 n = IntGroupGetNthPoint(ig, 0);
6767 if (n >= mol->natoms)
6768 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6769 ap = ATOM_AT_INDEX(mol->atoms, n);
6772 /* Change the residue number */
6773 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6774 /* Change the residue name if necessary */
6778 seqs[1] = kInvalidIndex; */
6779 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6781 IntGroupRelease(ig);
6787 * offset_residue(group, offset) -> Molecule
6789 * Offset the residue number of the specified atoms. If any of the residue number gets
6790 * negative, then exception is thrown.
6791 * This operation is undoable.
6794 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6799 Data_Get_Struct(self, Molecule, mol);
6800 ig = s_Molecule_AtomGroupFromValue(self, range);
6801 ofs = NUM2INT(offset);
6802 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6804 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6805 IntGroupRelease(ig);
6811 * renumber_atoms(array) -> IntGroup
6813 * Change the order of atoms so that the atoms specified in the array argument appear
6814 * in this order from the top of the molecule. The atoms that are not included in array
6815 * are placed after these atoms, and these atoms are returned as an intGroup.
6816 * This operation is undoable.
6819 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6825 VALUE *valp, retval;
6826 Data_Get_Struct(self, Molecule, mol);
6827 if (TYPE(array) != T_ARRAY)
6828 array = rb_funcall(array, rb_intern("to_a"), 0);
6829 n = RARRAY_LEN(array);
6830 valp = RARRAY_PTR(array);
6831 new2old = ALLOC_N(Int, n + 1);
6832 for (i = 0; i < n; i++)
6833 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6834 new2old[i] = kInvalidIndex;
6835 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6837 rb_raise(rb_eMolbyError, "Atom index out of range");
6839 rb_raise(rb_eMolbyError, "Duplicate entry");
6841 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6842 retval = IntGroup_Alloc(rb_cIntGroup);
6843 Data_Get_Struct(retval, IntGroup, ig);
6844 if (mol->natoms > n)
6845 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6852 * set_atom_attr(index, key, value)
6854 * Set the atom attribute for the specified atom.
6855 * This operation is undoable.
6858 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6862 Data_Get_Struct(self, Molecule, mol);
6863 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6864 oldval = s_AtomRef_GetAttr(aref, key);
6867 s_AtomRef_SetAttr(aref, key, val);
6873 * get_atom_attr(index, key)
6875 * Get the atom attribute for the specified atom.
6878 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6880 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6883 #pragma mark ------ Undo Support ------
6887 * register_undo(script, *args)
6889 * Register an undo operation with the current molecule.
6892 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6897 Data_Get_Struct(self, Molecule, mol);
6898 rb_scan_args(argc, argv, "1*", &script, &args);
6899 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6900 MolActionCallback_registerUndo(mol, act);
6906 * undo_enabled? -> bool
6908 * Returns true if undo is enabled for this molecule; otherwise no.
6911 s_Molecule_UndoEnabled(VALUE self)
6914 Data_Get_Struct(self, Molecule, mol);
6915 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6922 * undo_enabled = bool
6924 * Enable or disable undo.
6927 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6930 Data_Get_Struct(self, Molecule, mol);
6931 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6935 #pragma mark ------ Measure ------
6938 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6940 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6941 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6942 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6944 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6950 * center_of_mass(group = nil) -> Vector3D
6952 * Calculate the center of mass for the given set of atoms. The argument
6953 * group is null, then all atoms are considered.
6956 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6962 Data_Get_Struct(self, Molecule, mol);
6963 rb_scan_args(argc, argv, "01", &group);
6964 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6965 s_Molecule_DoCenterOfMass(mol, &v, ig);
6967 IntGroupRelease(ig);
6968 return ValueFromVector(&v);
6973 * centralize(group = nil) -> self
6975 * Translate the molecule so that the center of mass of the given group is located
6976 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6979 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6985 Data_Get_Struct(self, Molecule, mol);
6986 rb_scan_args(argc, argv, "01", &group);
6987 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6988 s_Molecule_DoCenterOfMass(mol, &v, ig);
6990 IntGroupRelease(ig);
6994 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7000 * bounds(group = nil) -> [min, max]
7002 * Calculate the boundary. The return value is an array of two Vector3D objects.
7005 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7013 Data_Get_Struct(self, Molecule, mol);
7014 rb_scan_args(argc, argv, "01", &group);
7015 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7016 if (ig != NULL && IntGroupGetCount(ig) == 0)
7017 rb_raise(rb_eMolbyError, "atom group is empty");
7018 vmin.x = vmin.y = vmin.z = 1e30;
7019 vmax.x = vmax.y = vmax.z = -1e30;
7020 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7022 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7038 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7041 /* Get atom position or a vector */
7043 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7045 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7046 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7047 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7049 VectorFromValue(val, vp);
7055 * measure_bond(n1, n2) -> Float
7057 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7058 * or Vector3D values.
7059 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7062 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7066 Data_Get_Struct(self, Molecule, mol);
7067 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7068 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7069 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7074 * measure_angle(n1, n2, n3) -> Float
7076 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7077 * or Vector3D values. The return value is in degree.
7078 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7081 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7086 Data_Get_Struct(self, Molecule, mol);
7087 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7088 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7089 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7090 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7092 return Qnil; /* Cannot define */
7093 else return rb_float_new(d);
7098 * measure_dihedral(n1, n2, n3, n4) -> Float
7100 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7101 * or Vector3D values. The return value is in degree.
7102 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7105 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7108 Vector v1, v2, v3, v4;
7110 Data_Get_Struct(self, Molecule, mol);
7111 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7112 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7113 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7114 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7115 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7117 return Qnil; /* Cannot define */
7118 else return rb_float_new(d);
7123 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7125 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7126 * first and second atom in the pair should belong to group1 and group2, respectively.
7127 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7130 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7133 VALUE limval, gval1, gval2, rval, igval;
7134 IntGroup *ig1, *ig2;
7135 IntGroupIterator iter1, iter2;
7141 MDExclusion *exinfo;
7144 Data_Get_Struct(self, Molecule, mol);
7145 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7146 lim = NUM2DBL(rb_Float(limval));
7148 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7150 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7152 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7154 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7156 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7158 if (!RTEST(igval)) {
7159 /* Use the exclusion table in MDArena */
7160 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7161 VALUE mval = ValueFromMolecule(mol);
7162 s_RebuildMDParameterIfNecessary(mval, Qnil);
7164 exinfo = mol->arena->exinfo; /* May be NULL */
7165 exlist = mol->arena->exlist;
7170 IntGroupIteratorInit(ig1, &iter1);
7171 IntGroupIteratorInit(ig2, &iter2);
7174 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7176 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7178 if (exinfo != NULL) {
7179 exn1 = exinfo[n[0]].index1;
7180 exn2 = exinfo[n[0] + 1].index1;
7181 } else exn1 = exn2 = -1;
7182 IntGroupIteratorReset(&iter2);
7183 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7184 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7186 continue; /* Same atom */
7187 if (exinfo != NULL) {
7188 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7189 for (i = exn1; i < exn2; i++) {
7190 if (exlist[i] == n[1])
7194 continue; /* Should be excluded */
7196 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7197 /* Is this pair already registered? */
7199 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7200 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7204 /* Not registered yet */
7205 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7210 IntGroupIteratorRelease(&iter2);
7211 IntGroupIteratorRelease(&iter1);
7212 IntGroupRelease(ig2);
7213 IntGroupRelease(ig1);
7214 rval = rb_ary_new2(npairs);
7215 if (pairs != NULL) {
7216 for (i = 0; i < npairs; i++) {
7217 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7226 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7228 * Find atoms that are within the threshold distance from the given atom.
7229 * (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.)
7230 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7231 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7232 * If limit is not given, a default value of 1.2 is used.
7233 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7236 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7239 VALUE aval, limval, radval;
7240 double limit, radius;
7241 Int n1, nbonds, *bonds, an;
7243 Data_Get_Struct(self, Molecule, mol);
7244 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7245 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)) {
7246 VectorFromValue(aval, &v);
7248 radius = gElementParameters[6].radius;
7250 radius = NUM2DBL(rb_Float(radval));
7253 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7254 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7255 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7256 if (an >= 0 && an < gCountElementParameters)
7257 radius = gElementParameters[an].radius;
7258 else radius = gElementParameters[6].radius;
7263 limit = NUM2DBL(rb_Float(limval));
7264 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7266 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7267 aval = rb_ary_new();
7269 for (n1 = 0; n1 < nbonds; n1++)
7270 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7278 * guess_bonds(limit = 1.2) -> Integer
7280 * Create bonds between atoms that are within the threshold distance.
7281 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7282 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7283 * If limit is not given, a default value of 1.2 is used.
7284 * The number of the newly created bonds is returned.
7285 * This operation is undoable.
7288 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7294 Data_Get_Struct(self, Molecule, mol);
7295 rb_scan_args(argc, argv, "01", &limval);
7299 limit = NUM2DBL(rb_Float(limval));
7300 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7302 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7305 return INT2NUM(nbonds);
7308 #pragma mark ------ Cell and Symmetry ------
7312 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7314 * Returns the unit cell parameters. If cell is not set, returns nil.
7317 s_Molecule_Cell(VALUE self)
7322 Data_Get_Struct(self, Molecule, mol);
7323 if (mol->cell == NULL)
7325 val = rb_ary_new2(6);
7326 for (i = 0; i < 6; i++)
7327 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7328 if (mol->cell->has_sigma) {
7329 for (i = 0; i < 6; i++) {
7330 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7338 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7339 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7341 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7342 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7343 This operation is undoable.
7344 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7347 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7351 int i, convert_coord, n;
7353 Data_Get_Struct(self, Molecule, mol);
7354 rb_scan_args(argc, argv, "11", &val, &cval);
7359 val = rb_ary_to_ary(val);
7360 len = RARRAY_LEN(val);
7363 } else if (len >= 6) {
7365 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7366 for (i = 0; i < n; i++)
7367 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7369 convert_coord = (RTEST(cval) ? 1 : 0);
7370 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7376 * box -> [avec, bvec, cvec, origin, flags]
7378 * Get the unit cell information in the form of a periodic bounding box.
7379 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7380 * Integers which define whether the system is periodic along the axis.
7381 * If no unit cell is defined, nil is returned.
7384 s_Molecule_Box(VALUE self)
7388 Data_Get_Struct(self, Molecule, mol);
7389 if (mol == NULL || mol->cell == NULL)
7391 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7392 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7393 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7394 v[3] = ValueFromVector(&(mol->cell->origin));
7395 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7396 val = rb_ary_new4(5, v);
7402 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7403 * set_box(d, origin = [0, 0, 0])
7406 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7407 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7409 Flags, if present, is a 3-member array of Integers defining whether the system is
7410 periodic along the axis.
7411 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7412 In the second form, an isotropic box with cell-length d is set.
7413 In the third form, the existing box is cleared.
7414 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7417 s_Molecule_SetBox(VALUE self, VALUE aval)
7421 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7423 Vector origin = {0, 0, 0};
7426 int i, convertCoordinates = 0;
7427 Data_Get_Struct(self, Molecule, mol);
7429 MolActionCreateAndPerform(mol, gMolActionClearBox);
7432 aval = rb_ary_to_ary(aval);
7433 for (i = 0; i < 6; i++) {
7434 if (i < RARRAY_LEN(aval))
7435 v[i] = (RARRAY_PTR(aval))[i];
7439 MolActionCreateAndPerform(mol, gMolActionClearBox);
7442 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7443 d = NUM2DBL(rb_Float(v[0]));
7444 for (i = 0; i < 3; i++)
7445 VecScale(vv[i], ax[i], d);
7447 VectorFromValue(v[1], &origin);
7448 flags[0] = flags[1] = flags[2] = 1;
7450 for (i = 0; i < 3; i++) {
7453 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7454 d = NUM2DBL(rb_Float(v[i]));
7455 VecScale(vv[i], ax[i], d);
7457 VectorFromValue(v[i], &vv[i]);
7459 flags[i] = (VecLength2(vv[i]) > 0.0);
7462 VectorFromValue(v[3], &origin);
7464 for (i = 0; i < 3; i++) {
7465 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7466 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7470 convertCoordinates = 1;
7472 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7478 * cell_periodicity -> [n1, n2, n3]
7480 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7484 s_Molecule_CellPeriodicity(VALUE self)
7487 Data_Get_Struct(self, Molecule, mol);
7488 if (mol->cell == NULL)
7490 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7495 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7496 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7498 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7499 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7500 * If cell is not defined, exception is raised.
7501 * This operation is undoable.
7504 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7508 Data_Get_Struct(self, Molecule, mol);
7509 if (mol->cell == NULL)
7510 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7513 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7514 flag = NUM2INT(rb_Integer(arg));
7518 arg = rb_ary_to_ary(arg);
7520 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7521 arg0 = RARRAY_PTR(arg)[i];
7522 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7523 flag |= (1 << (2 - i));
7526 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7532 * cell_flexibility -> bool
7534 * Returns the unit cell is flexible or not
7537 s_Molecule_CellFlexibility(VALUE self)
7539 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7542 Data_Get_Struct(self, Molecule, mol);
7543 if (mol->cell == NULL)
7545 if (mol->useFlexibleCell)
7547 else return Qfalse; */
7552 * self.cell_flexibility = bool
7553 * set_cell_flexibility(bool)
7555 * Change the unit cell is flexible or not
7558 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7560 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7563 Data_Get_Struct(self, Molecule, mol);
7564 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7570 * cell_transform -> Transform
7572 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7573 * If cell is not defined, nil is returned.
7576 s_Molecule_CellTransform(VALUE self)
7579 Data_Get_Struct(self, Molecule, mol);
7580 if (mol == NULL || mol->cell == NULL)
7582 return ValueFromTransform(&(mol->cell->tr));
7587 * symmetry -> Array of Transforms
7588 * symmetries -> Array of Transforms
7590 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7591 * returns an empty array.
7594 s_Molecule_Symmetry(VALUE self)
7599 Data_Get_Struct(self, Molecule, mol);
7600 if (mol->nsyms <= 0)
7601 return rb_ary_new();
7602 val = rb_ary_new2(mol->nsyms);
7603 for (i = 0; i < mol->nsyms; i++) {
7604 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7611 * nsymmetries -> Integer
7613 * Get the number of currently defined symmetry operations.
7616 s_Molecule_Nsymmetries(VALUE self)
7619 Data_Get_Struct(self, Molecule, mol);
7620 return INT2NUM(mol->nsyms);
7625 * add_symmetry(Transform) -> Integer
7627 * Add a new symmetry operation. If no symmetry operation is defined and the
7628 * given argument is not an identity transform, then also add an identity
7629 * transform at the index 0.
7630 * Returns the total number of symmetries after operation.
7633 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7637 Data_Get_Struct(self, Molecule, mol);
7638 TransformFromValue(trans, &tr);
7639 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7640 return INT2NUM(mol->nsyms);
7645 * remove_symmetry(count = nil) -> Integer
7646 * remove_symmetries(count = nil) -> Integer
7648 * Remove the specified number of symmetry operations. The last added ones are removed
7649 * first. If count is nil, then all symmetry operations are removed. Returns the
7650 * number of leftover symmetries.
7653 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7658 Data_Get_Struct(self, Molecule, mol);
7659 rb_scan_args(argc, argv, "01", &cval);
7663 n = NUM2INT(rb_Integer(cval));
7664 if (n < 0 || n > mol->nsyms)
7665 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7666 if (n == mol->nsyms)
7669 for (i = 0; i < n; i++)
7670 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7671 return INT2NUM(mol->nsyms);
7676 * wrap_unit_cell(group) -> Vector3D
7678 * Move the specified group so that the center of mass of the group is within the
7679 * unit cell. The offset vector is returned. If no periodic box is defined,
7680 * exception is raised.
7683 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7688 Data_Get_Struct(self, Molecule, mol);
7689 if (mol->cell == NULL)
7690 rb_raise(rb_eMolbyError, "no unit cell is defined");
7691 ig = s_Molecule_AtomGroupFromValue(self, gval);
7692 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7693 TransformVec(&v, mol->cell->rtr, &cv);
7694 if (mol->cell->flags[0])
7696 if (mol->cell->flags[1])
7698 if (mol->cell->flags[2])
7700 TransformVec(&dv, mol->cell->tr, &v);
7702 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7703 IntGroupRelease(ig);
7704 return ValueFromVector(&dv);
7709 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7711 * Expand the specified part of the molecule by the given symmetry operation.
7712 * Returns the array of atom indices corresponding to the expanded atoms.
7713 * If allow_overlap is true, then new atoms are created even when the
7714 * coordinates coincide with the some other atom (special position) of the
7715 * same element; otherwise, such atom will not be created and the index of the
7716 * existing atom is given in the returned array.
7719 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7722 VALUE gval, sval, xval, yval, zval, rval, oval;
7724 Int n[4], allow_overlap;
7728 Data_Get_Struct(self, Molecule, mol);
7729 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7730 n[0] = NUM2INT(rb_Integer(sval));
7731 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7732 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7733 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7734 allow_overlap = (RTEST(oval) ? 1 : 0);
7735 ig = s_Molecule_AtomGroupFromValue(self, gval);
7736 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7737 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7738 natoms = mol->natoms;
7740 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7742 rval = rb_ary_new2(nidx);
7743 while (--nidx >= 0) {
7744 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7746 /* if (natoms == mol->natoms)
7749 rval = IntGroup_Alloc(rb_cIntGroup);
7750 Data_Get_Struct(rval, IntGroup, ig);
7751 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7758 * amend_by_symmetry(group = nil) -> IntGroup
7760 * Expand the specified part of the molecule by the given symmetry operation.
7761 * Returns an IntGroup containing the added atoms.
7764 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7769 Data_Get_Struct(self, Molecule, mol);
7770 rb_scan_args(argc, argv, "01", &gval);
7772 ig = s_Molecule_AtomGroupFromValue(self, gval);
7774 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7775 rval = ValueFromIntGroup(ig2);
7776 IntGroupRelease(ig2);
7780 #pragma mark ------ Transforms ------
7784 * translate(vec, group = nil) -> Molecule
7786 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7787 * This operation is undoable.
7790 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7796 Data_Get_Struct(self, Molecule, mol);
7797 rb_scan_args(argc, argv, "11", &vec, &group);
7798 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7799 VectorFromValue(vec, &v);
7800 // MoleculeTranslate(mol, &v, ig);
7801 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7803 IntGroupRelease(ig);
7809 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7811 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7812 * If group is given, only atoms in the group are moved.
7813 * This operation is undoable.
7816 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7819 volatile VALUE aval, anval, cval, gval;
7824 Data_Get_Struct(self, Molecule, mol);
7825 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7826 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7827 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7828 VectorFromValue(aval, &av);
7830 cv.x = cv.y = cv.z = 0.0;
7832 VectorFromValue(cval, &cv);
7833 if (TransformForRotation(tr, &av, angle, &cv))
7834 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7835 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7837 IntGroupRelease(ig);
7843 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7845 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7846 * axis must not be a zero vector.
7847 * If group is given, only atoms in the group are moved.
7848 * This operation is undoable.
7851 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7854 volatile VALUE aval, cval, gval;
7858 Data_Get_Struct(self, Molecule, mol);
7859 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7860 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7861 VectorFromValue(aval, &av);
7863 cv.x = cv.y = cv.z = 0.0;
7865 VectorFromValue(cval, &cv);
7866 if (TransformForReflection(tr, &av, &cv))
7867 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7868 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7870 IntGroupRelease(ig);
7876 * invert(center = [0,0,0], group = nil) -> Molecule
7878 * Invert the molecule with the given center.
7879 * If group is given, only atoms in the group are moved.
7880 * This operation is undoable.
7883 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7886 volatile VALUE cval, gval;
7890 Data_Get_Struct(self, Molecule, mol);
7891 rb_scan_args(argc, argv, "02", &cval, &gval);
7892 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7894 cv.x = cv.y = cv.z = 0.0;
7896 VectorFromValue(cval, &cv);
7897 TransformForInversion(tr, &cv);
7898 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7900 IntGroupRelease(ig);
7906 * transform(transform, group = nil) -> Molecule
7908 * Transform the molecule by the given Transform object.
7909 * If group is given, only atoms in the group are moved.
7910 * This operation is undoable.
7913 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7919 Data_Get_Struct(self, Molecule, mol);
7920 rb_scan_args(argc, argv, "11", &trans, &group);
7921 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7922 TransformFromValue(trans, &tr);
7923 /* MoleculeTransform(mol, tr, ig); */
7924 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7926 IntGroupRelease(ig);
7932 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7934 * Get the transform corresponding to the symmetry operation. The symop can either be
7935 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7936 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7937 * Otherwise, the returned transform is for fractional coordinates.
7938 * Raises exception when no cell or no transform are defined.
7941 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7947 Data_Get_Struct(self, Molecule, mol);
7948 if (mol->cell == NULL)
7949 rb_raise(rb_eMolbyError, "no unit cell is defined");
7950 if (mol->nsyms == 0)
7951 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7952 rb_scan_args(argc, argv, "11", &sval, &fval);
7953 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7954 symop.sym = NUM2INT(rb_Integer(sval));
7955 symop.dx = symop.dy = symop.dz = 0;
7957 sval = rb_ary_to_ary(sval);
7958 if (RARRAY_LEN(sval) < 4)
7959 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7960 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7961 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7962 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7963 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7965 if (symop.sym >= mol->nsyms)
7966 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7967 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7968 return ValueFromTransform(&tr);
7973 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7975 * Get the symmetry operation corresponding to the given transform.
7976 * If is_cartesian is true, the given transform is for cartesian coordinates.
7977 * Otherwise, the given transform is for fractional coordinates.
7978 * Raises exception when no cell or no transform are defined.
7981 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7988 Data_Get_Struct(self, Molecule, mol);
7989 if (mol->cell == NULL)
7990 rb_raise(rb_eMolbyError, "no unit cell is defined");
7991 if (mol->nsyms == 0)
7992 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7993 rb_scan_args(argc, argv, "11", &tval, &fval);
7994 TransformFromValue(tval, &tr);
7995 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7997 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7999 return Qnil; /* Not found */
8003 #pragma mark ------ Frames ------
8007 * select_frame(index)
8010 * Select the specified frame. If successful, returns true, otherwise returns false.
8013 s_Molecule_SelectFrame(VALUE self, VALUE val)
8016 int ival = NUM2INT(val);
8017 Data_Get_Struct(self, Molecule, mol);
8018 ival = MoleculeSelectFrame(mol, ival, 1);
8028 * Get the current frame.
8031 s_Molecule_Frame(VALUE self)
8034 Data_Get_Struct(self, Molecule, mol);
8035 return INT2NUM(mol->cframe);
8040 * nframes -> Integer
8042 * Get the number of frames.
8045 s_Molecule_Nframes(VALUE self)
8048 Data_Get_Struct(self, Molecule, mol);
8049 return INT2NUM(MoleculeGetNumberOfFrames(mol));
8054 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8055 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8057 * Insert new frames at the indices specified by the intGroup. If the first argument is
8058 * an integer, a single new frame is inserted at that index. If the first argument is
8059 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8060 * should be an array of arrays of Vector3Ds, then those coordinates are set
8061 * to the new frame. Otherwise, the coordinates of current molecule are copied
8063 * Returns an intGroup representing the inserted frames if successful, nil if not.
8066 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8068 VALUE val, coords, cells;
8071 int count, ival, i, j, len, len_c, len2, nframes;
8074 Data_Get_Struct(self, Molecule, mol);
8075 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8076 if (coords != Qnil) {
8077 if (TYPE(coords) != T_ARRAY)
8078 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8079 len = RARRAY_LEN(coords);
8081 if (cells != Qnil) {
8082 if (mol->cell == NULL)
8083 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8084 if (TYPE(cells) != T_ARRAY)
8085 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8086 len_c = RARRAY_LEN(cells);
8088 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
8089 nframes = MoleculeGetNumberOfFrames(mol);
8091 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8092 val = ValueFromIntGroup(ig);
8094 ig = IntGroupFromValue(val);
8096 count = IntGroupGetCount(ig); /* Count is updated here */
8097 vp = ALLOC_N(Vector, mol->natoms * count);
8099 vp2 = ALLOC_N(Vector, 4 * count);
8103 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8104 ptr = RARRAY_PTR(coords);
8105 for (i = 0; i < count; i++) {
8106 if (TYPE(ptr[i]) != T_ARRAY)
8107 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8108 len2 = RARRAY_LEN(ptr[i]);
8109 if (len2 < mol->natoms)
8110 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8111 ptr2 = RARRAY_PTR(ptr[i]);
8112 for (j = 0; j < mol->natoms; j++)
8113 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8117 for (i = 0; i < count; i++) {
8118 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8119 vp[i * mol->natoms + j] = ap->r;
8125 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8126 ptr = RARRAY_PTR(cells);
8127 for (i = 0; i < count; i++) {
8128 if (TYPE(ptr[i]) != T_ARRAY)
8129 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8130 len2 = RARRAY_LEN(ptr[i]);
8132 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8133 ptr2 = RARRAY_PTR(ptr[i]);
8134 for (j = 0; j < 4; j++)
8135 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8138 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8139 IntGroupRelease(ig);
8143 return (ival >= 0 ? val : Qnil);
8148 * create_frame(coordinates = nil) -> Integer
8149 * create_frames(coordinates = nil) -> Integer
8151 * Same as molecule.insert_frames(nil, coordinates).
8154 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8157 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8159 return s_Molecule_InsertFrames(3, vals, self);
8164 * remove_frames(IntGroup, wantCoordinates = false)
8166 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8167 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8168 * removed frames is returned if operation is successful.
8171 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8178 Data_Get_Struct(self, Molecule, mol);
8179 rb_scan_args(argc, argv, "11", &val, &flag);
8180 ig = IntGroupFromValue(val);
8181 count = IntGroupGetCount(ig);
8183 /* Create return value before removing frames */
8188 retval = rb_ary_new2(count);
8189 for (i = 0; i < count; i++) {
8190 n = IntGroupGetNthPoint(ig, i);
8191 coords = rb_ary_new2(mol->natoms);
8192 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8193 if (n < ap->nframes && n != mol->cframe)
8196 rb_ary_push(coords, ValueFromVector(&v));
8198 rb_ary_push(retval, coords);
8200 } else retval = Qtrue;
8201 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8208 * each_frame {|n| ...}
8210 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8211 * the frame number. After completion, the original frame number is restored.
8214 s_Molecule_EachFrame(VALUE self)
8216 int i, cframe, nframes;
8218 Data_Get_Struct(self, Molecule, mol);
8219 cframe = mol->cframe;
8220 nframes = MoleculeGetNumberOfFrames(mol);
8222 for (i = 0; i < nframes; i++) {
8223 MoleculeSelectFrame(mol, i, 1);
8224 rb_yield(INT2NUM(i));
8226 MoleculeSelectFrame(mol, cframe, 1);
8233 * get_coord_from_frame(index, group = nil)
8235 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8236 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8237 * copied; now they are always copied)
8240 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8243 VALUE ival, gval, cval;
8244 Int index, i, j, n, nn;
8246 IntGroupIterator iter;
8249 Data_Get_Struct(self, Molecule, mol);
8250 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8252 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8253 index = NUM2INT(rb_Integer(ival));
8254 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8256 rb_raise(rb_eMolbyError, "No frame is present");
8258 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8261 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8263 ig = s_Molecule_AtomGroupFromValue(self, gval);
8265 n = IntGroupGetCount(ig);
8267 vp = (Vector *)calloc(sizeof(Vector), n);
8268 IntGroupIteratorInit(ig, &iter);
8271 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8272 ap = ATOM_AT_INDEX(mol->atoms, i);
8273 if (index < ap->nframes) {
8274 vp[j] = ap->frames[index];
8282 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8284 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8285 vp = mol->frame_cells + index * 4;
8286 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8288 IntGroupIteratorRelease(&iter);
8290 /* Copy the extra properties */
8291 IntGroupRelease(ig);
8292 for (i = 0; i < mol->nmolprops; i++) {
8293 Double *dp = (Double *)malloc(sizeof(Double));
8295 IntGroupAdd(ig, mol->cframe, 1);
8296 *dp = mol->molprops[i].propvals[index];
8297 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8299 IntGroupRelease(ig);
8307 * reorder_frames(old_indices)
8309 * Reorder the frames. The argument is an array of integers that specify the 'old'
8310 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8311 * same as the old frames 2/0/1, respectively.
8312 * The argument must have the same number of integers as the number of frames.
8315 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8318 Int *ip, *ip2, i, n, nframes;
8319 Data_Get_Struct(self, Molecule, mol);
8320 aval = rb_ary_to_ary(aval);
8321 nframes = MoleculeGetNumberOfFrames(mol);
8322 if (RARRAY_LEN(aval) != nframes)
8323 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8324 ip2 = (Int *)calloc(sizeof(Int), nframes);
8325 ip = (Int *)calloc(sizeof(Int), nframes);
8326 for (i = 0; i < nframes; i++) {
8327 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8328 if (n < 0 || n >= nframes || ip2[n] != 0) {
8331 if (n < 0 || n >= nframes)
8332 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8334 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8340 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8345 #pragma mark ------ Fragments ------
8349 * fragment(n1, *exatoms) -> IntGroup
8350 * fragment(group, *exatoms) -> IntGroup
8352 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8353 * those atoms will not be counted during the search.
8356 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8359 IntGroup *baseg, *ig, *exatoms;
8361 volatile VALUE nval, exval;
8362 Data_Get_Struct(self, Molecule, mol);
8363 rb_scan_args(argc, argv, "1*", &nval, &exval);
8364 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8366 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8368 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8370 if (RARRAY_LEN(exval) == 0) {
8373 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8374 Data_Get_Struct(exval, IntGroup, exatoms);
8376 if (baseg == NULL) {
8377 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8379 IntGroupIterator iter;
8380 IntGroupIteratorInit(baseg, &iter);
8381 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8384 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8386 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8388 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8390 IntGroupAddIntGroup(ig, subg);
8391 IntGroupRelease(subg);
8396 IntGroupIteratorRelease(&iter);
8397 IntGroupRelease(baseg);
8400 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8401 nval = ValueFromIntGroup(ig);
8402 IntGroupRelease(ig);
8408 * fragments(exclude = nil)
8410 * Returns the fragments as an array of IntGroups.
8411 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8412 * in defining the fragment.
8415 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8418 IntGroup *ag, *fg, *eg;
8419 VALUE gval, exval, retval;
8420 Data_Get_Struct(self, Molecule, mol);
8423 if (mol->natoms == 0)
8424 return rb_ary_new();
8425 rb_scan_args(argc, argv, "01", &exval);
8429 eg = IntGroupFromValue(exval);
8430 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8432 IntGroupRemoveIntGroup(ag, eg);
8433 retval = rb_ary_new();
8434 while (IntGroupGetCount(ag) > 0) {
8435 int n = IntGroupGetNthPoint(ag, 0);
8436 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8438 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8439 gval = ValueFromIntGroup(fg);
8440 rb_ary_push(retval, gval);
8441 IntGroupRemoveIntGroup(ag, fg);
8442 IntGroupRelease(fg);
8444 IntGroupRelease(ag);
8446 IntGroupRelease(eg);
8452 * each_fragment(exclude = nil) {|group| ...}
8454 * Execute the block, with the IntGroup object for each fragment as the argument.
8455 * Atoms or bonds should not be added or removed during the execution of the block.
8456 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8457 * in defining the fragment.
8460 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8463 IntGroup *ag, *fg, *eg;
8465 Data_Get_Struct(self, Molecule, mol);
8466 if (mol == NULL || mol->natoms == 0)
8468 rb_scan_args(argc, argv, "01", &exval);
8472 eg = IntGroupFromValue(exval);
8473 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8475 IntGroupRemoveIntGroup(ag, eg);
8476 while (IntGroupGetCount(ag) > 0) {
8477 int n = IntGroupGetNthPoint(ag, 0);
8478 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8480 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8481 gval = ValueFromIntGroup(fg);
8483 IntGroupRemoveIntGroup(ag, fg);
8484 IntGroupRelease(fg);
8486 IntGroupRelease(ag);
8488 IntGroupRelease(eg);
8494 * detachable?(group) -> [n1, n2]
8496 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8497 * of the molecule via only one bond. If it is, then the indices of the atoms
8498 * belonging to the bond is returned, the first element being the atom included
8499 * in the fragment. Otherwise, Qnil is returned.
8502 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8508 Data_Get_Struct(self, Molecule, mol);
8509 ig = s_Molecule_AtomGroupFromValue(self, gval);
8510 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8511 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8512 } else retval = Qnil;
8513 IntGroupRelease(ig);
8519 * bonds_on_border(group = selection) -> Array of Array of two Integers
8521 * Returns an array of bonds that connect an atom in the group and an atom out
8522 * of the group. The first atom in the bond always belongs to the group. If no
8523 * such bonds are present, an empty array is returned.
8526 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8531 Data_Get_Struct(self, Molecule, mol);
8532 rb_scan_args(argc, argv, "01", &gval);
8534 ig = MoleculeGetSelection(mol);
8538 ig = s_Molecule_AtomGroupFromValue(self, gval);
8540 retval = rb_ary_new();
8543 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8545 IntGroupIterator iter;
8547 IntGroupIteratorInit(bg, &iter);
8548 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8549 /* The atoms at the border */
8551 n1 = mol->bonds[i * 2];
8552 n2 = mol->bonds[i * 2 + 1];
8553 if (IntGroupLookupPoint(ig, n1) < 0) {
8557 if (IntGroupLookupPoint(ig, n1) < 0)
8558 continue; /* Actually this is an internal error */
8560 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8562 IntGroupIteratorRelease(&iter);
8564 IntGroupRelease(bg);
8565 IntGroupRelease(ig);
8569 /* Calculate the transform that moves the current coordinates to the reference
8570 coordinates with least displacements. */
8572 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8580 Double eigen_val[3];
8581 Vector eigen_vec[3];
8583 IntGroupIterator iter;
8585 natoms = mol->natoms;
8587 IntGroupIteratorInit(ig, &iter);
8589 /* Calculate the weighted center */
8593 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8594 ap1 = ATOM_AT_INDEX(ap, in);
8595 w1 = (weights != NULL ? weights[i] : ap1->weight);
8596 VecScaleInc(org1, ap1->r, w1);
8597 VecScaleInc(org2, ref[i], w1);
8601 VecScaleSelf(org1, w);
8602 VecScaleSelf(org2, w);
8604 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8605 /* Matrix to diagonalize = R * tR */
8606 memset(r, 0, sizeof(Mat33));
8607 memset(q, 0, sizeof(Mat33));
8608 memset(u, 0, sizeof(Mat33));
8610 IntGroupIteratorReset(&iter);
8611 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8613 ap1 = ATOM_AT_INDEX(ap, in);
8614 w1 = (weights != NULL ? weights[i] : ap1->weight);
8616 VecSub(v1, ap1->r, org1);
8617 VecSub(v2, ref[i], org2);
8618 r[0] += w1 * v1.x * v2.x;
8619 r[1] += w1 * v1.y * v2.x;
8620 r[2] += w1 * v1.z * v2.x;
8621 r[3] += w1 * v1.x * v2.y;
8622 r[4] += w1 * v1.y * v2.y;
8623 r[5] += w1 * v1.z * v2.y;
8624 r[6] += w1 * v1.x * v2.z;
8625 r[7] += w1 * v1.y * v2.z;
8626 r[8] += w1 * v1.z * v2.z;
8629 for (i = 0; i < 9; i++)
8631 for (i = 0; i < 3; i++) {
8632 for (j = 0; j < 3; j++) {
8633 for (k = 0; k < 3; k++) {
8634 q[i+j*3] += r[i+k*3] * r[j+k*3];
8639 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8640 IntGroupIteratorRelease(&iter);
8641 return -1.0; /* Cannot determine the eigenvector */
8644 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8645 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8646 MatrixTranspose(r, r);
8647 for (i = 0; i < 3; i++) {
8648 MatrixVec(&s[i], r, &eigen_vec[i]);
8649 w1 = 1.0 / sqrt(eigen_val[i]);
8650 VecScaleSelf(s[i], w1);
8652 for (k = 0; k < 3; k++) {
8653 u[0] += s[k].x * eigen_vec[k].x;
8654 u[1] += s[k].y * eigen_vec[k].x;
8655 u[2] += s[k].z * eigen_vec[k].x;
8656 u[3] += s[k].x * eigen_vec[k].y;
8657 u[4] += s[k].y * eigen_vec[k].y;
8658 u[5] += s[k].z * eigen_vec[k].y;
8659 u[6] += s[k].x * eigen_vec[k].z;
8660 u[7] += s[k].y * eigen_vec[k].z;
8661 u[8] += s[k].z * eigen_vec[k].z;
8664 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8665 MatrixVec(&org1, u, &org1);
8667 for (i = 0; i < 9; i++)
8673 /* Calculate rmsd */
8674 IntGroupIteratorReset(&iter);
8676 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8678 ap1 = ATOM_AT_INDEX(ap, in);
8679 TransformVec(&tv, trans, &ap1->r);
8681 w += VecLength2(tv);
8684 IntGroupIteratorRelease(&iter);
8690 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8692 * Calculate the transform to fit the given group to the set of reference coordinates.
8693 * The reference coordinates ref is given as either a frame number, an array of
8694 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8695 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8696 * Return values are the transform (that converts the present coordinates to the
8697 * target coordinates) and root mean square deviation (without weight).
8700 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8704 VALUE gval, rval, wval;
8706 IntGroupIterator iter;
8707 int nn, errnum, i, j, in, status;
8709 Double *weights, dval[3];
8712 Data_Get_Struct(self, Molecule, mol);
8713 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8715 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8717 ig = s_Molecule_AtomGroupFromValue(self, gval);
8718 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8719 IntGroupRelease(ig);
8720 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8722 ref = (Vector *)calloc(sizeof(Vector), nn);
8723 weights = (Double *)calloc(sizeof(Double), nn);
8724 IntGroupIteratorInit(ig, &iter);
8725 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8726 int fn = NUM2INT(rb_Integer(rval));
8727 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8732 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8733 ap = ATOM_AT_INDEX(mol->atoms, in);
8734 if (fn < ap->nframes)
8735 ref[i] = ap->frames[fn];
8736 else ref[i] = ap->r;
8738 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8739 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8740 if (m->row * m->column < nn * 3) {
8744 for (i = 0; i < nn; i++) {
8745 ref[i].x = m->data[i * 3];
8746 ref[i].y = m->data[i * 3 + 1];
8747 ref[i].z = m->data[i * 3 + 2];
8751 rval = rb_protect(rb_ary_to_ary, rval, &status);
8756 if (RARRAY_LEN(rval) < nn) {
8760 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8761 /* Array of 3*nn numbers */
8762 if (RARRAY_LEN(rval) < nn * 3) {
8766 for (i = 0; i < nn; i++) {
8767 for (j = 0; j < 3; j++) {
8768 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8773 dval[j] = NUM2DBL(aval);
8780 /* Array of nn Vector3Ds or Arrays */
8781 for (i = 0; i < nn; i++) {
8782 aval = (RARRAY_PTR(rval))[i];
8783 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8784 VectorFromValue(aval, &ref[i]);
8786 aval = rb_protect(rb_ary_to_ary, aval, &status);
8791 if (RARRAY_LEN(aval) < 3) {
8796 for (j = 0; j < 3; j++) {
8797 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8802 dval[j] = NUM2DBL(aaval);
8812 /* Use atomic weights */
8813 IntGroupIteratorReset(&iter);
8814 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8815 ap = ATOM_AT_INDEX(mol->atoms, in);
8816 weights[i] = ap->weight;
8819 wval = rb_protect(rb_ary_to_ary, wval, &status);
8824 if (RARRAY_LEN(wval) < nn) {
8828 for (i = 0; i < nn; i++) {
8829 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8834 weights[i] = NUM2DBL(wwval);
8837 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8844 IntGroupIteratorRelease(&iter);
8848 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8849 } else if (errnum == 1) {
8850 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8851 } else if (errnum == 2) {
8852 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8853 } else if (errnum == 3) {
8854 rb_jump_tag(status);
8855 } else if (errnum == 4) {
8856 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8857 } else if (errnum == 5) {
8858 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8859 } else if (errnum == 6) {
8860 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8862 return Qnil; /* Not reached */
8865 #pragma mark ------ Screen Display ------
8871 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8874 s_Molecule_Display(VALUE self)
8877 Data_Get_Struct(self, Molecule, mol);
8878 if (mol->mview != NULL)
8879 MainViewCallback_display(mol->mview);
8887 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8890 s_Molecule_MakeFront(VALUE self)
8893 Data_Get_Struct(self, Molecule, mol);
8894 if (mol->mview != NULL)
8895 MainViewCallback_makeFront(mol->mview);
8901 * update_enabled? -> bool
8903 * Returns true if screen update is enabled; otherwise no.
8906 s_Molecule_UpdateEnabled(VALUE self)
8909 Data_Get_Struct(self, Molecule, mol);
8910 if (mol->mview != NULL && !mol->mview->freezeScreen)
8917 * update_enabled = bool
8919 * Enable or disable screen update. This is effective for automatic update on modification.
8920 * Explicit call to molecule.display() always updates the screen.
8923 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8926 Data_Get_Struct(self, Molecule, mol);
8927 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8928 if (mol->mview != NULL)
8929 mol->mview->freezeScreen = (val == Qfalse);
8937 * show_unitcell(bool)
8938 * show_unitcell = bool
8940 * Set the flag whether to show the unit cell. If no argument is given, the
8941 * current flag is returned.
8944 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8947 Data_Get_Struct(self, Molecule, mol);
8948 if (mol->mview == NULL)
8951 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8952 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8954 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8960 * show_hydrogens(bool)
8961 * show_hydrogens = bool
8963 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8964 * current flag is returned.
8967 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8970 Data_Get_Struct(self, Molecule, mol);
8971 if (mol->mview == NULL)
8974 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8975 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8977 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8983 * show_dummy_atoms(bool)
8984 * show_dummy_atoms = bool
8986 * Set the flag whether to show the dummy atoms. If no argument is given, the
8987 * current flag is returned.
8990 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8993 Data_Get_Struct(self, Molecule, mol);
8994 if (mol->mview == NULL)
8997 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8998 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9000 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9006 * show_expanded(bool)
9007 * show_expanded = bool
9009 * Set the flag whether to show the expanded atoms. If no argument is given, the
9010 * current flag is returned.
9013 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9016 Data_Get_Struct(self, Molecule, mol);
9017 if (mol->mview == NULL)
9020 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9021 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9023 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9029 * show_ellipsoids(bool)
9030 * show_ellipsoids = bool
9032 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9033 * current flag is returned.
9036 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9039 Data_Get_Struct(self, Molecule, mol);
9040 if (mol->mview == NULL)
9043 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9044 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9046 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9051 * is_atom_visible(index) -> Boolean
9053 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9054 * as well as the molecule attributes (showHydrogens, etc.)
9057 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9062 Data_Get_Struct(self, Molecule, mol);
9063 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9064 if (idx < 0 || idx >= mol->natoms)
9066 ap = ATOM_AT_INDEX(mol->atoms, idx);
9067 if (mol->mview != NULL) {
9068 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9070 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9072 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9075 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9080 * hidden_atoms -> IntGroup
9082 * Returns the currently hidden atoms.
9085 s_Molecule_HiddenAtoms(VALUE self)
9087 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9088 return Qnil; /* Not reached */
9093 * set_hidden_atoms(IntGroup)
9094 * self.hidden_atoms = IntGroup
9096 * Hide the specified atoms. This operation is _not_ undoable.
9099 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9101 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9102 return Qnil; /* Not reached */
9107 * show_graphite -> Integer
9108 * show_graphite = Integer
9109 * show_graphite = boolean
9111 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9112 * number of rings to display for each direction.
9113 * If the argument is boolean, only the show/hide flag is set.
9116 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9119 Data_Get_Struct(self, Molecule, mol);
9120 if (mol->mview == NULL)
9123 if (argv[0] == Qnil || argv[0] == Qfalse)
9124 mol->mview->showGraphiteFlag = 0;
9125 else if (argv[0] == Qtrue)
9126 mol->mview->showGraphiteFlag = 1;
9128 int n = NUM2INT(rb_Integer(argv[0]));
9130 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9131 mol->mview->showGraphite = n;
9133 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9135 return INT2NUM(mol->mview->showGraphite);
9140 * show_graphite? -> boolean
9142 * Return whether the graphite is set visible or not.
9145 s_Molecule_ShowGraphiteFlag(VALUE self)
9148 Data_Get_Struct(self, Molecule, mol);
9149 if (mol->mview == NULL)
9151 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9156 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9157 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9158 * show_periodic_image = boolean
9160 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9161 * set but no visual effects are observed.
9162 * If the argument is boolean, only the show/hide flag is modified.
9165 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9171 Data_Get_Struct(self, Molecule, mol);
9172 if (mol->mview == NULL)
9174 rb_scan_args(argc, argv, "01", &val);
9176 /* Change current settings */
9177 if (val == Qnil || val == Qfalse)
9178 mol->mview->showPeriodicImageFlag = 0;
9179 else if (val == Qtrue)
9180 mol->mview->showPeriodicImageFlag = 1;
9182 val = rb_ary_to_ary(val);
9183 for (i = 0; i < 6; i++) {
9184 if (i < RARRAY_LEN(val))
9185 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9187 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9188 rb_raise(rb_eMolbyError, "bad arguments");
9189 for (i = 0; i < 6; i++)
9190 mol->mview->showPeriodicImage[i] = ival[i];
9192 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9195 for (i = 0; i < 6; i++)
9196 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9202 * show_periodic_image? -> boolean
9204 * Return whether the periodic images are set to visible or not. This flag is
9205 * independent from the show_periodic_image settings.
9208 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9211 Data_Get_Struct(self, Molecule, mol);
9212 if (mol->mview == NULL)
9214 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9219 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9220 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9221 * show_rotation_center = boolean
9223 * Set to show the rotation center of the screen.
9224 * If the argument is boolean, only the show/hide flag is modified.
9227 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9230 Data_Get_Struct(self, Molecule, mol);
9231 if (mol->mview == NULL)
9234 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9235 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9237 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9246 * Set the flag whether to draw the model in line mode. If no argument is given, the
9247 * current flag is returned.
9250 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9253 Data_Get_Struct(self, Molecule, mol);
9254 if (mol->mview == NULL)
9257 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9258 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9260 return (mol->mview->lineMode ? Qtrue : Qfalse);
9265 * atom_radius = float
9268 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9270 * If no argument is given, the current value is returned.
9273 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9276 Data_Get_Struct(self, Molecule, mol);
9277 if (mol->mview == NULL)
9280 double rad = NUM2DBL(rb_Float(argv[0]));
9282 mol->mview->atomRadius = rad;
9283 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9287 return rb_float_new(mol->mview->atomRadius);
9292 * bond_radius = float
9295 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9296 * If no argument is given, the current value is returned.
9299 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9302 Data_Get_Struct(self, Molecule, mol);
9303 if (mol->mview == NULL)
9306 double rad = NUM2DBL(rb_Float(argv[0]));
9308 mol->mview->bondRadius = rad;
9309 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9313 return rb_float_new(mol->mview->bondRadius);
9318 * atom_resolution = integer
9321 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9322 * (Default = 12; minimum = 6)
9323 * If no argument is given, the current value is returned.
9326 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9329 Data_Get_Struct(self, Molecule, mol);
9330 if (mol->mview == NULL)
9333 int res = NUM2INT(rb_Integer(argv[0]));
9335 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9336 mol->mview->atomResolution = res;
9337 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9338 return INT2NUM(res);
9340 return INT2NUM(mol->mview->atomResolution);
9345 * bond_resolution = integer
9348 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9349 * (Default = 8; minimum = 4)
9350 * If no argument is given, the current value is returned.
9353 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9356 Data_Get_Struct(self, Molecule, mol);
9357 if (mol->mview == NULL)
9360 int res = NUM2INT(rb_Integer(argv[0]));
9362 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9363 mol->mview->bondResolution = res;
9364 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9365 return INT2NUM(res);
9367 return INT2NUM(mol->mview->bondResolution);
9374 * Resize the model drawing to fit in the window.
9377 s_Molecule_ResizeToFit(VALUE self)
9380 Data_Get_Struct(self, Molecule, mol);
9381 if (mol->mview != NULL)
9382 MainView_resizeToFit(mol->mview);
9388 * get_view_rotation -> [[ax, ay, az], angle]
9390 * Get the current rotation for the view. Angle is in degree, not radian.
9393 s_Molecule_GetViewRotation(VALUE self)
9398 Data_Get_Struct(self, Molecule, mol);
9399 if (mol->mview == NULL)
9401 TrackballGetRotate(mol->mview->track, f);
9402 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9406 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9411 * get_view_scale -> float
9413 * Get the current scale for the view.
9416 s_Molecule_GetViewScale(VALUE self)
9419 Data_Get_Struct(self, Molecule, mol);
9420 if (mol->mview == NULL)
9422 return rb_float_new(TrackballGetScale(mol->mview->track));
9427 * get_view_center -> Vector
9429 * Get the current center point of the view.
9432 s_Molecule_GetViewCenter(VALUE self)
9437 Data_Get_Struct(self, Molecule, mol);
9438 if (mol->mview == NULL)
9440 TrackballGetTranslate(mol->mview->track, f);
9441 v.x = -f[0] * mol->mview->dimension;
9442 v.y = -f[1] * mol->mview->dimension;
9443 v.z = -f[2] * mol->mview->dimension;
9444 return ValueFromVector(&v);
9449 * set_view_rotation([ax, ay, az], angle) -> self
9451 * Set the current rotation for the view. Angle is in degree, not radian.
9454 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9459 Data_Get_Struct(self, Molecule, mol);
9460 if (mol->mview == NULL)
9462 VectorFromValue(aval, &v);
9463 if (NormalizeVec(&v, &v))
9464 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9468 f[0] = -NUM2DBL(rb_Float(angval));
9469 TrackballSetRotate(mol->mview->track, f);
9470 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9476 * set_view_scale(scale) -> self
9478 * Set the current scale for the view.
9481 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9484 Data_Get_Struct(self, Molecule, mol);
9485 if (mol->mview == NULL)
9487 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9488 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9494 * set_view_center(vec) -> self
9496 * Set the current center point of the view.
9499 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9504 Data_Get_Struct(self, Molecule, mol);
9505 if (mol->mview == NULL)
9507 VectorFromValue(aval, &v);
9508 f[0] = -v.x / mol->mview->dimension;
9509 f[1] = -v.y / mol->mview->dimension;
9510 f[2] = -v.z / mol->mview->dimension;
9511 TrackballSetTranslate(mol->mview->track, f);
9512 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9518 * set_background_color(red, green, blue)
9520 * Set the background color of the model window.
9523 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9526 Data_Get_Struct(self, Molecule, mol);
9527 if (mol->mview != NULL) {
9528 VALUE rval, gval, bval;
9529 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9530 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9537 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9539 * Export the current graphic to a PNG or TIF file (determined by the extension).
9540 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9541 * If either width or height is not specified, then the screen width/height is used instead.
9544 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9547 VALUE fval, sval, bval, wval, hval;
9550 int bg_color, width, height;
9551 Data_Get_Struct(self, Molecule, mol);
9552 if (mol->mview == NULL)
9553 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9554 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9555 fname = FileStringValuePtr(fval);
9558 else scale = NUM2DBL(rb_Float(sval));
9561 else bg_color = NUM2INT(rb_Integer(bval));
9564 else width = NUM2INT(rb_Integer(wval));
9567 else height = NUM2INT(rb_Integer(hval));
9568 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9573 #pragma mark ------ Graphics ------
9576 s_CalculateGraphicNormals(MainViewGraphic *gp)
9580 if (gp == NULL || gp->npoints < 3)
9582 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9583 v1.x = gp->points[3] - gp->points[0];
9584 v1.y = gp->points[4] - gp->points[1];
9585 v1.z = gp->points[5] - gp->points[2];
9586 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9587 for (i = 2; i < gp->npoints; i++) {
9588 v2.x = gp->points[i * 3] - gp->points[0];
9589 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9590 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9591 VecCross(v3, v1, v2);
9592 NormalizeVec(&v3, &v3);
9593 gp->normals[i * 3] = v3.x;
9594 gp->normals[i * 3 + 1] = v3.y;
9595 gp->normals[i * 3 + 2] = v3.z;
9598 /* normals[0] = average of all nv[i] (i=2..n-1) */
9600 for (i = 2; i < gp->npoints; i++) {
9601 v1.x += gp->normals[i * 3];
9602 v1.y += gp->normals[i * 3 + 1];
9603 v1.z += gp->normals[i * 3 + 2];
9605 NormalizeVec(&v1, &v1);
9606 gp->normals[0] = v1.x;
9607 gp->normals[1] = v1.y;
9608 gp->normals[2] = v1.z;
9609 /* normals[1] = nv[2].normalize */
9610 v2.x = gp->normals[6];
9611 v2.y = gp->normals[7];
9612 v2.z = gp->normals[8];
9613 NormalizeVec(&v1, &v2);
9614 gp->normals[3] = v1.x;
9615 gp->normals[4] = v1.y;
9616 gp->normals[5] = v1.z;
9617 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9618 for (i = 2; i < gp->npoints; i++) {
9619 if (i == gp->npoints - 1)
9622 v3.x = gp->normals[i * 3 + 3];
9623 v3.y = gp->normals[i * 3 + 4];
9624 v3.z = gp->normals[i * 3 + 5];
9627 NormalizeVec(&v1, &v2);
9628 gp->normals[i * 3] = v1.x;
9629 gp->normals[i * 3 + 1] = v1.y;
9630 gp->normals[i * 3 + 2] = v1.z;
9637 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9639 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9640 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9641 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9642 * points: an array of Vectors
9646 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9652 VALUE kval, cval, pval, fval, ival;
9653 Data_Get_Struct(self, Molecule, mol);
9654 if (mol->mview == NULL)
9655 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9656 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9657 idx = NUM2INT(rb_Integer(ival));
9659 idx = mol->mview->ngraphics;
9660 else if (idx < 0 || idx > mol->mview->ngraphics)
9661 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9662 memset(&g, 0, sizeof(g));
9664 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9665 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9667 kval = rb_obj_as_string(kval);
9668 p = StringValuePtr(kval);
9669 if (strcmp(p, "line") == 0)
9670 g.kind = kMainViewGraphicLine;
9671 else if (strcmp(p, "poly") == 0)
9672 g.kind = kMainViewGraphicPoly;
9673 else if (strcmp(p, "cylinder") == 0)
9674 g.kind = kMainViewGraphicCylinder;
9675 else if (strcmp(p, "cone") == 0)
9676 g.kind = kMainViewGraphicCone;
9677 else if (strcmp(p, "ellipsoid") == 0)
9678 g.kind = kMainViewGraphicEllipsoid;
9679 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9681 g.closed = (RTEST(fval) ? 1 : 0);
9682 cval = rb_ary_to_ary(cval);
9683 n = RARRAY_LEN(cval);
9684 if (n < 3 || n >= 5)
9685 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9688 for (i = 0; i < n; i++)
9689 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9690 pval = rb_ary_to_ary(pval);
9691 n = RARRAY_LEN(pval);
9692 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9694 rb_raise(rb_eArgError, "no control points are given");
9696 case kMainViewGraphicLine:
9698 rb_raise(rb_eArgError, "the line object must have at least two control points");
9700 case kMainViewGraphicPoly:
9702 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9704 case kMainViewGraphicCylinder:
9705 case kMainViewGraphicCone:
9707 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9710 case kMainViewGraphicEllipsoid:
9714 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9717 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9718 for (i = 0; i < n; i++) {
9720 VALUE rval = RARRAY_PTR(pval)[i];
9722 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9723 /* The float argument can also be given as a vector (for simplify undo registration) */
9724 VectorFromValue(rval, &v);
9726 v.x = NUM2DBL(rb_Float(rval));
9730 VectorFromValue(rval, &v);
9732 g.points[i * 3] = v.x;
9733 g.points[i * 3 + 1] = v.y;
9734 g.points[i * 3 + 2] = v.z;
9736 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9738 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9739 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9740 g.points[7] = g.points[11] = g.points[3];
9742 if (g.kind == kMainViewGraphicPoly) {
9743 /* Calculate normals */
9744 s_CalculateGraphicNormals(&g);
9746 MainView_insertGraphic(mol->mview, idx, &g);
9751 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9752 MolActionCallback_registerUndo(mol, act);
9753 MolActionRelease(act);
9756 return INT2NUM(idx);
9761 * create_graphic(kind, color, points, fill = nil) -> integer
9763 * Create a new graphic object. The arguments are similar as insert_graphic.
9766 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9769 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9770 args[0] = INT2NUM(-1);
9771 return s_Molecule_InsertGraphic(argc + 1, args, self);
9776 * remove_graphic(index) -> integer
9778 * Remove a graphic object.
9781 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9785 Data_Get_Struct(self, Molecule, mol);
9786 if (mol->mview == NULL)
9787 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9788 i = NUM2INT(rb_Integer(ival));
9789 if (i < 0 || i >= mol->mview->ngraphics)
9790 rb_raise(rb_eArgError, "graphic index is out of range");
9792 /* Prepare data for undo */
9793 MainViewGraphic *gp;
9798 gp = mol->mview->graphics + i;
9799 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9800 for (n = 0; n < gp->npoints; n++) {
9801 vp[n].x = gp->points[n * 3];
9802 vp[n].y = gp->points[n * 3 + 1];
9803 vp[n].z = gp->points[n * 3 + 2];
9805 col[0] = gp->rgba[0];
9806 col[1] = gp->rgba[1];
9807 col[2] = gp->rgba[2];
9808 col[3] = gp->rgba[3];
9809 if (gp->visible == 0) {
9810 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9811 MolActionCallback_registerUndo(mol, act);
9812 MolActionRelease(act);
9814 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9815 MolActionCallback_registerUndo(mol, act);
9817 MolActionRelease(act);
9819 MainView_removeGraphic(mol->mview, i);
9825 * ngraphics -> integer
9827 * Get the number of graphic objects.
9830 s_Molecule_NGraphics(VALUE self)
9833 Data_Get_Struct(self, Molecule, mol);
9834 if (mol->mview == NULL)
9835 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9836 return INT2NUM(mol->mview->ngraphics);
9841 * get_graphic_point(graphic_index, point_index) -> value
9842 * get_graphic_points(graphic_index) -> values
9844 * Get the point_index-th control point of graphic_index-th graphic object.
9845 * Get an array of all control points with the given values.
9849 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9851 MainViewGraphic *gp;
9856 Data_Get_Struct(self, Molecule, mol);
9857 if (mol->mview == NULL)
9858 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9859 rb_scan_args(argc, argv, "11", &gval, &pval);
9860 index = NUM2INT(rb_Integer(gval));
9861 if (index < 0 || index >= mol->mview->ngraphics)
9862 rb_raise(rb_eArgError, "the graphic index is out of range");
9863 gp = mol->mview->graphics + index;
9865 pindex = NUM2INT(rb_Integer(pval));
9866 if (pindex < 0 || pindex >= gp->npoints)
9867 rb_raise(rb_eArgError, "the point index is out of range");
9868 v.x = gp->points[pindex * 3];
9869 v.y = gp->points[pindex * 3 + 1];
9870 v.z = gp->points[pindex * 3 + 2];
9871 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9872 return rb_float_new(v.x);
9874 return ValueFromVector(&v);
9877 pval = rb_ary_new();
9878 for (pindex = 0; pindex < gp->npoints; pindex++) {
9879 v.x = gp->points[pindex * 3];
9880 v.y = gp->points[pindex * 3 + 1];
9881 v.z = gp->points[pindex * 3 + 2];
9882 rb_ary_push(pval, ValueFromVector(&v));
9890 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9891 * set_graphic_points(graphic_index, new_values) -> new_values
9893 * Change the point_index-th control point of graphic_index-th graphic object.
9894 * Replace the control points with the given values.
9898 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9900 MainViewGraphic *gp;
9904 VALUE gval, pval, nval;
9906 Data_Get_Struct(self, Molecule, mol);
9907 if (mol->mview == NULL)
9908 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9909 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9910 index = NUM2INT(rb_Integer(gval));
9911 if (index < 0 || index >= mol->mview->ngraphics)
9912 rb_raise(rb_eArgError, "the graphic index is out of range");
9913 gp = mol->mview->graphics + index;
9915 pindex = NUM2INT(rb_Integer(pval));
9916 if (pindex < 0 || pindex >= gp->npoints)
9917 rb_raise(rb_eArgError, "the point index is out of range");
9918 v0.x = gp->points[pindex * 3];
9919 v0.y = gp->points[pindex * 3 + 1];
9920 v0.z = gp->points[pindex * 3 + 2];
9921 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9922 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9923 v.x = NUM2DBL(rb_Float(nval));
9925 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9926 v.x = NUM2DBL(rb_Float(nval));
9928 gp->points[7] = gp->points[11] = v.x;
9929 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9930 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9933 v.x = kInvalidFloat;
9935 } else VectorFromValue(nval, &v);
9937 gp->points[pindex * 3] = v.x;
9938 gp->points[pindex * 3 + 1] = v.y;
9939 gp->points[pindex * 3 + 2] = v.z;
9940 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9944 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9945 for (pindex = 0; pindex < gp->npoints; pindex++) {
9946 vp[pindex].x = gp->points[pindex * 3];
9947 vp[pindex].y = gp->points[pindex * 3 + 1];
9948 vp[pindex].z = gp->points[pindex * 3 + 2];
9950 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9952 pval = rb_ary_to_ary(pval);
9953 len = RARRAY_LEN(pval);
9954 if (gp->npoints < len) {
9955 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9957 } else if (gp->npoints > len) {
9960 case kMainViewGraphicLine: len2 = 2; break;
9961 case kMainViewGraphicPoly: len2 = 3; break;
9962 case kMainViewGraphicCylinder: len2 = 3; break;
9963 case kMainViewGraphicCone: len2 = 3; break;
9964 case kMainViewGraphicEllipsoid: len2 = 4; break;
9970 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9971 aval = RARRAY_PTR(pval)[pindex];
9972 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9973 v.x = NUM2DBL(rb_Float(aval));
9975 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9976 v.x = NUM2DBL(rb_Float(aval));
9978 gp->points[7] = gp->points[11] = v.x;
9979 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9981 } else VectorFromValue(aval, &v);
9982 gp->points[pindex * 3] = v.x;
9983 gp->points[pindex * 3 + 1] = v.y;
9984 gp->points[pindex * 3 + 2] = v.z;
9987 if (gp->kind == kMainViewGraphicPoly) {
9988 /* Calculate normals */
9989 s_CalculateGraphicNormals(gp);
9991 MolActionCallback_registerUndo(mol, act);
9992 MolActionRelease(act);
9993 MoleculeCallback_notifyModification(mol, 0);
9999 * get_graphic_color(graphic_index) -> value
10001 * Get the color of graphic_index-th graphic object
10004 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10006 MainViewGraphic *gp;
10009 Data_Get_Struct(self, Molecule, mol);
10010 if (mol->mview == NULL)
10011 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10012 index = NUM2INT(rb_Integer(gval));
10013 if (index < 0 || index >= mol->mview->ngraphics)
10014 rb_raise(rb_eArgError, "the graphic index is out of range");
10015 gp = mol->mview->graphics + index;
10016 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]));
10021 * set_graphic_color(graphic_index, new_value) -> new_value
10023 * Change the color of graphic_index-th graphic object
10027 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10029 MainViewGraphic *gp;
10034 Data_Get_Struct(self, Molecule, mol);
10035 if (mol->mview == NULL)
10036 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10037 index = NUM2INT(rb_Integer(gval));
10038 if (index < 0 || index >= mol->mview->ngraphics)
10039 rb_raise(rb_eArgError, "the graphic index is out of range");
10040 gp = mol->mview->graphics + index;
10041 for (i = 0; i < 4; i++)
10042 c[i] = gp->rgba[i];
10043 cval = rb_ary_to_ary(cval);
10044 n = RARRAY_LEN(cval);
10045 if (n != 3 && n != 4)
10046 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10048 for (i = 0; i < n; i++) {
10049 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10053 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10054 MolActionCallback_registerUndo(mol, act);
10055 MolActionRelease(act);
10056 MoleculeCallback_notifyModification(mol, 0);
10062 * show_graphic(graphic_index) -> self
10064 * Enable the visible flag of the graphic_index-th graphic object
10068 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10070 MainViewGraphic *gp;
10073 Data_Get_Struct(self, Molecule, mol);
10074 if (mol->mview == NULL)
10075 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10076 index = NUM2INT(rb_Integer(gval));
10077 if (index < 0 || index >= mol->mview->ngraphics)
10078 rb_raise(rb_eArgError, "the graphic index is out of range");
10079 gp = mol->mview->graphics + index;
10081 MoleculeCallback_notifyModification(mol, 0);
10087 * hide_graphic(graphic_index) -> self
10089 * Disable the visible flag of the graphic_index-th graphic object
10093 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10095 MainViewGraphic *gp;
10098 Data_Get_Struct(self, Molecule, mol);
10099 if (mol->mview == NULL)
10100 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10101 index = NUM2INT(rb_Integer(gval));
10102 if (index < 0 || index >= mol->mview->ngraphics)
10103 rb_raise(rb_eArgError, "the graphic index is out of range");
10104 gp = mol->mview->graphics + index;
10106 MoleculeCallback_notifyModification(mol, 0);
10112 * show_text(string)
10114 * Show the string in the info text box.
10117 s_Molecule_ShowText(VALUE self, VALUE arg)
10120 Data_Get_Struct(self, Molecule, mol);
10121 if (mol->mview != NULL)
10122 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10126 #pragma mark ------ MD Support ------
10130 * md_arena -> MDArena
10132 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10133 * this molecule, a new arena is created.
10136 s_Molecule_MDArena(VALUE self)
10140 Data_Get_Struct(self, Molecule, mol);
10141 if (mol->arena == NULL)
10143 retval = ValueFromMDArena(mol->arena);
10149 * set_parameter_attr(type, index, key, value, src) -> value
10151 * This method is used only internally.
10154 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10156 /* This method is called from MolAction to change a MM parameter attribute. */
10159 ParameterRef *pref;
10161 Data_Get_Struct(self, Molecule, mol);
10162 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10163 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10165 /* This is the special part of this method; it allows modification of the src field. */
10166 /* (ParameterRef#set_attr sets 0 to the src field) */
10167 Data_Get_Struct(pval, ParameterRef, pref);
10168 up = ParameterRefGetPar(pref);
10169 up->bond.src = FIX2INT(sval);
10176 * parameter -> Parameter
10178 * Get the local parameter of this molecule. If not defined, returns nil.
10181 s_Molecule_Parameter(VALUE self)
10184 Data_Get_Struct(self, Molecule, mol);
10185 /* if (mol->par == NULL)
10187 return s_NewParameterValueFromValue(self);
10192 * start_step -> Integer
10194 * Returns the start step (defined by dcd format).
10197 s_Molecule_StartStep(VALUE self)
10200 Data_Get_Struct(self, Molecule, mol);
10201 return INT2NUM(mol->startStep);
10206 * start_step = Integer
10208 * Set the start step (defined by dcd format).
10211 s_Molecule_SetStartStep(VALUE self, VALUE val)
10214 Data_Get_Struct(self, Molecule, mol);
10215 mol->startStep = NUM2INT(rb_Integer(val));
10221 * steps_per_frame -> Integer
10223 * Returns the number of steps between frames (defined by dcd format).
10226 s_Molecule_StepsPerFrame(VALUE self)
10229 Data_Get_Struct(self, Molecule, mol);
10230 return INT2NUM(mol->stepsPerFrame);
10235 * steps_per_frame = Integer
10237 * Set the number of steps between frames (defined by dcd format).
10240 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10243 Data_Get_Struct(self, Molecule, mol);
10244 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10250 * ps_per_step -> Float
10252 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10255 s_Molecule_PsPerStep(VALUE self)
10258 Data_Get_Struct(self, Molecule, mol);
10259 return rb_float_new(mol->psPerStep);
10264 * ps_per_step = Float
10266 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10269 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10272 Data_Get_Struct(self, Molecule, mol);
10273 mol->psPerStep = NUM2DBL(rb_Float(val));
10278 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10280 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.");
10283 #pragma mark ------ MO Handling ------
10287 * selectedMO -> IntGroup
10289 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10290 * is not selected, returns nil. If the MO info table is selected but no MOs
10291 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10294 s_Molecule_SelectedMO(VALUE self)
10299 Data_Get_Struct(self, Molecule, mol);
10300 if (mol->mview == NULL)
10302 ig = MainView_selectedMO(mol->mview);
10305 IntGroupOffset(ig, 1);
10306 val = ValueFromIntGroup(ig);
10307 IntGroupRelease(ig);
10313 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10315 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10316 * If the molecule does not contain a basis set information, then returns nil.
10319 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10322 Vector o, dx, dy, dz;
10325 Int npoints = 80 * 80 * 80;
10326 Data_Get_Struct(self, Molecule, mol);
10327 if (mol->bset == NULL)
10329 rb_scan_args(argc, argv, "01", &nval);
10331 npoints = NUM2INT(rb_Integer(nval));
10332 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10334 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));
10338 s_Cubegen_callback(double progress, void *ref)
10340 MyAppCallback_setProgressValue(progress);
10341 if (MyAppCallback_checkInterrupt())
10348 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10349 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10351 * Calculate the molecular orbital with number mo and create a 'cube' file.
10352 * In the first form, the cube size is estimated from the atomic coordinates. In the
10353 * second form, the cube dimension is explicitly given.
10354 * Returns fname when successful, nil otherwise.
10355 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10356 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10357 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10360 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10362 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10364 Int mono, nx, ny, nz, npoints;
10365 Vector o, dx, dy, dz;
10368 Data_Get_Struct(self, Molecule, mol);
10369 if (mol->bset == NULL)
10370 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10371 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10373 /* Set up parameters */
10374 mono = NUM2INT(rb_Integer(mval));
10375 if (mono < 0 || mono > mol->bset->ncomps)
10376 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);
10378 if (mol->bset->rflag != 0)
10379 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10380 mono += mol->bset->ncomps;
10383 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10384 /* Automatic grid formation */
10386 npoints = NUM2INT(rb_Integer(oval));
10390 else if (npoints < 8)
10391 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10392 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10393 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10397 VectorFromValue(oval, &o);
10398 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10399 VectorFromValue(dxval, &dx);
10401 dx.x = NUM2DBL(rb_Float(dxval));
10404 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10405 VectorFromValue(dyval, &dy);
10407 dy.y = NUM2DBL(rb_Float(dyval));
10410 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10411 VectorFromValue(dzval, &dz);
10413 dz.z = NUM2DBL(rb_Float(dzval));
10416 nx = NUM2INT(rb_Integer(nxval));
10417 ny = NUM2INT(rb_Integer(nyval));
10418 nz = NUM2INT(rb_Integer(nzval));
10419 if (nx <= 0 || ny <= 0 || nz <= 0)
10420 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10421 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10422 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);
10426 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10429 else if (index < 0)
10430 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10432 /* Output to file */
10433 MoleculeCallback_displayName(mol, buf, sizeof buf);
10434 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10436 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10438 /* Discard the cube */
10439 MoleculeClearCubeAtIndex(mol, index);
10447 * Clear the MO surface if present.
10450 s_Molecule_ClearSurface(VALUE self)
10453 Data_Get_Struct(self, Molecule, mol);
10454 if (mol->mcube != NULL)
10455 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10463 * Hide the MO surface if present.
10466 s_Molecule_HideSurface(VALUE self)
10469 Data_Get_Struct(self, Molecule, mol);
10470 if (mol->mcube != NULL) {
10471 mol->mcube->hidden = 1;
10472 MoleculeCallback_notifyModification(mol, 0);
10481 * Show the MO surface if present.
10484 s_Molecule_ShowSurface(VALUE self)
10487 Data_Get_Struct(self, Molecule, mol);
10488 if (mol->mcube != NULL) {
10489 mol->mcube->hidden = 0;
10490 MoleculeCallback_notifyModification(mol, 0);
10497 * create_surface(mo, attr = nil)
10499 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10500 * then it denotes the beta orbital.
10501 * If mo is nil, then the attributes of the current surface are modified.
10503 * :npoints : the approximate number of grid points
10504 * :expand : the scale factor to expand/shrink the display box size for each atom,
10505 * :thres : the threshold for the isovalue surface
10506 * If the molecule does not contain MO information, raises exception.
10509 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10512 Vector o, dx, dy, dz;
10513 Int nmo, nx, ny, nz, i;
10514 Int need_recalc = 0;
10515 VALUE nval, hval, aval;
10520 Data_Get_Struct(self, Molecule, mol);
10521 rb_scan_args(argc, argv, "11", &nval, &hval);
10522 if (mol->bset == NULL)
10523 rb_raise(rb_eMolbyError, "No MO information is given");
10524 if (nval == Qnil) {
10526 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10527 nmo = mol->bset->nmos + 1;
10529 nmo = NUM2INT(rb_Integer(nval));
10530 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10531 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);
10533 nmo = -nmo + mol->bset->ncomps;
10535 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10536 npoints = NUM2INT(rb_Integer(aval));
10538 } else if (mol->mcube != NULL) {
10539 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10540 } else npoints = 80 * 80 * 80;
10541 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10542 expand = NUM2DBL(rb_Float(aval));
10543 } else if (mol->mcube != NULL) {
10544 expand = mol->mcube->expand;
10545 } else expand = 1.0;
10546 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10547 thres = NUM2DBL(rb_Float(aval));
10548 } else if (mol->mcube != NULL) {
10549 thres = mol->mcube->thres;
10550 } else thres = 0.05;
10551 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10552 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10553 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10554 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10555 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10557 for (nx = 0; nx < 2; nx++) {
10558 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10559 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10560 aval = rb_ary_to_ary(aval);
10561 if (RARRAY_LEN(aval) < 3) {
10563 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10565 for (i = 0; i < 4; i++)
10566 d[i] = mol->mcube->c[nx].rgba[i];
10567 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10568 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10569 if (d[i] < 0.0 && d[i] > 1.0)
10572 for (i = 0; i < 4; i++)
10573 mol->mcube->c[nx].rgba[i] = d[i];
10576 if (mol->mcube->expand != expand)
10578 mol->mcube->thres = thres;
10579 mol->mcube->expand = expand;
10581 if (mol->mcube->idn < 0)
10582 return self; /* Only set attributes for now */
10584 nmo = mol->mcube->idn; /* Force recalculation */
10586 if (MoleculeUpdateMCube(mol, nmo) != 0)
10587 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10593 * set_surface_attr(attr = nil)
10595 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10598 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10603 return s_Molecule_CreateSurface(2, args, self);
10610 * Get the number of electrostatic potential info.
10613 s_Molecule_NElpots(VALUE self)
10616 Data_Get_Struct(self, Molecule, mol);
10617 return INT2NUM(mol->nelpots);
10624 * Get the electrostatic potential info at the given index. If present, then the
10625 * return value is [Vector, Float] (position and potential). If not present, then
10629 s_Molecule_Elpot(VALUE self, VALUE ival)
10633 Data_Get_Struct(self, Molecule, mol);
10634 idx = NUM2INT(rb_Integer(ival));
10635 if (idx < 0 || idx >= mol->nelpots)
10637 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10644 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10645 * cube and marching cube information are discarded. This operation is _not_ undoable!
10648 s_Molecule_ClearBasisSet(VALUE self)
10651 Data_Get_Struct(self, Molecule, mol);
10653 if (mol->bset != NULL) {
10654 BasisSetRelease(mol->bset);
10657 if (mol->mcube != NULL) {
10658 MoleculeDeallocateMCube(mol->mcube);
10667 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10669 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10670 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10674 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10677 int sym, nprims, a_idx, n;
10678 Data_Get_Struct(self, Molecule, mol);
10679 a_idx = NUM2INT(rb_Integer(aval));
10680 sym = NUM2INT(rb_Integer(symval));
10681 nprims = NUM2INT(rb_Integer(npval));
10682 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10684 rb_raise(rb_eMolbyError, "Molecule is emptry");
10686 rb_raise(rb_eMolbyError, "Low memory");
10688 rb_raise(rb_eMolbyError, "Unknown orbital type");
10690 rb_raise(rb_eMolbyError, "Unknown error");
10696 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10698 * To be used internally. Add a gaussian primitive coefficients.
10701 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10705 Double exponent, contraction, contraction_sp;
10706 Data_Get_Struct(self, Molecule, mol);
10707 exponent = NUM2DBL(rb_Float(expval));
10708 contraction = NUM2DBL(rb_Float(cval));
10709 contraction_sp = NUM2DBL(rb_Float(cspval));
10710 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10712 rb_raise(rb_eMolbyError, "Molecule is emptry");
10714 rb_raise(rb_eMolbyError, "Low memory");
10716 rb_raise(rb_eMolbyError, "Unknown error");
10722 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10724 * Get the Gaussian shell information for the given MO coefficient index.
10725 * The symmetry code is the same as in add_gaussian_orbital_shell.
10726 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10727 * is the number of MO component belonging to this shell.
10730 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10735 Data_Get_Struct(self, Molecule, mol);
10736 if (mol->bset == NULL)
10737 rb_raise(rb_eMolbyError, "No basis set information is defined");
10738 s_idx = NUM2INT(rb_Integer(sval));
10739 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10741 sp = mol->bset->shells + s_idx;
10744 case kGTOType_S: sym = 0; break;
10745 case kGTOType_SP: sym = -1; break;
10746 case kGTOType_P: sym = 1; break;
10747 case kGTOType_D: sym = 2; break;
10748 case kGTOType_D5: sym = -2; break;
10749 case kGTOType_F: sym = 3; break;
10750 case kGTOType_F7: sym = -3; break;
10751 case kGTOType_G: sym = 4; break;
10752 case kGTOType_G9: sym = -4; break;
10754 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10756 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10761 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10763 * Get the Gaussian primitive coefficients for the given MO component.
10766 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10772 VALUE retval, aval;
10773 Data_Get_Struct(self, Molecule, mol);
10774 if (mol->bset == NULL)
10775 rb_raise(rb_eMolbyError, "No basis set information is defined");
10776 s_idx = NUM2INT(rb_Integer(sval));
10777 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10779 sp = mol->bset->shells + s_idx;
10780 pp = mol->bset->priminfos + sp->p_idx;
10781 retval = rb_ary_new2(sp->nprim);
10782 for (i = 0; i < sp->nprim; i++) {
10783 if (sp->sym == kGTOType_SP) {
10784 /* With P contraction coefficient */
10785 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10787 /* Without P contraction coefficient */
10788 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10790 rb_ary_store(retval, i, aval);
10797 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10799 * Get the Gaussian shell information for the given MO coefficient index.
10802 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10805 Int n, c, atom_idx, shell_idx;
10807 Data_Get_Struct(self, Molecule, mol);
10808 if (mol->bset == NULL)
10809 rb_raise(rb_eMolbyError, "No basis set information is defined");
10810 c = NUM2INT(rb_Integer(cval));
10811 if (c < 0 || c >= mol->bset->ncomps)
10813 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10815 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10816 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10821 * clear_mo_coefficients
10823 * Clear the existing MO coefficients.
10826 s_Molecule_ClearMOCoefficients(VALUE self)
10829 Data_Get_Struct(self, Molecule, mol);
10830 if (mol->bset != NULL) {
10831 if (mol->bset->moenergies != NULL) {
10832 free(mol->bset->moenergies);
10833 mol->bset->moenergies = NULL;
10835 if (mol->bset->mo != NULL) {
10836 free(mol->bset->mo);
10837 mol->bset->mo = NULL;
10839 mol->bset->nmos = 0;
10846 * set_mo_coefficients(idx, energy, coefficients)
10848 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10849 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10850 * Energy is the MO energy, and coefficients is an array
10851 * of MO coefficients.
10854 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10857 Int idx, ncomps, i;
10860 Data_Get_Struct(self, Molecule, mol);
10861 idx = NUM2INT(rb_Integer(ival));
10862 energy = NUM2DBL(rb_Float(eval));
10863 aval = rb_ary_to_ary(aval);
10864 ncomps = RARRAY_LEN(aval);
10865 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10866 if (coeffs == NULL) {
10870 for (i = 0; i < ncomps; i++)
10871 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10872 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10875 rb_raise(rb_eMolbyError, "Molecule is emptry");
10877 rb_raise(rb_eMolbyError, "Low memory");
10879 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10881 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10883 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10885 rb_raise(rb_eMolbyError, "Unknown error");
10891 * get_mo_coefficients(idx)
10893 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10896 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10899 Int idx, ncomps, n;
10903 Data_Get_Struct(self, Molecule, mol);
10904 idx = NUM2INT(rb_Integer(ival));
10907 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10909 rb_raise(rb_eMolbyError, "Molecule is emptry");
10911 rb_raise(rb_eMolbyError, "No basis set information is present");
10913 return Qnil; /* Silently returns nil */
10914 retval = rb_ary_new2(ncomps);
10915 for (n = 0; n < ncomps; n++)
10916 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10923 * get_mo_energy(idx)
10925 * To be used internally. Get the MO energy for the given MO index (1-based).
10928 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10933 Data_Get_Struct(self, Molecule, mol);
10934 idx = NUM2INT(rb_Integer(ival));
10935 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10937 rb_raise(rb_eMolbyError, "Molecule is emptry");
10939 rb_raise(rb_eMolbyError, "No basis set information is present");
10942 return rb_float_new(energy);
10945 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10948 s_InitMOInfoKeys(void)
10950 if (sTypeSym == 0) {
10951 sTypeSym = ID2SYM(rb_intern("type"));
10952 sAlphaSym = ID2SYM(rb_intern("alpha"));
10953 sBetaSym = ID2SYM(rb_intern("beta"));
10954 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10955 sNshellsSym = ID2SYM(rb_intern("nshells"));
10961 * set_mo_info(hash)
10963 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10964 * :alpha=>integer, :beta=>integer
10967 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10971 Int rflag, na, nb, n;
10973 Data_Get_Struct(self, Molecule, mol);
10974 if (mol->bset != NULL) {
10975 rflag = mol->bset->rflag;
10976 na = mol->bset->ne_alpha;
10977 nb = mol->bset->ne_beta;
10983 if (hval != Qnil) {
10984 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10985 s = StringValuePtr(aval);
10986 if (strcasecmp(s, "RHF") == 0)
10988 else if (strcasecmp(s, "UHF") == 0)
10990 else if (strcasecmp(s, "ROHF") == 0)
10993 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10994 n = NUM2INT(rb_Integer(aval));
10998 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10999 n = NUM2INT(rb_Integer(aval));
11003 MoleculeSetMOInfo(mol, rflag, na, nb);
11012 * Get the MO info. The key is as described in set_mo_info.
11013 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11016 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11019 Data_Get_Struct(self, Molecule, mol);
11020 if (mol->bset == NULL)
11022 if (kval == sTypeSym) {
11023 switch (mol->bset->rflag) {
11024 case 0: return Ruby_NewEncodedStringValue2("UHF");
11025 case 1: return Ruby_NewEncodedStringValue2("RHF");
11026 case 2: return Ruby_NewEncodedStringValue2("ROHF");
11027 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11029 } else if (kval == sAlphaSym) {
11030 return INT2NUM(mol->bset->ne_alpha);
11031 } else if (kval == sBetaSym) {
11032 return INT2NUM(mol->bset->ne_beta);
11033 } else if (kval == sNcompsSym) {
11034 return INT2NUM(mol->bset->ncomps);
11035 } else if (kval == sNshellsSym) {
11036 return INT2NUM(mol->bset->nshells);
11038 kval = rb_inspect(kval);
11039 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11040 return Qnil; /* Does not reach here */
11048 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11051 s_Molecule_MOType(VALUE self)
11053 return s_Molecule_GetMOInfo(self, sTypeSym);
11056 #pragma mark ------ Molecular Topology ------
11060 * search_equivalent_atoms(ig = nil)
11062 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
11065 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11071 Data_Get_Struct(self, Molecule, mol);
11072 if (mol->natoms == 0)
11074 rb_scan_args(argc, argv, "01", &val);
11076 ig = s_Molecule_AtomGroupFromValue(self, val);
11078 result = MoleculeSearchEquivalentAtoms(mol, ig);
11079 if (result == NULL)
11080 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11082 IntGroupRelease(ig);
11083 val = rb_ary_new2(mol->natoms);
11084 for (i = 0; i < mol->natoms; i++)
11085 rb_ary_push(val, INT2NUM(result[i]));
11092 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11094 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11095 * Name is the name of the new pi anchor, and group is the atoms that define
11096 * the pi system. Type (a String) is an atom type for MM implementation.
11097 * Weights represent the relative significance of the component atoms; if omitted, then
11098 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
11099 * The weight values will be normalized so that the sum of the weights is 1.0.
11100 * The weight values must be positive.
11101 * Index is the atom index where the created pi-anchor is inserted in the
11102 * atoms array; if omitted, the pi-anchor is inserted after the component atom
11103 * having the largest index.
11104 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
11105 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11108 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11113 Int i, n, idx, last_component;
11117 if (argc < 2 || argc >= 6)
11118 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11122 Data_Get_Struct(self, Molecule, mol);
11126 ig = s_Molecule_AtomGroupFromValue(self, gval);
11127 if (ig == NULL || IntGroupGetCount(ig) == 0)
11128 rb_raise(rb_eMolbyError, "atom group is not given correctly");
11129 memset(&a, 0, sizeof(a));
11130 memset(&an, 0, sizeof(an));
11131 strncpy(a.aname, StringValuePtr(nval), 4);
11132 if (a.aname[0] == '_')
11133 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11134 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11135 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11136 if (n >= mol->natoms) {
11137 AtomConnectResize(&an.connect, 0);
11138 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11140 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11141 last_component = n;
11143 if (an.connect.count == 0)
11144 rb_raise(rb_eMolbyError, "no atoms are specified");
11145 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11146 for (i = 0; i < an.connect.count; i++) {
11147 an.coeffs[i] = 1.0 / an.connect.count;
11149 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11151 if (argv[0] != Qnil)
11152 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11156 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11157 if (argv[0] != Qnil) {
11158 VALUE aval = rb_ary_to_ary(argv[0]);
11160 if (RARRAY_LEN(aval) != an.connect.count)
11161 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11162 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11163 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11165 rb_raise(rb_eMolbyError, "the weight value must be positive");
11169 for (i = 0; i < an.connect.count; i++)
11170 an.coeffs[i] /= sum;
11175 if (argc > 0 && argv[0] != Qnil) {
11177 idx = NUM2INT(rb_Integer(argv[0]));
11179 if (idx < 0 || idx > mol->natoms) {
11180 /* Immediately after the last specified atom */
11181 idx = last_component + 1;
11183 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11184 memmove(a.anchor, &an, sizeof(PiAnchor));
11185 /* Use residue information of the last specified atom */
11186 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11187 a.resSeq = ap->resSeq;
11188 strncpy(a.resName, ap->resName, 4);
11189 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11191 MoleculeCalculatePiAnchorPosition(mol, idx);
11192 aref = AtomRefNew(mol, idx);
11193 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11196 #pragma mark ------ Molecular Properties ------
11200 * set_property(name, value[, index]) -> value
11201 * set_property(name, values, group) -> values
11203 * Set molecular property. A property is a floating-point number with a specified name,
11204 * and can be set for each frame separately. The name of the property is given as a String.
11205 * The value can be a single floating point number, which is set to the current frame.
11209 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11212 VALUE nval, vval, ival;
11215 Int i, n, idx, fidx;
11217 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11218 Data_Get_Struct(self, Molecule, mol);
11219 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11220 idx = NUM2INT(rb_Integer(nval));
11221 if (idx < 0 || idx >= mol->nmolprops)
11222 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11224 name = StringValuePtr(nval);
11225 idx = MoleculeLookUpProperty(mol, name);
11227 idx = MoleculeCreateProperty(mol, name);
11229 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11232 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11234 fidx = mol->cframe;
11236 fidx = NUM2INT(rb_Integer(ival));
11237 n = MoleculeGetNumberOfFrames(mol);
11238 if (fidx < 0 || fidx >= n)
11239 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11241 ig = IntGroupNewWithPoints(fidx, 1, -1);
11242 dp = (Double *)malloc(sizeof(Double));
11243 *dp = NUM2DBL(rb_Float(vval));
11246 vval = rb_ary_to_ary(vval);
11247 ig = IntGroupFromValue(ival);
11248 n = IntGroupGetCount(ig);
11250 rb_raise(rb_eMolbyError, "No frames are specified");
11251 if (RARRAY_LEN(vval) < n)
11252 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11253 dp = (Double *)calloc(sizeof(Double), n);
11254 for (i = 0; i < n; i++)
11255 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11258 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11260 IntGroupRelease(ig);
11266 * get_property(name[, index]) -> value
11267 * get_property(name, group) -> values
11269 * Get molecular property. In the first form, a property value for a single frame is returned.
11270 * (If index is omitted, then the value for the current frame is given)
11271 * In the second form, an array of property values for the given frames is returned.
11272 * If name is not one of known properties or a valid index integer, exception is raised.
11275 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11281 Int i, n, idx, fidx;
11283 rb_scan_args(argc, argv, "11", &nval, &ival);
11284 Data_Get_Struct(self, Molecule, mol);
11285 if (mol->nmolprops == 0)
11286 rb_raise(rb_eMolbyError, "The molecule has no properties");
11287 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11288 idx = NUM2INT(rb_Integer(nval));
11289 if (idx < 0 || idx >= mol->nmolprops)
11290 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11292 name = StringValuePtr(nval);
11293 idx = MoleculeLookUpProperty(mol, name);
11295 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11297 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11299 fidx = mol->cframe;
11301 fidx = NUM2INT(rb_Integer(ival));
11302 n = MoleculeGetNumberOfFrames(mol);
11303 if (fidx < 0 || fidx >= n)
11304 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11306 ig = IntGroupNewWithPoints(fidx, 1, -1);
11307 ival = INT2FIX(fidx);
11310 ig = IntGroupFromValue(ival);
11311 n = IntGroupGetCount(ig);
11313 return rb_ary_new();
11315 dp = (Double *)calloc(sizeof(Double), n);
11316 MoleculeGetProperty(mol, idx, ig, dp);
11317 if (FIXNUM_P(ival))
11318 ival = rb_float_new(dp[0]);
11320 ival = rb_ary_new();
11321 for (i = n - 1; i >= 0; i--) {
11322 nval = rb_float_new(dp[i]);
11323 rb_ary_store(ival, i, nval);
11327 IntGroupRelease(ig);
11333 * property_names -> Array
11335 * Get an array of property names.
11338 s_Molecule_PropertyNames(VALUE self)
11343 Data_Get_Struct(self, Molecule, mol);
11344 rval = rb_ary_new();
11345 for (i = mol->nmolprops - 1; i >= 0; i--) {
11346 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11347 rb_ary_store(rval, i, nval);
11352 #pragma mark ------ Class methods ------
11356 * current -> Molecule
11358 * Get the currently "active" molecule.
11361 s_Molecule_Current(VALUE klass)
11363 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11368 * Molecule[] -> Molecule
11369 * Molecule[n] -> Molecule
11370 * Molecule[name] -> Molecule
11371 * Molecule[name, k] -> Molecule
11372 * Molecule[regex] -> Molecule
11373 * Molecule[regex, k] -> Molecule
11375 * Molecule[] is equivalent to Molecule.current.
11376 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11377 * Molecule[name] gives the first document (in the order of creation time) that has
11378 * the given name. If a second argument (k) is given, the k-th document that has the
11379 * given name is returned.
11380 * Molecule[regex] gives the first document (in the order of creation time) that
11381 * has a name matching the regular expression. If a second argument (k) is given,
11382 * the k-th document that has a name matching the re is returned.
11385 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11391 rb_scan_args(argc, argv, "02", &val, &kval);
11393 return s_Molecule_Current(klass);
11394 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11395 idx = NUM2INT(val);
11396 mol = MoleculeCallback_moleculeAtIndex(idx);
11397 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11398 char *p = StringValuePtr(val);
11399 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11400 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11401 MoleculeCallback_displayName(mol, buf, sizeof buf);
11402 if (strcmp(buf, p) == 0 && --k == 0)
11405 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11406 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11407 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11409 MoleculeCallback_displayName(mol, buf, sizeof buf);
11410 name = Ruby_NewEncodedStringValue2(buf);
11411 if (rb_reg_match(val, name) != Qnil && --k == 0)
11414 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11418 else return ValueFromMolecule(mol);
11423 * list -> array of Molecules
11425 * Get the list of molecules associated to the documents, in the order of creation
11426 * time of the document. If no document is open, returns an empry array.
11429 s_Molecule_List(VALUE klass)
11435 ary = rb_ary_new();
11436 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11437 rb_ary_push(ary, ValueFromMolecule(mol));
11445 * ordered_list -> array of Molecules
11447 * Get the list of molecules associated to the documents, in the order of front-to-back
11448 * ordering of the associated window. If no document is open, returns an empry array.
11451 s_Molecule_OrderedList(VALUE klass)
11457 ary = rb_ary_new();
11458 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11459 rb_ary_push(ary, ValueFromMolecule(mol));
11465 #pragma mark ------ Call Subprocess ------
11467 /* The callback functions for call_subprocess_async */
11469 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11472 VALUE procval, retval, args[2];
11473 args[0] = ValueFromMolecule(mol);
11474 args[1] = INT2NUM(status);
11475 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11476 if (procval != Qnil) {
11477 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11478 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11485 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11488 VALUE procval, retval, args[2];
11489 args[0] = ValueFromMolecule(mol);
11490 args[1] = INT2NUM(tcount);
11491 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11492 if (procval != Qnil) {
11493 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11494 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11502 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11504 * Call subprocess asynchronically.
11505 * If end_callback is given, it will be called (with two arguments self and termination status)
11506 * when the subprocess terminated.
11507 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11508 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11509 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11510 * filename begins with ">>", then the message will be appended to the file.
11511 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11512 * If the argument is nil, then the message will be sent to the Ruby console.
11513 * Returns the process ID as an integer.
11516 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11518 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11522 FILE *fpout, *fperr;
11523 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11524 Data_Get_Struct(self, Molecule, mol);
11526 if (stdout_val == Qnil) {
11529 sout = StringValuePtr(stdout_val);
11530 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11533 if (strncmp(sout, ">>", 2) == 0) {
11535 fpout = fopen(sout, "a");
11539 fpout = fopen(sout, "w");
11542 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11545 if (stderr_val == Qnil) {
11548 serr = StringValuePtr(stderr_val);
11549 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11552 if (strncmp(serr, ">>", 2) == 0) {
11554 fpout = fopen(serr, "a");
11558 fperr = fopen(serr, "w");
11561 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11565 /* Register procs as instance variables */
11566 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11567 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11568 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11569 if (fpout != NULL && fpout != (FILE *)1)
11571 if (fperr != NULL && fperr != (FILE *)1)
11576 #pragma mark ====== Define Molby Classes ======
11583 /* Define module Molby */
11584 rb_mMolby = rb_define_module("Molby");
11586 /* Define Vector3D, Transform, IntGroup */
11589 /* Define MDArena */
11590 Init_MolbyMDTypes();
11592 /* class Molecule */
11593 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11595 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11596 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11597 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11598 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11599 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11601 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11602 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11603 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11604 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11605 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11606 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11607 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11608 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11609 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11610 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11611 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11612 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11613 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11614 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11615 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11616 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11617 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11618 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11619 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11620 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11621 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11622 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11624 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11625 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11626 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11627 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11628 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11630 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11631 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11632 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11633 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11634 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11635 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11636 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11637 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11638 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11639 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11640 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11641 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11642 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11643 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11644 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11646 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11647 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11648 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11649 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11650 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11652 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11653 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11654 rb_define_alias(rb_cMolecule, "+", "add");
11655 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11656 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11657 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11658 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11659 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11660 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11661 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11662 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11663 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11664 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11665 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11666 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11667 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11668 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11669 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11670 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11671 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11672 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11673 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11674 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11675 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11677 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11678 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11679 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11680 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11681 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11683 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11684 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11685 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11686 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11687 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11688 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11689 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11690 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11691 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11693 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11694 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11695 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11696 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11697 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11698 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11699 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11700 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11701 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11702 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11703 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11704 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11705 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11706 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11707 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11708 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11709 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11710 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11711 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11712 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11713 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11714 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11716 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11717 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11718 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11719 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11720 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11721 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11722 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11724 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11725 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11726 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11727 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11728 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11729 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11730 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11731 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11732 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11733 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11734 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11735 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11736 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11738 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11739 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11740 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11741 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11742 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11743 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11745 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11746 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11747 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11748 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11749 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11750 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11751 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11752 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11753 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11754 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11755 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11756 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11757 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11758 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11759 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11760 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11761 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11762 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11763 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11764 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11765 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11766 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11767 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11768 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11769 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11770 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11771 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11772 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11773 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11774 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11775 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11776 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11777 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11778 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11779 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11780 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11781 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11782 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11783 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11784 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11785 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11786 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11787 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11788 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11789 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11790 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11791 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11792 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11793 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11794 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11795 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11796 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11797 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11798 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11800 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11801 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11802 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11803 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11804 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11805 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11806 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11807 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11808 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11809 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11810 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11811 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11812 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11814 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11815 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11816 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11817 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11818 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11819 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11820 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11821 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11822 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11823 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11824 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11825 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11826 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11827 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11829 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11830 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11831 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11832 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11833 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11834 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11835 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11836 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11837 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11838 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11839 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11840 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11841 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11842 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11843 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11844 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11845 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11846 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11847 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11848 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11849 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11850 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11851 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11853 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11854 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11856 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11857 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11858 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11860 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11861 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11862 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11863 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11865 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11867 /* class MolEnumerable */
11868 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11869 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11870 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11871 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11872 rb_define_alias(rb_cMolEnumerable, "size", "length");
11873 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11874 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11876 /* class AtomRef */
11877 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11878 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11880 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11881 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11882 s_AtomAttrDefTable[i].id = rb_intern(buf);
11883 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11885 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11887 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11888 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11889 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11890 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11891 s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11892 rb_global_variable(&s_SetAtomAttrString);
11893 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11894 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11896 /* class Parameter */
11897 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11898 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11899 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11900 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11901 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11902 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11903 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11904 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11905 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11906 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11907 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11908 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11909 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11910 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11911 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11912 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11913 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11914 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11915 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11916 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11917 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11918 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11919 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11920 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11921 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11922 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11923 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11924 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11925 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11926 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11927 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11928 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11929 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11930 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11931 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11932 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11933 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11934 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11935 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11936 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11937 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11938 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11939 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11940 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11941 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11942 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11943 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11944 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11945 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11946 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11947 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11948 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11949 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11950 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11952 /* class ParEnumerable */
11953 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11954 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11955 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11956 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11957 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11958 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11959 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11960 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11961 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11962 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11963 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11965 /* class ParameterRef */
11966 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11967 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11969 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11970 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11971 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11972 if (s_ParameterAttrDefTable[i].symref != NULL)
11973 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11974 if (s_ParameterAttrDefTable[i].setter != NULL) {
11976 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11979 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11980 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11981 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11982 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11983 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11984 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11985 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11986 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11988 /* class MolbyError */
11989 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11991 /* module Kernel */
11992 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11993 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11994 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11995 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11996 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11997 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11998 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11999 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12000 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12001 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12002 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12003 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12004 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12005 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12006 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12007 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12008 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12009 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12010 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12011 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12012 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12013 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12014 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12015 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12016 rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12017 rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12018 rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12019 rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12020 rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12021 rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12024 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12026 s_ID_equal = rb_intern("==");
12027 g_RubyID_call = rb_intern("call");
12029 s_InitMOInfoKeys();
12031 /* Symbols for graphics */
12032 s_LineSym = ID2SYM(rb_intern("line"));
12033 s_PolySym = ID2SYM(rb_intern("poly"));
12034 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12035 s_ConeSym = ID2SYM(rb_intern("cone"));
12036 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12039 #pragma mark ====== Interface with RubyDialog class ======
12042 RubyDialogCallback_parentModule(void)
12044 return (RubyValue)rb_mMolby;
12047 #pragma mark ====== External functions ======
12049 static VALUE s_ruby_top_self = Qfalse;
12050 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12051 static VALUE s_ruby_export_local_variables = Qfalse;
12054 s_evalRubyScriptOnMoleculeSub(VALUE val)
12056 void **ptr = (void **)val;
12057 Molecule *mol = (Molecule *)ptr[1];
12058 VALUE sval, fnval, lnval, retval;
12061 /* Clear the error information (store in the history array if necessary) */
12062 sval = rb_errinfo();
12063 if (sval != Qnil) {
12064 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12065 rb_set_errinfo(Qnil);
12068 if (s_ruby_top_self == Qfalse) {
12069 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12071 if (s_ruby_get_binding_for_molecule == Qfalse) {
12073 "lambda { |_mol_, _bind_| \n"
12074 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12075 " _proc_.call(_mol_) } ";
12076 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12077 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12079 if (s_ruby_export_local_variables == Qfalse) {
12081 "lambda { |_bind_| \n"
12082 " # find local variables newly defined in _bind_ \n"
12083 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12084 " _a_.each { |_vsym_| \n"
12085 " _vname_ = _vsym_.to_s \n"
12086 " _vval_ = _bind_.eval(_vname_) \n"
12087 " # Define local variable \n"
12088 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12089 " # Then set value \n"
12090 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12093 s_ruby_export_local_variables = rb_eval_string(s2);
12094 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12096 if (ptr[2] == NULL) {
12098 /* String literal: we need to specify string encoding */
12099 #if defined(__WXMSW__)
12100 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12102 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12104 sval = Ruby_NewEncodedStringValue2(scr);
12106 fnval = Ruby_NewEncodedStringValue2("(eval)");
12107 lnval = INT2FIX(0);
12109 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12110 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12111 lnval = INT2FIX(1);
12113 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12115 VALUE mval = ValueFromMolecule(mol);
12116 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12118 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12120 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12126 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12130 VALUE save_interrupt_flag;
12131 /* char *save_ruby_sourcefile;
12132 int save_ruby_sourceline; */
12133 if (gMolbyIsCheckingInterrupt) {
12134 MolActionAlertRubyIsRunning();
12136 return (RubyValue)Qnil;
12139 args[0] = (void *)script;
12140 args[1] = (void *)mol;
12141 args[2] = (void *)fname;
12142 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12143 /* save_ruby_sourcefile = ruby_sourcefile;
12144 save_ruby_sourceline = ruby_sourceline; */
12145 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12146 if (*status != 0) {
12147 /* Is this 'exit' exception? */
12148 VALUE last_exception = rb_gv_get("$!");
12149 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12150 /* Capture exit and return the status value */
12151 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12153 rb_set_errinfo(Qnil);
12156 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12157 /* ruby_sourcefile = save_ruby_sourcefile;
12158 ruby_sourceline = save_ruby_sourceline; */
12165 Ruby_inspectValue(RubyValue value)
12168 static char buf[256];
12169 VALUE val = (VALUE)value;
12171 val = rb_protect(rb_inspect, val, &status);
12174 char *str = StringValuePtr(val);
12175 strncpy(buf, str, sizeof(buf) - 1);
12176 buf[sizeof(buf) - 1] = 0;
12178 snprintf(buf, sizeof(buf), "Error status = %d", status);
12184 Ruby_showValue(RubyValue value, char **outValueString)
12186 VALUE val = (VALUE)value;
12187 if (gMolbyIsCheckingInterrupt) {
12188 MolActionAlertRubyIsRunning();
12195 val = rb_protect(rb_inspect, val, &status);
12199 str = StringValuePtr(val);
12200 if (outValueString != NULL)
12201 *outValueString = strdup(str);
12202 MyAppCallback_showScriptMessage("%s", str);
12204 if (outValueString != NULL)
12205 *outValueString = NULL;
12211 Ruby_showError(int status)
12213 static const int tag_raise = 6;
12214 char *main_message = "Molby script error";
12215 char *msg = NULL, *msg2;
12216 VALUE val, backtrace;
12217 int interrupted = 0;
12218 int exit_status = -1;
12219 if (status == tag_raise) {
12220 VALUE errinfo = rb_errinfo();
12221 VALUE eclass = CLASS_OF(errinfo);
12222 if (eclass == rb_eInterrupt) {
12223 main_message = "Molby script interrupted";
12226 } else if (eclass == rb_eSystemExit) {
12227 main_message = "Molby script exit";
12229 val = rb_eval_string_protect("$!.status", &status);
12231 exit_status = NUM2INT(rb_Integer(val));
12232 asprintf(&msg, "Molby script exit with status %d", exit_status);
12234 asprintf(&msg, "Molby script exit with unknown status");
12239 if (exit_status != 0) {
12240 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12242 val = rb_eval_string_protect("$!.to_s", &status);
12244 msg = RSTRING_PTR(val);
12246 msg = "(message not available)";
12248 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12250 msg2 = strdup(msg);
12252 MyAppCallback_messageBox(msg2, main_message, 0, 3);
12254 if (interrupted == 2) {
12256 if (!gUseGUI && exit_status == 0)
12257 exit(0); // Capture exit(0) here and force exit
12262 /* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
12264 Molby_loadScript(const char *script, int from_file)
12269 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12271 rb_eval_string_protect(script, &status);
12277 Molby_getDescription(char **versionString, char **auxString)
12279 extern const char *gVersionString, *gCopyrightString;
12280 extern int gRevisionNumber;
12281 extern char *gLastBuildString;
12283 char *revisionString;
12284 if (gRevisionNumber > 0) {
12285 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12286 } else revisionString = "";
12288 asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12289 #if defined(__WXMSW__)
12290 #if TARGET_ARCH == 64
12298 gVersionString, revisionString, gCopyrightString, gLastBuildString);
12303 "ruby %s, http://www.ruby-lang.org/\n"
12305 "FFTW 3.3.2, http://www.fftw.org/\n"
12306 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12307 " and Massachusetts Institute of Technology",
12308 MyAppCallback_getGUIDescriptionString(),
12309 gRubyVersion, gRubyCopyright);
12313 "ruby %s, http://www.ruby-lang.org/\n"
12315 "FFTW 3.3.2, http://www.fftw.org/\n"
12316 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12317 " and Massachusetts Institute of Technology",
12318 gRubyVersion, gRubyCopyright);
12321 if (revisionString[0] != 0)
12322 free(revisionString);
12323 if (versionString != NULL)
12324 *versionString = s1;
12325 if (auxString != NULL)
12330 Molby_startup(const char *script, const char *dir)
12335 char *respath, *p, *wbuf;
12337 /* Get version/copyright string from Ruby interpreter */
12339 gRubyVersion = strdup(ruby_version);
12340 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12341 " ", /* Indent for displaying in About dialog */
12342 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12345 /* Read build and revision information for Molby */
12348 extern int gRevisionNumber;
12349 extern char *gLastBuildString;
12350 FILE *fp = fopen("../buildInfo.txt", "r");
12351 gLastBuildString = "";
12353 if (fgets(buf, sizeof(buf), fp) != NULL) {
12354 char *p1 = strchr(buf, '\"');
12355 char *p2 = strrchr(buf, '\"');
12356 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12357 memmove(buf, p1 + 1, p2 - p1 - 1);
12358 buf[p2 - p1 - 1] = 0;
12359 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12364 fp = fopen("../revisionInfo.txt", "r");
12365 gRevisionNumber = 0;
12367 if (fgets(buf, sizeof(buf), fp) != NULL) {
12368 gRevisionNumber = strtol(buf, NULL, 0);
12376 Molby_getDescription(&wbuf, &wbuf2);
12377 MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12382 /* Read atom display parameters */
12383 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12384 MyAppCallback_setConsoleColor(1);
12385 MyAppCallback_showScriptMessage("%s", wbuf);
12386 MyAppCallback_setConsoleColor(0);
12390 /* Read default parameters */
12391 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12392 if (wbuf != NULL) {
12393 MyAppCallback_setConsoleColor(1);
12394 MyAppCallback_showScriptMessage("%s", wbuf);
12395 MyAppCallback_setConsoleColor(0);
12399 /* Initialize Ruby interpreter */
12402 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12403 it causes rb_bug() (= fatal error) during ruby_init().
12404 As a workaround, these standard streams are reopend as
12406 freopen("NUL", "r", stdin);
12407 freopen("NUL", "w", stdout);
12408 freopen("NUL", "w", stderr);
12414 /* Initialize CP932/Windows-31J encodings */
12415 extern void Init_shift_jis(void), Init_windows_31j(void), Init_trans_japanese_sjis(void);
12416 extern int rb_enc_alias(const char *, const char *);
12418 Init_windows_31j();
12419 Init_trans_japanese_sjis();
12420 rb_enc_alias("CP932", "Windows-31J");
12423 #if defined(__WXMSW__)
12425 /* Set default external encoding */
12426 /* The following snippet is taken from encoding.c */
12427 extern void rb_enc_set_default_external(VALUE encoding);
12428 char cp[sizeof(int) * 8 / 3 + 22];
12431 snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12432 enc = rb_eval_string_protect(cp, &status);
12433 if (status == 0 && !NIL_P(enc)) {
12434 rb_enc_set_default_external(enc);
12439 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12441 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12442 ruby_incpush(libpath);
12446 ruby_script("Molby");
12448 /* Find the resource path (the parent directory of the given directory) */
12449 respath = strdup(dir);
12450 p = strrchr(respath, '/');
12451 if (p == NULL && PATH_SEPARATOR != '/')
12452 p = strrchr(respath, PATH_SEPARATOR);
12455 val = Ruby_NewFileStringValue(respath);
12456 rb_define_global_const("MolbyResourcePath", val);
12459 /* Define Molby classes */
12462 RubyDialogInitClass();
12464 rb_define_const(rb_mMolby, "ResourcePath", val);
12465 val = Ruby_NewFileStringValue(dir);
12466 rb_define_const(rb_mMolby, "ScriptPath", val);
12467 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12468 val = Ruby_NewFileStringValue(p);
12469 rb_define_const(rb_mMolby, "MbsfPath", val);
12472 p = MyAppCallback_getHomeDir();
12473 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12474 rb_define_const(rb_mMolby, "HomeDirectory", val);
12476 p = MyAppCallback_getDocumentHomeDir();
12477 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12478 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12482 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12484 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12487 /* Create objects for stdout and stderr */
12488 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12489 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12490 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12491 rb_gv_set("$stdout", val);
12492 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12493 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12494 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12495 rb_gv_set("$stderr", val);
12497 /* Create objects for stdin */
12498 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12499 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12500 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12501 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12502 rb_gv_set("$stdin", val);
12505 /* Global variable to hold error information */
12506 rb_define_variable("$backtrace", &gMolbyBacktrace);
12507 rb_define_variable("$error_history", &gMolbyErrorHistory);
12508 gMolbyErrorHistory = rb_ary_new();
12510 /* Global variables for script menus */
12511 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12512 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12513 gScriptMenuCommands = rb_ary_new();
12514 gScriptMenuEnablers = rb_ary_new();
12517 /* Register interrupt check code */
12518 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12519 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12520 s_SetIntervalTimer(0, 50);
12523 /* Read the startup script */
12524 if (script != NULL && script[0] != 0) {
12525 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12527 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12530 Ruby_showError(status);
12532 MyAppCallback_showScriptMessage("Done.\n");
12537 Molby_buildARGV(int argc, const char **argv)
12540 rb_ary_clear(rb_argv);
12541 for (i = 0; i < argc; i++) {
12542 VALUE arg = rb_tainted_str_new2(argv[i]);
12544 rb_ary_push(rb_argv, arg);